func (s *ManifoldSuite) TestInputs(c *gc.C) { manifold := retrystrategy.Manifold(retrystrategy.ManifoldConfig{ AgentName: "wut", APICallerName: "exactly", }) c.Check(manifold.Inputs, jc.DeepEquals, []string{"wut", "exactly"}) }
func (s *ManifoldSuite) TestStartFacadeValueError(c *gc.C) { manifold := retrystrategy.Manifold(retrystrategy.ManifoldConfig{ AgentApiManifoldConfig: s.stubAgentApiManifoldConfig, NewFacade: s.newFacade(&fakeFacadeErr{err: errors.New("blop")}), }) w, err := manifold.Start(s.getResource) c.Assert(errors.Cause(err), gc.ErrorMatches, "blop") c.Assert(w, gc.IsNil) }
func (s *ManifoldSuite) TestStartFacadeValueError(c *gc.C) { manifold := retrystrategy.Manifold(retrystrategy.ManifoldConfig{ AgentName: "agent", APICallerName: "api-caller", NewFacade: s.newFacade(&fakeFacadeErr{err: errors.New("blop")}), }) w, err := manifold.Start(s.context) c.Assert(errors.Cause(err), gc.ErrorMatches, "blop") c.Assert(w, gc.IsNil) }
func (s *ManifoldSuite) TestStartWorkerError(c *gc.C) { manifold := retrystrategy.Manifold(retrystrategy.ManifoldConfig{ AgentApiManifoldConfig: s.stubAgentApiManifoldConfig, NewFacade: s.newFacade(&fakeFacade{}), NewWorker: s.newWorker(nil, errors.New("blam")), }) w, err := manifold.Start(s.getResource) c.Assert(err, gc.ErrorMatches, "blam") c.Assert(w, gc.IsNil) }
func (s *ManifoldSuite) TestStartWorkerError(c *gc.C) { manifold := retrystrategy.Manifold(retrystrategy.ManifoldConfig{ AgentName: "agent", APICallerName: "api-caller", NewFacade: s.newFacade(&fakeFacade{}), NewWorker: s.newWorker(nil, errors.New("blam")), }) w, err := manifold.Start(s.context) c.Assert(err, gc.ErrorMatches, "blam") c.Assert(w, gc.IsNil) }
func (s *ManifoldSuite) TestStartMissingAgent(c *gc.C) { manifold := retrystrategy.Manifold(retrystrategy.ManifoldConfig{ AgentApiManifoldConfig: s.stubAgentApiManifoldConfig, }) getResource := dt.StubGetResource(dt.StubResources{ "wut": dt.StubResource{Error: dependency.ErrMissing}, }) w, err := manifold.Start(getResource) c.Assert(errors.Cause(err), gc.Equals, dependency.ErrMissing) c.Assert(w, gc.IsNil) }
func (s *ManifoldSuite) TestStartSuccess(c *gc.C) { fakeWorker := &fakeWorker{} manifold := retrystrategy.Manifold(retrystrategy.ManifoldConfig{ AgentApiManifoldConfig: s.stubAgentApiManifoldConfig, NewFacade: s.newFacade(&fakeFacade{}), NewWorker: s.newWorker(fakeWorker, nil), }) w, err := manifold.Start(s.getResource) c.Assert(err, jc.ErrorIsNil) c.Assert(w, gc.Equals, fakeWorker) }
func (s *ManifoldSuite) TestStartSuccess(c *gc.C) { fakeWorker := &fakeWorker{} manifold := retrystrategy.Manifold(retrystrategy.ManifoldConfig{ AgentName: "agent", APICallerName: "api-caller", NewFacade: s.newFacade(&fakeFacade{}), NewWorker: s.newWorker(fakeWorker, nil), }) w, err := manifold.Start(s.context) c.Assert(err, jc.ErrorIsNil) c.Assert(w, gc.Equals, fakeWorker) }
func (s *ManifoldSuite) TestStartMissingAPI(c *gc.C) { manifold := retrystrategy.Manifold(retrystrategy.ManifoldConfig{ AgentName: "agent", APICallerName: "api-caller", }) context := dt.StubContext(nil, map[string]interface{}{ "agent": s.fakeAgent, "api-caller": dependency.ErrMissing, }) w, err := manifold.Start(context) c.Assert(errors.Cause(err), gc.Equals, dependency.ErrMissing) c.Assert(w, gc.IsNil) }
func (s *ManifoldSuite) TestOutputBadTarget(c *gc.C) { manifold := retrystrategy.Manifold(retrystrategy.ManifoldConfig{ AgentApiManifoldConfig: s.stubAgentApiManifoldConfig, NewFacade: s.newFacade(&fakeFacade{}), NewWorker: retrystrategy.NewRetryStrategyWorker, }) w, err := manifold.Start(s.getResource) s.AddCleanup(func(c *gc.C) { w.Kill() }) c.Assert(err, jc.ErrorIsNil) var out interface{} err = manifold.Output(w, &out) c.Assert(err.Error(), gc.Equals, "out should be a *params.RetryStrategy; is *interface {}") }
func (s *ManifoldSuite) TestOutputBadInput(c *gc.C) { manifold := retrystrategy.Manifold(retrystrategy.ManifoldConfig{ AgentApiManifoldConfig: s.stubAgentApiManifoldConfig, NewFacade: s.newFacade(&fakeFacade{}), NewWorker: s.newWorker(&fakeWorker{}, nil), }) w, err := manifold.Start(s.getResource) c.Assert(err, jc.ErrorIsNil) var out params.RetryStrategy err = manifold.Output(w, &out) c.Assert(out, gc.Equals, params.RetryStrategy{}) c.Assert(err.Error(), gc.Equals, "in should be a *retryStrategyWorker; is *retrystrategy_test.fakeWorker") }
func (s *ManifoldSuite) TestOutputBadInput(c *gc.C) { manifold := retrystrategy.Manifold(retrystrategy.ManifoldConfig{ AgentName: "agent", APICallerName: "api-caller", NewFacade: s.newFacade(&fakeFacade{}), NewWorker: s.newWorker(&fakeWorker{}, nil), }) w, err := manifold.Start(s.context) c.Assert(err, jc.ErrorIsNil) var out params.RetryStrategy err = manifold.Output(w, &out) c.Assert(out, gc.Equals, params.RetryStrategy{}) c.Assert(err.Error(), gc.Equals, "in should be a *retryStrategyWorker; is *retrystrategy_test.fakeWorker") }
func (s *ManifoldSuite) TestOutputSuccess(c *gc.C) { manifold := retrystrategy.Manifold(retrystrategy.ManifoldConfig{ AgentApiManifoldConfig: s.stubAgentApiManifoldConfig, NewFacade: s.newFacade(&fakeFacade{}), NewWorker: retrystrategy.NewRetryStrategyWorker, }) w, err := manifold.Start(s.getResource) s.AddCleanup(func(c *gc.C) { w.Kill() }) c.Assert(err, jc.ErrorIsNil) var out params.RetryStrategy err = manifold.Output(w, &out) c.Assert(err, jc.ErrorIsNil) c.Assert(out, gc.Equals, fakeStrategy) }
func (s *ManifoldSuite) TestOutputSuccess(c *gc.C) { manifold := retrystrategy.Manifold(retrystrategy.ManifoldConfig{ AgentName: "agent", APICallerName: "api-caller", NewFacade: s.newFacade(&fakeFacade{}), NewWorker: retrystrategy.NewRetryStrategyWorker, }) w, err := manifold.Start(s.context) s.AddCleanup(func(c *gc.C) { w.Kill() }) c.Assert(err, jc.ErrorIsNil) var out params.RetryStrategy err = manifold.Output(w, &out) c.Assert(err, jc.ErrorIsNil) c.Assert(out, gc.Equals, fakeStrategy) }
// Manifolds returns a set of co-configured manifolds covering the various // responsibilities of a standalone unit agent. It also accepts the logSource // argument because we haven't figured out how to thread all the logging bits // through a dependency engine yet. // // Thou Shalt Not Use String Literals In This Function. Or Else. func Manifolds(config ManifoldsConfig) dependency.Manifolds { // connectFilter exists to let us retry api connections immediately // on password change, rather than causing the dependency engine to // wait for a while. connectFilter := func(err error) error { cause := errors.Cause(err) if cause == apicaller.ErrChangedPassword { return dependency.ErrBounce } else if cause == apicaller.ErrConnectImpossible { return worker.ErrTerminateAgent } return err } return dependency.Manifolds{ // The agent manifold references the enclosing agent, and is the // foundation stone on which most other manifolds ultimately depend. // (Currently, that is "all manifolds", but consider a shared clock.) agentName: agent.Manifold(config.Agent), // The machine lock manifold is a thin concurrent wrapper around an // FSLock in an agreed location. We expect it to be replaced with an // in-memory lock when the unit agent moves into the machine agent. machineLockName: machinelock.Manifold(machinelock.ManifoldConfig{ AgentName: agentName, }), // The api-config-watcher manifold monitors the API server // addresses in the agent config and bounces when they // change. It's required as part of model migrations. apiConfigWatcherName: apiconfigwatcher.Manifold(apiconfigwatcher.ManifoldConfig{ AgentName: agentName, AgentConfigChanged: config.AgentConfigChanged, }), // The api caller is a thin concurrent wrapper around a connection // to some API server. It's used by many other manifolds, which all // select their own desired facades. It will be interesting to see // how this works when we consolidate the agents; might be best to // handle the auth changes server-side..? apiCallerName: apicaller.Manifold(apicaller.ManifoldConfig{ AgentName: agentName, APIConfigWatcherName: apiConfigWatcherName, APIOpen: apicaller.APIOpen, NewConnection: apicaller.ScaryConnect, Filter: connectFilter, }), // The log sender is a leaf worker that sends log messages to some // API server, when configured so to do. We should only need one of // these in a consolidated agent. logSenderName: logsender.Manifold(logsender.ManifoldConfig{ APICallerName: apiCallerName, LogSource: config.LogSource, }), // The upgrader is a leaf worker that returns a specific error type // recognised by the unit agent, causing other workers to be stopped // and the agent to be restarted running the new tools. We should only // need one of these in a consolidated agent, but we'll need to be // careful about behavioural differences, and interactions with the // upgradesteps worker. upgraderName: upgrader.Manifold(upgrader.ManifoldConfig{ AgentName: agentName, APICallerName: apiCallerName, }), migrationFortressName: fortress.Manifold(), // The migration minion handles the agent side aspects of model migrations. migrationMinionName: migrationminion.Manifold(migrationminion.ManifoldConfig{ AgentName: agentName, APICallerName: apiCallerName, FortressName: migrationFortressName, NewFacade: migrationminion.NewFacade, NewWorker: migrationminion.NewWorker, }), // The logging config updater is a leaf worker that indirectly // controls the messages sent via the log sender according to // changes in environment config. We should only need one of // these in a consolidated agent. loggingConfigUpdaterName: logger.Manifold(logger.ManifoldConfig{ AgentName: agentName, APICallerName: apiCallerName, }), // The api address updater is a leaf worker that rewrites agent config // as the controller addresses change. We should only need one of // these in a consolidated agent. apiAddressUpdaterName: apiaddressupdater.Manifold(apiaddressupdater.ManifoldConfig{ AgentName: agentName, APICallerName: apiCallerName, }), // The proxy config updater is a leaf worker that sets http/https/apt/etc // proxy settings. // TODO(fwereade): timing of this is suspicious. There was superstitious // code trying to run this early; if that ever helped, it was only by // coincidence. Probably we ought to be making components that might // need proxy config into explicit dependencies of the proxy updater... proxyConfigUpdaterName: proxyupdater.Manifold(proxyupdater.ManifoldConfig{ APICallerName: apiCallerName, }), // The charmdir resource coordinates whether the charm directory is // available or not; after 'start' hook and before 'stop' hook // executes, and not during upgrades. charmDirName: fortress.Manifold(), // The leadership tracker attempts to secure and retain leadership of // the unit's service, and is consulted on such matters by the // uniter. As it stannds today, we'll need one per unit in a // consolidated agent. leadershipTrackerName: leadership.Manifold(leadership.ManifoldConfig{ AgentName: agentName, APICallerName: apiCallerName, LeadershipGuarantee: config.LeadershipGuarantee, }), // HookRetryStrategy uses a retrystrategy worker to get a // retry strategy that will be used by the uniter to run its hooks. hookRetryStrategyName: retrystrategy.Manifold(retrystrategy.ManifoldConfig{ AgentName: agentName, APICallerName: apiCallerName, NewFacade: retrystrategy.NewFacade, NewWorker: retrystrategy.NewRetryStrategyWorker, }), // The uniter installs charms; manages the unit's presence in its // relations; creates suboordinate units; runs all the hooks; sends // metrics; etc etc etc. We expect to break it up further in the // coming weeks, and to need one per unit in a consolidated agent // (and probably one for each component broken out). uniterName: uniter.Manifold(uniter.ManifoldConfig{ AgentName: agentName, APICallerName: apiCallerName, LeadershipTrackerName: leadershipTrackerName, MachineLockName: machineLockName, CharmDirName: charmDirName, HookRetryStrategyName: hookRetryStrategyName, }), // TODO (mattyw) should be added to machine agent. metricSpoolName: spool.Manifold(spool.ManifoldConfig{ AgentName: agentName, }), // The metric collect worker executes the collect-metrics hook in a // restricted context that can safely run concurrently with other hooks. metricCollectName: collect.Manifold(collect.ManifoldConfig{ AgentName: agentName, MetricSpoolName: metricSpoolName, CharmDirName: charmDirName, }), // The meter status worker executes the meter-status-changed hook when it detects // that the meter status has changed. meterStatusName: meterstatus.Manifold(meterstatus.ManifoldConfig{ AgentName: agentName, APICallerName: apiCallerName, MachineLockName: machineLockName, NewHookRunner: meterstatus.NewHookRunner, NewMeterStatusAPIClient: msapi.NewClient, NewConnectedStatusWorker: meterstatus.NewConnectedStatusWorker, NewIsolatedStatusWorker: meterstatus.NewIsolatedStatusWorker, }), // The metric sender worker periodically sends accumulated metrics to the controller. metricSenderName: sender.Manifold(sender.ManifoldConfig{ AgentName: agentName, APICallerName: apiCallerName, MetricSpoolName: metricSpoolName, }), } }
func (s *ManifoldSuite) TestInputs(c *gc.C) { manifold := retrystrategy.Manifold(retrystrategy.ManifoldConfig{ AgentApiManifoldConfig: s.stubAgentApiManifoldConfig, }) c.Check(manifold.Inputs, jc.DeepEquals, []string{"wut", "exactly"}) }