github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/worker/uniter/uniter.go (about)

     1  // Copyright 2012-2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package uniter
     5  
     6  import (
     7  	"fmt"
     8  	"os"
     9  	"strings"
    10  
    11  	"github.com/juju/errors"
    12  	"github.com/juju/loggo"
    13  	"github.com/juju/names"
    14  	"github.com/juju/utils/exec"
    15  	"github.com/juju/utils/fslock"
    16  	corecharm "gopkg.in/juju/charm.v4"
    17  	"launchpad.net/tomb"
    18  
    19  	"github.com/juju/juju/api/uniter"
    20  	"github.com/juju/juju/apiserver/params"
    21  	"github.com/juju/juju/state/watcher"
    22  	"github.com/juju/juju/version"
    23  	"github.com/juju/juju/worker"
    24  	"github.com/juju/juju/worker/uniter/charm"
    25  	"github.com/juju/juju/worker/uniter/filter"
    26  	"github.com/juju/juju/worker/uniter/operation"
    27  	"github.com/juju/juju/worker/uniter/runner"
    28  	"github.com/juju/juju/worker/uniter/runner/jujuc"
    29  )
    30  
    31  var logger = loggo.GetLogger("juju.worker.uniter")
    32  
    33  // A UniterExecutionObserver gets the appropriate methods called when a hook
    34  // is executed and either succeeds or fails.  Missing hooks don't get reported
    35  // in this way.
    36  type UniterExecutionObserver interface {
    37  	HookCompleted(hookName string)
    38  	HookFailed(hookName string)
    39  }
    40  
    41  // Uniter implements the capabilities of the unit agent. It is not intended to
    42  // implement the actual *behaviour* of the unit agent; that responsibility is
    43  // delegated to Mode values, which are expected to react to events and direct
    44  // the uniter's responses to them.
    45  type Uniter struct {
    46  	tomb      tomb.Tomb
    47  	st        *uniter.State
    48  	paths     Paths
    49  	f         filter.Filter
    50  	unit      *uniter.Unit
    51  	relations Relations
    52  
    53  	deployer          *deployerProxy
    54  	operationFactory  operation.Factory
    55  	operationExecutor operation.Executor
    56  
    57  	hookLock    *fslock.Lock
    58  	runListener *RunListener
    59  
    60  	ranConfigChanged bool
    61  
    62  	// The execution observer is only used in tests at this stage. Should this
    63  	// need to be extended, perhaps a list of observers would be needed.
    64  	observer UniterExecutionObserver
    65  
    66  	// collectMetricsAt defines a function that will be used to generate signals
    67  	// for the collect-metrics hook.
    68  	collectMetricsAt CollectMetricsSignal
    69  }
    70  
    71  // NewUniter creates a new Uniter which will install, run, and upgrade
    72  // a charm on behalf of the unit with the given unitTag, by executing
    73  // hooks and operations provoked by changes in st.
    74  func NewUniter(st *uniter.State, unitTag names.UnitTag, dataDir string, hookLock *fslock.Lock) *Uniter {
    75  	u := &Uniter{
    76  		st:               st,
    77  		paths:            NewPaths(dataDir, unitTag),
    78  		hookLock:         hookLock,
    79  		collectMetricsAt: inactiveMetricsTimer,
    80  	}
    81  	go func() {
    82  		defer u.tomb.Done()
    83  		u.tomb.Kill(u.loop(unitTag))
    84  	}()
    85  	return u
    86  }
    87  
    88  func (u *Uniter) loop(unitTag names.UnitTag) (err error) {
    89  	if err := u.init(unitTag); err != nil {
    90  		if err == worker.ErrTerminateAgent {
    91  			return err
    92  		}
    93  		return fmt.Errorf("failed to initialize uniter for %q: %v", unitTag, err)
    94  	}
    95  	defer u.runListener.Close()
    96  	logger.Infof("unit %q started", u.unit)
    97  
    98  	// Start filtering state change events for consumption by modes.
    99  	u.f, err = filter.NewFilter(u.st, unitTag)
   100  	if err != nil {
   101  		return err
   102  	}
   103  	defer watcher.Stop(u.f, &u.tomb)
   104  	go func() {
   105  		u.tomb.Kill(u.f.Wait())
   106  	}()
   107  
   108  	// Run modes until we encounter an error.
   109  	mode := ModeContinue
   110  	for err == nil {
   111  		select {
   112  		case <-u.tomb.Dying():
   113  			err = tomb.ErrDying
   114  		default:
   115  			mode, err = mode(u)
   116  			switch cause := errors.Cause(err); cause {
   117  			case operation.ErrHookFailed:
   118  				mode, err = ModeHookError, nil
   119  			case operation.ErrNeedsReboot:
   120  				err = worker.ErrRebootMachine
   121  			case tomb.ErrDying, worker.ErrTerminateAgent:
   122  				err = cause
   123  			}
   124  		}
   125  	}
   126  	logger.Infof("unit %q shutting down: %s", u.unit, err)
   127  	return err
   128  }
   129  
   130  func (u *Uniter) setupLocks() (err error) {
   131  	if message := u.hookLock.Message(); u.hookLock.IsLocked() && message != "" {
   132  		// Look to see if it was us that held the lock before.  If it was, we
   133  		// should be safe enough to break it, as it is likely that we died
   134  		// before unlocking, and have been restarted by the init system.
   135  		parts := strings.SplitN(message, ":", 2)
   136  		if len(parts) > 1 && parts[0] == u.unit.Name() {
   137  			if err := u.hookLock.BreakLock(); err != nil {
   138  				return err
   139  			}
   140  		}
   141  	}
   142  	return nil
   143  }
   144  
   145  func (u *Uniter) init(unitTag names.UnitTag) (err error) {
   146  	u.unit, err = u.st.Unit(unitTag)
   147  	if err != nil {
   148  		return err
   149  	}
   150  	if u.unit.Life() == params.Dead {
   151  		// If we started up already dead, we should not progress further. If we
   152  		// become Dead immediately after starting up, we may well complete any
   153  		// operations in progress before detecting it; but that race is fundamental
   154  		// and inescapable, whereas this one is not.
   155  		return worker.ErrTerminateAgent
   156  	}
   157  	if err = u.setupLocks(); err != nil {
   158  		return err
   159  	}
   160  	if err := jujuc.EnsureSymlinks(u.paths.ToolsDir); err != nil {
   161  		return err
   162  	}
   163  	if err := os.MkdirAll(u.paths.State.RelationsDir, 0755); err != nil {
   164  		return errors.Trace(err)
   165  	}
   166  	relations, err := newRelations(u.st, unitTag, u.paths, u.tomb.Dying())
   167  	if err != nil {
   168  		return errors.Annotatef(err, "cannot create relations")
   169  	}
   170  	u.relations = relations
   171  
   172  	deployer, err := charm.NewDeployer(
   173  		u.paths.State.CharmDir,
   174  		u.paths.State.DeployerDir,
   175  		charm.NewBundlesDir(u.paths.State.BundlesDir),
   176  	)
   177  	if err != nil {
   178  		return errors.Annotatef(err, "cannot create deployer")
   179  	}
   180  	u.deployer = &deployerProxy{deployer}
   181  	runnerFactory, err := runner.NewFactory(
   182  		u.st, unitTag, u.relations.GetInfo, u.paths,
   183  	)
   184  	if err != nil {
   185  		return err
   186  	}
   187  	u.operationFactory = operation.NewFactory(
   188  		u.deployer,
   189  		runnerFactory,
   190  		&operationCallbacks{u},
   191  		u.tomb.Dying(),
   192  	)
   193  
   194  	operationExecutor, err := operation.NewExecutor(
   195  		u.paths.State.OperationsFile, u.getServiceCharmURL,
   196  	)
   197  	if err != nil {
   198  		return err
   199  	}
   200  	u.operationExecutor = operationExecutor
   201  
   202  	logger.Debugf("starting juju-run listener on unix:%s", u.paths.Runtime.JujuRunSocket)
   203  	u.runListener, err = NewRunListener(u, u.paths.Runtime.JujuRunSocket)
   204  	if err != nil {
   205  		return err
   206  	}
   207  	// The socket needs to have permissions 777 in order for other users to use it.
   208  	if version.Current.OS != version.Windows {
   209  		return os.Chmod(u.paths.Runtime.JujuRunSocket, 0777)
   210  	}
   211  	return nil
   212  }
   213  
   214  func (u *Uniter) Kill() {
   215  	u.tomb.Kill(nil)
   216  }
   217  
   218  func (u *Uniter) Wait() error {
   219  	return u.tomb.Wait()
   220  }
   221  
   222  func (u *Uniter) Stop() error {
   223  	u.tomb.Kill(nil)
   224  	return u.Wait()
   225  }
   226  
   227  func (u *Uniter) Dead() <-chan struct{} {
   228  	return u.tomb.Dead()
   229  }
   230  
   231  func (u *Uniter) getServiceCharmURL() (*corecharm.URL, error) {
   232  	// TODO(fwereade): pretty sure there's no reason to make 2 API calls here.
   233  	service, err := u.st.Service(u.unit.ServiceTag())
   234  	if err != nil {
   235  		return nil, err
   236  	}
   237  	charmURL, _, err := service.CharmURL()
   238  	return charmURL, err
   239  }
   240  
   241  func (u *Uniter) operationState() operation.State {
   242  	return u.operationExecutor.State()
   243  }
   244  
   245  // initializeMetricsCollector enables the periodic collect-metrics hook
   246  // for charms that declare metrics.
   247  func (u *Uniter) initializeMetricsCollector() error {
   248  	charm, err := corecharm.ReadCharmDir(u.paths.State.CharmDir)
   249  	if err != nil {
   250  		return err
   251  	}
   252  	u.collectMetricsAt = getMetricsTimer(charm)
   253  	return nil
   254  }
   255  
   256  // RunCommands executes the supplied commands in a hook context.
   257  func (u *Uniter) RunCommands(args RunCommandsArgs) (results *exec.ExecResponse, err error) {
   258  	// TODO(fwereade): this is *still* all sorts of messed-up and not especially
   259  	// goroutine-safe, but that's not what I'm fixing at the moment. We could
   260  	// address this by:
   261  	//  1) implementing an operation to encapsulate the relations.Update call
   262  	//  2) (quick+dirty) mutex runOperation until we can
   263  	//  3) (correct) feed RunCommands requests into the mode funcs (or any queue
   264  	//     that replaces them) such that they're handled and prioritised like
   265  	//     every other operation.
   266  	logger.Tracef("run commands: %s", args.Commands)
   267  
   268  	type responseInfo struct {
   269  		response *exec.ExecResponse
   270  		err      error
   271  	}
   272  	responseChan := make(chan responseInfo, 1)
   273  	sendResponse := func(response *exec.ExecResponse, err error) {
   274  		responseChan <- responseInfo{response, err}
   275  	}
   276  
   277  	commandArgs := operation.CommandArgs{
   278  		Commands:        args.Commands,
   279  		RelationId:      args.RelationId,
   280  		RemoteUnitName:  args.RemoteUnitName,
   281  		ForceRemoteUnit: args.ForceRemoteUnit,
   282  	}
   283  	err = u.runOperation(newCommandsOp(commandArgs, sendResponse))
   284  	if err == nil {
   285  		select {
   286  		case response := <-responseChan:
   287  			results, err = response.response, response.err
   288  		default:
   289  			err = errors.New("command response never sent")
   290  		}
   291  	}
   292  	if errors.Cause(err) == operation.ErrNeedsReboot {
   293  		u.tomb.Kill(worker.ErrRebootMachine)
   294  		err = nil
   295  	}
   296  	if err != nil {
   297  		u.tomb.Kill(err)
   298  	}
   299  	return results, err
   300  }
   301  
   302  // runOperation uses the uniter's operation factory to run the supplied creation
   303  // func, and then runs the resulting operation.
   304  //
   305  // This has a number of advantages over having mode funcs use the factory and
   306  // executor directly:
   307  //   * it cuts down on duplicated code in the mode funcs, making the logic easier
   308  //     to parse
   309  //   * it narrows the (conceptual) interface exposed to the mode funcs -- one day
   310  //     we might even be able to use a (real) interface and maybe even approach a
   311  //     point where we can run direct unit tests(!) on the modes themselves.
   312  //   * it opens a path to fixing RunCommands -- all operation creation and
   313  //     execution is done in a single place, and it's much easier to force those
   314  //     onto a single thread.
   315  //       * this can't be done quite yet, though, because relation changes are
   316  //         not yet encapsulated in operations, and that needs to happen before
   317  //         RunCommands will *actually* be goroutine-safe.
   318  func (u *Uniter) runOperation(creator creator) error {
   319  	op, err := creator(u.operationFactory)
   320  	if err != nil {
   321  		return errors.Annotatef(err, "cannot create operation")
   322  	}
   323  	return u.operationExecutor.Run(op)
   324  }