github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/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/errors"
    10  	"github.com/juju/utils/clock"
    11  	"github.com/juju/utils/voyeur"
    12  
    13  	coreagent "github.com/juju/juju/agent"
    14  	"github.com/juju/juju/api"
    15  	"github.com/juju/juju/api/base"
    16  	msapi "github.com/juju/juju/api/meterstatus"
    17  	"github.com/juju/juju/cmd/jujud/agent/engine"
    18  	"github.com/juju/juju/worker"
    19  	"github.com/juju/juju/worker/agent"
    20  	"github.com/juju/juju/worker/apiaddressupdater"
    21  	"github.com/juju/juju/worker/apicaller"
    22  	"github.com/juju/juju/worker/apiconfigwatcher"
    23  	"github.com/juju/juju/worker/dependency"
    24  	"github.com/juju/juju/worker/fortress"
    25  	"github.com/juju/juju/worker/leadership"
    26  	"github.com/juju/juju/worker/logger"
    27  	"github.com/juju/juju/worker/logsender"
    28  	"github.com/juju/juju/worker/meterstatus"
    29  	"github.com/juju/juju/worker/metrics/collect"
    30  	"github.com/juju/juju/worker/metrics/sender"
    31  	"github.com/juju/juju/worker/metrics/spool"
    32  	"github.com/juju/juju/worker/migrationflag"
    33  	"github.com/juju/juju/worker/migrationminion"
    34  	"github.com/juju/juju/worker/proxyupdater"
    35  	"github.com/juju/juju/worker/retrystrategy"
    36  	"github.com/juju/juju/worker/uniter"
    37  	"github.com/juju/juju/worker/upgrader"
    38  )
    39  
    40  // ManifoldsConfig allows specialisation of the result of Manifolds.
    41  type ManifoldsConfig struct {
    42  
    43  	// Agent contains the agent that will be wrapped and made available to
    44  	// its dependencies via a dependency.Engine.
    45  	Agent coreagent.Agent
    46  
    47  	// LogSource will be read from by the logsender component.
    48  	LogSource logsender.LogRecordCh
    49  
    50  	// LeadershipGuarantee controls the behaviour of the leadership tracker.
    51  	LeadershipGuarantee time.Duration
    52  
    53  	// AgentConfigChanged is set whenever the unit agent's config
    54  	// is updated.
    55  	AgentConfigChanged *voyeur.Value
    56  
    57  	// ValidateMigration is called by the migrationminion during the
    58  	// migration process to check that the agent will be ok when
    59  	// connected to the new target controller.
    60  	ValidateMigration func(base.APICaller) error
    61  }
    62  
    63  // Manifolds returns a set of co-configured manifolds covering the various
    64  // responsibilities of a standalone unit agent. It also accepts the logSource
    65  // argument because we haven't figured out how to thread all the logging bits
    66  // through a dependency engine yet.
    67  //
    68  // Thou Shalt Not Use String Literals In This Function. Or Else.
    69  func Manifolds(config ManifoldsConfig) dependency.Manifolds {
    70  
    71  	// connectFilter exists to let us retry api connections immediately
    72  	// on password change, rather than causing the dependency engine to
    73  	// wait for a while.
    74  	connectFilter := func(err error) error {
    75  		cause := errors.Cause(err)
    76  		if cause == apicaller.ErrChangedPassword {
    77  			return dependency.ErrBounce
    78  		} else if cause == apicaller.ErrConnectImpossible {
    79  			return worker.ErrTerminateAgent
    80  		}
    81  		return err
    82  	}
    83  
    84  	return dependency.Manifolds{
    85  
    86  		// The agent manifold references the enclosing agent, and is the
    87  		// foundation stone on which most other manifolds ultimately depend.
    88  		// (Currently, that is "all manifolds", but consider a shared clock.)
    89  		agentName: agent.Manifold(config.Agent),
    90  
    91  		// The api-config-watcher manifold monitors the API server
    92  		// addresses in the agent config and bounces when they
    93  		// change. It's required as part of model migrations.
    94  		apiConfigWatcherName: apiconfigwatcher.Manifold(apiconfigwatcher.ManifoldConfig{
    95  			AgentName:          agentName,
    96  			AgentConfigChanged: config.AgentConfigChanged,
    97  		}),
    98  
    99  		// The api caller is a thin concurrent wrapper around a connection
   100  		// to some API server. It's used by many other manifolds, which all
   101  		// select their own desired facades. It will be interesting to see
   102  		// how this works when we consolidate the agents; might be best to
   103  		// handle the auth changes server-side..?
   104  		apiCallerName: apicaller.Manifold(apicaller.ManifoldConfig{
   105  			AgentName:            agentName,
   106  			APIConfigWatcherName: apiConfigWatcherName,
   107  			APIOpen:              api.Open,
   108  			NewConnection:        apicaller.ScaryConnect,
   109  			Filter:               connectFilter,
   110  		}),
   111  
   112  		// The log sender is a leaf worker that sends log messages to some
   113  		// API server, when configured so to do. We should only need one of
   114  		// these in a consolidated agent.
   115  		logSenderName: logsender.Manifold(logsender.ManifoldConfig{
   116  			APICallerName: apiCallerName,
   117  			LogSource:     config.LogSource,
   118  		}),
   119  
   120  		// The upgrader is a leaf worker that returns a specific error type
   121  		// recognised by the unit agent, causing other workers to be stopped
   122  		// and the agent to be restarted running the new tools. We should only
   123  		// need one of these in a consolidated agent, but we'll need to be
   124  		// careful about behavioural differences, and interactions with the
   125  		// upgradesteps worker.
   126  		upgraderName: upgrader.Manifold(upgrader.ManifoldConfig{
   127  			AgentName:     agentName,
   128  			APICallerName: apiCallerName,
   129  		}),
   130  
   131  		// The migration workers collaborate to run migrations;
   132  		// and to create a mechanism for running other workers
   133  		// so they can't accidentally interfere with a migration
   134  		// in progress. Such a manifold should (1) depend on the
   135  		// migration-inactive flag, to know when to start or die;
   136  		// and (2) occupy the migration-fortress, so as to avoid
   137  		// possible interference with the minion (which will not
   138  		// take action until it's gained sole control of the
   139  		// fortress).
   140  		migrationFortressName: fortress.Manifold(),
   141  		migrationInactiveFlagName: migrationflag.Manifold(migrationflag.ManifoldConfig{
   142  			APICallerName: apiCallerName,
   143  			Check:         migrationflag.IsTerminal,
   144  			NewFacade:     migrationflag.NewFacade,
   145  			NewWorker:     migrationflag.NewWorker,
   146  		}),
   147  		migrationMinionName: migrationminion.Manifold(migrationminion.ManifoldConfig{
   148  			AgentName:         agentName,
   149  			APICallerName:     apiCallerName,
   150  			FortressName:      migrationFortressName,
   151  			APIOpen:           api.Open,
   152  			ValidateMigration: config.ValidateMigration,
   153  			NewFacade:         migrationminion.NewFacade,
   154  			NewWorker:         migrationminion.NewWorker,
   155  		}),
   156  
   157  		// The logging config updater is a leaf worker that indirectly
   158  		// controls the messages sent via the log sender according to
   159  		// changes in environment config. We should only need one of
   160  		// these in a consolidated agent.
   161  		loggingConfigUpdaterName: ifNotMigrating(logger.Manifold(logger.ManifoldConfig{
   162  			AgentName:     agentName,
   163  			APICallerName: apiCallerName,
   164  		})),
   165  
   166  		// The api address updater is a leaf worker that rewrites agent config
   167  		// as the controller addresses change. We should only need one of
   168  		// these in a consolidated agent.
   169  		apiAddressUpdaterName: ifNotMigrating(apiaddressupdater.Manifold(apiaddressupdater.ManifoldConfig{
   170  			AgentName:     agentName,
   171  			APICallerName: apiCallerName,
   172  		})),
   173  
   174  		// The proxy config updater is a leaf worker that sets http/https/apt/etc
   175  		// proxy settings.
   176  		// TODO(fwereade): timing of this is suspicious. There was superstitious
   177  		// code trying to run this early; if that ever helped, it was only by
   178  		// coincidence. Probably we ought to be making components that might
   179  		// need proxy config into explicit dependencies of the proxy updater...
   180  		proxyConfigUpdaterName: ifNotMigrating(proxyupdater.Manifold(proxyupdater.ManifoldConfig{
   181  			AgentName:     agentName,
   182  			APICallerName: apiCallerName,
   183  			WorkerFunc:    proxyupdater.NewWorker,
   184  		})),
   185  
   186  		// The charmdir resource coordinates whether the charm directory is
   187  		// available or not; after 'start' hook and before 'stop' hook
   188  		// executes, and not during upgrades.
   189  		charmDirName: ifNotMigrating(fortress.Manifold()),
   190  
   191  		// The leadership tracker attempts to secure and retain leadership of
   192  		// the unit's service, and is consulted on such matters by the
   193  		// uniter. As it stannds today, we'll need one per unit in a
   194  		// consolidated agent.
   195  		leadershipTrackerName: ifNotMigrating(leadership.Manifold(leadership.ManifoldConfig{
   196  			AgentName:           agentName,
   197  			APICallerName:       apiCallerName,
   198  			Clock:               clock.WallClock,
   199  			LeadershipGuarantee: config.LeadershipGuarantee,
   200  		})),
   201  
   202  		// HookRetryStrategy uses a retrystrategy worker to get a
   203  		// retry strategy that will be used by the uniter to run its hooks.
   204  		hookRetryStrategyName: ifNotMigrating(retrystrategy.Manifold(retrystrategy.ManifoldConfig{
   205  			AgentName:     agentName,
   206  			APICallerName: apiCallerName,
   207  			NewFacade:     retrystrategy.NewFacade,
   208  			NewWorker:     retrystrategy.NewRetryStrategyWorker,
   209  		})),
   210  
   211  		// The uniter installs charms; manages the unit's presence in its
   212  		// relations; creates suboordinate units; runs all the hooks; sends
   213  		// metrics; etc etc etc. We expect to break it up further in the
   214  		// coming weeks, and to need one per unit in a consolidated agent
   215  		// (and probably one for each component broken out).
   216  		uniterName: ifNotMigrating(uniter.Manifold(uniter.ManifoldConfig{
   217  			AgentName:       agentName,
   218  			APICallerName:   apiCallerName,
   219  			MachineLockName: coreagent.MachineLockName,
   220  			Clock:           clock.WallClock,
   221  			LeadershipTrackerName: leadershipTrackerName,
   222  			CharmDirName:          charmDirName,
   223  			HookRetryStrategyName: hookRetryStrategyName,
   224  		})),
   225  
   226  		// TODO (mattyw) should be added to machine agent.
   227  		metricSpoolName: ifNotMigrating(spool.Manifold(spool.ManifoldConfig{
   228  			AgentName: agentName,
   229  		})),
   230  
   231  		// The metric collect worker executes the collect-metrics hook in a
   232  		// restricted context that can safely run concurrently with other hooks.
   233  		metricCollectName: ifNotMigrating(collect.Manifold(collect.ManifoldConfig{
   234  			AgentName:       agentName,
   235  			MetricSpoolName: metricSpoolName,
   236  			CharmDirName:    charmDirName,
   237  		})),
   238  
   239  		// The meter status worker executes the meter-status-changed hook when it detects
   240  		// that the meter status has changed.
   241  		meterStatusName: ifNotMigrating(meterstatus.Manifold(meterstatus.ManifoldConfig{
   242  			AgentName:                agentName,
   243  			APICallerName:            apiCallerName,
   244  			MachineLockName:          coreagent.MachineLockName,
   245  			Clock:                    clock.WallClock,
   246  			NewHookRunner:            meterstatus.NewHookRunner,
   247  			NewMeterStatusAPIClient:  msapi.NewClient,
   248  			NewConnectedStatusWorker: meterstatus.NewConnectedStatusWorker,
   249  			NewIsolatedStatusWorker:  meterstatus.NewIsolatedStatusWorker,
   250  		})),
   251  
   252  		// The metric sender worker periodically sends accumulated metrics to the controller.
   253  		metricSenderName: ifNotMigrating(sender.Manifold(sender.ManifoldConfig{
   254  			AgentName:       agentName,
   255  			APICallerName:   apiCallerName,
   256  			MetricSpoolName: metricSpoolName,
   257  		})),
   258  	}
   259  }
   260  
   261  var ifNotMigrating = engine.Housing{
   262  	Flags: []string{
   263  		migrationInactiveFlagName,
   264  	},
   265  	Occupy: migrationFortressName,
   266  }.Decorate
   267  
   268  const (
   269  	agentName            = "agent"
   270  	apiConfigWatcherName = "api-config-watcher"
   271  	apiCallerName        = "api-caller"
   272  	logSenderName        = "log-sender"
   273  	upgraderName         = "upgrader"
   274  
   275  	migrationFortressName     = "migration-fortress"
   276  	migrationInactiveFlagName = "migration-inactive-flag"
   277  	migrationMinionName       = "migration-minion"
   278  
   279  	loggingConfigUpdaterName = "logging-config-updater"
   280  	proxyConfigUpdaterName   = "proxy-config-updater"
   281  	apiAddressUpdaterName    = "api-address-updater"
   282  
   283  	charmDirName          = "charm-dir"
   284  	leadershipTrackerName = "leadership-tracker"
   285  	hookRetryStrategyName = "hook-retry-strategy"
   286  	uniterName            = "uniter"
   287  
   288  	metricSpoolName   = "metric-spool"
   289  	meterStatusName   = "meter-status"
   290  	metricCollectName = "metric-collect"
   291  	metricSenderName  = "metric-sender"
   292  )