<commit_before>// Copyright 2013, Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package topo

import (
	"errors"
	"flag"
	"fmt"
	"time"

	log "github.com/golang/glog"
)

var (
	// ErrNodeExists is returned by functions to specify the
	// requested resource already exists.
	ErrNodeExists = errors.New("node already exists")

	// ErrNoNode is returned by functions to specify the requested
	// resource does not exist.
	ErrNoNode = errors.New("node doesn't exist")

	// ErrNotEmpty is returned by functions to specify a child of the
	// resource is still present and prevents the action from completing.
	ErrNotEmpty = errors.New("node not empty")

	// ErrTimeout is returned by functions that wait for a result
	// when the timeout value is reached.
	ErrTimeout = errors.New("deadline exceeded")

	// ErrInterrupted is returned by functions that wait for a result
	// when they are interrupted.
	ErrInterrupted = errors.New("interrupted")

	// ErrBadVersion is returned by an update function that
	// failed to update the data because the version was different
	ErrBadVersion = errors.New("bad node version")
)

// topo.Server is the interface used to talk to a persistent
// backend storage server and locking service.
//
// Zookeeper is a good example of this, and zktopo contains the
// implementation for this using zookeeper.
//
// Inside Google, we use Chubby.
type Server interface {
	// topo.Server management interface.
	Close()

	//
	// Cell management, global
	//

	// GetKnownCells returns the list of known cells running our processes.
	// It is possible to find all tablets in the entire system
	// by then calling GetTabletsByCell on every cell, for instance.
	// They shall be sorted.
	GetKnownCells() ([]string, error)

	//
	// Keyspace management, global.
	//

	// CreateKeyspace creates the given keyspace, assuming it doesn't exist
	// yet. Can return ErrNodeExists if it already exists.
	CreateKeyspace(keyspace string) error

	// GetKeyspaces returns the known keyspaces. They shall be sorted.
	GetKeyspaces() ([]string, error)

	// DeleteKeyspaceShards deletes all the shards in a keyspace.
	// Use with caution.
	DeleteKeyspaceShards(keyspace string) error

	//
	// Shard management, global.
	//

	// CreateShard creates an empty shard, assuming it doesn't exist
	// yet. The contents of the shard will be a new Shard{} object,
	// with KeyRange populated by the result of ValidateShardName().
	// Can return ErrNodeExists if it already exists.
	CreateShard(keyspace, shard string, value *Shard) error

	// UpdateShard unconditionnally updates the shard information
	// pointed at by si.keyspace / si.shard to the *si value.
	// This will only be called with a lock on the shard.
	// Can return ErrNoNode if the shard doesn't exist yet.
	UpdateShard(si *ShardInfo) error

	// ValidateShard performs routine checks on the shard.
	ValidateShard(keyspace, shard string) error

	// GetShard reads a shard and returns it.
	// Can return ErrNoNode
	GetShard(keyspace, shard string) (si *ShardInfo, err error)

	// GetShardNames returns the known shards in a keyspace.
	// Can return ErrNoNode if the keyspace wasn't created,
	// or if DeleteKeyspaceShards was called. They shall be sorted.
	GetShardNames(keyspace string) ([]string, error)

	//
	// Tablet management, per cell.
	//

	// CreateTablet creates the given tablet, assuming it doesn't exist
	// yet. It does *not* create the tablet replication paths.
	// Can return ErrNodeExists if it already exists.
	CreateTablet(tablet *Tablet) error

	// UpdateTablet updates a given tablet. The version is used
	// for atomic updates. UpdateTablet will return ErrNoNode if
	// the tablet doesn't exist and ErrBadVersion if the version
	// has changed.
	UpdateTablet(tablet *TabletInfo, existingVersion int64) (newVersion int64, err error)

	// UpdateTabletFields updates the current tablet record
	// with new values, independently of the version
	// Can return ErrNoNode if the tablet doesn't exist.
	UpdateTabletFields(tabletAlias TabletAlias, update func(*Tablet) error) error

	// DeleteTablet removes a tablet from the system.
	// We assume no RPC is currently running to it.
	// TODO(alainjobart) verify this assumption, link with RPC code.
	// Can return ErrNoNode if the tablet doesn't exist.
	DeleteTablet(alias TabletAlias) error

	// ValidateTablet performs routine checks on the tablet.
	ValidateTablet(alias TabletAlias) error

	// GetTablet returns the tablet data (includes the current version).
	// Can return ErrNoNode if the tablet doesn't exist.
	GetTablet(alias TabletAlias) (*TabletInfo, error)

	// GetTabletsByCell returns all the tablets in the given cell.
	// Can return ErrNoNode if no tablet was ever created in that cell.
	GetTabletsByCell(cell string) ([]TabletAlias, error)

	//
	// Replication graph management, global.
	//
	// Uses a path for replication, use "" to get the masters,
	// /master to get the slaves.
	//

	// GetReplicationPaths returns the replication paths for the parent path
	// - get the master(s): GetReplicationPaths(..., "")
	// - get the slaves: GetReplicationPaths(..., "/nyc-00020100")
	GetReplicationPaths(keyspace, shard, repPath string) ([]TabletAlias, error)

	// CreateReplicationPath creates a replication path.
	// Can return ErrNodeExists if it already exists.
	CreateReplicationPath(keyspace, shard, repPath string) error

	// DeleteReplicationPath removes a replication path.
	// Can returnErrNoNode if it doesn't exist.
	DeleteReplicationPath(keyspace, shard, repPath string) error

	//
	// Serving Graph management, per cell.
	//

	// GetSrvTabletTypesPerShard returns the existing serving types
	// for a shard.
	// Can return ErrNoNode.
	GetSrvTabletTypesPerShard(cell, keyspace, shard string) ([]TabletType, error)

	// UpdateSrvTabletType updates the serving records for a cell,
	// keyspace, shard, tabletType.
	UpdateSrvTabletType(cell, keyspace, shard string, tabletType TabletType, addrs *VtnsAddrs) error

	// GetSrvTabletType returns the VtnsAddrs list of serving addresses
	// for a TabletType inside a shard.
	// Can return ErrNoNode.
	GetSrvTabletType(cell, keyspace, shard string, tabletType TabletType) (*VtnsAddrs, error)

	// DeleteSrvTabletType deletes the serving records for a cell,
	// keyspace, shard, tabletType.
	// Can return ErrNoNode.
	DeleteSrvTabletType(cell, keyspace, shard string, tabletType TabletType) error

	// UpdateSrvShard updates the serving records for a cell,
	// keyspace, shard.
	UpdateSrvShard(cell, keyspace, shard string, srvShard *SrvShard) error

	// GetSrvShard reads a SrvShard record.
	// Can return ErrNoNode.
	GetSrvShard(cell, keyspace, shard string) (*SrvShard, error)

	// UpdateSrvKeyspace updates the serving records for a cell, keyspace.
	UpdateSrvKeyspace(cell, keyspace string, srvKeyspace *SrvKeyspace) error

	// GetSrvKeyspace reads a SrvKeyspace record.
	// Can return ErrNoNode.
	GetSrvKeyspace(cell, keyspace string) (*SrvKeyspace, error)

	// GetSrvKeyspaceNames returns the list of visible Keyspaces
	// in this cell. They shall be sorted.
	GetSrvKeyspaceNames(cell string) ([]string, error)

	// UpdateTabletEndpoint updates a single tablet record in the
	// already computed serving graph. The update has to be somewhat
	// atomic, so it requires Server intrisic knowledge.
	// If the node doesn't exist, it is not updated, this is not an error.
	UpdateTabletEndpoint(cell, keyspace, shard string, tabletType TabletType, addr *VtnsAddr) error

	//
	// Keyspace and Shard locks for actions, global.
	//

	// LockKeyspaceForAction locks the keyspace in order to
	// perform the action described by contents. It will wait for
	// the lock for at most duration. The wait can be interrupted
	// if the interrupted channel is closed. It returns the lock
	// path.
	// Can return ErrTimeout or ErrInterrupted
	LockKeyspaceForAction(keyspace, contents string, timeout time.Duration, interrupted chan struct{}) (string, error)

	// UnlockKeyspaceForAction unlocks a keyspace.
	UnlockKeyspaceForAction(keyspace, lockPath, results string) error

	// LockShardForAction locks the shard in order to
	// perform the action described by contents. It will wait for
	// the lock for at most duration. The wait can be interrupted
	// if the interrupted channel is closed. It returns the lock
	// path.
	// Can return ErrTimeout or ErrInterrupted
	LockShardForAction(keyspace, shard, contents string, timeout time.Duration, interrupted chan struct{}) (string, error)

	// UnlockShardForAction unlocks a shard.
	UnlockShardForAction(keyspace, shard, lockPath, results string) error

	//
	// Remote Tablet Actions, local cell.
	//

	// WriteTabletAction initiates a remote action on the tablet.
	// Actions are queued up, and executed sequentially.  An
	// action is identified by the returned string, actionPath.
	WriteTabletAction(tabletAlias TabletAlias, contents string) (string, error)

	// WaitForTabletAction waits for a tablet action to complete. It
	// will wait for the result for at most duration. The wait can
	// be interrupted if the interrupted channel is closed.
	// Can return ErrTimeout or ErrInterrupted
	WaitForTabletAction(actionPath string, waitTime time.Duration, interrupted chan struct{}) (string, error)

	// PurgeTabletActions removes all queued actions for a tablet.
	// This might break the locking mechanism of the remote action
	// queue, used with caution.
	PurgeTabletActions(tabletAlias TabletAlias, canBePurged func(data string) bool) error

	//
	// Supporting the local agent process, local cell.
	//

	// ValidateTabletActions checks a tablet can execute remote
	// actions.
	ValidateTabletActions(tabletAlias TabletAlias) error

	// CreateTabletPidNode will keep a PID node up to date with
	// this tablet's current PID, until 'done' is closed.
	CreateTabletPidNode(tabletAlias TabletAlias, contents string, done chan struct{}) error

	// ValidateTabletPidNode makes sure a PID file exists for the tablet
	ValidateTabletPidNode(tabletAlias TabletAlias) error

	// GetSubprocessFlags returns the flags required to run a
	// subprocess that uses the same Server parameters as
	// this process.
	GetSubprocessFlags() []string

	// ActionEventLoop is the main loop for the action processing engine.
	// It will feed events to the dispatchAction callback.
	// If dispatchAction returns an error, we'll wait a bit before trying
	// again.
	// If 'done' is closed, the loop returns.
	ActionEventLoop(tabletAlias TabletAlias, dispatchAction func(actionPath, data string) error, done chan struct{})

	// ReadTabletActionPath reads the actionPath and returns the
	// associated TabletAlias, the data (originally written by
	// WriteTabletAction), and its version
	ReadTabletActionPath(actionPath string) (TabletAlias, string, int64, error)

	// UpdateTabletAction updates the actionPath with the new data.
	// version is the current version we're expecting. Use -1 to set
	// any version.
	// Can return ErrBadVersion.
	UpdateTabletAction(actionPath, data string, version int64) error

	// StoreTabletActionResponse stores the data for the response.
	// This will not unblock the caller yet.
	StoreTabletActionResponse(actionPath, data string) error

	// UnblockTabletAction will let the client continue.
	// StoreTabletActionResponse must have been called already.
	UnblockTabletAction(actionPath string) error
}

// Registry for Server implementations.
var serverImpls map[string]Server = make(map[string]Server)

// Which implementation to use
var topoImplementation = flag.String("topo_implementation", "zookeeper", "The topology implementation to use.")

// RegisterServer adds an implementation for a Server.
// If an implementation with that name already exists, panics.
// Call this in the 'init' function in your module.
func RegisterServer(name string, ts Server) {
	if serverImpls[name] != nil {
		panic(fmt.Errorf("Duplicate topo.Server registration for %v", name))
	}
	serverImpls[name] = ts
}

// Returns a specific Server by name, or nil.
func GetServerByName(name string) Server {
	return serverImpls[name]
}

// Returns 'our' Server:
// - If only one is registered, that's the one.
// - If more than one are registered, use the 'topo_implementation' flag
//   (which defaults to zookeeper).
// - Then panics.
func GetServer() Server {
	if len(serverImpls) == 1 {
		for name, ts := range serverImpls {
			log.V(6).Infof("Using only topo.Server: %v", name)
			return ts
		}
	}

	result := serverImpls[*topoImplementation]
	if result == nil {
		panic(fmt.Errorf("No topo.Server named %v", *topoImplementation))
	}
	log.V(6).Infof("Using topo.Server: %v", *topoImplementation)
	return result
}

// Close all registered Server.
func CloseServers() {
	for name, ts := range serverImpls {
		log.V(6).Infof("Closing topo.Server: %v", name)
		ts.Close()
	}
}
<commit_msg>Fixing comments.<commit_after>// Copyright 2013, Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package topo

import (
	"errors"
	"flag"
	"fmt"
	"time"

	log "github.com/golang/glog"
)

var (
	// ErrNodeExists is returned by functions to specify the
	// requested resource already exists.
	ErrNodeExists = errors.New("node already exists")

	// ErrNoNode is returned by functions to specify the requested
	// resource does not exist.
	ErrNoNode = errors.New("node doesn't exist")

	// ErrNotEmpty is returned by functions to specify a child of the
	// resource is still present and prevents the action from completing.
	ErrNotEmpty = errors.New("node not empty")

	// ErrTimeout is returned by functions that wait for a result
	// when the timeout value is reached.
	ErrTimeout = errors.New("deadline exceeded")

	// ErrInterrupted is returned by functions that wait for a result
	// when they are interrupted.
	ErrInterrupted = errors.New("interrupted")

	// ErrBadVersion is returned by an update function that
	// failed to update the data because the version was different
	ErrBadVersion = errors.New("bad node version")
)

