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