github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/cmd/jujud/agent/machine/manifolds.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package machine
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"github.com/juju/utils/voyeur"
     9  
    10  	coreagent "github.com/juju/juju/agent"
    11  	"github.com/juju/juju/api"
    12  	apideployer "github.com/juju/juju/api/deployer"
    13  	"github.com/juju/juju/cmd/jujud/agent/util"
    14  	"github.com/juju/juju/state"
    15  	"github.com/juju/juju/worker"
    16  	"github.com/juju/juju/worker/agent"
    17  	"github.com/juju/juju/worker/apiaddressupdater"
    18  	"github.com/juju/juju/worker/apicaller"
    19  	"github.com/juju/juju/worker/apiconfigwatcher"
    20  	"github.com/juju/juju/worker/authenticationworker"
    21  	"github.com/juju/juju/worker/dependency"
    22  	"github.com/juju/juju/worker/deployer"
    23  	"github.com/juju/juju/worker/diskmanager"
    24  	"github.com/juju/juju/worker/fortress"
    25  	"github.com/juju/juju/worker/gate"
    26  	"github.com/juju/juju/worker/hostkeyreporter"
    27  	"github.com/juju/juju/worker/identityfilewriter"
    28  	"github.com/juju/juju/worker/logger"
    29  	"github.com/juju/juju/worker/logsender"
    30  	"github.com/juju/juju/worker/machineactions"
    31  	"github.com/juju/juju/worker/machiner"
    32  	"github.com/juju/juju/worker/migrationminion"
    33  	"github.com/juju/juju/worker/proxyupdater"
    34  	"github.com/juju/juju/worker/reboot"
    35  	"github.com/juju/juju/worker/resumer"
    36  	workerstate "github.com/juju/juju/worker/state"
    37  	"github.com/juju/juju/worker/stateconfigwatcher"
    38  	"github.com/juju/juju/worker/storageprovisioner"
    39  	"github.com/juju/juju/worker/terminationworker"
    40  	"github.com/juju/juju/worker/toolsversionchecker"
    41  	"github.com/juju/juju/worker/upgrader"
    42  	"github.com/juju/juju/worker/upgradesteps"
    43  	"github.com/juju/utils/clock"
    44  	"github.com/juju/version"
    45  )
    46  
    47  // ManifoldsConfig allows specialisation of the result of Manifolds.
    48  type ManifoldsConfig struct {
    49  	// Agent contains the agent that will be wrapped and made available to
    50  	// its dependencies via a dependency.Engine.
    51  	Agent coreagent.Agent
    52  
    53  	// AgentConfigChanged is set whenever the machine agent's config
    54  	// is updated.
    55  	AgentConfigChanged *voyeur.Value
    56  
    57  	// RootDir is the root directory that any worker that needs to
    58  	// access local filesystems should use as a base. In actual use it
    59  	// will be "" but it may be overriden in tests.
    60  	RootDir string
    61  
    62  	// PreviousAgentVersion passes through the version the machine
    63  	// agent was running before the current restart.
    64  	PreviousAgentVersion version.Number
    65  
    66  	// UpgradeStepsLock is passed to the upgrade steps gate to
    67  	// coordinate workers that shouldn't do anything until the
    68  	// upgrade-steps worker is done.
    69  	UpgradeStepsLock gate.Lock
    70  
    71  	// UpgradeCheckLock is passed to the upgrade check gate to
    72  	// coordinate workers that shouldn't do anything until the
    73  	// upgrader worker completes it's first check.
    74  	UpgradeCheckLock gate.Lock
    75  
    76  	// OpenState is function used by the state manifold to create a
    77  	// *state.State.
    78  	OpenState func(coreagent.Config) (*state.State, error)
    79  
    80  	// OpenStateForUpgrade is a function the upgradesteps worker can
    81  	// use to establish a connection to state.
    82  	OpenStateForUpgrade func() (*state.State, error)
    83  
    84  	// StartStateWorkers is function called by the stateworkers
    85  	// manifold to start workers which rely on a *state.State but
    86  	// which haven't been converted to run directly under the
    87  	// dependency engine yet. This will go once these workers have
    88  	// been converted.
    89  	StartStateWorkers func(*state.State) (worker.Worker, error)
    90  
    91  	// StartAPIWorkers is passed to the apiworkers manifold. It starts
    92  	// workers which rely on an API connection (which have not yet
    93  	// been converted to work directly with the dependency engine).
    94  	StartAPIWorkers func(api.Connection) (worker.Worker, error)
    95  
    96  	// PreUpgradeSteps is a function that is used by the upgradesteps
    97  	// worker to ensure that conditions are OK for an upgrade to
    98  	// proceed.
    99  	PreUpgradeSteps func(*state.State, coreagent.Config, bool, bool) error
   100  
   101  	// LogSource defines the channel type used to send log message
   102  	// structs within the machine agent.
   103  	LogSource logsender.LogRecordCh
   104  
   105  	// newDeployContext gives the tests the opportunity to create a deployer.Context
   106  	// that can be used for testing so as to avoid (1) deploying units to the system
   107  	// running the tests and (2) get access to the *State used internally, so that
   108  	// tests can be run without waiting for the 5s watcher refresh time to which we would
   109  	// otherwise be restricted.
   110  	NewDeployContext func(st *apideployer.State, agentConfig coreagent.Config) deployer.Context
   111  
   112  	// Clock is used by the storageprovisioner worker.
   113  	Clock clock.Clock
   114  }
   115  
   116  // Manifolds returns a set of co-configured manifolds covering the
   117  // various responsibilities of a machine agent.
   118  //
   119  // Thou Shalt Not Use String Literals In This Function. Or Else.
   120  func Manifolds(config ManifoldsConfig) dependency.Manifolds {
   121  
   122  	// connectFilter exists:
   123  	//  1) to let us retry api connections immediately on password change,
   124  	//     rather than causing the dependency engine to wait for a while;
   125  	//  2) to ensure that certain connection failures correctly trigger
   126  	//     complete agent removal. (It's not safe to let any agent other
   127  	//     than the machine mess around with SetCanUninstall).
   128  	connectFilter := func(err error) error {
   129  		cause := errors.Cause(err)
   130  		if cause == apicaller.ErrConnectImpossible {
   131  			err2 := coreagent.SetCanUninstall(config.Agent)
   132  			if err2 != nil {
   133  				return errors.Trace(err2)
   134  			}
   135  			return worker.ErrTerminateAgent
   136  		} else if cause == apicaller.ErrChangedPassword {
   137  			return dependency.ErrBounce
   138  		}
   139  		return err
   140  	}
   141  
   142  	return dependency.Manifolds{
   143  		// The agent manifold references the enclosing agent, and is the
   144  		// foundation stone on which most other manifolds ultimately depend.
   145  		agentName: agent.Manifold(config.Agent),
   146  
   147  		// The termination worker returns ErrTerminateAgent if a
   148  		// termination signal is received by the process it's running
   149  		// in. It has no inputs and its only output is the error it
   150  		// returns. It depends on the uninstall file having been
   151  		// written *by the manual provider* at install time; it would
   152  		// be Very Wrong Indeed to use SetCanUninstall in conjunction
   153  		// with this code.
   154  		terminationName: terminationworker.Manifold(),
   155  
   156  		// The stateconfigwatcher manifold watches the machine agent's
   157  		// configuration and reports if state serving info is
   158  		// present. It will bounce itself if state serving info is
   159  		// added or removed. It is intended as a dependency just for
   160  		// the state manifold.
   161  		stateConfigWatcherName: stateconfigwatcher.Manifold(stateconfigwatcher.ManifoldConfig{
   162  			AgentName:          agentName,
   163  			AgentConfigChanged: config.AgentConfigChanged,
   164  		}),
   165  
   166  		// The state manifold creates a *state.State and makes it
   167  		// available to other manifolds. It pings the mongodb session
   168  		// regularly and will die if pings fail.
   169  		stateName: workerstate.Manifold(workerstate.ManifoldConfig{
   170  			AgentName:              agentName,
   171  			StateConfigWatcherName: stateConfigWatcherName,
   172  			OpenState:              config.OpenState,
   173  		}),
   174  
   175  		// The stateworkers manifold starts workers which rely on a
   176  		// *state.State but which haven't been converted to run
   177  		// directly under the dependency engine yet. This manifold
   178  		// will be removed once all such workers have been converted;
   179  		// until then, the workers are expected to handle their own
   180  		// checks for upgrades etc, rather than blocking this whole
   181  		// worker on upgrade completion.
   182  		stateWorkersName: StateWorkersManifold(StateWorkersConfig{
   183  			StateName:         stateName,
   184  			StartStateWorkers: config.StartStateWorkers,
   185  		}),
   186  
   187  		// The api-config-watcher manifold monitors the API server
   188  		// addresses in the agent config and bounces when they
   189  		// change. It's required as part of model migrations.
   190  		apiConfigWatcherName: apiconfigwatcher.Manifold(apiconfigwatcher.ManifoldConfig{
   191  			AgentName:          agentName,
   192  			AgentConfigChanged: config.AgentConfigChanged,
   193  		}),
   194  
   195  		// The api caller is a thin concurrent wrapper around a connection
   196  		// to some API server. It's used by many other manifolds, which all
   197  		// select their own desired facades. It will be interesting to see
   198  		// how this works when we consolidate the agents; might be best to
   199  		// handle the auth changes server-side..?
   200  		apiCallerName: apicaller.Manifold(apicaller.ManifoldConfig{
   201  			AgentName:            agentName,
   202  			APIConfigWatcherName: apiConfigWatcherName,
   203  			APIOpen:              apicaller.APIOpen,
   204  			NewConnection:        apicaller.ScaryConnect,
   205  			Filter:               connectFilter,
   206  		}),
   207  
   208  		// The upgrade steps gate is used to coordinate workers which
   209  		// shouldn't do anything until the upgrade-steps worker has
   210  		// finished running any required upgrade steps. The flag of
   211  		// similar name is used to implement the isFullyUpgraded func
   212  		// that keeps upgrade concerns out of unrelated manifolds.
   213  		upgradeStepsGateName: gate.ManifoldEx(config.UpgradeStepsLock),
   214  		upgradeStepsFlagName: gate.FlagManifold(gate.FlagManifoldConfig{
   215  			GateName:  upgradeStepsGateName,
   216  			NewWorker: gate.NewFlagWorker,
   217  		}),
   218  
   219  		// The upgrade check gate is used to coordinate workers which
   220  		// shouldn't do anything until the upgrader worker has
   221  		// completed its first check for a new tools version to
   222  		// upgrade to. The flag of similar name is used to implement
   223  		// the isFullyUpgraded func that keeps upgrade concerns out of
   224  		// unrelated manifolds.
   225  		upgradeCheckGateName: gate.ManifoldEx(config.UpgradeCheckLock),
   226  		upgradeCheckFlagName: gate.FlagManifold(gate.FlagManifoldConfig{
   227  			GateName:  upgradeCheckGateName,
   228  			NewWorker: gate.NewFlagWorker,
   229  		}),
   230  
   231  		// The upgrader is a leaf worker that returns a specific error
   232  		// type recognised by the machine agent, causing other workers
   233  		// to be stopped and the agent to be restarted running the new
   234  		// tools. We should only need one of these in a consolidated
   235  		// agent, but we'll need to be careful about behavioural
   236  		// differences, and interactions with the upgrade-steps
   237  		// worker.
   238  		upgraderName: upgrader.Manifold(upgrader.ManifoldConfig{
   239  			AgentName:            agentName,
   240  			APICallerName:        apiCallerName,
   241  			UpgradeStepsGateName: upgradeStepsGateName,
   242  			UpgradeCheckGateName: upgradeCheckGateName,
   243  			PreviousAgentVersion: config.PreviousAgentVersion,
   244  		}),
   245  
   246  		// The upgradesteps worker runs soon after the machine agent
   247  		// starts and runs any steps required to upgrade to the
   248  		// running jujud version. Once upgrade steps have run, the
   249  		// upgradesteps gate is unlocked and the worker exits.
   250  		upgradeStepsName: upgradesteps.Manifold(upgradesteps.ManifoldConfig{
   251  			AgentName:            agentName,
   252  			APICallerName:        apiCallerName,
   253  			UpgradeStepsGateName: upgradeStepsGateName,
   254  			OpenStateForUpgrade:  config.OpenStateForUpgrade,
   255  			PreUpgradeSteps:      config.PreUpgradeSteps,
   256  		}),
   257  
   258  		// The migration minion handles the agent side aspects of model migrations.
   259  		migrationFortressName: ifFullyUpgraded(fortress.Manifold()),
   260  		migrationMinionName: ifFullyUpgraded(migrationminion.Manifold(migrationminion.ManifoldConfig{
   261  			AgentName:     agentName,
   262  			APICallerName: apiCallerName,
   263  			FortressName:  migrationFortressName,
   264  
   265  			NewFacade: migrationminion.NewFacade,
   266  			NewWorker: migrationminion.NewWorker,
   267  		})),
   268  
   269  		// The serving-info-setter manifold sets grabs the state
   270  		// serving info from the API connection and writes it to the
   271  		// agent config.
   272  		servingInfoSetterName: ifFullyUpgraded(ServingInfoSetterManifold(ServingInfoSetterConfig{
   273  			AgentName:     agentName,
   274  			APICallerName: apiCallerName,
   275  		})),
   276  
   277  		// The apiworkers manifold starts workers which rely on the
   278  		// machine agent's API connection but have not been converted
   279  		// to work directly under the dependency engine. It waits for
   280  		// upgrades to be finished before starting these workers.
   281  		apiWorkersName: ifFullyUpgraded(APIWorkersManifold(APIWorkersConfig{
   282  			APICallerName:   apiCallerName,
   283  			StartAPIWorkers: config.StartAPIWorkers,
   284  		})),
   285  
   286  		// The reboot manifold manages a worker which will reboot the
   287  		// machine when requested. It needs an API connection and
   288  		// waits for upgrades to be complete.
   289  		rebootName: ifFullyUpgraded(reboot.Manifold(reboot.ManifoldConfig{
   290  			AgentName:     agentName,
   291  			APICallerName: apiCallerName,
   292  		})),
   293  
   294  		// The logging config updater is a leaf worker that indirectly
   295  		// controls the messages sent via the log sender or rsyslog,
   296  		// according to changes in environment config. We should only need
   297  		// one of these in a consolidated agent.
   298  		loggingConfigUpdaterName: ifFullyUpgraded(logger.Manifold(logger.ManifoldConfig{
   299  			AgentName:     agentName,
   300  			APICallerName: apiCallerName,
   301  		})),
   302  
   303  		// The diskmanager worker periodically lists block devices on the
   304  		// machine it runs on. This worker will be run on all Juju-managed
   305  		// machines (one per machine agent).
   306  		diskManagerName: ifFullyUpgraded(diskmanager.Manifold(diskmanager.ManifoldConfig{
   307  			AgentName:     agentName,
   308  			APICallerName: apiCallerName,
   309  		})),
   310  
   311  		// The proxy config updater is a leaf worker that sets http/https/apt/etc
   312  		// proxy settings.
   313  		proxyConfigUpdater: ifFullyUpgraded(proxyupdater.Manifold(proxyupdater.ManifoldConfig{
   314  			AgentName:     agentName,
   315  			APICallerName: apiCallerName,
   316  		})),
   317  
   318  		// The api address updater is a leaf worker that rewrites agent config
   319  		// as the state server addresses change. We should only need one of
   320  		// these in a consolidated agent.
   321  		apiAddressUpdaterName: ifFullyUpgraded(apiaddressupdater.Manifold(apiaddressupdater.ManifoldConfig{
   322  			AgentName:     agentName,
   323  			APICallerName: apiCallerName,
   324  		})),
   325  
   326  		// The machiner Worker will wait for the identified machine to become
   327  		// Dying and make it Dead; or until the machine becomes Dead by other
   328  		// means.
   329  		machinerName: ifFullyUpgraded(machiner.Manifold(machiner.ManifoldConfig{
   330  			AgentName:     agentName,
   331  			APICallerName: apiCallerName,
   332  		})),
   333  
   334  		// The log sender is a leaf worker that sends log messages to some
   335  		// API server, when configured so to do. We should only need one of
   336  		// these in a consolidated agent.
   337  		//
   338  		// NOTE: the LogSource will buffer a large number of messages as an upgrade
   339  		// runs; it currently seems better to fill the buffer and send when stable,
   340  		// optimising for stable controller upgrades rather than up-to-the-moment
   341  		// observable normal-machine upgrades.
   342  		logSenderName: ifFullyUpgraded(logsender.Manifold(logsender.ManifoldConfig{
   343  			APICallerName: apiCallerName,
   344  			LogSource:     config.LogSource,
   345  		})),
   346  
   347  		// The deployer worker is responsible for deploying and recalling unit
   348  		// agents, according to changes in a set of state units; and for the
   349  		// final removal of its agents' units from state when they are no
   350  		// longer needed.
   351  		deployerName: ifFullyUpgraded(deployer.Manifold(deployer.ManifoldConfig{
   352  			NewDeployContext: config.NewDeployContext,
   353  			AgentName:        agentName,
   354  			APICallerName:    apiCallerName,
   355  		})),
   356  
   357  		authenticationWorkerName: ifFullyUpgraded(authenticationworker.Manifold(authenticationworker.ManifoldConfig{
   358  			AgentName:     agentName,
   359  			APICallerName: apiCallerName,
   360  		})),
   361  
   362  		// The storageProvisioner worker manages provisioning
   363  		// (deprovisioning), and attachment (detachment) of first-class
   364  		// volumes and filesystems.
   365  		storageProvisionerName: ifFullyUpgraded(storageprovisioner.MachineManifold(storageprovisioner.MachineManifoldConfig{
   366  			AgentName:     agentName,
   367  			APICallerName: apiCallerName,
   368  			Clock:         config.Clock,
   369  		})),
   370  
   371  		resumerName: ifFullyUpgraded(resumer.Manifold(resumer.ManifoldConfig{
   372  			AgentName:     agentName,
   373  			APICallerName: apiCallerName,
   374  		})),
   375  
   376  		identityFileWriterName: ifFullyUpgraded(identityfilewriter.Manifold(identityfilewriter.ManifoldConfig{
   377  			AgentName:     agentName,
   378  			APICallerName: apiCallerName,
   379  		})),
   380  
   381  		toolsVersionCheckerName: ifFullyUpgraded(toolsversionchecker.Manifold(toolsversionchecker.ManifoldConfig{
   382  			AgentName:     agentName,
   383  			APICallerName: apiCallerName,
   384  		})),
   385  
   386  		machineActionName: ifFullyUpgraded(machineactions.Manifold(machineactions.ManifoldConfig{
   387  			AgentName:     agentName,
   388  			APICallerName: apiCallerName,
   389  			NewFacade:     machineactions.NewFacade,
   390  			NewWorker:     machineactions.NewMachineActionsWorker,
   391  		})),
   392  
   393  		hostKeyReporterName: ifFullyUpgraded(hostkeyreporter.Manifold(hostkeyreporter.ManifoldConfig{
   394  			AgentName:     agentName,
   395  			APICallerName: apiCallerName,
   396  			RootDir:       config.RootDir,
   397  			NewFacade:     hostkeyreporter.NewFacade,
   398  			NewWorker:     hostkeyreporter.NewWorker,
   399  		})),
   400  	}
   401  }
   402  
   403  var ifFullyUpgraded = util.Housing{
   404  	Flags: []string{
   405  		upgradeStepsFlagName,
   406  		upgradeCheckFlagName,
   407  	},
   408  }.Decorate
   409  
   410  const (
   411  	agentName              = "agent"
   412  	terminationName        = "termination-signal-handler"
   413  	stateConfigWatcherName = "state-config-watcher"
   414  	stateName              = "state"
   415  	stateWorkersName       = "unconverted-state-workers"
   416  	apiCallerName          = "api-caller"
   417  
   418  	upgraderName         = "upgrader"
   419  	upgradeStepsName     = "upgrade-steps-runner"
   420  	upgradeStepsGateName = "upgrade-steps-gate"
   421  	upgradeStepsFlagName = "upgrade-steps-flag"
   422  	upgradeCheckGateName = "upgrade-check-gate"
   423  	upgradeCheckFlagName = "upgrade-check-flag"
   424  
   425  	migrationFortressName = "migration-fortress"
   426  	migrationMinionName   = "migration-minion"
   427  
   428  	servingInfoSetterName    = "serving-info-setter"
   429  	apiWorkersName           = "unconverted-api-workers"
   430  	rebootName               = "reboot-executor"
   431  	loggingConfigUpdaterName = "logging-config-updater"
   432  	diskManagerName          = "disk-manager"
   433  	proxyConfigUpdater       = "proxy-config-updater"
   434  	apiAddressUpdaterName    = "api-address-updater"
   435  	machinerName             = "machiner"
   436  	logSenderName            = "log-sender"
   437  	deployerName             = "unit-agent-deployer"
   438  	authenticationWorkerName = "ssh-authkeys-updater"
   439  	storageProvisionerName   = "storage-provisioner"
   440  	resumerName              = "mgo-txn-resumer"
   441  	identityFileWriterName   = "ssh-identity-writer"
   442  	toolsVersionCheckerName  = "tools-version-checker"
   443  	apiConfigWatcherName     = "api-config-watcher"
   444  	machineActionName        = "machine-action-runner"
   445  	hostKeyReporterName      = "host-key-reporter"
   446  )