// topo.Server is the interface used to talk to a persistent
// backend storage server and locking service.
//
// Zookeeper is a good example of this, and zktopo contains the
// implementation for this using zookeeper.
//
// Inside Google, we use Chubby.
type Server interface {
	// topo.Server management interface.
	Close()

	//
	// Cell management, global
	//

	// GetKnownCells returns the list of known cells running our processes.
	// It is possible to find all tablets in the entire system
	// by then calling GetTabletsByCell on every cell, for instance.
	// They shall be sorted.
	GetKnownCells() ([]string, error)

	//
	// Keyspace management, global.
	//

	// CreateKeyspace creates the given keyspace, assuming it doesn't exist
	// yet. Can return ErrNodeExists if it already exists.
	CreateKeyspace(keyspace string) error

	// GetKeyspaces returns the known keyspaces. They shall be sorted.
	GetKeyspaces() ([]string, error)

	// DeleteKeyspaceShards deletes all the shards in a keyspace.
	// Use with caution.
	DeleteKeyspaceShards(keyspace string) error

	//
	// Shard management, global.
	//

	// CreateShard creates an empty shard, assuming it doesn't exist
	// yet. The contents of the shard will be a new Shard{} object,
	// with KeyRange populated by the result of ValidateShardName().
	// Can return ErrNodeExists if it already exists.
	CreateShard(keyspace, shard string, value *Shard) error

	// UpdateShard unconditionnally updates the shard information
	// pointed at by si.keyspace / si.shard to the *si value.
	// This will only be called with a lock on the shard.
	// Can return ErrNoNode if the shard doesn't exist yet.
	UpdateShard(si *ShardInfo) error

	// ValidateShard performs routine checks on the shard.
	ValidateShard(keyspace, shard string) error

	// GetShard reads a shard and returns it.
	// Can return ErrNoNode
	GetShard(keyspace, shard string) (si *ShardInfo, err error)

	// GetShardNames returns the known shards in a keyspace.
	// Can return ErrNoNode if the keyspace wasn't created,
	// or if DeleteKeyspaceShards was called. They shall be sorted.
	GetShardNames(keyspace string) ([]string, error)

	//
	// Tablet management, per cell.
	//

	// CreateTablet creates the given tablet, assuming it doesn't exist
	// yet. It does *not* create the tablet replication paths.
	// Can return ErrNodeExists if it already exists.
	CreateTablet(tablet *Tablet) error

	// UpdateTablet updates a given tablet. The version is used
	// for atomic updates. UpdateTablet will return ErrNoNode if
	// the tablet doesn't exist and ErrBadVersion if the version
	// has changed.
	UpdateTablet(tablet *TabletInfo, existingVersion int64) (newVersion int64, err error)

	// UpdateTabletFields updates the current tablet record
	// with new values, independently of the version
	// Can return ErrNoNode if the tablet doesn't exist.
	UpdateTabletFields(tabletAlias TabletAlias, update func(*Tablet) error) error

	// DeleteTablet removes a tablet from the system.
	// We assume no RPC is currently running to it.
	// TODO(alainjobart) verify this assumption, link with RPC code.
	// Can return ErrNoNode if the tablet doesn't exist.
	DeleteTablet(alias TabletAlias) error

	// ValidateTablet performs routine checks on the tablet.
	ValidateTablet(alias TabletAlias) error

	// GetTablet returns the tablet data (includes the current version).
	// Can return ErrNoNode if the tablet doesn't exist.
	GetTablet(alias TabletAlias) (*TabletInfo, error)

	// GetTabletsByCell returns all the tablets in the given cell.
	// Can return ErrNoNode if no tablet was ever created in that cell.
	GetTabletsByCell(cell string) ([]TabletAlias, error)

	//
	// Replication graph management, global.
	//
	// Uses a path for replication, use "" to get the masters,
	// /master to get the slaves.
	//

	// GetReplicationPaths returns the replication paths for the parent path
	// - get the master(s): GetReplicationPaths(..., "")
	// - get the slaves: GetReplicationPaths(..., "/nyc-00020100")
	GetReplicationPaths(keyspace, shard, repPath string) ([]TabletAlias, error)

	// CreateReplicationPath creates a replication path.
	// Can return ErrNodeExists if it already exists.
	CreateReplicationPath(keyspace, shard, repPath string) error

	// DeleteReplicationPath removes a replication path.
	// Can returnErrNoNode if it doesn't exist.
	DeleteReplicationPath(keyspace, shard, repPath string) error

	//
	// Serving Graph management, per cell.
	//

	// GetSrvTabletTypesPerShard returns the existing serving types
	// for a shard.
	// Can return ErrNoNode.
	GetSrvTabletTypesPerShard(cell, keyspace, shard string) ([]TabletType, error)

	// UpdateSrvTabletType updates the serving records for a cell,
	// keyspace, shard, tabletType.
	UpdateSrvTabletType(cell, keyspace, shard string, tabletType TabletType, addrs *VtnsAddrs) error

	// GetSrvTabletType returns the VtnsAddrs list of serving addresses
	// for a TabletType inside a shard.
	// Can return ErrNoNode.
	GetSrvTabletType(cell, keyspace, shard string, tabletType TabletType) (*VtnsAddrs, error)

	// DeleteSrvTabletType deletes the serving records for a cell,
	// keyspace, shard, tabletType.
	// Can return ErrNoNode.
	DeleteSrvTabletType(cell, keyspace, shard string, tabletType TabletType) error

	// UpdateSrvShard updates the serving records for a cell,
	// keyspace, shard.
	UpdateSrvShard(cell, keyspace, shard string, srvShard *SrvShard) error

	// GetSrvShard reads a SrvShard record.
	// Can return ErrNoNode.
	GetSrvShard(cell, keyspace, shard string) (*SrvShard, error)

	// UpdateSrvKeyspace updates the serving records for a cell, keyspace.
	UpdateSrvKeyspace(cell, keyspace string, srvKeyspace *SrvKeyspace) error

	// GetSrvKeyspace reads a SrvKeyspace record.
	// Can return ErrNoNode.
	GetSrvKeyspace(cell, keyspace string) (*SrvKeyspace, error)

	// GetSrvKeyspaceNames returns the list of visible Keyspaces
	// in this cell. They shall be sorted.
	GetSrvKeyspaceNames(cell string) ([]string, error)

	// UpdateTabletEndpoint updates a single tablet record in the
	// already computed serving graph. The update has to be somewhat
	// atomic, so it requires Server intrisic knowledge.
	// If the node doesn't exist, it is not updated, this is not an error.
	UpdateTabletEndpoint(cell, keyspace, shard string, tabletType TabletType, addr *VtnsAddr) error

	//
	// Keyspace and Shard locks for actions, global.
	//

	// LockKeyspaceForAction locks the keyspace in order to
	// perform the action described by contents. It will wait for
	// the lock for at most duration. The wait can be interrupted
	// if the interrupted channel is closed. It returns the lock
	// path.
	// Can return ErrTimeout or ErrInterrupted
	LockKeyspaceForAction(keyspace, contents string, timeout time.Duration, interrupted chan struct{}) (string, error)

	// UnlockKeyspaceForAction unlocks a keyspace.
	UnlockKeyspaceForAction(keyspace, lockPath, results string) error

	// LockShardForAction locks the shard in order to
	// perform the action described by contents. It will wait for
	// the lock for at most duration. The wait can be interrupted
	// if the interrupted channel is closed. It returns the lock
	// path.
	// Can return ErrTimeout or ErrInterrupted
	LockShardForAction(keyspace, shard, contents string, timeout time.Duration, interrupted chan struct{}) (string, error)

	// UnlockShardForAction unlocks a shard.
	UnlockShardForAction(keyspace, shard, lockPath, results string) error

	//
	// Remote Tablet Actions, local cell.
	//

	// WriteTabletAction initiates a remote action on the tablet.
	// Actions are queued up, and executed sequentially.  An
	// action is identified by the returned string, actionPath.
	WriteTabletAction(tabletAlias TabletAlias, contents string) (string, error)

	// WaitForTabletAction waits for a tablet action to complete. It
	// will wait for the result for at most duration. The wait can
	// be interrupted if the interrupted channel is closed.
	// Can return ErrTimeout or ErrInterrupted
	WaitForTabletAction(actionPath string, waitTime time.Duration, interrupted chan struct{}) (string, error)

	// PurgeTabletActions removes all queued actions for a tablet.
	// This might break the locking mechanism of the remote action
	// queue, used with caution.
	PurgeTabletActions(tabletAlias TabletAlias, canBePurged func(data string) bool) error

	//
	// Supporting the local agent process, local cell.
	//

	// ValidateTabletActions checks a tablet can execute remote
	// actions.
	ValidateTabletActions(tabletAlias TabletAlias) error

	// CreateTabletPidNode will keep a PID node up to date with
	// this tablet's current PID, until 'done' is closed.
	CreateTabletPidNode(tabletAlias TabletAlias, contents string, done chan struct{}) error

	// ValidateTabletPidNode makes sure a PID file exists for the tablet
	ValidateTabletPidNode(tabletAlias TabletAlias) error

	// GetSubprocessFlags returns the flags required to run a
	// subprocess that uses the same Server parameters as
	// this process.
	GetSubprocessFlags() []string

	// ActionEventLoop is the main loop for the action processing engine.
	// It will feed events to the dispatchAction callback.
	// If dispatchAction returns an error, we'll wait a bit before trying
	// again.
	// If 'done' is closed, the loop returns.
	ActionEventLoop(tabletAlias TabletAlias, dispatchAction func(actionPath, data string) error, done chan struct{})

	// ReadTabletActionPath reads the actionPath and returns the
	// associated TabletAlias, the data (originally written by
	// WriteTabletAction), and its version
	ReadTabletActionPath(actionPath string) (TabletAlias, string, int64, error)

	// UpdateTabletAction updates the actionPath with the new data.
	// version is the current version we're expecting. Use -1 to set
	// any version.
	// Can return ErrBadVersion.
	UpdateTabletAction(actionPath, data string, version int64) error

	// StoreTabletActionResponse stores the data for the response.
	// This will not unblock the caller yet.
	StoreTabletActionResponse(actionPath, data string) error

	// UnblockTabletAction will let the client continue.
	// StoreTabletActionResponse must have been called already.
	UnblockTabletAction(actionPath string) error
}

// Registry for Server implementations.
var serverImpls map[string]Server = make(map[string]Server)

// Which implementation to use
var topoImplementation = flag.String("topo_implementation", "zookeeper", "the topology implementation to use")

// RegisterServer adds an implementation for a Server.
// If an implementation with that name already exists, panics.
// Call this in the 'init' function in your module.
func RegisterServer(name string, ts Server) {
	if serverImpls[name] != nil {
		panic(fmt.Errorf("Duplicate topo.Server registration for %v", name))
	}
	serverImpls[name] = ts
}

// Returns a specific Server by name, or nil.
func GetServerByName(name string) Server {
	return serverImpls[name]
}

// GetServer returns 'our' Server, going down this list:
// - If only one is registered, that's the one.
// - If more than one are registered, use the 'topo_implementation' flag
//   (which defaults to zookeeper).
// - Then panics.
func GetServer() Server {
	if len(serverImpls) == 1 {
		for name, ts := range serverImpls {
			log.V(6).Infof("Using only topo.Server: %v", name)
			return ts
		}
	}

	result := serverImpls[*topoImplementation]
	if result == nil {
		panic(fmt.Errorf("No topo.Server named %v", *topoImplementation))
	}
	log.V(6).Infof("Using topo.Server: %v", *topoImplementation)
	return result
}

