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