github.com/cloudbase/juju-core@v0.0.0-20140504232958-a7271ac7912f/worker/uniter/uniter.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package uniter
     5  
     6  import (
     7  	stderrors "errors"
     8  	"fmt"
     9  	"math/rand"
    10  	"path/filepath"
    11  	"strings"
    12  	"sync"
    13  
    14  	"github.com/juju/loggo"
    15  	"launchpad.net/tomb"
    16  
    17  	corecharm "launchpad.net/juju-core/charm"
    18  	"launchpad.net/juju-core/charm/hooks"
    19  	"launchpad.net/juju-core/environs/config"
    20  	"launchpad.net/juju-core/juju/osenv"
    21  	"launchpad.net/juju-core/state/api/params"
    22  	"launchpad.net/juju-core/state/api/uniter"
    23  	apiwatcher "launchpad.net/juju-core/state/api/watcher"
    24  	"launchpad.net/juju-core/state/watcher"
    25  	"launchpad.net/juju-core/utils/exec"
    26  	"launchpad.net/juju-core/utils/fslock"
    27  	"launchpad.net/juju-core/worker/uniter/charm"
    28  	"launchpad.net/juju-core/worker/uniter/hook"
    29  	"launchpad.net/juju-core/worker/uniter/relation"
    30  )
    31  
    32  var logger = loggo.GetLogger("juju.worker.uniter")
    33  
    34  const (
    35  	// These work fine for linux, but should we need to work with windows
    36  	// workloads in the future, we'll need to move these into a file that is
    37  	// compiled conditionally for different targets and use tcp (most likely).
    38  
    39  	// gsamfira: On Windows this file will be a text file we will use it to store
    40  	// the TCP port nr until we implement RPC over named pipes
    41  	RunListenerFile = "run.socket"
    42  )
    43  
    44  // A UniterExecutionObserver gets the appropriate methods called when a hook
    45  // is executed and either succeeds or fails.  Missing hooks don't get reported
    46  // in this way.
    47  type UniterExecutionObserver interface {
    48  	HookCompleted(hookName string)
    49  	HookFailed(hookName string)
    50  }
    51  
    52  // Uniter implements the capabilities of the unit agent. It is not intended to
    53  // implement the actual *behaviour* of the unit agent; that responsibility is
    54  // delegated to Mode values, which are expected to react to events and direct
    55  // the uniter's responses to them.
    56  type Uniter struct { 
    57  	tomb          tomb.Tomb
    58  	st            *uniter.State
    59  	f             *filter
    60  	unit          *uniter.Unit
    61  	service       *uniter.Service
    62  	relationers   map[int]*Relationer
    63  	relationHooks chan hook.Info
    64  	uuid          string
    65  	envName       string
    66  
    67  	dataDir      string
    68  	baseDir      string
    69  	toolsDir     string
    70  	relationsDir string
    71  	charm        *charm.GitDir
    72  	deployer     charm.Deployer
    73  	s            *State
    74  	sf           *StateFile
    75  	rand         *rand.Rand
    76  	hookLock     *fslock.Lock
    77  	runListener  *RunListener
    78  	tcpSock      string
    79  
    80  	proxy      osenv.ProxySettings
    81  	proxyMutex sync.Mutex
    82  
    83  	ranConfigChanged bool
    84  	// The execution observer is only used in tests at this stage. Should this
    85  	// need to be extended, perhaps a list of observers would be needed.
    86  	observer UniterExecutionObserver
    87  }
    88  
    89  // NewUniter creates a new Uniter which will install, run, and upgrade
    90  // a charm on behalf of the unit with the given unitTag, by executing
    91  // hooks and operations provoked by changes in st.
    92  func NewUniter(st *uniter.State, unitTag string, dataDir string) *Uniter {
    93  	u := &Uniter{
    94  		st:      st,
    95  		dataDir: dataDir,
    96  	}
    97  	go func() {
    98  		defer u.tomb.Done()
    99  		u.tomb.Kill(u.loop(unitTag))
   100  	}()
   101  	return u
   102  }
   103  
   104  func (u *Uniter) loop(unitTag string) (err error) {
   105  	if err = u.init(unitTag); err != nil {
   106  		return err
   107  	}
   108  	defer u.runListener.Close()
   109  	logger.Infof("unit %q started", u.unit)
   110  
   111  	environWatcher, err := u.st.WatchForEnvironConfigChanges()
   112  	if err != nil {
   113  		return err
   114  	}
   115  	defer watcher.Stop(environWatcher, &u.tomb)
   116  	u.watchForProxyChanges(environWatcher)
   117  
   118  	// Start filtering state change events for consumption by modes.
   119  	u.f, err = newFilter(u.st, unitTag)
   120  	if err != nil {
   121  		return err
   122  	}
   123  	defer watcher.Stop(u.f, &u.tomb)
   124  	go func() {
   125  		u.tomb.Kill(u.f.Wait())
   126  	}()
   127  
   128  	// Run modes until we encounter an error.
   129  	mode := ModeInit
   130  	for err == nil {
   131  		select {
   132  		case <-u.tomb.Dying():
   133  			err = tomb.ErrDying
   134  		default:
   135  			mode, err = mode(u)
   136  		}
   137  	}
   138  	logger.Infof("unit %q shutting down: %s", u.unit, err)
   139  	return err
   140  }
   141  
   142  func (u *Uniter) setupLocks() (err error) {
   143  	lockDir := filepath.Join(u.dataDir, "locks")
   144  	u.hookLock, err = fslock.NewLock(lockDir, "uniter-hook-execution")
   145  	if err != nil {
   146  		return err
   147  	}
   148  	if message := u.hookLock.Message(); u.hookLock.IsLocked() && message != "" {
   149  		// Look to see if it was us that held the lock before.  If it was, we
   150  		// should be safe enough to break it, as it is likely that we died
   151  		// before unlocking, and have been restarted by upstart.
   152  		parts := strings.SplitN(message, ":", 2)
   153  		if len(parts) > 1 && parts[0] == u.unit.Name() {
   154  			if err := u.hookLock.BreakLock(); err != nil {
   155  				return err
   156  			}
   157  		}
   158  	}
   159  	return nil
   160  }
   161  
   162  func (u *Uniter) Kill() {
   163  	u.tomb.Kill(nil)
   164  }
   165  
   166  func (u *Uniter) Wait() error {
   167  	return u.tomb.Wait()
   168  }
   169  
   170  func (u *Uniter) Stop() error {
   171  	u.tomb.Kill(nil)
   172  	return u.Wait()
   173  }
   174  
   175  func (u *Uniter) Dead() <-chan struct{} {
   176  	return u.tomb.Dead()
   177  }
   178  
   179  // writeState saves uniter state with the supplied values, and infers the appropriate
   180  // value of Started.
   181  func (u *Uniter) writeState(op Op, step OpStep, hi *hook.Info, url *corecharm.URL) error {
   182  	s := State{
   183  		Started:  op == RunHook && hi.Kind == hooks.Start || u.s != nil && u.s.Started,
   184  		Op:       op,
   185  		OpStep:   step,
   186  		Hook:     hi,
   187  		CharmURL: url,
   188  	}
   189  	if err := u.sf.Write(s.Started, s.Op, s.OpStep, s.Hook, s.CharmURL); err != nil {
   190  		return err
   191  	}
   192  	u.s = &s
   193  	return nil
   194  }
   195  
   196  // deploy deploys the supplied charm URL, and sets follow-up hook operation state
   197  // as indicated by reason.
   198  func (u *Uniter) deploy(curl *corecharm.URL, reason Op) error {
   199  	if reason != Install && reason != Upgrade {
   200  		panic(fmt.Errorf("%q is not a deploy operation", reason))
   201  	}
   202  	var hi *hook.Info
   203  	if u.s != nil && (u.s.Op == RunHook || u.s.Op == Upgrade) {
   204  		// If this upgrade interrupts a RunHook, we need to preserve the hook
   205  		// info so that we can return to the appropriate error state. However,
   206  		// if we're resuming (or have force-interrupted) an Upgrade, we also
   207  		// need to preserve whatever hook info was preserved when we initially
   208  		// started upgrading, to ensure we still return to the correct state.
   209  		hi = u.s.Hook
   210  	}
   211  	if u.s == nil || u.s.OpStep != Done {
   212  		// Get the new charm bundle before announcing intention to use it.
   213  		logger.Infof("fetching charm %q", curl)
   214  		sch, err := u.st.Charm(curl)
   215  		if err != nil {
   216  			return err
   217  		}
   218  		if err = u.deployer.Stage(sch, u.tomb.Dying()); err != nil {
   219  			return err
   220  		}
   221  
   222  		// Set the new charm URL - this returns when the operation is complete,
   223  		// at which point we can refresh the local copy of the unit to get a
   224  		// version with the correct charm URL, and can go ahead and deploy
   225  		// the charm proper.
   226  		if err := u.f.SetCharm(curl); err != nil {
   227  			return err
   228  		}
   229  		if err := u.unit.Refresh(); err != nil {
   230  			return err
   231  		}
   232  		logger.Infof("deploying charm %q", curl)
   233  		if err = u.writeState(reason, Pending, hi, curl); err != nil {
   234  			return err
   235  		}
   236  		if err = u.deployer.Deploy(); err != nil {
   237  			return err
   238  		}
   239  		if err = u.writeState(reason, Done, hi, curl); err != nil {
   240  			return err
   241  		}
   242  	}
   243  	logger.Infof("charm %q is deployed", curl)
   244  	status := Queued
   245  	if hi != nil {
   246  		// If a hook operation was interrupted, restore it.
   247  		status = Pending
   248  	} else {
   249  		// Otherwise, queue the relevant post-deploy hook.
   250  		hi = &hook.Info{}
   251  		switch reason {
   252  		case Install:
   253  			hi.Kind = hooks.Install
   254  		case Upgrade:
   255  			hi.Kind = hooks.UpgradeCharm
   256  		}
   257  	}
   258  	return u.writeState(RunHook, status, hi, nil)
   259  }
   260  
   261  // errHookFailed indicates that a hook failed to execute, but that the Uniter's
   262  // operation is not affected by the error.
   263  var errHookFailed = stderrors.New("hook execution failed")
   264  
   265  func (u *Uniter) getHookContext(hctxId string, relationId int, remoteUnitName string) (context *HookContext, err error) {
   266  
   267  	apiAddrs, err := u.st.APIAddresses()
   268  	if err != nil {
   269  		return nil, err
   270  	}
   271  	ownerTag, err := u.service.GetOwnerTag()
   272  	if err != nil {
   273  		return nil, err
   274  	}
   275  	ctxRelations := map[int]*ContextRelation{}
   276  	for id, r := range u.relationers {
   277  		ctxRelations[id] = r.Context()
   278  	}
   279  
   280  	u.proxyMutex.Lock()
   281  	defer u.proxyMutex.Unlock()
   282  
   283  	// Make a copy of the proxy settings.
   284  	proxySettings := u.proxy
   285  	return NewHookContext(u.unit, hctxId, u.uuid, u.envName, relationId,
   286  		remoteUnitName, ctxRelations, apiAddrs, ownerTag, proxySettings)
   287  }
   288  
   289  func (u *Uniter) acquireHookLock(message string) (err error) {
   290  	// We want to make sure we don't block forever when locking, but take the
   291  	// tomb into account.
   292  	checkTomb := func() error {
   293  		select {
   294  		case <-u.tomb.Dying():
   295  			return tomb.ErrDying
   296  		default:
   297  			// no-op to fall through to return.
   298  		}
   299  		return nil
   300  	}
   301  	if err = u.hookLock.LockWithFunc(message, checkTomb); err != nil {
   302  		return err
   303  	}
   304  	return nil
   305  }
   306  
   307  // RunCommands executes the supplied commands in a hook context.
   308  func (u *Uniter) RunCommands(commands string) (results *exec.ExecResponse, err error) {
   309  	logger.Tracef("run commands: %s", commands)
   310  	hctxId := fmt.Sprintf("%s:run-commands:%d", u.unit.Name(), u.rand.Int63())
   311  	lockMessage := fmt.Sprintf("%s: running commands", u.unit.Name())
   312  	if err = u.acquireHookLock(lockMessage); err != nil {
   313  		return nil, err
   314  	}
   315  	defer u.hookLock.Unlock()
   316  
   317  	hctx, err := u.getHookContext(hctxId, -1, "")
   318  	if err != nil {
   319  		return nil, err
   320  	}
   321  	srv, socketPath, err := u.startJujucServer(hctx)
   322  	if err != nil {
   323  		return nil, err
   324  	}
   325  	defer srv.Close()
   326  
   327  	result, err := hctx.RunCommands(commands, u.charm.Path(), u.toolsDir, socketPath)
   328  	if result != nil {
   329  		logger.Tracef("run commands: rc=%v\nstdout:\n%sstderr:\n%s", result.Code, result.Stdout, result.Stderr)
   330  	}
   331  	return result, err
   332  }
   333  
   334  func (u *Uniter) notifyHookInternal(hook string, hctx *HookContext, method func(string)) {
   335  	if r, ok := hctx.HookRelation(); ok {
   336  		remote, _ := hctx.RemoteUnitName()
   337  		if remote != "" {
   338  			remote = " " + remote
   339  		}
   340  		hook = hook + remote + " " + r.FakeId()
   341  	}
   342  	method(hook)
   343  }
   344  
   345  func (u *Uniter) notifyHookCompleted(hook string, hctx *HookContext) {
   346  	if u.observer != nil {
   347  		u.notifyHookInternal(hook, hctx, u.observer.HookCompleted)
   348  	}
   349  }
   350  
   351  func (u *Uniter) notifyHookFailed(hook string, hctx *HookContext) {
   352  	if u.observer != nil {
   353  		u.notifyHookInternal(hook, hctx, u.observer.HookFailed)
   354  	}
   355  }
   356  
   357  
   358  // commitHook ensures that state is consistent with the supplied hook, and
   359  // that the fact of the hook's completion is persisted.
   360  func (u *Uniter) commitHook(hi hook.Info) error {
   361  	logger.Infof("committing %q hook", hi.Kind)
   362  	if hi.Kind.IsRelation() {
   363  		if err := u.relationers[hi.RelationId].CommitHook(hi); err != nil {
   364  			return err
   365  		}
   366  		if hi.Kind == hooks.RelationBroken {
   367  			delete(u.relationers, hi.RelationId)
   368  		}
   369  	}
   370  	if hi.Kind == hooks.ConfigChanged {
   371  		u.ranConfigChanged = true
   372  	}
   373  	if err := u.writeState(Continue, Pending, &hi, nil); err != nil {
   374  		return err
   375  	}
   376  	logger.Infof("committed %q hook", hi.Kind)
   377  	return nil
   378  }
   379  
   380  // currentHookName returns the current full hook name.
   381  func (u *Uniter) currentHookName() string {
   382  	hookInfo := u.s.Hook
   383  	hookName := string(hookInfo.Kind)
   384  	if hookInfo.Kind.IsRelation() {
   385  		relationer := u.relationers[hookInfo.RelationId]
   386  		name := relationer.ru.Endpoint().Name
   387  		hookName = fmt.Sprintf("%s-%s", name, hookInfo.Kind)
   388  	}
   389  	return hookName
   390  }
   391  
   392  // restoreRelations reconciles the supplied relation state dirs with the
   393  // remote state of the corresponding relations.
   394  func (u *Uniter) restoreRelations() error {
   395  	// TODO(dimitern): Get these from state, not from disk.
   396  	dirs, err := relation.ReadAllStateDirs(u.relationsDir)
   397  	if err != nil {
   398  		return err
   399  	}
   400  	for id, dir := range dirs {
   401  		remove := false
   402  		rel, err := u.st.RelationById(id)
   403  		if params.IsCodeNotFoundOrCodeUnauthorized(err) {
   404  			remove = true
   405  		} else if err != nil {
   406  			return err
   407  		}
   408  		err = u.addRelation(rel, dir)
   409  		if params.IsCodeCannotEnterScope(err) {
   410  			remove = true
   411  		} else if err != nil {
   412  			return err
   413  		}
   414  		if remove {
   415  			// If the previous execution was interrupted in the process of
   416  			// joining or departing the relation, the directory will be empty
   417  			// and the state is sane.
   418  			if err := dir.Remove(); err != nil {
   419  				return fmt.Errorf("cannot synchronize relation state: %v", err)
   420  			}
   421  		}
   422  	}
   423  	return nil
   424  }
   425  
   426  // updateRelations responds to changes in the life states of the relations
   427  // with the supplied ids. If any id corresponds to an alive relation not
   428  // known to the unit, the uniter will join that relation and return its
   429  // relationer in the added list.
   430  func (u *Uniter) updateRelations(ids []int) (added []*Relationer, err error) {
   431  	for _, id := range ids {
   432  		if r, found := u.relationers[id]; found {
   433  			rel := r.ru.Relation()
   434  			if err := rel.Refresh(); err != nil {
   435  				return nil, fmt.Errorf("cannot update relation %q: %v", rel, err)
   436  			}
   437  			if rel.Life() == params.Dying {
   438  				if err := r.SetDying(); err != nil {
   439  					return nil, err
   440  				} else if r.IsImplicit() {
   441  					delete(u.relationers, id)
   442  				}
   443  			}
   444  			continue
   445  		}
   446  		// Relations that are not alive are simply skipped, because they
   447  		// were not previously known anyway.
   448  		rel, err := u.st.RelationById(id)
   449  		if err != nil {
   450  			if params.IsCodeNotFoundOrCodeUnauthorized(err) {
   451  				continue
   452  			}
   453  			return nil, err
   454  		}
   455  		if rel.Life() != params.Alive {
   456  			continue
   457  		}
   458  		// Make sure we ignore relations not implemented by the unit's charm
   459  		ch, err := corecharm.ReadDir(u.charm.Path())
   460  		if err != nil {
   461  			return nil, err
   462  		}
   463  		if ep, err := rel.Endpoint(); err != nil {
   464  			return nil, err
   465  		} else if !ep.ImplementedBy(ch) {
   466  			logger.Warningf("skipping relation with unknown endpoint %q", ep.Name)
   467  			continue
   468  		}
   469  		dir, err := relation.ReadStateDir(u.relationsDir, id)
   470  		if err != nil {
   471  			return nil, err
   472  		}
   473  		err = u.addRelation(rel, dir)
   474  		if err == nil {
   475  			added = append(added, u.relationers[id])
   476  			continue
   477  		}
   478  		e := dir.Remove()
   479  		if !params.IsCodeCannotEnterScope(err) {
   480  			return nil, err
   481  		}
   482  		if e != nil {
   483  			return nil, e
   484  		}
   485  	}
   486  	if ok, err := u.unit.IsPrincipal(); err != nil {
   487  		return nil, err
   488  	} else if ok {
   489  		return added, nil
   490  	}
   491  	// If no Alive relations remain between a subordinate unit's service
   492  	// and its principal's service, the subordinate must become Dying.
   493  	keepAlive := false
   494  	for _, r := range u.relationers {
   495  		scope := r.ru.Endpoint().Scope
   496  		if scope == corecharm.ScopeContainer && !r.dying {
   497  			keepAlive = true
   498  			break
   499  		}
   500  	}
   501  	if !keepAlive {
   502  		if err := u.unit.Destroy(); err != nil {
   503  			return nil, err
   504  		}
   505  	}
   506  	return added, nil
   507  }
   508  
   509  // addRelation causes the unit agent to join the supplied relation, and to
   510  // store persistent state in the supplied dir.
   511  func (u *Uniter) addRelation(rel *uniter.Relation, dir *relation.StateDir) error {
   512  	logger.Infof("joining relation %q", rel)
   513  	ru, err := rel.Unit(u.unit)
   514  	if err != nil {
   515  		return err
   516  	}
   517  	r := NewRelationer(ru, dir, u.relationHooks)
   518  	w, err := u.unit.Watch()
   519  	if err != nil {
   520  		return err
   521  	}
   522  	defer watcher.Stop(w, &u.tomb)
   523  	for {
   524  		select {
   525  		case <-u.tomb.Dying():
   526  			return tomb.ErrDying
   527  		case _, ok := <-w.Changes():
   528  			if !ok {
   529  				return watcher.MustErr(w)
   530  			}
   531  			err := r.Join()
   532  			if params.IsCodeCannotEnterScopeYet(err) {
   533  				logger.Infof("cannot enter scope for relation %q; waiting for subordinate to be removed", rel)
   534  				continue
   535  			} else if err != nil {
   536  				return err
   537  			}
   538  			logger.Infof("joined relation %q", rel)
   539  			u.relationers[rel.Id()] = r
   540  			return nil
   541  		}
   542  	}
   543  }
   544  
   545  // updatePackageProxy updates the package proxy settings from the
   546  // environment.
   547  func (u *Uniter) updatePackageProxy(cfg *config.Config) {
   548  	u.proxyMutex.Lock()
   549  	defer u.proxyMutex.Unlock()
   550  
   551  	newSettings := cfg.ProxySettings()
   552  	if u.proxy != newSettings {
   553  		u.proxy = newSettings
   554  		logger.Debugf("Updated proxy settings: %#v", u.proxy)
   555  		// Update the environment values used by the process.
   556  		u.proxy.SetEnvironmentValues()
   557  	}
   558  }
   559  
   560  // watchForProxyChanges kicks off a go routine to listen to the watcher and
   561  // update the proxy settings.
   562  func (u *Uniter) watchForProxyChanges(environWatcher apiwatcher.NotifyWatcher) {
   563  	go func() {
   564  		for {
   565  			select {
   566  			case <-u.tomb.Dying():
   567  				return
   568  			case _, ok := <-environWatcher.Changes():
   569  				logger.Debugf("new environment change")
   570  				if !ok {
   571  					return
   572  				}
   573  				environConfig, err := u.st.EnvironConfig()
   574  				if err != nil {
   575  					logger.Errorf("cannot load environment configuration: %v", err)
   576  				} else {
   577  					u.updatePackageProxy(environConfig)
   578  				}
   579  			}
   580  		}
   581  	}()
   582  }