// Close all registered Server.
func CloseServers() {
	for name, ts := range serverImpls {
		log.V(6).Infof("Closing topo.Server: %v", name)
		ts.Close()
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os/exec"
	"time"

	pbb "github.com/brotherlogic/buildserver/proto"
	pb "github.com/brotherlogic/gobuildslave/proto"
	"github.com/brotherlogic/goserver/utils"
	pbt "github.com/brotherlogic/tracer/proto"
	"golang.org/x/net/context"
)

const (
	pendWait = time.Minute
)

func (s *Server) runTransition(ctx context.Context, job *pb.JobAssignment) {
	utils.SendTrace(ctx, fmt.Sprintf("run_transition_%v", job.State), time.Now(), pbt.Milestone_MARKER, job.Job.Name)
	stState := job.State
	switch job.State {
	case pb.State_ACKNOWLEDGED:
		key := s.scheduleBuild(ctx, job.Job)
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("SCHED: %v @ %v", key, time.Now())
		s.stateMutex.Unlock()
		if !job.Job.Bootstrap {
			if key != "" {
				job.Server = s.Registry.Identifier
				job.State = pb.State_BUILT
				job.RunningVersion = key
			}
		} else {
			job.CommandKey = key
			job.State = pb.State_BUILDING
			job.Server = s.Registry.Identifier
		}
	case pb.State_BUILDING:
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("BUILD(%v): %v", job.CommandKey, s.scheduler.getState(job.CommandKey))
		s.stateMutex.Unlock()
		if s.taskComplete(job.CommandKey) {
			job.State = pb.State_BUILT
		}
	case pb.State_BUILT:
		output := s.scheduler.getOutput(job.CommandKey)
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("BUILT(%v): (%v): %v", job.CommandKey, len(output), output)
		s.stateMutex.Unlock()
		if job.Job.Bootstrap && len(output) > 0 {
			if job.BuildFail == 5 {
				s.deliverCrashReport(ctx, job, output)
				job.BuildFail = 0
			}
			job.BuildFail++
			job.State = pb.State_DIED
		} else {
			job.BuildFail = 0
			key := s.scheduleRun(job.Job)
			job.CommandKey = key
			job.StartTime = time.Now().Unix()
			job.State = pb.State_PENDING
			if _, ok := s.pendingMap[time.Now().Weekday()]; !ok {
				s.pendingMap[time.Now().Weekday()] = make(map[string]int)
			}
			s.pendingMap[time.Now().Weekday()][job.Job.Name]++
		}
	case pb.State_PENDING:
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("OUTPUT = %v", s.scheduler.getOutput(job.CommandKey))
		s.stateMutex.Unlock()
		if time.Now().Add(-time.Minute).Unix() > job.StartTime {
			job.State = pb.State_RUNNING
		}
	case pb.State_RUNNING:
		output := s.scheduler.getOutput(job.CommandKey)
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("ROUTPUT = %v, %v", s.scheduler.getOutput(job.CommandKey), s.scheduler.getStatus(job.CommandKey))
		job.Status = s.scheduler.getStatus(job.CommandKey)
		s.stateMutex.Unlock()
		if s.taskComplete(job.CommandKey) {
			s.stateMutex.Lock()
			s.stateMap[job.Job.Name] = fmt.Sprintf("COMPLETE = %v", output)
			s.stateMutex.Unlock()
			s.deliverCrashReport(ctx, job, output)
			job.State = pb.State_DIED
		}

		err := s.discover.discover(job.Job.Name, s.Registry.Identifier)
		if err != nil {
			if job.DiscoverCount > 30 {
				s.RaiseIssue(ctx, "Cannot Discover Running Server", fmt.Sprintf("%v on %v is not discoverable, despite running (%v) the output says %v", job.Job.Name, s.Registry.Identifier, err, output), false)
			}
			job.DiscoverCount++
		} else {
			job.DiscoverCount = 0
		}

		// Restart this job if we need to
		if !job.Job.Bootstrap {
			version, err := s.getVersion(ctx, job.Job)
			if err == nil && version.Version != job.RunningVersion {
				s.versions[job.Job.Name] = version
				s.stateMutex.Lock()
				s.stateMap[job.Job.Name] = fmt.Sprintf("VERSION_MISMATCH = %v,%v", version, job.RunningVersion)
				s.stateMutex.Unlock()
				s.scheduler.killJob(job.CommandKey)
				job.State = pb.State_ACKNOWLEDGED
			}
		}
	case pb.State_DIED:
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("DIED %v", job.CommandKey)
		s.stateMutex.Unlock()
		s.scheduler.removeJob(job.CommandKey)
		job.State = pb.State_ACKNOWLEDGED
	}

	if job.State != stState {
		s.stateTime[job.Job.Name] = time.Now()
	}

	if job.State == pb.State_DIED {
	}

	utils.SendTrace(ctx, fmt.Sprintf("end_transition_%v_%v", job.State, stState), time.Now(), pbt.Milestone_MARKER, job.Job.Name)
	utils.SendTrace(ctx, fmt.Sprintf("end_transition_func_%v", job.State), time.Now(), pbt.Milestone_MARKER, job.Job.Name)
}

type translator interface {
	build(job *pb.Job) *exec.Cmd
	run(job *pb.Job) *exec.Cmd
}

type checker interface {
	isAlive(ctx context.Context, job *pb.JobAssignment) bool
}

func (s *Server) getVersion(ctx context.Context, job *pb.Job) (*pbb.Version, error) {
	versions, err := s.builder.build(ctx, job)
	if err != nil {
		return &pbb.Version{}, err
	}

	if len(versions) == 0 {
		return &pbb.Version{}, nil
	}

	return versions[0], nil

}

// scheduleBuild builds out the job, returning the current version
func (s *Server) scheduleBuild(ctx context.Context, job *pb.Job) string {
	utils.SendTrace(ctx, fmt.Sprintf("schedule_build_%v", job.Bootstrap), time.Now(), pbt.Milestone_MARKER, job.Name)
	if job.Bootstrap {
		c := s.translator.build(job)
		return s.scheduler.Schedule(&rCommand{command: c})
	}

	versions, err := s.builder.build(ctx, job)

	s.lastCopyStatus = fmt.Sprintf("%v", err)
	if len(versions) == 0 {
		s.Log(fmt.Sprintf("No versions for %v because %v", job.Name, err))
		s.stateMutex.Lock()
		s.stateMap[job.Name] = fmt.Sprintf("No Versions: %v", err)
		s.stateMutex.Unlock()
		return ""
	}

	t := time.Now()
	err = s.builder.copy(ctx, versions[0])
	s.lastCopyTime = time.Now().Sub(t)
	s.lastCopyStatus = fmt.Sprintf("%v", err)
	if err != nil {
		s.stateMutex.Lock()
		s.stateMap[job.Name] = fmt.Sprintf("Copy fail (%v) -> %v", time.Now().Sub(t), err)
		s.stateMutex.Unlock()
		return ""
	}
	s.stateMutex.Lock()
	s.stateMap[job.Name] = fmt.Sprintf("Found version %v", versions[0].Version)
	s.stateMutex.Unlock()
	return versions[0].Version
}

func (s *Server) scheduleRun(job *pb.Job) string {
	c := s.translator.run(job)
	return s.scheduler.Schedule(&rCommand{command: c})
}

func (s *Server) taskComplete(key string) bool {
	return s.scheduler.schedulerComplete(key)
}
<commit_msg>Saves the version alongside the binary. This closes #360<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"os/exec"
	"time"

	pbb "github.com/brotherlogic/buildserver/proto"
	pb "github.com/brotherlogic/gobuildslave/proto"
	"github.com/brotherlogic/goserver/utils"
	pbt "github.com/brotherlogic/tracer/proto"
	"github.com/golang/protobuf/proto"
	"golang.org/x/net/context"
)

const (
	pendWait = time.Minute
)

func (s *Server) runTransition(ctx context.Context, job *pb.JobAssignment) {
	utils.SendTrace(ctx, fmt.Sprintf("run_transition_%v", job.State), time.Now(), pbt.Milestone_MARKER, job.Job.Name)
	stState := job.State
	switch job.State {
	case pb.State_ACKNOWLEDGED:
		key := s.scheduleBuild(ctx, job.Job)
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("SCHED: %v @ %v", key, time.Now())
		s.stateMutex.Unlock()
		if !job.Job.Bootstrap {
			if key != "" {
				job.Server = s.Registry.Identifier
				job.State = pb.State_BUILT
				job.RunningVersion = key
			}
		} else {
			job.CommandKey = key
			job.State = pb.State_BUILDING
			job.Server = s.Registry.Identifier
		}
	case pb.State_BUILDING:
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("BUILD(%v): %v", job.CommandKey, s.scheduler.getState(job.CommandKey))
		s.stateMutex.Unlock()
		if s.taskComplete(job.CommandKey) {
			job.State = pb.State_BUILT
		}
	case pb.State_BUILT:
		output := s.scheduler.getOutput(job.CommandKey)
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("BUILT(%v): (%v): %v", job.CommandKey, len(output), output)
		s.stateMutex.Unlock()
		if job.Job.Bootstrap && len(output) > 0 {
			if job.BuildFail == 5 {
				s.deliverCrashReport(ctx, job, output)
				job.BuildFail = 0
			}
			job.BuildFail++
			job.State = pb.State_DIED
		} else {
			job.BuildFail = 0
			key := s.scheduleRun(job.Job)
			job.CommandKey = key
			job.StartTime = time.Now().Unix()
			job.State = pb.State_PENDING
			if _, ok := s.pendingMap[time.Now().Weekday()]; !ok {
				s.pendingMap[time.Now().Weekday()] = make(map[string]int)
			}
			s.pendingMap[time.Now().Weekday()][job.Job.Name]++
		}
	case pb.State_PENDING:
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("OUTPUT = %v", s.scheduler.getOutput(job.CommandKey))
		s.stateMutex.Unlock()
		if time.Now().Add(-time.Minute).Unix() > job.StartTime {
			job.State = pb.State_RUNNING
		}
	case pb.State_RUNNING:
		output := s.scheduler.getOutput(job.CommandKey)
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("ROUTPUT = %v, %v", s.scheduler.getOutput(job.CommandKey), s.scheduler.getStatus(job.CommandKey))
		job.Status = s.scheduler.getStatus(job.CommandKey)
		s.stateMutex.Unlock()
		if s.taskComplete(job.CommandKey) {
			s.stateMutex.Lock()
			s.stateMap[job.Job.Name] = fmt.Sprintf("COMPLETE = %v", output)
			s.stateMutex.Unlock()
			s.deliverCrashReport(ctx, job, output)
			job.State = pb.State_DIED
		}

		err := s.discover.discover(job.Job.Name, s.Registry.Identifier)
		if err != nil {
			if job.DiscoverCount > 30 {
				s.RaiseIssue(ctx, "Cannot Discover Running Server", fmt.Sprintf("%v on %v is not discoverable, despite running (%v) the output says %v", job.Job.Name, s.Registry.Identifier, err, output), false)
			}
			job.DiscoverCount++
		} else {
			job.DiscoverCount = 0
		}

		// Restart this job if we need to
		if !job.Job.Bootstrap {
			version, err := s.getVersion(ctx, job.Job)
			if err == nil && version.Version != job.RunningVersion {
				s.versions[job.Job.Name] = version
				s.stateMutex.Lock()
				s.stateMap[job.Job.Name] = fmt.Sprintf("VERSION_MISMATCH = %v,%v", version, job.RunningVersion)
				s.stateMutex.Unlock()
				s.scheduler.killJob(job.CommandKey)
				job.State = pb.State_ACKNOWLEDGED
			}
		}
	case pb.State_DIED:
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("DIED %v", job.CommandKey)
		s.stateMutex.Unlock()
		s.scheduler.removeJob(job.CommandKey)
		job.State = pb.State_ACKNOWLEDGED
	}

	if job.State != stState {
		s.stateTime[job.Job.Name] = time.Now()
	}

	if job.State == pb.State_DIED {
	}

	utils.SendTrace(ctx, fmt.Sprintf("end_transition_%v_%v", job.State, stState), time.Now(), pbt.Milestone_MARKER, job.Job.Name)
	utils.SendTrace(ctx, fmt.Sprintf("end_transition_func_%v", job.State), time.Now(), pbt.Milestone_MARKER, job.Job.Name)
}

type translator interface {
	build(job *pb.Job) *exec.Cmd
	run(job *pb.Job) *exec.Cmd
}

type checker interface {
	isAlive(ctx context.Context, job *pb.JobAssignment) bool
}

func (s *Server) getVersion(ctx context.Context, job *pb.Job) (*pbb.Version, error) {
	versions, err := s.builder.build(ctx, job)
	if err != nil {
		return &pbb.Version{}, err
	}

	if len(versions) == 0 {
		return &pbb.Version{}, nil
	}

	return versions[0], nil

}

// scheduleBuild builds out the job, returning the current version
func (s *Server) scheduleBuild(ctx context.Context, job *pb.Job) string {
	utils.SendTrace(ctx, fmt.Sprintf("schedule_build_%v", job.Bootstrap), time.Now(), pbt.Milestone_MARKER, job.Name)
	if job.Bootstrap {
		c := s.translator.build(job)
		return s.scheduler.Schedule(&rCommand{command: c})
	}

	versions, err := s.builder.build(ctx, job)

	s.lastCopyStatus = fmt.Sprintf("%v", err)
	if len(versions) == 0 {
		s.Log(fmt.Sprintf("No versions for %v because %v", job.Name, err))
		s.stateMutex.Lock()
		s.stateMap[job.Name] = fmt.Sprintf("No Versions: %v", err)
		s.stateMutex.Unlock()
		return ""
	}

	t := time.Now()
	err = s.builder.copy(ctx, versions[0])
	s.lastCopyTime = time.Now().Sub(t)
	s.lastCopyStatus = fmt.Sprintf("%v", err)
	if err != nil {
		s.stateMutex.Lock()
		s.stateMap[job.Name] = fmt.Sprintf("Copy fail (%v) -> %v", time.Now().Sub(t), err)
		s.stateMutex.Unlock()
		return ""
	}
	s.stateMutex.Lock()
	s.stateMap[job.Name] = fmt.Sprintf("Found version %v", versions[0].Version)
	s.stateMutex.Unlock()

	//Save the version file alongside the binary
	data, _ := proto.Marshal(versions[0])
	ioutil.WriteFile("/home/simon/gobuild/bin/"+job.Name+".version", data, 0644)

	return versions[0].Version
}

func (s *Server) scheduleRun(job *pb.Job) string {
	c := s.translator.run(job)
	return s.scheduler.Schedule(&rCommand{command: c})
}

func (s *Server) taskComplete(key string) bool {
	return s.scheduler.schedulerComplete(key)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"os/exec"
	"time"

	pbb "github.com/brotherlogic/buildserver/proto"
	pbfc "github.com/brotherlogic/filecopier/proto"
	pb "github.com/brotherlogic/gobuildslave/proto"
	"golang.org/x/net/context"
)

const (
	pendWait = time.Minute
)

