github.com/cloudbase/juju-core@v0.0.0-20140504232958-a7271ac7912f/worker/uniter/modes.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  
    10  	"launchpad.net/tomb"
    11  
    12  	"launchpad.net/juju-core/charm"
    13  	"launchpad.net/juju-core/charm/hooks"
    14  	"launchpad.net/juju-core/environs"
    15  	"launchpad.net/juju-core/state/api/params"
    16  	"launchpad.net/juju-core/state/watcher"
    17  	"launchpad.net/juju-core/worker"
    18  	ucharm "launchpad.net/juju-core/worker/uniter/charm"
    19  	"launchpad.net/juju-core/worker/uniter/hook"
    20  )
    21  
    22  // Mode defines the signature of the functions that implement the possible
    23  // states of a running Uniter.
    24  type Mode func(u *Uniter) (Mode, error)
    25  
    26  // ModeInit is the initial Uniter mode.
    27  func ModeInit(u *Uniter) (next Mode, err error) {
    28  	defer modeContext("ModeInit", &err)()
    29  	logger.Infof("updating unit addresses")
    30  	// TODO(dimitern): We might be able to drop all this address stuff
    31  	// entirely once we have machine addresses.
    32  	providerType, err := u.st.ProviderType()
    33  	if err != nil {
    34  		return nil, err
    35  	}
    36  	provider, err := environs.Provider(providerType)
    37  	if err != nil {
    38  		return nil, err
    39  	}
    40  	if private, err := provider.PrivateAddress(); err != nil {
    41  		logger.Errorf("cannot get unit's private address: %v", err)
    42  		return nil, err
    43  	} else if err = u.unit.SetPrivateAddress(private); err != nil {
    44  		logger.Errorf("cannot set unit's private address: %v", err)
    45  		return nil, err
    46  	}
    47  	if public, err := provider.PublicAddress(); err != nil {
    48  		logger.Errorf("cannot get unit's public address: %v", err)
    49  		return nil, err
    50  	} else if err = u.unit.SetPublicAddress(public); err != nil {
    51  		logger.Errorf("cannot set unit's public address: %v", err)
    52  		return nil, err
    53  	}
    54  	logger.Infof("reconciling relation state")
    55  	if err := u.restoreRelations(); err != nil {
    56  		return nil, err
    57  	}
    58  	return ModeContinue, nil
    59  }
    60  
    61  // ModeContinue determines what action to take based on persistent uniter state.
    62  func ModeContinue(u *Uniter) (next Mode, err error) {
    63  	defer modeContext("ModeContinue", &err)()
    64  
    65  	// If we haven't yet loaded state, do so.
    66  	if u.s == nil {
    67  		logger.Infof("loading uniter state")
    68  		if u.s, err = u.sf.Read(); err == ErrNoStateFile {
    69  			// When no state exists, start from scratch.
    70  			logger.Infof("charm is not deployed")
    71  			curl, _, err := u.service.CharmURL()
    72  			if err != nil {
    73  				return nil, err
    74  			}
    75  			return ModeInstalling(curl), nil
    76  		} else if err != nil {
    77  			return nil, err
    78  		}
    79  	}
    80  
    81  	// Filter out states not related to charm deployment.
    82  	switch u.s.Op {
    83  	case Continue:
    84  		logger.Infof("continuing after %q hook", u.s.Hook.Kind)
    85  		switch u.s.Hook.Kind {
    86  		case hooks.Stop:
    87  			return ModeTerminating, nil
    88  		case hooks.UpgradeCharm:
    89  			return ModeConfigChanged, nil
    90  		case hooks.ConfigChanged:
    91  			if !u.s.Started {
    92  				return ModeStarting, nil
    93  			}
    94  		}
    95  		if !u.ranConfigChanged {
    96  			return ModeConfigChanged, nil
    97  		}
    98  		return ModeAbide, nil
    99  	case RunHook:
   100  		if u.s.OpStep == Queued {
   101  			logger.Infof("found queued %q hook", u.s.Hook.Kind)
   102  			if err = u.runHook(*u.s.Hook); err != nil && err != errHookFailed {
   103  				return nil, err
   104  			}
   105  			return ModeContinue, nil
   106  		}
   107  		if u.s.OpStep == Done {
   108  			logger.Infof("found uncommitted %q hook", u.s.Hook.Kind)
   109  			if err = u.commitHook(*u.s.Hook); err != nil {
   110  				return nil, err
   111  			}
   112  			return ModeContinue, nil
   113  		}
   114  		logger.Infof("awaiting error resolution for %q hook", u.s.Hook.Kind)
   115  		return ModeHookError, nil
   116  	}
   117  
   118  	// Resume interrupted deployment operations.
   119  	curl := u.s.CharmURL
   120  	if u.s.Op == Install {
   121  		logger.Infof("resuming charm install")
   122  		return ModeInstalling(curl), nil
   123  	} else if u.s.Op == Upgrade {
   124  		logger.Infof("resuming charm upgrade")
   125  		return ModeUpgrading(curl), nil
   126  	}
   127  	panic(fmt.Errorf("unhandled uniter operation %q", u.s.Op))
   128  }
   129  
   130  // ModeInstalling is responsible for the initial charm deployment.
   131  func ModeInstalling(curl *charm.URL) Mode {
   132  	name := fmt.Sprintf("ModeInstalling %s", curl)
   133  	return func(u *Uniter) (next Mode, err error) {
   134  		defer modeContext(name, &err)()
   135  		if err = u.deploy(curl, Install); err != nil {
   136  			return nil, err
   137  		}
   138  		return ModeContinue, nil
   139  	}
   140  }
   141  
   142  // ModeUpgrading is responsible for upgrading the charm.
   143  func ModeUpgrading(curl *charm.URL) Mode {
   144  	name := fmt.Sprintf("ModeUpgrading %s", curl)
   145  	return func(u *Uniter) (next Mode, err error) {
   146  		defer modeContext(name, &err)()
   147  		if err = u.deploy(curl, Upgrade); err == ucharm.ErrConflict {
   148  			return ModeConflicted(curl), nil
   149  		} else if err != nil {
   150  			return nil, err
   151  		}
   152  		return ModeContinue, nil
   153  	}
   154  }
   155  
   156  // ModeConfigChanged runs the "config-changed" hook.
   157  func ModeConfigChanged(u *Uniter) (next Mode, err error) {
   158  	defer modeContext("ModeConfigChanged", &err)()
   159  	if !u.s.Started {
   160  		if err = u.unit.SetStatus(params.StatusInstalled, "", nil); err != nil {
   161  			return nil, err
   162  		}
   163  	}
   164  	u.f.DiscardConfigEvent()
   165  	if err := u.runHook(hook.Info{Kind: hooks.ConfigChanged}); err == errHookFailed {
   166  		return ModeHookError, nil
   167  	} else if err != nil {
   168  		return nil, err
   169  	}
   170  	return ModeContinue, nil
   171  }
   172  
   173  // ModeStarting runs the "start" hook.
   174  func ModeStarting(u *Uniter) (next Mode, err error) {
   175  	defer modeContext("ModeStarting", &err)()
   176  	if err := u.runHook(hook.Info{Kind: hooks.Start}); err == errHookFailed {
   177  		return ModeHookError, nil
   178  	} else if err != nil {
   179  		return nil, err
   180  	}
   181  	return ModeContinue, nil
   182  }
   183  
   184  // ModeStopping runs the "stop" hook.
   185  func ModeStopping(u *Uniter) (next Mode, err error) {
   186  	defer modeContext("ModeStopping", &err)()
   187  	if err := u.runHook(hook.Info{Kind: hooks.Stop}); err == errHookFailed {
   188  		return ModeHookError, nil
   189  	} else if err != nil {
   190  		return nil, err
   191  	}
   192  	return ModeContinue, nil
   193  }
   194  
   195  // ModeTerminating marks the unit dead and returns ErrTerminateAgent.
   196  func ModeTerminating(u *Uniter) (next Mode, err error) {
   197  	defer modeContext("ModeTerminating", &err)()
   198  	if err = u.unit.SetStatus(params.StatusStopped, "", nil); err != nil {
   199  		return nil, err
   200  	}
   201  	w, err := u.unit.Watch()
   202  	if err != nil {
   203  		return nil, err
   204  	}
   205  	defer watcher.Stop(w, &u.tomb)
   206  	for {
   207  		select {
   208  		case <-u.tomb.Dying():
   209  			return nil, tomb.ErrDying
   210  		case _, ok := <-w.Changes():
   211  			if !ok {
   212  				return nil, watcher.MustErr(w)
   213  			}
   214  			if err := u.unit.Refresh(); err != nil {
   215  				return nil, err
   216  			}
   217  			if hasSubs, err := u.unit.HasSubordinates(); err != nil {
   218  				return nil, err
   219  			} else if hasSubs {
   220  				continue
   221  			}
   222  			// The unit is known to be Dying; so if it didn't have subordinates
   223  			// just above, it can't acquire new ones before this call.
   224  			if err := u.unit.EnsureDead(); err != nil {
   225  				return nil, err
   226  			}
   227  			return nil, worker.ErrTerminateAgent
   228  		}
   229  	}
   230  }
   231  
   232  // ModeAbide is the Uniter's usual steady state. It watches for and responds to:
   233  // * service configuration changes
   234  // * charm upgrade requests
   235  // * relation changes
   236  // * unit death
   237  func ModeAbide(u *Uniter) (next Mode, err error) {
   238  	defer modeContext("ModeAbide", &err)()
   239  	if u.s.Op != Continue {
   240  		return nil, fmt.Errorf("insane uniter state: %#v", u.s)
   241  	}
   242  	if err = u.unit.SetStatus(params.StatusStarted, "", nil); err != nil {
   243  		return nil, err
   244  	}
   245  	u.f.WantUpgradeEvent(false)
   246  	for _, r := range u.relationers {
   247  		r.StartHooks()
   248  	}
   249  	defer func() {
   250  		for _, r := range u.relationers {
   251  			if e := r.StopHooks(); e != nil && err == nil {
   252  				err = e
   253  			}
   254  		}
   255  	}()
   256  	select {
   257  	case <-u.f.UnitDying():
   258  		return modeAbideDyingLoop(u)
   259  	default:
   260  	}
   261  	return modeAbideAliveLoop(u)
   262  }
   263  
   264  // modeAbideAliveLoop handles all state changes for ModeAbide when the unit
   265  // is in an Alive state.
   266  func modeAbideAliveLoop(u *Uniter) (Mode, error) {
   267  	for {
   268  		hi := hook.Info{}
   269  		select {
   270  		case <-u.tomb.Dying():
   271  			return nil, tomb.ErrDying
   272  		case <-u.f.UnitDying():
   273  			return modeAbideDyingLoop(u)
   274  		case <-u.f.ConfigEvents():
   275  			hi = hook.Info{Kind: hooks.ConfigChanged}
   276  		case hi = <-u.relationHooks:
   277  		case ids := <-u.f.RelationsEvents():
   278  			added, err := u.updateRelations(ids)
   279  			if err != nil {
   280  				return nil, err
   281  			}
   282  			for _, r := range added {
   283  				r.StartHooks()
   284  			}
   285  			continue
   286  		case curl := <-u.f.UpgradeEvents():
   287  			return ModeUpgrading(curl), nil
   288  		}
   289  		if err := u.runHook(hi); err == errHookFailed {
   290  			return ModeHookError, nil
   291  		} else if err != nil {
   292  			return nil, err
   293  		}
   294  	}
   295  }
   296  
   297  // modeAbideDyingLoop handles the proper termination of all relations in
   298  // response to a Dying unit.
   299  func modeAbideDyingLoop(u *Uniter) (next Mode, err error) {
   300  	if err := u.unit.Refresh(); err != nil {
   301  		return nil, err
   302  	}
   303  	if err = u.unit.DestroyAllSubordinates(); err != nil {
   304  		return nil, err
   305  	}
   306  	for id, r := range u.relationers {
   307  		if err := r.SetDying(); err != nil {
   308  			return nil, err
   309  		} else if r.IsImplicit() {
   310  			delete(u.relationers, id)
   311  		}
   312  	}
   313  	for {
   314  		if len(u.relationers) == 0 {
   315  			return ModeStopping, nil
   316  		}
   317  		hi := hook.Info{}
   318  		select {
   319  		case <-u.tomb.Dying():
   320  			return nil, tomb.ErrDying
   321  		case <-u.f.ConfigEvents():
   322  			hi = hook.Info{Kind: hooks.ConfigChanged}
   323  		case hi = <-u.relationHooks:
   324  		}
   325  		if err = u.runHook(hi); err == errHookFailed {
   326  			return ModeHookError, nil
   327  		} else if err != nil {
   328  			return nil, err
   329  		}
   330  	}
   331  }
   332  
   333  // ModeHookError is responsible for watching and responding to:
   334  // * user resolution of hook errors
   335  // * charm upgrade requests
   336  func ModeHookError(u *Uniter) (next Mode, err error) {
   337  	defer modeContext("ModeHookError", &err)()
   338  	if u.s.Op != RunHook || u.s.OpStep != Pending {
   339  		return nil, fmt.Errorf("insane uniter state: %#v", u.s)
   340  	}
   341  	msg := fmt.Sprintf("hook failed: %q", u.currentHookName())
   342  	// Create error information for status.
   343  	data := params.StatusData{"hook": u.currentHookName()}
   344  	if u.s.Hook.Kind.IsRelation() {
   345  		data["relation-id"] = u.s.Hook.RelationId
   346  		if u.s.Hook.RemoteUnit != "" {
   347  			data["remote-unit"] = u.s.Hook.RemoteUnit
   348  		}
   349  	}
   350  	if err = u.unit.SetStatus(params.StatusError, msg, data); err != nil {
   351  		return nil, err
   352  	}
   353  	u.f.WantResolvedEvent()
   354  	u.f.WantUpgradeEvent(true)
   355  	for {
   356  		select {
   357  		case <-u.tomb.Dying():
   358  			return nil, tomb.ErrDying
   359  		case rm := <-u.f.ResolvedEvents():
   360  			switch rm {
   361  			case params.ResolvedRetryHooks:
   362  				err = u.runHook(*u.s.Hook)
   363  			case params.ResolvedNoHooks:
   364  				err = u.commitHook(*u.s.Hook)
   365  			default:
   366  				return nil, fmt.Errorf("unknown resolved mode %q", rm)
   367  			}
   368  			if e := u.f.ClearResolved(); e != nil {
   369  				return nil, e
   370  			}
   371  			if err == errHookFailed {
   372  				continue
   373  			} else if err != nil {
   374  				return nil, err
   375  			}
   376  			return ModeContinue, nil
   377  		case curl := <-u.f.UpgradeEvents():
   378  			return ModeUpgrading(curl), nil
   379  		}
   380  	}
   381  }
   382  
   383  // ModeConflicted is responsible for watching and responding to:
   384  // * user resolution of charm upgrade conflicts
   385  // * forced charm upgrade requests
   386  func ModeConflicted(curl *charm.URL) Mode {
   387  	return func(u *Uniter) (next Mode, err error) {
   388  		defer modeContext("ModeConflicted", &err)()
   389  		// TODO(mue) Add helpful data here too in later CL.
   390  		if err = u.unit.SetStatus(params.StatusError, "upgrade failed", nil); err != nil {
   391  			return nil, err
   392  		}
   393  		u.f.WantResolvedEvent()
   394  		u.f.WantUpgradeEvent(true)
   395  		select {
   396  		case <-u.tomb.Dying():
   397  			return nil, tomb.ErrDying
   398  		case <-u.f.ResolvedEvents():
   399  			err = u.deployer.NotifyResolved()
   400  			if e := u.f.ClearResolved(); e != nil {
   401  				return nil, e
   402  			}
   403  			if err != nil {
   404  				return nil, err
   405  			}
   406  		case curl = <-u.f.UpgradeEvents():
   407  			if err := u.deployer.NotifyRevert(); err != nil {
   408  				return nil, err
   409  			}
   410  		}
   411  		return ModeUpgrading(curl), nil
   412  	}
   413  }
   414  
   415  // modeContext returns a function that implements logging and common error
   416  // manipulation for Mode funcs.
   417  func modeContext(name string, err *error) func() {
   418  	logger.Infof("%s starting", name)
   419  	return func() {
   420  		logger.Debugf("%s exiting", name)
   421  		switch *err {
   422  		case nil, tomb.ErrDying, worker.ErrTerminateAgent:
   423  		default:
   424  			*err = stderrors.New(name + ": " + (*err).Error())
   425  		}
   426  	}
   427  }