github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/cmd/jujud/util/util.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package util
     5  
     6  import (
     7  	"fmt"
     8  	"io"
     9  	"strconv"
    10  
    11  	"github.com/juju/errors"
    12  	"github.com/juju/loggo"
    13  	"github.com/juju/utils/series"
    14  
    15  	"github.com/juju/juju/agent"
    16  	"github.com/juju/juju/apiserver/params"
    17  	"github.com/juju/juju/juju/paths"
    18  	"github.com/juju/juju/mongo"
    19  	"github.com/juju/juju/state"
    20  	"github.com/juju/juju/worker"
    21  	"github.com/juju/juju/worker/upgrader"
    22  )
    23  
    24  var (
    25  	logger            = loggo.GetLogger("juju.cmd.jujud.util")
    26  	DataDir           = paths.MustSucceed(paths.DataDir(series.HostSeries()))
    27  	EnsureMongoServer = mongo.EnsureServer
    28  )
    29  
    30  // RequiredError is useful when complaining about missing command-line options.
    31  func RequiredError(name string) error {
    32  	return fmt.Errorf("--%s option must be set", name)
    33  }
    34  
    35  // IsFatal determines if an error is fatal to the process.
    36  func IsFatal(err error) bool {
    37  	err = errors.Cause(err)
    38  	switch err {
    39  	case worker.ErrTerminateAgent, worker.ErrRebootMachine, worker.ErrShutdownMachine:
    40  		return true
    41  	}
    42  
    43  	if isUpgraded(err) {
    44  		return true
    45  	}
    46  	_, ok := err.(*FatalError)
    47  	return ok
    48  }
    49  
    50  func isUpgraded(err error) bool {
    51  	_, ok := err.(*upgrader.UpgradeReadyError)
    52  	return ok
    53  }
    54  
    55  // FatalError is an error type designated for fatal errors.
    56  type FatalError struct {
    57  	Err string
    58  }
    59  
    60  // Error returns an error string.
    61  func (e *FatalError) Error() string {
    62  	return e.Err
    63  }
    64  
    65  func importance(err error) int {
    66  	err = errors.Cause(err)
    67  	switch {
    68  	case err == nil:
    69  		return 0
    70  	default:
    71  		return 1
    72  	case isUpgraded(err):
    73  		return 2
    74  	case err == worker.ErrRebootMachine:
    75  		return 3
    76  	case err == worker.ErrShutdownMachine:
    77  		return 3
    78  	case err == worker.ErrTerminateAgent:
    79  		return 4
    80  	}
    81  }
    82  
    83  // MoreImportant returns whether err0 is more important than err1 -
    84  // that is, whether we should act on err0 in preference to err1.
    85  func MoreImportant(err0, err1 error) bool {
    86  	return importance(err0) > importance(err1)
    87  }
    88  
    89  // MoreImportantError returns the most important error
    90  func MoreImportantError(err0, err1 error) error {
    91  	if importance(err0) > importance(err1) {
    92  		return err0
    93  	}
    94  	return err1
    95  }
    96  
    97  // AgentDone processes the error returned by an exiting agent.
    98  func AgentDone(logger loggo.Logger, err error) error {
    99  	err = errors.Cause(err)
   100  	switch err {
   101  	case worker.ErrTerminateAgent, worker.ErrRebootMachine, worker.ErrShutdownMachine:
   102  		// These errors are swallowed here because we want to exit
   103  		// the agent process without error, to avoid the init system
   104  		// restarting us.
   105  		err = nil
   106  	}
   107  	if ug, ok := err.(*upgrader.UpgradeReadyError); ok {
   108  		if err := ug.ChangeAgentTools(); err != nil {
   109  			// Return and let the init system deal with the restart.
   110  			err = errors.Annotate(err, "cannot change agent tools")
   111  			logger.Infof(err.Error())
   112  			return err
   113  		}
   114  	}
   115  	return err
   116  }
   117  
   118  // Pinger provides a type that knows how to ping.
   119  type Pinger interface {
   120  
   121  	// Ping pings something.
   122  	Ping() error
   123  }
   124  
   125  // ConnectionIsFatal returns a function suitable for passing as the
   126  // isFatal argument to worker.NewRunner, that diagnoses an error as
   127  // fatal if the connection has failed or if the error is otherwise
   128  // fatal.
   129  func ConnectionIsFatal(logger loggo.Logger, conns ...Pinger) func(err error) bool {
   130  	return func(err error) bool {
   131  		if IsFatal(err) {
   132  			return true
   133  		}
   134  		for _, conn := range conns {
   135  			if ConnectionIsDead(logger, conn) {
   136  				return true
   137  			}
   138  		}
   139  		return false
   140  	}
   141  }
   142  
   143  // ConnectionIsDead returns true if the given pinger fails to ping.
   144  var ConnectionIsDead = func(logger loggo.Logger, conn Pinger) bool {
   145  	if err := conn.Ping(); err != nil {
   146  		logger.Infof("error pinging %T: %v", conn, err)
   147  		return true
   148  	}
   149  	return false
   150  }
   151  
   152  // NewEnsureServerParams creates an EnsureServerParams from an agent
   153  // configuration.
   154  func NewEnsureServerParams(agentConfig agent.Config) (mongo.EnsureServerParams, error) {
   155  	// If oplog size is specified in the agent configuration, use that.
   156  	// Otherwise leave the default zero value to indicate to EnsureServer
   157  	// that it should calculate the size.
   158  	var oplogSize int
   159  	if oplogSizeString := agentConfig.Value(agent.MongoOplogSize); oplogSizeString != "" {
   160  		var err error
   161  		if oplogSize, err = strconv.Atoi(oplogSizeString); err != nil {
   162  			return mongo.EnsureServerParams{}, fmt.Errorf("invalid oplog size: %q", oplogSizeString)
   163  		}
   164  	}
   165  
   166  	// If numa ctl preference is specified in the agent configuration, use that.
   167  	// Otherwise leave the default false value to indicate to EnsureServer
   168  	// that numactl should not be used.
   169  	var numaCtlPolicy bool
   170  	if numaCtlString := agentConfig.Value(agent.NUMACtlPreference); numaCtlString != "" {
   171  		var err error
   172  		if numaCtlPolicy, err = strconv.ParseBool(numaCtlString); err != nil {
   173  			return mongo.EnsureServerParams{}, fmt.Errorf("invalid numactl preference: %q", numaCtlString)
   174  		}
   175  	}
   176  
   177  	si, ok := agentConfig.StateServingInfo()
   178  	if !ok {
   179  		return mongo.EnsureServerParams{}, fmt.Errorf("agent config has no state serving info")
   180  	}
   181  
   182  	params := mongo.EnsureServerParams{
   183  		APIPort:        si.APIPort,
   184  		StatePort:      si.StatePort,
   185  		Cert:           si.Cert,
   186  		PrivateKey:     si.PrivateKey,
   187  		CAPrivateKey:   si.CAPrivateKey,
   188  		SharedSecret:   si.SharedSecret,
   189  		SystemIdentity: si.SystemIdentity,
   190  
   191  		DataDir:              agentConfig.DataDir(),
   192  		OplogSize:            oplogSize,
   193  		SetNUMAControlPolicy: numaCtlPolicy,
   194  	}
   195  	return params, nil
   196  }
   197  
   198  // NewCloseWorker returns a task that wraps the given task,
   199  // closing the given closer when it finishes.
   200  func NewCloseWorker(logger loggo.Logger, worker worker.Worker, closer io.Closer) worker.Worker {
   201  	return &CloseWorker{
   202  		worker: worker,
   203  		closer: closer,
   204  		logger: logger,
   205  	}
   206  }
   207  
   208  // CloseWorker is a worker which closes the given closer when finished
   209  // with a task.
   210  type CloseWorker struct {
   211  	worker worker.Worker
   212  	closer io.Closer
   213  	logger loggo.Logger
   214  }
   215  
   216  func (c *CloseWorker) Kill() {
   217  	c.worker.Kill()
   218  }
   219  
   220  func (c *CloseWorker) Wait() error {
   221  	err := c.worker.Wait()
   222  	if err := c.closer.Close(); err != nil {
   223  		c.logger.Errorf("closeWorker: close error: %v", err)
   224  	}
   225  	return err
   226  }
   227  
   228  // ParamsStateServingInfoToStateStateServingInfo converts a
   229  // params.StateServingInfo to a state.StateServingInfo.
   230  func ParamsStateServingInfoToStateStateServingInfo(i params.StateServingInfo) state.StateServingInfo {
   231  	return state.StateServingInfo{
   232  		APIPort:        i.APIPort,
   233  		StatePort:      i.StatePort,
   234  		Cert:           i.Cert,
   235  		PrivateKey:     i.PrivateKey,
   236  		CAPrivateKey:   i.CAPrivateKey,
   237  		SharedSecret:   i.SharedSecret,
   238  		SystemIdentity: i.SystemIdentity,
   239  	}
   240  }