func (s *Server) runTransition(ctx context.Context, job *pb.JobAssignment) {
	log.Printf("TRANSITION %v", job)
	startState := job.State
	switch job.State {
	case pb.State_ACKNOWLEDGED:
		key := s.scheduleBuild(ctx, job)
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("SCHED: %v @ %v", key, time.Now())
		s.stateMutex.Unlock()
		if !job.Job.Bootstrap {
			if key != "" {
				job.State = pb.State_BUILT
				job.RunningVersion = key
			} else {
				// Bootstrap this job since we don't have an initial version
				if job.Job.PartialBootstrap {
					job.Job.Bootstrap = true
				}
			}
		} else {
			job.CommandKey = key
			job.State = pb.State_BUILDING
		}
	case pb.State_BUILDING:
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("BUILD(%v): %v", job.CommandKey, s.scheduler.getState(job.CommandKey))
		s.stateMutex.Unlock()
		if s.taskComplete(job.CommandKey) {
			job.State = pb.State_BUILT
		}
	case pb.State_BUILT:
		output, _ := s.scheduler.getOutput(job.CommandKey)
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("BUILT(%v): (%v): %v", job.CommandKey, len(output), output)
		s.stateMutex.Unlock()
		if job.Job.Bootstrap && len(output) > 0 {
			if job.BuildFail == 5 {
				s.deliverCrashReport(ctx, job, output)
				job.BuildFail = 0
			}
			job.BuildFail++
			job.State = pb.State_DIED
		} else {
			job.BuildFail = 0
			key := s.scheduleRun(job.Job)
			job.CommandKey = key
			job.StartTime = time.Now().Unix()
			job.State = pb.State_PENDING
			if _, ok := s.pendingMap[time.Now().Weekday()]; !ok {
				s.pendingMap[time.Now().Weekday()] = make(map[string]int)
			}
			s.pendingMap[time.Now().Weekday()][job.Job.Name]++
		}
	case pb.State_PENDING:
		if job.Job.PartialBootstrap && job.Job.Bootstrap {
			job.Job.Bootstrap = false
		}
		s.stateMutex.Lock()
		out, _ := s.scheduler.getOutput(job.CommandKey)
		s.stateMap[job.Job.Name] = fmt.Sprintf("OUTPUT = %v", out)
		s.stateMutex.Unlock()
		if time.Now().Add(-time.Minute).Unix() > job.StartTime {
			job.State = pb.State_RUNNING
		}
	case pb.State_RUNNING:
		output, errout := s.scheduler.getOutput(job.CommandKey)
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("ROUTPUT = %v, %v", output, s.scheduler.getStatus(job.CommandKey))
		job.Status = s.scheduler.getStatus(job.CommandKey)
		s.stateMutex.Unlock()
		if len(job.CommandKey) > 0 && s.taskComplete(job.CommandKey) {
			s.stateMutex.Lock()
			s.stateMap[job.Job.Name] = fmt.Sprintf("COMPLETE = (%v, %v)", job, output)
			s.stateMutex.Unlock()
			s.deliverCrashReport(ctx, job, output)
			job.State = pb.State_DIED
		}

		if s.discover != nil && s.Registry != nil {
			port, err := s.discover.discover(job.Job.Name, s.Registry.Identifier)
			if err != nil {
				if job.DiscoverCount > 30 {
					output2, errout2 := s.scheduler.getErrOutput(job.CommandKey)
					s.RaiseIssue(ctx, "Cannot Discover Running Server", fmt.Sprintf("%v on %v is not discoverable, despite running (%v) the output says %v (%v), %v, %v", job.Job.Name, s.Registry.Identifier, err, output, errout, output2, errout2), false)
				}
				job.DiscoverCount++
			} else {
				job.Port = port
				job.DiscoverCount = 0
			}
		}

		// Restart this job if we need to
		if !job.Job.Bootstrap {
			if time.Now().Sub(time.Unix(job.LastVersionPull, 0)) > time.Minute*5 {
				version, err := s.getVersion(ctx, job.Job)
				job.LastVersionPull = time.Now().Unix()

				if err == nil && version.Version != job.RunningVersion {
					s.stateMutex.Lock()
					s.stateMap[job.Job.Name] = fmt.Sprintf("VERSION_MISMATCH = %v,%v", version, job.RunningVersion)
					s.stateMutex.Unlock()
					s.scheduler.killJob(job.CommandKey)
				}
			}
		}
	case pb.State_BRINK_OF_DEATH:
		if s.version.confirm(ctx, job.Job.Name) {
			s.scheduler.killJob(job.CommandKey)
			job.State = pb.State_ACKNOWLEDGED
		}
	case pb.State_DIED:
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("DIED %v", job.CommandKey)
		s.stateMutex.Unlock()
		s.scheduler.removeJob(job.CommandKey)
		job.State = pb.State_ACKNOWLEDGED
	}

	if job.State != startState {
		job.LastTransitionTime = time.Now().Unix()
	}
}

type translator interface {
	build(job *pb.Job) *exec.Cmd
	run(job *pb.Job) *exec.Cmd
}

type checker interface {
	isAlive(ctx context.Context, job *pb.JobAssignment) bool
}

func (s *Server) getVersion(ctx context.Context, job *pb.Job) (*pbb.Version, error) {
	version, err := s.builder.build(ctx, job)
	if err != nil {
		return &pbb.Version{}, err
	}

	return version, nil

}

func updateJob(err error, job *pb.JobAssignment, resp *pbfc.CopyResponse) {
	if err == nil {
		job.QueuePos = resp.IndexInQueue
	}

}

// scheduleBuild builds out the job, returning the current version
func (s *Server) scheduleBuild(ctx context.Context, job *pb.JobAssignment) string {
	if job.Job.Bootstrap {
		c := s.translator.build(job.Job)
		return s.scheduler.Schedule(&rCommand{command: c, base: job.Job.Name})
	}

	val, err := s.builder.build(ctx, job.Job)
	if err != nil {
		return ""
	}
	return val.Version
}

func (s *Server) scheduleRun(job *pb.Job) string {
	//Copy over any existing new versions
	key := s.scheduler.Schedule(&rCommand{command: exec.Command("mv", "$GOPATH/bin/"+job.GetName()+".new", "$GOPATH/bin/"+job.GetName()), base: job.Name})
	for !s.taskComplete(key) {
		time.Sleep(time.Second)
	}

	c := s.translator.run(job)
	return s.scheduler.Schedule(&rCommand{command: c, base: job.Name})
}

func (s *Server) taskComplete(key string) bool {
	return s.scheduler.schedulerComplete(key)
}
<commit_msg>Boing<commit_after>package main

import (
	"fmt"
	"log"
	"os/exec"
	"time"

	pbb "github.com/brotherlogic/buildserver/proto"
	pbfc "github.com/brotherlogic/filecopier/proto"
	pb "github.com/brotherlogic/gobuildslave/proto"
	"golang.org/x/net/context"
)

const (
	pendWait = time.Minute
)

func (s *Server) runTransition(ctx context.Context, job *pb.JobAssignment) {
	log.Printf("TRANSITION %v", job)
	startState := job.State
	switch job.State {
	case pb.State_ACKNOWLEDGED:
		key := s.scheduleBuild(ctx, job)
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("SCHED: %v @ %v", key, time.Now())
		s.stateMutex.Unlock()
		if !job.Job.Bootstrap {
			if key != "" {
				job.State = pb.State_BUILT
				job.RunningVersion = key
			} else {
				// Bootstrap this job since we don't have an initial version
				if job.Job.PartialBootstrap {
					job.Job.Bootstrap = true
				}
			}
		} else {
			job.CommandKey = key
			job.State = pb.State_BUILDING
		}
	case pb.State_BUILDING:
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("BUILD(%v): %v", job.CommandKey, s.scheduler.getState(job.CommandKey))
		s.stateMutex.Unlock()
		if s.taskComplete(job.CommandKey) {
			job.State = pb.State_BUILT
		}
	case pb.State_BUILT:
		output, _ := s.scheduler.getOutput(job.CommandKey)
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("BUILT(%v): (%v): %v", job.CommandKey, len(output), output)
		s.stateMutex.Unlock()
		if job.Job.Bootstrap && len(output) > 0 {
			if job.BuildFail == 5 {
				s.deliverCrashReport(ctx, job, output)
				job.BuildFail = 0
			}
			job.BuildFail++
			fmt.Sprintf("BUILD FAIL: %v", output)
			job.State = pb.State_DIED
		} else {
			job.BuildFail = 0
			key := s.scheduleRun(job.Job)
			job.CommandKey = key
			job.StartTime = time.Now().Unix()
			job.State = pb.State_PENDING
			if _, ok := s.pendingMap[time.Now().Weekday()]; !ok {
				s.pendingMap[time.Now().Weekday()] = make(map[string]int)
			}
			s.pendingMap[time.Now().Weekday()][job.Job.Name]++
		}
	case pb.State_PENDING:
		if job.Job.PartialBootstrap && job.Job.Bootstrap {
			job.Job.Bootstrap = false
		}
		s.stateMutex.Lock()
		out, _ := s.scheduler.getOutput(job.CommandKey)
		s.stateMap[job.Job.Name] = fmt.Sprintf("OUTPUT = %v", out)
		s.stateMutex.Unlock()
		if time.Now().Add(-time.Minute).Unix() > job.StartTime {
			job.State = pb.State_RUNNING
		}
	case pb.State_RUNNING:
		output, errout := s.scheduler.getOutput(job.CommandKey)
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("ROUTPUT = %v, %v", output, s.scheduler.getStatus(job.CommandKey))
		job.Status = s.scheduler.getStatus(job.CommandKey)
		s.stateMutex.Unlock()
		if len(job.CommandKey) > 0 && s.taskComplete(job.CommandKey) {
			s.stateMutex.Lock()
			s.stateMap[job.Job.Name] = fmt.Sprintf("COMPLETE = (%v, %v)", job, output)
			s.stateMutex.Unlock()
			s.deliverCrashReport(ctx, job, output)
			job.State = pb.State_DIED
		}

		if s.discover != nil && s.Registry != nil {
			port, err := s.discover.discover(job.Job.Name, s.Registry.Identifier)
			if err != nil {
				if job.DiscoverCount > 30 {
					output2, errout2 := s.scheduler.getErrOutput(job.CommandKey)
					s.RaiseIssue(ctx, "Cannot Discover Running Server", fmt.Sprintf("%v on %v is not discoverable, despite running (%v) the output says %v (%v), %v, %v", job.Job.Name, s.Registry.Identifier, err, output, errout, output2, errout2), false)
				}
				job.DiscoverCount++
			} else {
				job.Port = port
				job.DiscoverCount = 0
			}
		}

		// Restart this job if we need to
		if !job.Job.Bootstrap {
			if time.Now().Sub(time.Unix(job.LastVersionPull, 0)) > time.Minute*5 {
				version, err := s.getVersion(ctx, job.Job)
				job.LastVersionPull = time.Now().Unix()

				if err == nil && version.Version != job.RunningVersion {
					s.stateMutex.Lock()
					s.stateMap[job.Job.Name] = fmt.Sprintf("VERSION_MISMATCH = %v,%v", version, job.RunningVersion)
					s.stateMutex.Unlock()
					s.scheduler.killJob(job.CommandKey)
				}
			}
		}
	case pb.State_BRINK_OF_DEATH:
		if s.version.confirm(ctx, job.Job.Name) {
			s.scheduler.killJob(job.CommandKey)
			job.State = pb.State_ACKNOWLEDGED
		}
	case pb.State_DIED:
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("DIED %v", job.CommandKey)
		s.stateMutex.Unlock()
		s.scheduler.removeJob(job.CommandKey)
		job.State = pb.State_ACKNOWLEDGED
	}

	if job.State != startState {
		job.LastTransitionTime = time.Now().Unix()
	}
}

type translator interface {
	build(job *pb.Job) *exec.Cmd
	run(job *pb.Job) *exec.Cmd
}

type checker interface {
	isAlive(ctx context.Context, job *pb.JobAssignment) bool
}

func (s *Server) getVersion(ctx context.Context, job *pb.Job) (*pbb.Version, error) {
	version, err := s.builder.build(ctx, job)
	if err != nil {
		return &pbb.Version{}, err
	}

	return version, nil

}

func updateJob(err error, job *pb.JobAssignment, resp *pbfc.CopyResponse) {
	if err == nil {
		job.QueuePos = resp.IndexInQueue
	}

}

// scheduleBuild builds out the job, returning the current version
func (s *Server) scheduleBuild(ctx context.Context, job *pb.JobAssignment) string {
	if job.Job.Bootstrap {
		c := s.translator.build(job.Job)
		return s.scheduler.Schedule(&rCommand{command: c, base: job.Job.Name})
	}

	val, err := s.builder.build(ctx, job.Job)
	if err != nil {
		return ""
	}
	return val.Version
}

func (s *Server) scheduleRun(job *pb.Job) string {
	//Copy over any existing new versions
	key := s.scheduler.Schedule(&rCommand{command: exec.Command("mv", "$GOPATH/bin/"+job.GetName()+".new", "$GOPATH/bin/"+job.GetName()), base: job.Name})
	for !s.taskComplete(key) {
		time.Sleep(time.Second)
	}

	c := s.translator.run(job)
	return s.scheduler.Schedule(&rCommand{command: c, base: job.Name})
}

func (s *Server) taskComplete(key string) bool {
	return s.scheduler.schedulerComplete(key)
}
<|endoftext|>


<commit_before>package anidb

import (
	"encoding/gob"
	"github.com/Kovensky/go-anidb/udp"
	"log"
	"sync"
	"time"
)

func init() {
	gob.RegisterName("*github.com/Kovensky/go-anidb.banCache", &banCache{})
}

const banDuration = 30*time.Minute + 1*time.Second

type banCache struct{ time.Time }

func (c *banCache) Touch() {
	c.Time = time.Now()
}
func (c *banCache) IsStale() bool {
	return time.Now().Sub(c.Time) > banDuration
}

// Returns whether the last UDP API access returned a 555 BANNED message.
func Banned() bool {
	var banTime banCache
	cache.Get(&banTime, "banned")

	stale := banTime.IsStale()
	if stale {
		cache.Delete("banned")
	}
	return !stale
}

func setBanned() {
	cache.Set(&banCache{}, "banned")
}

type paramSet struct {
	cmd    string
	params paramMap
	ch     chan udpapi.APIReply
}

type udpWrap struct {
	*udpapi.AniDBUDP

	sendLock    sync.Mutex
	sendQueueCh chan paramSet

	credLock    sync.Mutex
	credentials *credentials
	connected   bool
}

func newUDPWrap() *udpWrap {
	u := &udpWrap{
		AniDBUDP:    udpapi.NewAniDBUDP(),
		sendQueueCh: make(chan paramSet, 10),
	}
	go u.sendQueue()
	return u
}

type paramMap udpapi.ParamMap // shortcut

type noauthAPIReply struct {
	udpapi.APIReply
}

func (r *noauthAPIReply) Code() int {
	return 501
}

func (r *noauthAPIReply) Text() string {
	return "LOGIN FIRST"
}

func (r *noauthAPIReply) Error() error {
	return &udpapi.APIError{Code: r.Code(), Desc: r.Text()}
}

type bannedAPIReply struct {
	udpapi.APIReply
}

