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

     1  // Copyright 2017 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package caasoperator
     5  
     6  import (
     7  	"time"
     8  
     9  	"github.com/juju/clock"
    10  	"github.com/juju/utils/voyeur"
    11  	"github.com/prometheus/client_golang/prometheus"
    12  	"gopkg.in/juju/worker.v1"
    13  	"gopkg.in/juju/worker.v1/dependency"
    14  
    15  	coreagent "github.com/juju/juju/agent"
    16  	"github.com/juju/juju/api"
    17  	"github.com/juju/juju/api/base"
    18  	caasoperatorapi "github.com/juju/juju/api/caasoperator"
    19  	"github.com/juju/juju/cmd/jujud/agent/engine"
    20  	"github.com/juju/juju/core/machinelock"
    21  	"github.com/juju/juju/worker/agent"
    22  	"github.com/juju/juju/worker/apiaddressupdater"
    23  	"github.com/juju/juju/worker/apicaller"
    24  	"github.com/juju/juju/worker/apiconfigwatcher"
    25  	"github.com/juju/juju/worker/caasoperator"
    26  	"github.com/juju/juju/worker/fortress"
    27  	"github.com/juju/juju/worker/gate"
    28  	"github.com/juju/juju/worker/logger"
    29  	"github.com/juju/juju/worker/logsender"
    30  	"github.com/juju/juju/worker/migrationflag"
    31  	"github.com/juju/juju/worker/migrationminion"
    32  	"github.com/juju/juju/worker/retrystrategy"
    33  	"github.com/juju/juju/worker/uniter"
    34  )
    35  
    36  // ManifoldsConfig allows specialisation of the result of Manifolds.
    37  type ManifoldsConfig struct {
    38  
    39  	// Agent contains the agent that will be wrapped and made available to
    40  	// its dependencies via a dependency.Engine.
    41  	Agent coreagent.Agent
    42  
    43  	// AgentConfigChanged is set whenever the unit agent's config
    44  	// is updated.
    45  	AgentConfigChanged *voyeur.Value
    46  
    47  	// Clock contains the clock that will be made available to manifolds.
    48  	Clock clock.Clock
    49  
    50  	// LogSource will be read from by the logsender component.
    51  	LogSource logsender.LogRecordCh
    52  
    53  	// UpdateLoggerConfig is a function that will save the specified
    54  	// config value as the logging config in the agent.conf file.
    55  	UpdateLoggerConfig func(string) error
    56  
    57  	// PrometheusRegisterer is a prometheus.Registerer that may be used
    58  	// by workers to register Prometheus metric collectors.
    59  	PrometheusRegisterer prometheus.Registerer
    60  
    61  	// LeadershipGuarantee controls the behaviour of the leadership tracker.
    62  	LeadershipGuarantee time.Duration
    63  
    64  	// ValidateMigration is called by the migrationminion during the
    65  	// migration process to check that the agent will be ok when
    66  	// connected to the new target controller.
    67  	ValidateMigration func(base.APICaller) error
    68  
    69  	// UpgradeStepsLock is passed to the upgrade steps gate to
    70  	// coordinate workers that shouldn't do anything until the
    71  	// upgrade-steps worker is done.
    72  	UpgradeStepsLock gate.Lock
    73  
    74  	// MachineLock is a central source for acquiring the machine lock.
    75  	// This is used by a number of workers to ensure serialisation of actions
    76  	// across the machine.
    77  	MachineLock machinelock.Lock
    78  }
    79  
    80  // Manifolds returns a set of co-configured manifolds covering the various
    81  // responsibilities of a caasoperator agent. It also accepts the logSource
    82  // argument because we haven't figured out how to thread all the logging bits
    83  // through a dependency engine yet.
    84  //
    85  // Thou Shalt Not Use String Literals In This Function. Or Else.
    86  func Manifolds(config ManifoldsConfig) dependency.Manifolds {
    87  
    88  	return dependency.Manifolds{
    89  
    90  		// The agent manifold references the enclosing agent, and is the
    91  		// foundation stone on which most other manifolds ultimately depend.
    92  		agentName: agent.Manifold(config.Agent),
    93  
    94  		// The api-config-watcher manifold monitors the API server
    95  		// addresses in the agent config and bounces when they
    96  		// change. It's required as part of model migrations.
    97  		apiConfigWatcherName: apiconfigwatcher.Manifold(apiconfigwatcher.ManifoldConfig{
    98  			AgentName:          agentName,
    99  			AgentConfigChanged: config.AgentConfigChanged,
   100  		}),
   101  
   102  		apiCallerName: apicaller.Manifold(apicaller.ManifoldConfig{
   103  			AgentName:            agentName,
   104  			APIOpen:              api.Open,
   105  			APIConfigWatcherName: apiConfigWatcherName,
   106  			NewConnection:        apicaller.OnlyConnect,
   107  		}),
   108  
   109  		clockName: clockManifold(config.Clock),
   110  
   111  		// The log sender is a leaf worker that sends log messages to some
   112  		// API server, when configured so to do. We should only need one of
   113  		// these in a consolidated agent.
   114  		logSenderName: logsender.Manifold(logsender.ManifoldConfig{
   115  			APICallerName: apiCallerName,
   116  			LogSource:     config.LogSource,
   117  		}),
   118  
   119  		// The upgrade steps gate is used to coordinate workers which
   120  		// shouldn't do anything until the upgrade-steps worker has
   121  		// finished running any required upgrade steps. The flag of
   122  		// similar name is used to implement the isFullyUpgraded func
   123  		// that keeps upgrade concerns out of unrelated manifolds.
   124  		upgradeStepsGateName: gate.ManifoldEx(config.UpgradeStepsLock),
   125  		upgradeStepsFlagName: gate.FlagManifold(gate.FlagManifoldConfig{
   126  			GateName:  upgradeStepsGateName,
   127  			NewWorker: gate.NewFlagWorker,
   128  		}),
   129  
   130  		// The migration workers collaborate to run migrations;
   131  		// and to create a mechanism for running other workers
   132  		// so they can't accidentally interfere with a migration
   133  		// in progress. Such a manifold should (1) depend on the
   134  		// migration-inactive flag, to know when to start or die;
   135  		// and (2) occupy the migration-fortress, so as to avoid
   136  		// possible interference with the minion (which will not
   137  		// take action until it's gained sole control of the
   138  		// fortress).
   139  		migrationFortressName: ifFullyUpgraded(fortress.Manifold()),
   140  		migrationInactiveFlagName: migrationflag.Manifold(migrationflag.ManifoldConfig{
   141  			APICallerName: apiCallerName,
   142  			Check:         migrationflag.IsTerminal,
   143  			NewFacade:     migrationflag.NewFacade,
   144  			NewWorker:     migrationflag.NewWorker,
   145  		}),
   146  		migrationMinionName: migrationminion.Manifold(migrationminion.ManifoldConfig{
   147  			AgentName:         agentName,
   148  			APICallerName:     apiCallerName,
   149  			FortressName:      migrationFortressName,
   150  			APIOpen:           api.Open,
   151  			ValidateMigration: config.ValidateMigration,
   152  			NewFacade:         migrationminion.NewFacade,
   153  			NewWorker:         migrationminion.NewWorker,
   154  		}),
   155  
   156  		// The logging config updater is a leaf worker that indirectly
   157  		// controls the messages sent via the log sender according to
   158  		// changes in environment config. We should only need one of
   159  		// these in a consolidated agent.
   160  		loggingConfigUpdaterName: ifNotMigrating(logger.Manifold(logger.ManifoldConfig{
   161  			AgentName:       agentName,
   162  			APICallerName:   apiCallerName,
   163  			UpdateAgentFunc: config.UpdateLoggerConfig,
   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 charmdir resource coordinates whether the charm directory is
   175  		// available or not; after 'start' hook and before 'stop' hook
   176  		// executes, and not during upgrades.
   177  		charmDirName: ifNotMigrating(fortress.Manifold()),
   178  
   179  		// HookRetryStrategy uses a retrystrategy worker to get a
   180  		// retry strategy that will be used by the uniter to run its hooks.
   181  		hookRetryStrategyName: ifNotMigrating(retrystrategy.Manifold(retrystrategy.ManifoldConfig{
   182  			AgentName:     agentName,
   183  			APICallerName: apiCallerName,
   184  			NewFacade:     retrystrategy.NewFacade,
   185  			NewWorker:     retrystrategy.NewRetryStrategyWorker,
   186  		})),
   187  
   188  		// The operator installs and deploys charm containers;
   189  		// manages the unit's presence in its relations;
   190  		// creates suboordinate units; runs all the hooks;
   191  		// sends metrics; etc etc etc.
   192  
   193  		operatorName: ifNotMigrating(caasoperator.Manifold(caasoperator.ManifoldConfig{
   194  			AgentName:             agentName,
   195  			APICallerName:         apiCallerName,
   196  			ClockName:             clockName,
   197  			MachineLock:           config.MachineLock,
   198  			LeadershipGuarantee:   config.LeadershipGuarantee,
   199  			CharmDirName:          charmDirName,
   200  			HookRetryStrategyName: hookRetryStrategyName,
   201  			TranslateResolverErr:  uniter.TranslateFortressErrors,
   202  
   203  			NewWorker: caasoperator.NewWorker,
   204  			NewClient: func(caller base.APICaller) caasoperator.Client {
   205  				return caasoperatorapi.NewClient(caller)
   206  			},
   207  			NewCharmDownloader: func(caller base.APICaller) caasoperator.Downloader {
   208  				return api.NewCharmDownloader(caller)
   209  			},
   210  		})),
   211  	}
   212  }
   213  
   214  func clockManifold(clock clock.Clock) dependency.Manifold {
   215  	return dependency.Manifold{
   216  		Start: func(dependency.Context) (worker.Worker, error) {
   217  			return engine.NewValueWorker(clock)
   218  		},
   219  		Output: engine.ValueWorkerOutput,
   220  	}
   221  }
   222  
   223  var ifFullyUpgraded = engine.Housing{
   224  	Flags: []string{
   225  		upgradeStepsFlagName,
   226  	},
   227  }.Decorate
   228  
   229  var ifNotMigrating = engine.Housing{
   230  	Flags: []string{
   231  		migrationInactiveFlagName,
   232  	},
   233  	Occupy: migrationFortressName,
   234  }.Decorate
   235  
   236  const (
   237  	agentName            = "agent"
   238  	apiConfigWatcherName = "api-config-watcher"
   239  	apiCallerName        = "api-caller"
   240  	clockName            = "clock"
   241  	operatorName         = "operator"
   242  	logSenderName        = "log-sender"
   243  
   244  	charmDirName          = "charm-dir"
   245  	hookRetryStrategyName = "hook-retry-strategy"
   246  
   247  	upgradeStepsGateName = "upgrade-steps-gate"
   248  	upgradeStepsFlagName = "upgrade-steps-flag"
   249  
   250  	migrationFortressName     = "migration-fortress"
   251  	migrationInactiveFlagName = "migration-inactive-flag"
   252  	migrationMinionName       = "migration-minion"
   253  
   254  	loggingConfigUpdaterName = "logging-config-updater"
   255  	apiAddressUpdaterName    = "api-address-updater"
   256  )