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