func (r *bannedAPIReply) Code() int {
	return 555
}
func (r *bannedAPIReply) Text() string {
	return "BANNED"
}
func (r *bannedAPIReply) Error() error {
	return &udpapi.APIError{Code: r.Code(), Desc: r.Text()}
}

var bannedReply udpapi.APIReply = &bannedAPIReply{}

func logRequest(set paramSet) {
	switch set.cmd {
	case "AUTH":
		log.Printf("UDP>>> AUTH user=%s\n", set.params["user"])
	default:
		log.Printf("UDP>>> %s %s\n", set.cmd, udpapi.ParamMap(set.params).String())
	}
}

func logReply(reply udpapi.APIReply) {
	log.Printf("UDP<<< %d %s\n", reply.Code(), reply.Text())
}

func (udp *udpWrap) sendQueue() {
	initialWait := 6 * time.Second
	wait := initialWait
	for set := range udp.sendQueueCh {
	Retry:
		if Banned() {
			set.ch <- bannedReply
			close(set.ch)
			continue
		}

		logRequest(set)
		reply := <-udp.AniDBUDP.SendRecv(set.cmd, udpapi.ParamMap(set.params))

		if reply.Error() == udpapi.TimeoutError {
			// retry
			wait = (wait * 15) / 10
			if wait > time.Minute {
				wait = time.Minute
			}
			log.Printf("UDP--- Timeout; waiting %s before retry", wait)

			delete(set.params, "s")
			delete(set.params, "tag")

			time.Sleep(wait)
			goto Retry
		}
		logReply(reply)

		wait = initialWait

		switch reply.Code() {
		case 403, 501, 506: // not logged in, or session expired
			if r := udp.ReAuth(); r.Error() == nil {
				// retry

				delete(set.params, "s")
				delete(set.params, "tag")

				goto Retry
			}
		case 503, 504: // client library rejected
			panic(reply.Error())
		// 555: IP (and user, possibly client) temporarily banned
		// 601: Server down (treat the same as a ban)
		case 555, 601:
			setBanned()
		}
		set.ch <- reply
		close(set.ch)
	}
}

type errorReply struct {
	udpapi.APIReply
	err error
}

func (r *errorReply) Code() int {
	return 999
}
func (r *errorReply) Text() string {
	return r.err.Error()
}
func (r *errorReply) Error() error {
	return r.err
}

func (udp *udpWrap) SendRecv(cmd string, params paramMap) <-chan udpapi.APIReply {
	ch := make(chan udpapi.APIReply, 1)

	udp.sendLock.Lock()
	defer udp.sendLock.Unlock()

	if Banned() {
		ch <- bannedReply
		close(ch)
		return ch
	}

	if !udp.connected {
		if r := udp.ReAuth(); r.Error() != nil {
			ch <- r
			close(ch)
			return ch
		}
	}

	udp.sendQueueCh <- paramSet{
		cmd:    cmd,
		params: params,
		ch:     ch,
	}

	return ch
}
<commit_msg>anidb: Remove unused struct errorReply<commit_after>package anidb

import (
	"encoding/gob"
	"github.com/Kovensky/go-anidb/udp"
	"log"
	"sync"
	"time"
)

func init() {
	gob.RegisterName("*github.com/Kovensky/go-anidb.banCache", &banCache{})
}

const banDuration = 30*time.Minute + 1*time.Second

type banCache struct{ time.Time }

func (c *banCache) Touch() {
	c.Time = time.Now()
}
func (c *banCache) IsStale() bool {
	return time.Now().Sub(c.Time) > banDuration
}

// Returns whether the last UDP API access returned a 555 BANNED message.
func Banned() bool {
	var banTime banCache
	cache.Get(&banTime, "banned")

	stale := banTime.IsStale()
	if stale {
		cache.Delete("banned")
	}
	return !stale
}

func setBanned() {
	cache.Set(&banCache{}, "banned")
}

type paramSet struct {
	cmd    string
	params paramMap
	ch     chan udpapi.APIReply
}

type udpWrap struct {
	*udpapi.AniDBUDP

	sendLock    sync.Mutex
	sendQueueCh chan paramSet

	credLock    sync.Mutex
	credentials *credentials
	connected   bool
}

func newUDPWrap() *udpWrap {
	u := &udpWrap{
		AniDBUDP:    udpapi.NewAniDBUDP(),
		sendQueueCh: make(chan paramSet, 10),
	}
	go u.sendQueue()
	return u
}

type paramMap udpapi.ParamMap // shortcut

type noauthAPIReply struct {
	udpapi.APIReply
}

func (r *noauthAPIReply) Code() int {
	return 501
}

func (r *noauthAPIReply) Text() string {
	return "LOGIN FIRST"
}

func (r *noauthAPIReply) Error() error {
	return &udpapi.APIError{Code: r.Code(), Desc: r.Text()}
}

type bannedAPIReply struct {
	udpapi.APIReply
}

func (r *bannedAPIReply) Code() int {
	return 555
}
func (r *bannedAPIReply) Text() string {
	return "BANNED"
}
func (r *bannedAPIReply) Error() error {
	return &udpapi.APIError{Code: r.Code(), Desc: r.Text()}
}

var bannedReply udpapi.APIReply = &bannedAPIReply{}

func logRequest(set paramSet) {
	switch set.cmd {
	case "AUTH":
		log.Printf("UDP>>> AUTH user=%s\n", set.params["user"])
	default:
		log.Printf("UDP>>> %s %s\n", set.cmd, udpapi.ParamMap(set.params).String())
	}
}

func logReply(reply udpapi.APIReply) {
	log.Printf("UDP<<< %d %s\n", reply.Code(), reply.Text())
}

func (udp *udpWrap) sendQueue() {
	initialWait := 6 * time.Second
	wait := initialWait
	for set := range udp.sendQueueCh {
	Retry:
		if Banned() {
			set.ch <- bannedReply
			close(set.ch)
			continue
		}

		logRequest(set)
		reply := <-udp.AniDBUDP.SendRecv(set.cmd, udpapi.ParamMap(set.params))

		if reply.Error() == udpapi.TimeoutError {
			// retry
			wait = (wait * 15) / 10
			if wait > time.Minute {
				wait = time.Minute
			}
			log.Printf("UDP--- Timeout; waiting %s before retry", wait)

			delete(set.params, "s")
			delete(set.params, "tag")

			time.Sleep(wait)
			goto Retry
		}
		logReply(reply)

		wait = initialWait

		switch reply.Code() {
		case 403, 501, 506: // not logged in, or session expired
			if r := udp.ReAuth(); r.Error() == nil {
				// retry

				delete(set.params, "s")
				delete(set.params, "tag")

				goto Retry
			}
		case 503, 504: // client library rejected
			panic(reply.Error())
		// 555: IP (and user, possibly client) temporarily banned
		// 601: Server down (treat the same as a ban)
		case 555, 601:
			setBanned()
		}
		set.ch <- reply
		close(set.ch)
	}
}

func (udp *udpWrap) SendRecv(cmd string, params paramMap) <-chan udpapi.APIReply {
	ch := make(chan udpapi.APIReply, 1)

	udp.sendLock.Lock()
	defer udp.sendLock.Unlock()

	if Banned() {
		ch <- bannedReply
		close(ch)
		return ch
	}

	if !udp.connected {
		if r := udp.ReAuth(); r.Error() != nil {
			ch <- r
			close(ch)
			return ch
		}
	}

	udp.sendQueueCh <- paramSet{
		cmd:    cmd,
		params: params,
		ch:     ch,
	}

	return ch
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os/exec"
	"time"

	pbb "github.com/brotherlogic/buildserver/proto"
	pbfc "github.com/brotherlogic/filecopier/proto"
	pb "github.com/brotherlogic/gobuildslave/proto"
	"golang.org/x/net/context"
)

const (
	pendWait = time.Minute
)

func (s *Server) runTransition(ctx context.Context, job *pb.JobAssignment) {
	startState := job.State
	switch job.State {
	case pb.State_ACKNOWLEDGED:
		key := s.scheduleBuild(ctx, job)
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("SCHED: %v @ %v", key, time.Now())
		s.stateMutex.Unlock()
		if !job.Job.Bootstrap {
			if key != "" {
				job.Server = s.Registry.Identifier
				job.State = pb.State_BUILT
				job.RunningVersion = key
			} else {
				// Bootstrap this job since we don't have an initial version
				if job.Job.PartialBootstrap {
					job.Job.Bootstrap = true
				}
			}
		} else {
			job.CommandKey = key
			job.State = pb.State_BUILDING
			job.Server = s.Registry.Identifier
		}
	case pb.State_BUILDING:
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("BUILD(%v): %v", job.CommandKey, s.scheduler.getState(job.CommandKey))
		s.stateMutex.Unlock()
		if s.taskComplete(job.CommandKey) {
			job.State = pb.State_BUILT
		}
	case pb.State_BUILT:
		output, _ := s.scheduler.getOutput(job.CommandKey)
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("BUILT(%v): (%v): %v", job.CommandKey, len(output), output)
		s.stateMutex.Unlock()
		if job.Job.Bootstrap && len(output) > 0 {
			if job.BuildFail == 5 {
				s.deliverCrashReport(ctx, job, output)
				job.BuildFail = 0
			}
			job.BuildFail++
			job.State = pb.State_DIED
		} else {
			job.BuildFail = 0
			key := s.scheduleRun(job.Job)
			job.CommandKey = key
			job.StartTime = time.Now().Unix()
			job.State = pb.State_PENDING
			if _, ok := s.pendingMap[time.Now().Weekday()]; !ok {
				s.pendingMap[time.Now().Weekday()] = make(map[string]int)
			}
			s.pendingMap[time.Now().Weekday()][job.Job.Name]++
		}
	case pb.State_PENDING:
		if job.Job.PartialBootstrap && job.Job.Bootstrap {
			job.Job.Bootstrap = false
		}
		s.stateMutex.Lock()
		out, _ := s.scheduler.getOutput(job.CommandKey)
		s.stateMap[job.Job.Name] = fmt.Sprintf("OUTPUT = %v", out)
		s.stateMutex.Unlock()
		if time.Now().Add(-time.Minute).Unix() > job.StartTime {
			job.State = pb.State_RUNNING
		}
	case pb.State_RUNNING:
		output, errout := s.scheduler.getOutput(job.CommandKey)
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("ROUTPUT = %v, %v", output, s.scheduler.getStatus(job.CommandKey))
		job.Status = s.scheduler.getStatus(job.CommandKey)
		s.stateMutex.Unlock()
		if len(job.CommandKey) > 0 && s.taskComplete(job.CommandKey) {
			s.stateMutex.Lock()
			s.stateMap[job.Job.Name] = fmt.Sprintf("COMPLETE = (%v, %v)", job, output)
			s.stateMutex.Unlock()
			s.deliverCrashReport(ctx, job, output)
			job.State = pb.State_DIED
		}

		err := s.discover.discover(job.Job.Name, s.Registry.Identifier)
		if err != nil {
			if job.DiscoverCount > 30 {
				output2, errout2 := s.scheduler.getErrOutput(job.CommandKey)
				s.RaiseIssue(ctx, "Cannot Discover Running Server", fmt.Sprintf("%v on %v is not discoverable, despite running (%v) the output says %v (%v), %v, %v", job.Job.Name, s.Registry.Identifier, err, output, errout, output2, errout2), false)
			}
			job.DiscoverCount++
		} else {
			job.DiscoverCount = 0
		}

		// Restart this job if we need to
		if !job.Job.Bootstrap {
			if time.Now().Sub(time.Unix(job.LastVersionPull, 0)) > time.Minute*5 {
				version, err := s.getVersion(ctx, job.Job)
				job.LastVersionPull = time.Now().Unix()

				if err == nil && version.Version != job.RunningVersion {
					s.stateMutex.Lock()
					s.stateMap[job.Job.Name] = fmt.Sprintf("VERSION_MISMATCH = %v,%v", version, job.RunningVersion)
					s.stateMutex.Unlock()
					s.scheduler.killJob(job.CommandKey)
				}
			}
		}
	case pb.State_BRINK_OF_DEATH:
		if s.version.confirm(ctx, job.Job.Name) {
			s.scheduler.killJob(job.CommandKey)
			job.State = pb.State_ACKNOWLEDGED
		}
	case pb.State_DIED:
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("DIED %v", job.CommandKey)
		s.stateMutex.Unlock()
		s.scheduler.removeJob(job.CommandKey)
		job.State = pb.State_ACKNOWLEDGED
	}

	if job.State != startState {
		job.LastTransitionTime = time.Now().Unix()
	}
}

type translator interface {
	build(job *pb.Job) *exec.Cmd
	run(job *pb.Job) *exec.Cmd
}

type checker interface {
	isAlive(ctx context.Context, job *pb.JobAssignment) bool
}

func (s *Server) getVersion(ctx context.Context, job *pb.Job) (*pbb.Version, error) {
	version, err := s.builder.build(ctx, job)
	if err != nil {
		return &pbb.Version{}, err
	}

	return version, nil

}

func updateJob(err error, job *pb.JobAssignment, resp *pbfc.CopyResponse) {
	if err == nil {
		job.QueuePos = resp.IndexInQueue
	}

}

// scheduleBuild builds out the job, returning the current version
func (s *Server) scheduleBuild(ctx context.Context, job *pb.JobAssignment) string {
	if job.Job.Bootstrap {
		c := s.translator.build(job.Job)
		return s.scheduler.Schedule(&rCommand{command: c, base: job.Job.Name})
	}

	val, err := s.builder.build(ctx, job.Job)
	if err != nil {
		s.Log(fmt.Sprintf("Error on build: %v", err))
		return ""
	}
	return val.Version
}

func (s *Server) scheduleRun(job *pb.Job) string {
	c := s.translator.run(job)

	//Copy over any existing new versions
	s.scheduler.Schedule(&rCommand{command: exec.Command("mv", "$GOPATH/bin/"+job.GetName()+".new", "$GOPATH/bin/"+job.GetName()), base: job.Name})
	return s.scheduler.Schedule(&rCommand{command: c, base: job.Name})
}

