github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/cmd/jujud/agent/unit/manifolds.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package unit
     5  
     6  import (
     7  	"time"
     8  
     9  	"github.com/juju/clock"
    10  	"github.com/juju/errors"
    11  	"github.com/juju/loggo"
    12  	"github.com/juju/utils/voyeur"
    13  	"github.com/juju/version"
    14  	"github.com/prometheus/client_golang/prometheus"
    15  	"gopkg.in/juju/worker.v1/dependency"
    16  
    17  	coreagent "github.com/juju/juju/agent"
    18  	"github.com/juju/juju/api"
    19  	"github.com/juju/juju/api/base"
    20  	msapi "github.com/juju/juju/api/meterstatus"
    21  	"github.com/juju/juju/cmd/jujud/agent/engine"
    22  	"github.com/juju/juju/core/machinelock"
    23  	"github.com/juju/juju/core/status"
    24  	"github.com/juju/juju/state"
    25  	"github.com/juju/juju/utils/proxy"
    26  	"github.com/juju/juju/worker"
    27  	"github.com/juju/juju/worker/agent"
    28  	"github.com/juju/juju/worker/apiaddressupdater"
    29  	"github.com/juju/juju/worker/apicaller"
    30  	"github.com/juju/juju/worker/apiconfigwatcher"
    31  	"github.com/juju/juju/worker/fortress"
    32  	"github.com/juju/juju/worker/gate"
    33  	"github.com/juju/juju/worker/leadership"
    34  	"github.com/juju/juju/worker/logger"
    35  	"github.com/juju/juju/worker/logsender"
    36  	"github.com/juju/juju/worker/meterstatus"
    37  	"github.com/juju/juju/worker/metrics/collect"
    38  	"github.com/juju/juju/worker/metrics/sender"
    39  	"github.com/juju/juju/worker/metrics/spool"
    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/retrystrategy"
    44  	"github.com/juju/juju/worker/uniter"
    45  	"github.com/juju/juju/worker/upgrader"
    46  	"github.com/juju/juju/worker/upgradesteps"
    47  )
    48  
    49  // ManifoldsConfig allows specialisation of the result of Manifolds.
    50  type ManifoldsConfig struct {
    51  
    52  	// Agent contains the agent that will be wrapped and made available to
    53  	// its dependencies via a dependency.Engine.
    54  	Agent coreagent.Agent
    55  
    56  	// LogSource will be read from by the logsender component.
    57  	LogSource logsender.LogRecordCh
    58  
    59  	// LeadershipGuarantee controls the behaviour of the leadership tracker.
    60  	LeadershipGuarantee time.Duration
    61  
    62  	// AgentConfigChanged is set whenever the unit agent's config
    63  	// is updated.
    64  	AgentConfigChanged *voyeur.Value
    65  
    66  	// ValidateMigration is called by the migrationminion during the
    67  	// migration process to check that the agent will be ok when
    68  	// connected to the new target controller.
    69  	ValidateMigration func(base.APICaller) error
    70  
    71  	// PrometheusRegisterer is a prometheus.Registerer that may be used
    72  	// by workers to register Prometheus metric collectors.
    73  	PrometheusRegisterer prometheus.Registerer
    74  
    75  	// UpdateLoggerConfig is a function that will save the specified
    76  	// config value as the logging config in the agent.conf file.
    77  	UpdateLoggerConfig func(string) error
    78  
    79  	// PreviousAgentVersion passes through the version the unit
    80  	// agent was running before the current restart.
    81  	PreviousAgentVersion version.Number
    82  
    83  	// UpgradeStepsLock is passed to the upgrade steps gate to
    84  	// coordinate workers that shouldn't do anything until the
    85  	// upgrade-steps worker is done.
    86  	UpgradeStepsLock gate.Lock
    87  
    88  	// UpgradeCheckLock is passed to the upgrade check gate to
    89  	// coordinate workers that shouldn't do anything until the
    90  	// upgrader worker completes it's first check.
    91  	UpgradeCheckLock gate.Lock
    92  
    93  	// PreUpgradeSteps is a function that is used by the upgradesteps
    94  	// worker to ensure that conditions are OK for an upgrade to
    95  	// proceed.
    96  	PreUpgradeSteps func(*state.StatePool, coreagent.Config, bool, bool) error
    97  
    98  	// MachineLock is a central source for acquiring the machine lock.
    99  	// This is used by a number of workers to ensure serialisation of actions
   100  	// across the machine.
   101  	MachineLock machinelock.Lock
   102  }
   103  
   104  // Manifolds returns a set of co-configured manifolds covering the various
   105  // responsibilities of a standalone unit agent. It also accepts the logSource
   106  // argument because we haven't figured out how to thread all the logging bits
   107  // through a dependency engine yet.
   108  //
   109  // Thou Shalt Not Use String Literals In This Function. Or Else.
   110  func Manifolds(config ManifoldsConfig) dependency.Manifolds {
   111  
   112  	// connectFilter exists to let us retry api connections immediately
   113  	// on password change, rather than causing the dependency engine to
   114  	// wait for a while.
   115  	connectFilter := func(err error) error {
   116  		cause := errors.Cause(err)
   117  		if cause == apicaller.ErrChangedPassword {
   118  			return dependency.ErrBounce
   119  		} else if cause == apicaller.ErrConnectImpossible {
   120  			return worker.ErrTerminateAgent
   121  		}
   122  		return err
   123  	}
   124  
   125  	return dependency.Manifolds{
   126  
   127  		// The agent manifold references the enclosing agent, and is the
   128  		// foundation stone on which most other manifolds ultimately depend.
   129  		// (Currently, that is "all manifolds", but consider a shared clock.)
   130  		agentName: agent.Manifold(config.Agent),
   131  
   132  		// The api-config-watcher manifold monitors the API server
   133  		// addresses in the agent config and bounces when they
   134  		// change. It's required as part of model migrations.
   135  		apiConfigWatcherName: apiconfigwatcher.Manifold(apiconfigwatcher.ManifoldConfig{
   136  			AgentName:          agentName,
   137  			AgentConfigChanged: config.AgentConfigChanged,
   138  		}),
   139  
   140  		// The api caller is a thin concurrent wrapper around a connection
   141  		// to some API server. It's used by many other manifolds, which all
   142  		// select their own desired facades. It will be interesting to see
   143  		// how this works when we consolidate the agents; might be best to
   144  		// handle the auth changes server-side..?
   145  		apiCallerName: apicaller.Manifold(apicaller.ManifoldConfig{
   146  			AgentName:            agentName,
   147  			APIConfigWatcherName: apiConfigWatcherName,
   148  			APIOpen:              api.Open,
   149  			NewConnection:        apicaller.ScaryConnect,
   150  			Filter:               connectFilter,
   151  		}),
   152  
   153  		// The log sender is a leaf worker that sends log messages to some
   154  		// API server, when configured so to do. We should only need one of
   155  		// these in a consolidated agent.
   156  		logSenderName: logsender.Manifold(logsender.ManifoldConfig{
   157  			APICallerName: apiCallerName,
   158  			LogSource:     config.LogSource,
   159  		}),
   160  
   161  		// The upgrade steps gate is used to coordinate workers which
   162  		// shouldn't do anything until the upgrade-steps worker has
   163  		// finished running any required upgrade steps. The flag of
   164  		// similar name is used to implement the isFullyUpgraded func
   165  		// that keeps upgrade concerns out of unrelated manifolds.
   166  		upgradeStepsGateName: gate.ManifoldEx(config.UpgradeStepsLock),
   167  		upgradeStepsFlagName: gate.FlagManifold(gate.FlagManifoldConfig{
   168  			GateName:  upgradeStepsGateName,
   169  			NewWorker: gate.NewFlagWorker,
   170  		}),
   171  
   172  		// The upgrade check gate is used to coordinate workers which
   173  		// shouldn't do anything until the upgrader worker has
   174  		// completed its first check for a new tools version to
   175  		// upgrade to. The flag of similar name is used to implement
   176  		// the isFullyUpgraded func that keeps upgrade concerns out of
   177  		// unrelated manifolds.
   178  		upgradeCheckGateName: gate.ManifoldEx(config.UpgradeCheckLock),
   179  		upgradeCheckFlagName: gate.FlagManifold(gate.FlagManifoldConfig{
   180  			GateName:  upgradeCheckGateName,
   181  			NewWorker: gate.NewFlagWorker,
   182  		}),
   183  
   184  		// The upgrader is a leaf worker that returns a specific error type
   185  		// recognised by the unit agent, causing other workers to be stopped
   186  		// and the agent to be restarted running the new tools. We should only
   187  		// need one of these in a consolidated agent, but we'll need to be
   188  		// careful about behavioural differences, and interactions with the
   189  		// upgradesteps worker.
   190  		upgraderName: upgrader.Manifold(upgrader.ManifoldConfig{
   191  			AgentName:            agentName,
   192  			APICallerName:        apiCallerName,
   193  			UpgradeStepsGateName: upgradeStepsGateName,
   194  			UpgradeCheckGateName: upgradeCheckGateName,
   195  			PreviousAgentVersion: config.PreviousAgentVersion,
   196  		}),
   197  
   198  		// The upgradesteps worker runs soon after the unit agent
   199  		// starts and runs any steps required to upgrade to the
   200  		// running jujud version. Once upgrade steps have run, the
   201  		// upgradesteps gate is unlocked and the worker exits.
   202  		upgradeStepsName: upgradesteps.Manifold(upgradesteps.ManifoldConfig{
   203  			AgentName:            agentName,
   204  			APICallerName:        apiCallerName,
   205  			UpgradeStepsGateName: upgradeStepsGateName,
   206  			// Realistically,  units should not open state for any reason.
   207  			OpenStateForUpgrade: func() (*state.StatePool, error) {
   208  				return nil, errors.New("unit agent cannot open state")
   209  			},
   210  			PreUpgradeSteps: config.PreUpgradeSteps,
   211  			NewAgentStatusSetter: func(apiConn api.Connection) (upgradesteps.StatusSetter, error) {
   212  				return &noopStatusSetter{}, nil
   213  			},
   214  		}),
   215  
   216  		// The migration workers collaborate to run migrations;
   217  		// and to create a mechanism for running other workers
   218  		// so they can't accidentally interfere with a migration
   219  		// in progress. Such a manifold should (1) depend on the
   220  		// migration-inactive flag, to know when to start or die;
   221  		// and (2) occupy the migration-fortress, so as to avoid
   222  		// possible interference with the minion (which will not
   223  		// take action until it's gained sole control of the
   224  		// fortress).
   225  		migrationFortressName: ifFullyUpgraded(fortress.Manifold()),
   226  		migrationInactiveFlagName: migrationflag.Manifold(migrationflag.ManifoldConfig{
   227  			APICallerName: apiCallerName,
   228  			Check:         migrationflag.IsTerminal,
   229  			NewFacade:     migrationflag.NewFacade,
   230  			NewWorker:     migrationflag.NewWorker,
   231  		}),
   232  		migrationMinionName: migrationminion.Manifold(migrationminion.ManifoldConfig{
   233  			AgentName:         agentName,
   234  			APICallerName:     apiCallerName,
   235  			FortressName:      migrationFortressName,
   236  			APIOpen:           api.Open,
   237  			ValidateMigration: config.ValidateMigration,
   238  			NewFacade:         migrationminion.NewFacade,
   239  			NewWorker:         migrationminion.NewWorker,
   240  		}),
   241  
   242  		// The logging config updater is a leaf worker that indirectly
   243  		// controls the messages sent via the log sender according to
   244  		// changes in environment config. We should only need one of
   245  		// these in a consolidated agent.
   246  		loggingConfigUpdaterName: ifNotMigrating(logger.Manifold(logger.ManifoldConfig{
   247  			AgentName:       agentName,
   248  			APICallerName:   apiCallerName,
   249  			UpdateAgentFunc: config.UpdateLoggerConfig,
   250  		})),
   251  
   252  		// The api address updater is a leaf worker that rewrites agent config
   253  		// as the controller addresses change. We should only need one of
   254  		// these in a consolidated agent.
   255  		apiAddressUpdaterName: ifNotMigrating(apiaddressupdater.Manifold(apiaddressupdater.ManifoldConfig{
   256  			AgentName:     agentName,
   257  			APICallerName: apiCallerName,
   258  		})),
   259  
   260  		// The proxy config updater is a leaf worker that sets http/https/apt/etc
   261  		// proxy settings.
   262  		// TODO(fwereade): timing of this is suspicious. There was superstitious
   263  		// code trying to run this early; if that ever helped, it was only by
   264  		// coincidence. Probably we ought to be making components that might
   265  		// need proxy config into explicit dependencies of the proxy updater...
   266  		proxyConfigUpdaterName: ifNotMigrating(proxyupdater.Manifold(proxyupdater.ManifoldConfig{
   267  			AgentName:       agentName,
   268  			APICallerName:   apiCallerName,
   269  			Logger:          loggo.GetLogger("juju.worker.proxyupdater"),
   270  			WorkerFunc:      proxyupdater.NewWorker,
   271  			InProcessUpdate: proxy.DefaultConfig.Set,
   272  		})),
   273  
   274  		// The charmdir resource coordinates whether the charm directory is
   275  		// available or not; after 'start' hook and before 'stop' hook
   276  		// executes, and not during upgrades.
   277  		charmDirName: ifNotMigrating(fortress.Manifold()),
   278  
   279  		// The leadership tracker attempts to secure and retain leadership of
   280  		// the unit's service, and is consulted on such matters by the
   281  		// uniter. As it stands today, we'll need one per unit in a
   282  		// consolidated agent.
   283  		leadershipTrackerName: ifNotMigrating(leadership.Manifold(leadership.ManifoldConfig{
   284  			AgentName:           agentName,
   285  			APICallerName:       apiCallerName,
   286  			Clock:               clock.WallClock,
   287  			LeadershipGuarantee: config.LeadershipGuarantee,
   288  		})),
   289  
   290  		// HookRetryStrategy uses a retrystrategy worker to get a
   291  		// retry strategy that will be used by the uniter to run its hooks.
   292  		hookRetryStrategyName: ifNotMigrating(retrystrategy.Manifold(retrystrategy.ManifoldConfig{
   293  			AgentName:     agentName,
   294  			APICallerName: apiCallerName,
   295  			NewFacade:     retrystrategy.NewFacade,
   296  			NewWorker:     retrystrategy.NewRetryStrategyWorker,
   297  		})),
   298  
   299  		// The uniter installs charms; manages the unit's presence in its
   300  		// relations; creates suboordinate units; runs all the hooks; sends
   301  		// metrics; etc etc etc. We expect to break it up further in the
   302  		// coming weeks, and to need one per unit in a consolidated agent
   303  		// (and probably one for each component broken out).
   304  		uniterName: ifNotMigrating(uniter.Manifold(uniter.ManifoldConfig{
   305  			AgentName:             agentName,
   306  			APICallerName:         apiCallerName,
   307  			MachineLock:           config.MachineLock,
   308  			Clock:                 clock.WallClock,
   309  			LeadershipTrackerName: leadershipTrackerName,
   310  			CharmDirName:          charmDirName,
   311  			HookRetryStrategyName: hookRetryStrategyName,
   312  			TranslateResolverErr:  uniter.TranslateFortressErrors,
   313  		})),
   314  
   315  		// TODO (mattyw) should be added to machine agent.
   316  		metricSpoolName: ifNotMigrating(spool.Manifold(spool.ManifoldConfig{
   317  			AgentName: agentName,
   318  		})),
   319  
   320  		// The metric collect worker executes the collect-metrics hook in a
   321  		// restricted context that can safely run concurrently with other hooks.
   322  		metricCollectName: ifNotMigrating(collect.Manifold(collect.ManifoldConfig{
   323  			AgentName:       agentName,
   324  			MetricSpoolName: metricSpoolName,
   325  			CharmDirName:    charmDirName,
   326  		})),
   327  
   328  		// The meter status worker executes the meter-status-changed hook when it detects
   329  		// that the meter status has changed.
   330  		meterStatusName: ifNotMigrating(meterstatus.Manifold(meterstatus.ManifoldConfig{
   331  			AgentName:                agentName,
   332  			APICallerName:            apiCallerName,
   333  			MachineLock:              config.MachineLock,
   334  			Clock:                    clock.WallClock,
   335  			NewHookRunner:            meterstatus.NewHookRunner,
   336  			NewMeterStatusAPIClient:  msapi.NewClient,
   337  			NewConnectedStatusWorker: meterstatus.NewConnectedStatusWorker,
   338  			NewIsolatedStatusWorker:  meterstatus.NewIsolatedStatusWorker,
   339  		})),
   340  
   341  		// The metric sender worker periodically sends accumulated metrics to the controller.
   342  		metricSenderName: ifNotMigrating(sender.Manifold(sender.ManifoldConfig{
   343  			AgentName:       agentName,
   344  			APICallerName:   apiCallerName,
   345  			MetricSpoolName: metricSpoolName,
   346  		})),
   347  	}
   348  }
   349  
   350  var ifFullyUpgraded = engine.Housing{
   351  	Flags: []string{
   352  		upgradeStepsFlagName,
   353  		upgradeCheckFlagName,
   354  	},
   355  }.Decorate
   356  
   357  var ifNotMigrating = engine.Housing{
   358  	Flags: []string{
   359  		migrationInactiveFlagName,
   360  	},
   361  	Occupy: migrationFortressName,
   362  }.Decorate
   363  
   364  const (
   365  	agentName            = "agent"
   366  	apiConfigWatcherName = "api-config-watcher"
   367  	apiCallerName        = "api-caller"
   368  	logSenderName        = "log-sender"
   369  
   370  	upgraderName         = "upgrader"
   371  	upgradeStepsName     = "upgrade-steps-runner"
   372  	upgradeStepsGateName = "upgrade-steps-gate"
   373  	upgradeStepsFlagName = "upgrade-steps-flag"
   374  	upgradeCheckGateName = "upgrade-check-gate"
   375  	upgradeCheckFlagName = "upgrade-check-flag"
   376  
   377  	migrationFortressName     = "migration-fortress"
   378  	migrationInactiveFlagName = "migration-inactive-flag"
   379  	migrationMinionName       = "migration-minion"
   380  
   381  	loggingConfigUpdaterName = "logging-config-updater"
   382  	proxyConfigUpdaterName   = "proxy-config-updater"
   383  	apiAddressUpdaterName    = "api-address-updater"
   384  
   385  	charmDirName          = "charm-dir"
   386  	leadershipTrackerName = "leadership-tracker"
   387  	hookRetryStrategyName = "hook-retry-strategy"
   388  	uniterName            = "uniter"
   389  
   390  	metricSpoolName   = "metric-spool"
   391  	meterStatusName   = "meter-status"
   392  	metricCollectName = "metric-collect"
   393  	metricSenderName  = "metric-sender"
   394  )
   395  
   396  type noopStatusSetter struct{}
   397  
   398  // SetStatus implements upgradesteps.StatusSetter
   399  func (a *noopStatusSetter) SetStatus(setableStatus status.Status, info string, data map[string]interface{}) error {
   400  	return nil
   401  }