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