func (s *Server) taskComplete(key string) bool {
	return s.scheduler.schedulerComplete(key)
}
<commit_msg>Waits for copy before restart<commit_after>package main

import (
	"fmt"
	"os/exec"
	"time"

	pbb "github.com/brotherlogic/buildserver/proto"
	pbfc "github.com/brotherlogic/filecopier/proto"
	pb "github.com/brotherlogic/gobuildslave/proto"
	"golang.org/x/net/context"
)

const (
	pendWait = time.Minute
)

func (s *Server) runTransition(ctx context.Context, job *pb.JobAssignment) {
	startState := job.State
	switch job.State {
	case pb.State_ACKNOWLEDGED:
		key := s.scheduleBuild(ctx, job)
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("SCHED: %v @ %v", key, time.Now())
		s.stateMutex.Unlock()
		if !job.Job.Bootstrap {
			if key != "" {
				job.Server = s.Registry.Identifier
				job.State = pb.State_BUILT
				job.RunningVersion = key
			} else {
				// Bootstrap this job since we don't have an initial version
				if job.Job.PartialBootstrap {
					job.Job.Bootstrap = true
				}
			}
		} else {
			job.CommandKey = key
			job.State = pb.State_BUILDING
			job.Server = s.Registry.Identifier
		}
	case pb.State_BUILDING:
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("BUILD(%v): %v", job.CommandKey, s.scheduler.getState(job.CommandKey))
		s.stateMutex.Unlock()
		if s.taskComplete(job.CommandKey) {
			job.State = pb.State_BUILT
		}
	case pb.State_BUILT:
		output, _ := s.scheduler.getOutput(job.CommandKey)
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("BUILT(%v): (%v): %v", job.CommandKey, len(output), output)
		s.stateMutex.Unlock()
		if job.Job.Bootstrap && len(output) > 0 {
			if job.BuildFail == 5 {
				s.deliverCrashReport(ctx, job, output)
				job.BuildFail = 0
			}
			job.BuildFail++
			job.State = pb.State_DIED
		} else {
			job.BuildFail = 0
			key := s.scheduleRun(job.Job)
			job.CommandKey = key
			job.StartTime = time.Now().Unix()
			job.State = pb.State_PENDING
			if _, ok := s.pendingMap[time.Now().Weekday()]; !ok {
				s.pendingMap[time.Now().Weekday()] = make(map[string]int)
			}
			s.pendingMap[time.Now().Weekday()][job.Job.Name]++
		}
	case pb.State_PENDING:
		if job.Job.PartialBootstrap && job.Job.Bootstrap {
			job.Job.Bootstrap = false
		}
		s.stateMutex.Lock()
		out, _ := s.scheduler.getOutput(job.CommandKey)
		s.stateMap[job.Job.Name] = fmt.Sprintf("OUTPUT = %v", out)
		s.stateMutex.Unlock()
		if time.Now().Add(-time.Minute).Unix() > job.StartTime {
			job.State = pb.State_RUNNING
		}
	case pb.State_RUNNING:
		output, errout := s.scheduler.getOutput(job.CommandKey)
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("ROUTPUT = %v, %v", output, s.scheduler.getStatus(job.CommandKey))
		job.Status = s.scheduler.getStatus(job.CommandKey)
		s.stateMutex.Unlock()
		if len(job.CommandKey) > 0 && s.taskComplete(job.CommandKey) {
			s.stateMutex.Lock()
			s.stateMap[job.Job.Name] = fmt.Sprintf("COMPLETE = (%v, %v)", job, output)
			s.stateMutex.Unlock()
			s.deliverCrashReport(ctx, job, output)
			job.State = pb.State_DIED
		}

		err := s.discover.discover(job.Job.Name, s.Registry.Identifier)
		if err != nil {
			if job.DiscoverCount > 30 {
				output2, errout2 := s.scheduler.getErrOutput(job.CommandKey)
				s.RaiseIssue(ctx, "Cannot Discover Running Server", fmt.Sprintf("%v on %v is not discoverable, despite running (%v) the output says %v (%v), %v, %v", job.Job.Name, s.Registry.Identifier, err, output, errout, output2, errout2), false)
			}
			job.DiscoverCount++
		} else {
			job.DiscoverCount = 0
		}

		// Restart this job if we need to
		if !job.Job.Bootstrap {
			if time.Now().Sub(time.Unix(job.LastVersionPull, 0)) > time.Minute*5 {
				version, err := s.getVersion(ctx, job.Job)
				job.LastVersionPull = time.Now().Unix()

				if err == nil && version.Version != job.RunningVersion {
					s.stateMutex.Lock()
					s.stateMap[job.Job.Name] = fmt.Sprintf("VERSION_MISMATCH = %v,%v", version, job.RunningVersion)
					s.stateMutex.Unlock()
					s.scheduler.killJob(job.CommandKey)
				}
			}
		}
	case pb.State_BRINK_OF_DEATH:
		if s.version.confirm(ctx, job.Job.Name) {
			s.scheduler.killJob(job.CommandKey)
			job.State = pb.State_ACKNOWLEDGED
		}
	case pb.State_DIED:
		s.stateMutex.Lock()
		s.stateMap[job.Job.Name] = fmt.Sprintf("DIED %v", job.CommandKey)
		s.stateMutex.Unlock()
		s.scheduler.removeJob(job.CommandKey)
		job.State = pb.State_ACKNOWLEDGED
	}

	if job.State != startState {
		job.LastTransitionTime = time.Now().Unix()
	}
}

type translator interface {
	build(job *pb.Job) *exec.Cmd
	run(job *pb.Job) *exec.Cmd
}

type checker interface {
	isAlive(ctx context.Context, job *pb.JobAssignment) bool
}

func (s *Server) getVersion(ctx context.Context, job *pb.Job) (*pbb.Version, error) {
	version, err := s.builder.build(ctx, job)
	if err != nil {
		return &pbb.Version{}, err
	}

	return version, nil

}

func updateJob(err error, job *pb.JobAssignment, resp *pbfc.CopyResponse) {
	if err == nil {
		job.QueuePos = resp.IndexInQueue
	}

}

// scheduleBuild builds out the job, returning the current version
func (s *Server) scheduleBuild(ctx context.Context, job *pb.JobAssignment) string {
	if job.Job.Bootstrap {
		c := s.translator.build(job.Job)
		return s.scheduler.Schedule(&rCommand{command: c, base: job.Job.Name})
	}

	val, err := s.builder.build(ctx, job.Job)
	if err != nil {
		s.Log(fmt.Sprintf("Error on build: %v", err))
		return ""
	}
	return val.Version
}

func (s *Server) scheduleRun(job *pb.Job) string {
	//Copy over any existing new versions
	key := s.scheduler.Schedule(&rCommand{command: exec.Command("mv", "$GOPATH/bin/"+job.GetName()+".new", "$GOPATH/bin/"+job.GetName()), base: job.Name})
	for !s.taskComplete(key) {
		time.Sleep(time.Second)
	}

	c := s.translator.run(job)
	return s.scheduler.Schedule(&rCommand{command: c, base: job.Name})
}

func (s *Server) taskComplete(key string) bool {
	return s.scheduler.schedulerComplete(key)
}
<|endoftext|>


<commit_before>package gocbcore

import (
	"encoding/binary"
)

type SnapshotState uint32

func (s SnapshotState) HasInMemory() bool {
	return uint32(s)&1 != 0
}
func (s SnapshotState) HasOnDisk() bool {
	return uint32(s)&2 != 0
}

type StreamObserver interface {
	SnapshotMarker(startSeqNo, endSeqNo uint64, snapshotType SnapshotState)
	Mutation(seqNo, revNo uint64, flags, expiry, lockTime uint32, cas uint64, datatype uint8, vbId uint16, key, value []byte)
	Deletion(seqNo, revNo, cas uint64, datatype uint8, vbId uint16, key []byte)
	Expiration(seqNo, revNo, cas uint64, datatype uint8, vbId uint16, key []byte)
	Flush()
	End(err error)
}

type OpenStreamCallback func(error)
type CloseStreamCallback func(error)

func (c *Agent) OpenStream(vbId uint16, vbUuid, startSeqNo, endSeqNo uint64, evtHandler StreamObserver, cb OpenStreamCallback) (PendingOp, error) {
	handler := func(resp *memdResponse, err error) {
		if resp.Magic == ReqMagic {
			// This is one of the stream events
			switch resp.Opcode {
			case CmdDcpSnapshotMarker:
				newStartSeqNo := binary.BigEndian.Uint64(resp.Extras[0:])
				newEndSeqNo := binary.BigEndian.Uint64(resp.Extras[8:])
				snapshotType := binary.BigEndian.Uint32(resp.Extras[16:])
				evtHandler.SnapshotMarker(newStartSeqNo, newEndSeqNo, SnapshotState(snapshotType))
			case CmdDcpMutation:
				vbId := uint16(resp.Status)
				seqNo := binary.BigEndian.Uint64(resp.Extras[0:])
				revNo := binary.BigEndian.Uint64(resp.Extras[8:])
				flags := binary.BigEndian.Uint32(resp.Extras[16:])
				expiry := binary.BigEndian.Uint32(resp.Extras[20:])
				lockTime := binary.BigEndian.Uint32(resp.Extras[24:])
				evtHandler.Mutation(seqNo, revNo, flags, expiry, lockTime, resp.Cas, resp.Datatype, vbId, resp.Key, resp.Value)
			case CmdDcpDeletion:
			case CmdDcpExpiration:
			case CmdDcpFlush:
			case CmdDcpStreamEnd:
			}
		} else {
			// This is the response to the open stream request.
			cb(err)
		}
	}

	extraBuf := make([]byte, 48)
	binary.BigEndian.PutUint32(extraBuf[0:], 0)
	binary.BigEndian.PutUint32(extraBuf[4:], 0)
	binary.BigEndian.PutUint64(extraBuf[8:], startSeqNo)
	binary.BigEndian.PutUint64(extraBuf[16:], endSeqNo)
	binary.BigEndian.PutUint64(extraBuf[24:], vbUuid)
	binary.BigEndian.PutUint64(extraBuf[32:], startSeqNo)
	binary.BigEndian.PutUint64(extraBuf[40:], endSeqNo)

	req := &memdQRequest{
		memdRequest: memdRequest{
			Magic:    ReqMagic,
			Opcode:   CmdDcpStreamReq,
			Datatype: 0,
			Cas:      0,
			Extras:   extraBuf,
			Key:      nil,
			Value:    nil,
			Vbucket:  vbId,
		},
		Callback:   handler,
		ReplicaIdx: -1,
		Persistent: true,
	}
	return c.dispatchOp(req)
}

func (c *Agent) CloseStream(vbId uint16, cb CloseStreamCallback) (PendingOp, error) {
	handler := func(resp *memdResponse, err error) {
		cb(err)
	}

	req := &memdQRequest{
		memdRequest: memdRequest{
			Magic:    ReqMagic,
			Opcode:   CmdDcpCloseStream,
			Datatype: 0,
			Cas:      0,
			Extras:   nil,
			Key:      nil,
			Value:    nil,
			Vbucket:  vbId,
		},
		Callback:   handler,
		ReplicaIdx: -1,
		Persistent: true,
	}
	return c.dispatchOp(req)
}
<commit_msg>Fix bug causing errors to fail to propagate from OpenStream requests.<commit_after>package gocbcore

import (
	"encoding/binary"
)

type SnapshotState uint32

func (s SnapshotState) HasInMemory() bool {
	return uint32(s)&1 != 0
}
func (s SnapshotState) HasOnDisk() bool {
	return uint32(s)&2 != 0
}

type StreamObserver interface {
	SnapshotMarker(startSeqNo, endSeqNo uint64, snapshotType SnapshotState)
	Mutation(seqNo, revNo uint64, flags, expiry, lockTime uint32, cas uint64, datatype uint8, vbId uint16, key, value []byte)
	Deletion(seqNo, revNo, cas uint64, vbId uint16, key []byte)
	Expiration(seqNo, revNo, cas uint64, vbId uint16, key []byte)
	Flush()
	End(err error)
}

type OpenStreamCallback func(error)
type CloseStreamCallback func(error)

func (c *Agent) OpenStream(vbId uint16, vbUuid, startSeqNo, endSeqNo uint64, evtHandler StreamObserver, cb OpenStreamCallback) (PendingOp, error) {
	streamOpened := false

	handler := func(resp *memdResponse, err error) {
		if err != nil {
			if !streamOpened {
				cb(err)
				return
			}

			// We need to shutdown the stream here as well...
			evtHandler.End(err)
			return
		}

		if resp.Magic == ResMagic {
			// This is the response to the open stream request.
			streamOpened = true
			return
		}

		// This is one of the stream events
		switch resp.Opcode {
		case CmdDcpSnapshotMarker:
			newStartSeqNo := binary.BigEndian.Uint64(resp.Extras[0:])
			newEndSeqNo := binary.BigEndian.Uint64(resp.Extras[8:])
			snapshotType := binary.BigEndian.Uint32(resp.Extras[16:])
			evtHandler.SnapshotMarker(newStartSeqNo, newEndSeqNo, SnapshotState(snapshotType))
		case CmdDcpMutation:
			vbId := uint16(resp.Status)
			seqNo := binary.BigEndian.Uint64(resp.Extras[0:])
			revNo := binary.BigEndian.Uint64(resp.Extras[8:])
			flags := binary.BigEndian.Uint32(resp.Extras[16:])
			expiry := binary.BigEndian.Uint32(resp.Extras[20:])
			lockTime := binary.BigEndian.Uint32(resp.Extras[24:])
			evtHandler.Mutation(seqNo, revNo, flags, expiry, lockTime, resp.Cas, resp.Datatype, vbId, resp.Key, resp.Value)
		case CmdDcpDeletion:
			vbId := uint16(resp.Status)
			seqNo := binary.BigEndian.Uint64(resp.Extras[0:])
			revNo := binary.BigEndian.Uint64(resp.Extras[8:])
			evtHandler.Deletion(seqNo, revNo, resp.Cas, vbId, resp.Key)
		case CmdDcpExpiration:
			vbId := uint16(resp.Status)
			seqNo := binary.BigEndian.Uint64(resp.Extras[0:])
			revNo := binary.BigEndian.Uint64(resp.Extras[8:])
			evtHandler.Expiration(seqNo, revNo, resp.Cas, vbId, resp.Key)
		case CmdDcpFlush:
		case CmdDcpStreamEnd:
		}
	}

	extraBuf := make([]byte, 48)
	binary.BigEndian.PutUint32(extraBuf[0:], 0)
	binary.BigEndian.PutUint32(extraBuf[4:], 0)
	binary.BigEndian.PutUint64(extraBuf[8:], startSeqNo)
	binary.BigEndian.PutUint64(extraBuf[16:], endSeqNo)
	binary.BigEndian.PutUint64(extraBuf[24:], vbUuid)
	binary.BigEndian.PutUint64(extraBuf[32:], startSeqNo)
	binary.BigEndian.PutUint64(extraBuf[40:], endSeqNo)

	req := &memdQRequest{
		memdRequest: memdRequest{
			Magic:    ReqMagic,
			Opcode:   CmdDcpStreamReq,
			Datatype: 0,
			Cas:      0,
			Extras:   extraBuf,
			Key:      nil,
			Value:    nil,
			Vbucket:  vbId,
		},
		Callback:   handler,
		ReplicaIdx: -1,
		Persistent: true,
	}
	return c.dispatchOp(req)
}

func (c *Agent) CloseStream(vbId uint16, cb CloseStreamCallback) (PendingOp, error) {
	handler := func(resp *memdResponse, err error) {
		cb(err)
	}

	req := &memdQRequest{
		memdRequest: memdRequest{
			Magic:    ReqMagic,
			Opcode:   CmdDcpCloseStream,
			Datatype: 0,
			Cas:      0,
			Extras:   nil,
			Key:      nil,
			Value:    nil,
			Vbucket:  vbId,
		},
		Callback:   handler,
		ReplicaIdx: -1,
		Persistent: true,
	}
	return c.dispatchOp(req)
}
<|endoftext|>


<commit_before>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package sparc64

import "cmd/internal/obj"

func ri(rd, imm22 int) uint32 {
	return uint32(rd&31<<25 | imm22&(1<<23-1))
}

func d22(a, disp22 int) uint32 {
	return uint32(a&1<<29 | disp22&(1<<23-1))
}

func d19(a, cc1, cc0, p, disp19 int) uint32 {
	return uint32(a&1<<29 | cc1&1<<21 | cc0&1<<20 | p&1<<19 | disp19&(1<<20-1))
}

func d30(disp30 int) uint32 {
	return uint32(disp30 & (1<<31 - 1))
}

func rrr(rd, rs1, imm_asi, rs2 int) uint32 {
	return uint32(rd&31<<25 | rs1&31<<14 | imm_asi&255<<5 | rs2&31)
}

func rrs(rd, rs1, simm13 int) uint32 {
	return uint32(rd&31<<25 | rs1&31<<14 | 1<<13 | simm13&(1<<14-1))
}

func op(op int) uint32 {
	return uint32(op << 30)
}

func op3(op, op3 int) uint32 {
	return uint32(op<<30 | op3<<19)
}

func op2(op2 int) uint32 {
	return uint32(op2 << 22)
}

func cond(cond int) uint32 {
	return uint32(cond << 25)
}

func opf(opf int) uint32 {
	return uint32(opf << 5)
}

func opcode(a int) uint32 {
	switch a {
	// Add.
	case AADD:
		return op3(2, 0)
	case AADDCC:
		return op3(2, 16)
	case AADDC:
		return op3(2, 8)
	case AADDCCC:
		return op3(2, 24)

	// AND logical operation.
	case AAND:
		return op3(2, 1)
	case AANDCC:
		return op3(2, 17)
	case AANDN:
		return op3(2, 5)
	case AANDNCC:
		return op3(2, 21)

	// Branch on integer condition codes with prediction (BPcc).
	case obj.AJMP:
		return cond(8) | op2(1)
	case ABN:
		return cond(0) | op2(1)
	case ABNE:
		return cond(9) | op2(1)
	case ABE:
		return cond(1) | op2(1)
	case ABG:
		return cond(10) | op2(1)
	case ABLE:
		return cond(2) | op2(1)
	case ABGE:
		return cond(11) | op2(1)
	case ABL:
		return cond(3) | op2(1)
	case ABGU:
		return cond(12) | op2(1)
	case ABLEU:
		return cond(4) | op2(1)
	case ABCC:
		return cond(13) | op2(1)
	case ABCS:
		return cond(5) | op2(1)
	case ABPOS:
		return cond(14) | op2(1)
	case ABNEG:
		return cond(6) | op2(1)
	case ABVC:
		return cond(15) | op2(1)
	case ABVS:
		return cond(7) | op2(1)

	// Branch on integer register with prediction (BPr).
	case ABRZ:
		return cond(1) | op2(3)
	case ABRLEZ:
		return cond(2) | op2(3)
	case ABRLZ:
		return cond(3) | op2(3)
	case ABRNZ:
		return cond(5) | op2(3)
	case ABRGZ:
		return cond(6) | op2(3)
	case ABRGEZ:
		return cond(7) | op2(3)

	// Call and link
	case obj.ACALL:
		return op(1)

	case ACASAW:
		return op3(3, 0x3C)
	case ACASA:
		return op3(3, 0x3E)

	case AFABSS:
		return op3(2, 0x34) | opf(9)
	case AFABSD:
		return op3(2, 0x34) | opf(10)

	case AFADDS:
		return op3(2, 0x34) | opf(0x41)
	case AFADDD:
		return op3(2, 0x34) | opf(0x42)

	// Branch on floating-point condition codes (FBfcc).
	case AFBA:
		return cond(8) | op2(6)
	case AFBN:
		return cond(0) | op2(6)
	case AFBU:
		return cond(7) | op2(6)
	case AFBG:
		return cond(6) | op2(6)
	case AFBUG:
		return cond(5) | op2(6)
	case AFBL:
		return cond(4) | op2(6)
	case AFBUL:
		return cond(3) | op2(6)
	case AFBLG:
		return cond(2) | op2(6)
	case AFBNE:
		return cond(1) | op2(6)
	case AFBE:
		return cond(9) | op2(6)
	case AFBUE:
		return cond(10) | op2(6)
	case AFBGE:
		return cond(11) | op2(6)
	case AFBUGE:
		return cond(12) | op2(6)
	case AFBLE:
		return cond(13) | op2(6)
	case AFBULE:
		return cond(14) | op2(6)
	case AFBO:
		return cond(15) | op2(6)

	// Floating-point compare.
	case AFCMPS:
		return op3(2, 0x35) | opf(0x31)
	case AFCMPD:
		return op3(2, 0x35) | opf(0x32)

	// Floating-point divide.
	case AFDIVS:
		return op3(2, 0x34) | opf(0x4D)
	case AFDIVD:
		return op3(2, 0x34) | opf(0x4E)

	// Convert 32-bit integer to floating point.
	case AFWTOS:
		return op3(2, 0x34) | opf(0xC4)
	case AFWTOD:
		return op3(2, 0x34) | opf(0xC8)

	case AFLUSH:
		return op3(2, 0x3B)

	// Floating-point move.
	case AFMOVS:
		return op3(2, 0x34) | opf(1)
	case AFMOVD:
		return op3(2, 0x34) | opf(2)

	// Floating-point multiply.
	case AFMULS:
		return op3(2, 0x34) | opf(0x49)
	case AFMULD:
		return op3(2, 0x34) | opf(0x4A)
	case AFSMULD:
		return op3(2, 0x34) | opf(0x69)

	// Floating-point negate.
	case AFNEGS:
		return op3(2, 0x34) | opf(5)
	case AFNEGD:
		return op3(2, 0x34) | opf(6)

	// Floating-point square root.
	case AFSQRTS:
		return op3(2, 0x34) | opf(0x29)
	case AFSQRTD:
		return op3(2, 0x34) | opf(0x2A)

	// Convert floating-point to integer.
	case AFSTOXD:
		return op3(2, 0x34) | opf(0x81)
	case AFDTOXD:
		return op3(2, 0x34) | opf(0x82)
	case AFSTOXW:
		return op3(2, 0x34) | opf(0xD1)
	case AFDTOXW:
		return op3(2, 0x34) | opf(0xD2)

	// Convert between floating-point formats.
	case AFSTOD:
		return op3(2, 0x34) | opf(0xC9)
	case AFDTOS:
		return op3(2, 0x34) | opf(0xC6)

	// Floating-point subtract.
	case AFSUBS:
		return op3(2, 0x34) | opf(0x45)
	case AFSUBD:
		return op3(2, 0x34) | opf(0x46)

	// Convert 64-bit integer to floating point.
	case AFXTOS:
		return op3(2, 0x34) | opf(0x84)
	case AFXTOD:
		return op3(2, 0x34) | opf(0x88)

	// Jump and link.
	case AJMPL:
		return op3(2, 0x38)

	// Load integer.
	case ALDSB:
		return op3(3, 9)
	case ALDSH:
		return op3(3, 10)
	case ALDSW:
		return op3(3, 8)
	case ALDUB:
		return op3(3, 1)
	case ALDUH:
		return op3(3, 2)
	case ALDUW:
		return op3(3, 0)
	case ALDD:
		return op3(3, 11)

	// Load floating-point register.
	case ALDSF:
		return op3(3, 0x20)
	case ALDDF:
		return op3(3, 0x23)

	// Memory Barrier.
	case AMEMBAR:
		return op3(2, 0x28) | 0xF<<14

	// Multiply and divide.
	case AMULD:
		return op3(2, 9)
	case ASDIVD:
		return op3(2, 0x2D)
	case AUDIVD:
		return op3(2, 0xD)

	// OR logical operation.
	case AOR:
		return op3(2, 2)
	case AORCC:
		return op3(2, 18)
	case AORN:
		return op3(2, 6)
	case AORNCC:
		return op3(2, 22)

	// Read ancillary state register.
	case ARDCCR:
		return op3(2, 0x28) | 2<<14
	case ARDTICK:
		return op3(2, 0x28) | 4<<14
	case ARDPC:
		return op3(2, 0x28) | 5<<14

	case ASETHI:
		return op2(4)

	// Shift.
	case ASLLW:
		return op3(2, 0x25)
	case ASRLW:
		return op3(2, 0x26)
	case ASRAW:
		return op3(2, 0x27)
	case ASLLD:
		return op3(2, 0x25) | 1<<12
	case ASRLD:
		return op3(2, 0x26) | 1<<12
	case ASRAD:
		return op3(2, 0x27) | 1<<12

	// Store Integer.
	case ASTB:
		return op3(3, 5)
	case ASTH:
		return op3(3, 6)
	case ASTW:
		return op3(3, 4)
	case ASTD:
		return op3(3, 14)

	// Store floating-point.
	case ASTSF:
		return op3(3, 0x24)
	case ASTDF:
		return op3(3, 0x27)

	// Subtract.
	case ASUB:
		return op3(2, 4)
	case ASUBCC:
		return op3(2, 20)
	case ASUBC:
		return op3(2, 12)
	case ASUBCCC:
		return op3(2, 28)

	// XOR logical operation.
	case AXOR:
		return op3(2, 3)
	case AXORCC:
		return op3(2, 19)
	case AXNOR:
		return op3(2, 7)
	case AXNORCC:
		return op3(2, 23)

	default:
		panic("unknown instruction: " + obj.Aconv(a))
	}
}

func rclass(r int16) int {
	switch {
	case r == RegZero:
		return ClassZero
	case REG_R1 <= r && r <= REG_R31:
		return ClassReg
	case REG_F0 <= r && r <= REG_F31:
		return ClassFloatReg
	case r == REG_BSP || r == REG_BFP:
		return ClassBiased
	}
	return ClassUnknown
}
<commit_msg>cmd/internal/obj/sparc64: fix encoding of FCMPS and FCMPD<commit_after>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package sparc64

import "cmd/internal/obj"

func ri(rd, imm22 int) uint32 {
	return uint32(rd&31<<25 | imm22&(1<<23-1))
}

func d22(a, disp22 int) uint32 {
	return uint32(a&1<<29 | disp22&(1<<23-1))
}

func d19(a, cc1, cc0, p, disp19 int) uint32 {
	return uint32(a&1<<29 | cc1&1<<21 | cc0&1<<20 | p&1<<19 | disp19&(1<<20-1))
}

func d30(disp30 int) uint32 {
	return uint32(disp30 & (1<<31 - 1))
}

func rrr(rd, rs1, imm_asi, rs2 int) uint32 {
	return uint32(rd&31<<25 | rs1&31<<14 | imm_asi&255<<5 | rs2&31)
}

func rrs(rd, rs1, simm13 int) uint32 {
	return uint32(rd&31<<25 | rs1&31<<14 | 1<<13 | simm13&(1<<14-1))
}

func op(op int) uint32 {
	return uint32(op << 30)
}

func op3(op, op3 int) uint32 {
	return uint32(op<<30 | op3<<19)
}

func op2(op2 int) uint32 {
	return uint32(op2 << 22)
}

func cond(cond int) uint32 {
	return uint32(cond << 25)
}

func opf(opf int) uint32 {
	return uint32(opf << 5)
}

func opcode(a int) uint32 {
	switch a {
	// Add.
	case AADD:
		return op3(2, 0)
	case AADDCC:
		return op3(2, 16)
	case AADDC:
		return op3(2, 8)
	case AADDCCC:
		return op3(2, 24)

	// AND logical operation.
	case AAND:
		return op3(2, 1)
	case AANDCC:
		return op3(2, 17)
	case AANDN:
		return op3(2, 5)
	case AANDNCC:
		return op3(2, 21)

	// Branch on integer condition codes with prediction (BPcc).
	case obj.AJMP:
		return cond(8) | op2(1)
	case ABN:
		return cond(0) | op2(1)
	case ABNE:
		return cond(9) | op2(1)
	case ABE:
		return cond(1) | op2(1)
	case ABG:
		return cond(10) | op2(1)
	case ABLE:
		return cond(2) | op2(1)
	case ABGE:
		return cond(11) | op2(1)
	case ABL:
		return cond(3) | op2(1)
	case ABGU:
		return cond(12) | op2(1)
	case ABLEU:
		return cond(4) | op2(1)
	case ABCC:
		return cond(13) | op2(1)
	case ABCS:
		return cond(5) | op2(1)
	case ABPOS:
		return cond(14) | op2(1)
	case ABNEG:
		return cond(6) | op2(1)
	case ABVC:
		return cond(15) | op2(1)
	case ABVS:
		return cond(7) | op2(1)

	// Branch on integer register with prediction (BPr).
	case ABRZ:
		return cond(1) | op2(3)
	case ABRLEZ:
		return cond(2) | op2(3)
	case ABRLZ:
		return cond(3) | op2(3)
	case ABRNZ:
		return cond(5) | op2(3)
	case ABRGZ:
		return cond(6) | op2(3)
	case ABRGEZ:
		return cond(7) | op2(3)

	// Call and link
	case obj.ACALL:
		return op(1)

	case ACASAW:
		return op3(3, 0x3C)
	case ACASA:
		return op3(3, 0x3E)

	case AFABSS:
		return op3(2, 0x34) | opf(9)
	case AFABSD:
		return op3(2, 0x34) | opf(10)

	case AFADDS:
		return op3(2, 0x34) | opf(0x41)
	case AFADDD:
		return op3(2, 0x34) | opf(0x42)

	// Branch on floating-point condition codes (FBfcc).
	case AFBA:
		return cond(8) | op2(6)
	case AFBN:
		return cond(0) | op2(6)
	case AFBU:
		return cond(7) | op2(6)
	case AFBG:
		return cond(6) | op2(6)
	case AFBUG:
		return cond(5) | op2(6)
	case AFBL:
		return cond(4) | op2(6)
	case AFBUL:
		return cond(3) | op2(6)
	case AFBLG:
		return cond(2) | op2(6)
	case AFBNE:
		return cond(1) | op2(6)
	case AFBE:
		return cond(9) | op2(6)
	case AFBUE:
		return cond(10) | op2(6)
	case AFBGE:
		return cond(11) | op2(6)
	case AFBUGE:
		return cond(12) | op2(6)
	case AFBLE:
		return cond(13) | op2(6)
	case AFBULE:
		return cond(14) | op2(6)
	case AFBO:
		return cond(15) | op2(6)

	// Floating-point compare.
	case AFCMPS:
		return op3(2, 0x35) | opf(0x51)
	case AFCMPD:
		return op3(2, 0x35) | opf(0x52)

	// Floating-point divide.
	case AFDIVS:
		return op3(2, 0x34) | opf(0x4D)
	case AFDIVD:
		return op3(2, 0x34) | opf(0x4E)

	// Convert 32-bit integer to floating point.
	case AFWTOS:
		return op3(2, 0x34) | opf(0xC4)
	case AFWTOD:
		return op3(2, 0x34) | opf(0xC8)

	case AFLUSH:
		return op3(2, 0x3B)

	// Floating-point move.
	case AFMOVS:
		return op3(2, 0x34) | opf(1)
	case AFMOVD:
		return op3(2, 0x34) | opf(2)

	// Floating-point multiply.
	case AFMULS:
		return op3(2, 0x34) | opf(0x49)
	case AFMULD:
		return op3(2, 0x34) | opf(0x4A)
	case AFSMULD:
		return op3(2, 0x34) | opf(0x69)

	// Floating-point negate.
	case AFNEGS:
		return op3(2, 0x34) | opf(5)
	case AFNEGD:
		return op3(2, 0x34) | opf(6)

	// Floating-point square root.
	case AFSQRTS:
		return op3(2, 0x34) | opf(0x29)
	case AFSQRTD:
		return op3(2, 0x34) | opf(0x2A)

	// Convert floating-point to integer.
	case AFSTOXD:
		return op3(2, 0x34) | opf(0x81)
	case AFDTOXD:
		return op3(2, 0x34) | opf(0x82)
	case AFSTOXW:
		return op3(2, 0x34) | opf(0xD1)
	case AFDTOXW:
		return op3(2, 0x34) | opf(0xD2)

	// Convert between floating-point formats.
	case AFSTOD:
		return op3(2, 0x34) | opf(0xC9)
	case AFDTOS:
		return op3(2, 0x34) | opf(0xC6)

	// Floating-point subtract.
	case AFSUBS:
		return op3(2, 0x34) | opf(0x45)
	case AFSUBD:
		return op3(2, 0x34) | opf(0x46)

	// Convert 64-bit integer to floating point.
	case AFXTOS:
		return op3(2, 0x34) | opf(0x84)
	case AFXTOD:
		return op3(2, 0x34) | opf(0x88)

	// Jump and link.
	case AJMPL:
		return op3(2, 0x38)

	// Load integer.
	case ALDSB:
		return op3(3, 9)
	case ALDSH:
		return op3(3, 10)
	case ALDSW:
		return op3(3, 8)
	case ALDUB:
		return op3(3, 1)
	case ALDUH:
		return op3(3, 2)
	case ALDUW:
		return op3(3, 0)
	case ALDD:
		return op3(3, 11)

	// Load floating-point register.
	case ALDSF:
		return op3(3, 0x20)
	case ALDDF:
		return op3(3, 0x23)

	// Memory Barrier.
	case AMEMBAR:
		return op3(2, 0x28) | 0xF<<14

	// Multiply and divide.
	case AMULD:
		return op3(2, 9)
	case ASDIVD:
		return op3(2, 0x2D)
	case AUDIVD:
		return op3(2, 0xD)

	// OR logical operation.
	case AOR:
		return op3(2, 2)
	case AORCC:
		return op3(2, 18)
	case AORN:
		return op3(2, 6)
	case AORNCC:
		return op3(2, 22)

	// Read ancillary state register.
	case ARDCCR:
		return op3(2, 0x28) | 2<<14
	case ARDTICK:
		return op3(2, 0x28) | 4<<14
	case ARDPC:
		return op3(2, 0x28) | 5<<14

	case ASETHI:
		return op2(4)

	// Shift.
	case ASLLW:
		return op3(2, 0x25)
	case ASRLW:
		return op3(2, 0x26)
	case ASRAW:
		return op3(2, 0x27)
	case ASLLD:
		return op3(2, 0x25) | 1<<12
	case ASRLD:
		return op3(2, 0x26) | 1<<12
	case ASRAD:
		return op3(2, 0x27) | 1<<12

	// Store Integer.
	case ASTB:
		return op3(3, 5)
	case ASTH:
		return op3(3, 6)
	case ASTW:
		return op3(3, 4)
	case ASTD:
		return op3(3, 14)

	// Store floating-point.
	case ASTSF:
		return op3(3, 0x24)
	case ASTDF:
		return op3(3, 0x27)

	// Subtract.
	case ASUB:
		return op3(2, 4)
	case ASUBCC:
		return op3(2, 20)
	case ASUBC:
		return op3(2, 12)
	case ASUBCCC:
		return op3(2, 28)

	// XOR logical operation.
	case AXOR:
		return op3(2, 3)
	case AXORCC:
		return op3(2, 19)
	case AXNOR:
		return op3(2, 7)
	case AXNORCC:
		return op3(2, 23)

	default:
		panic("unknown instruction: " + obj.Aconv(a))
	}
}

func rclass(r int16) int {
	switch {
	case r == RegZero:
		return ClassZero
	case REG_R1 <= r && r <= REG_R31:
		return ClassReg
	case REG_F0 <= r && r <= REG_F31:
		return ClassFloatReg
	case r == REG_BSP || r == REG_BFP:
		return ClassBiased
	}
	return ClassUnknown
}
<|endoftext|>


<commit_before>package goroutine

import (
	"fmt"
	"testing"
	"time"
)

func Test_go(t *testing.T) {
	pid, err := Start(new(Test))
	if err != nil {
		t.Error(err)
		return
	}
	go Cast(pid, "+", []interface{}{1, 2})
	add := func() {
		ret, err := Call(pid, "+", []interface{}{1, 2}, 2)
		if err != nil {
			fmt.Println(err.Error())
			t.Error(err)
			return
		}
		fmt.Println("1 + 2 = ", ret[0])
		if ret[0] != 3 {
			t.Errorf("1 + 2 != 3 ?????????????")
			return
		}
	}
	go add()
	sub := func() {
		ret, err := Call(pid, "-", []interface{}{1, 2}, 2)
		if err != nil {
			fmt.Println(err.Error())
			t.Error(err)
			return
		}
		fmt.Println("1 - 2 = ", ret[0])
		if ret[0] != -1 {
			t.Errorf("1 - 2 != -1 ?????????????")
			return
		}
	}
	go sub()
	mul := func() {
		ret, err := Call(pid, "*", []interface{}{1, 2}, 2)
		if err != nil {
			fmt.Println(err.Error())
			t.Error(err)
			return
		}
		fmt.Println("1 * 2 = ", ret[0])
		if ret[0] != 2 {
			t.Errorf("1 * 2 != 2 ?????????????")
			return
		}
	}
	go mul()
	time.Sleep(1e9)
	StopById(pid)
}

/*****************************实现进程装载器********************************/

type Test struct {
}

func (t *Test) name() string {
	return ""
}

func (t *Test) initGo() {
	fmt.Println("init..............")
}

func (t *Test) handler(msg string, args []interface{}, ret chan []interface{}) {
	fmt.Println("handler..............")
	// 异步的嘛
	if ret == nil {
		//...........do something...........
		return
	}
	// 同步的嘛
	switch msg {
	case "+":
		ret <- []interface{}{args[0].(int) + args[1].(int)}
	case "-":
		ret <- []interface{}{args[0].(int) - args[1].(int)}
	case "*":
		ret <- []interface{}{args[0].(int) * args[1].(int)}
	}
}

func (t *Test) closeGo() {
	fmt.Println("close..............")
}
<commit_msg>update go_test<commit_after>package goroutine

import (
	"fmt"
	"testing"
	"time"
)

func Test_go(t *testing.T) {
	pid, err := Start(new(Test))
	if err != nil {
		t.Error(err)
		return
	}
	go Cast(pid, "+", []interface{}{1, 2})
	add := func() {
		ret, err := Call(pid, "+", []interface{}{1, 2}, 2)
		if err != nil {
			fmt.Println(err.Error())
			t.Error(err)
			return
		}
		fmt.Println("1 + 2 = ", ret[0])
		if ret[0] != 3 {
			t.Errorf("1 + 2 != 3 ?????????????")
			return
		}
	}
	go add()
	sub := func() {
		ret, err := Call(pid, "-", []interface{}{1, 2}, 2)
		if err != nil {
			fmt.Println(err.Error())
			t.Error(err)
			return
		}
		fmt.Println("1 - 2 = ", ret[0])
		if ret[0] != -1 {
			t.Errorf("1 - 2 != -1 ?????????????")
			return
		}
	}
	go sub()
	mul := func() {
		ret, err := Call(pid, "*", []interface{}{1, 2}, 2)
		if err != nil {
			fmt.Println(err.Error())
			t.Error(err)
			return
		}
		fmt.Println("1 * 2 = ", ret[0])
		if ret[0] != 2 {
			t.Errorf("1 * 2 != 2 ?????????????")
			return
		}
	}
	go mul()
	time.Sleep(1e9)
	if err := StopById(pid); err != nil {
		t.Error(err)
		return
	}
	StopByName("calc")
}

/*****************************实现进程装载器********************************/

type Test struct {
}

func (t *Test) name() string {
	return "calc"
}

func (t *Test) initGo() {
	fmt.Println("init..............")
}

func (t *Test) handler(msg string, args []interface{}, ret chan []interface{}) {
	fmt.Println("handler..............", msg, args)
	// 异步的嘛
	if ret == nil {
		//...........do something...........
		return
	}
	// 同步的嘛
	switch msg {
	case "+":
		ret <- []interface{}{args[0].(int) + args[1].(int)}
	case "-":
		ret <- []interface{}{args[0].(int) - args[1].(int)}
	case "*":
		ret <- []interface{}{args[0].(int) * args[1].(int)}
	}
}

func (t *Test) closeGo() {
	fmt.Println("close..............")
}
<|endoftext|>


<commit_before>package groupme

type gmMessage struct {
	groupID  string `json:"group_id"`
	userName string `json:"name"`
	userID   string `json:"id"`
	text     string `json:"text"`
	userType string `json:"sender_type"`
}

func (m gmMessage) GroupID() string {
	return m.groupID
}

func (m gmMessage) UserName() string {
	return m.userName
}

func (m gmMessage) UserID() string {
	return m.userID
}

func (m gmMessage) Text() string {
	return m.text
}

func (m gmMessage) UserType() string {
	return m.userType
}
<commit_msg>Fix message being returned blank from the groupme service<commit_after>package groupme

type gmMessage struct {
	GID         string `json:"group_id"`
	Name        string `json:"name"`
	UID         string `json:"id"`
	MessageText string `json:"text"`
	SenderType  string `json:"sender_type"`
}

func (m gmMessage) GroupID() string {
	return m.GID
}

func (m gmMessage) UserName() string {
	return m.Name
}

func (m gmMessage) UserID() string {
	return m.UID
}

func (m gmMessage) Text() string {
	return m.MessageText
}

func (m gmMessage) UserType() string {
	return m.SenderType
}
<|endoftext|>