func (s *ManifoldSuite) TestStartMissingAPICaller(c *gc.C) { manifold := servicescaler.Manifold(servicescaler.ManifoldConfig{ APICallerName: "api-caller", }) getResource := dt.StubGetResource(dt.StubResources{ "api-caller": dt.StubResource{Error: dependency.ErrMissing}, }) worker, err := manifold.Start(getResource) c.Check(errors.Cause(err), gc.Equals, dependency.ErrMissing) c.Check(worker, gc.IsNil) }
func (s *ManifoldSuite) TestStartMissingAPICaller(c *gc.C) { manifold := servicescaler.Manifold(servicescaler.ManifoldConfig{ APICallerName: "api-caller", }) context := dt.StubContext(nil, map[string]interface{}{ "api-caller": dependency.ErrMissing, }) worker, err := manifold.Start(context) c.Check(errors.Cause(err), gc.Equals, dependency.ErrMissing) c.Check(worker, gc.IsNil) }
func (s *ManifoldSuite) TestStartFacadeError(c *gc.C) { expectCaller := &fakeCaller{} manifold := servicescaler.Manifold(servicescaler.ManifoldConfig{ APICallerName: "api-caller", NewFacade: func(apiCaller base.APICaller) (servicescaler.Facade, error) { c.Check(apiCaller, gc.Equals, expectCaller) return nil, errors.New("blort") }, }) context := dt.StubContext(nil, map[string]interface{}{ "api-caller": expectCaller, }) worker, err := manifold.Start(context) c.Check(err, gc.ErrorMatches, "blort") c.Check(worker, gc.IsNil) }
func (s *ManifoldSuite) TestStartFacadeError(c *gc.C) { expectCaller := &fakeCaller{} manifold := servicescaler.Manifold(servicescaler.ManifoldConfig{ APICallerName: "api-caller", NewFacade: func(apiCaller base.APICaller) (servicescaler.Facade, error) { c.Check(apiCaller, gc.Equals, expectCaller) return nil, errors.New("blort") }, }) getResource := dt.StubGetResource(dt.StubResources{ "api-caller": dt.StubResource{Output: expectCaller}, }) worker, err := manifold.Start(getResource) c.Check(err, gc.ErrorMatches, "blort") c.Check(worker, gc.IsNil) }
func (s *ManifoldSuite) TestSuccess(c *gc.C) { expectWorker := &fakeWorker{} manifold := servicescaler.Manifold(servicescaler.ManifoldConfig{ APICallerName: "api-caller", NewFacade: func(_ base.APICaller) (servicescaler.Facade, error) { return &fakeFacade{}, nil }, NewWorker: func(_ servicescaler.Config) (worker.Worker, error) { return expectWorker, nil }, }) context := dt.StubContext(nil, map[string]interface{}{ "api-caller": &fakeCaller{}, }) worker, err := manifold.Start(context) c.Check(err, jc.ErrorIsNil) c.Check(worker, gc.Equals, expectWorker) }
func (s *ManifoldSuite) TestSuccess(c *gc.C) { expectWorker := &fakeWorker{} manifold := servicescaler.Manifold(servicescaler.ManifoldConfig{ APICallerName: "api-caller", NewFacade: func(_ base.APICaller) (servicescaler.Facade, error) { return &fakeFacade{}, nil }, NewWorker: func(_ servicescaler.Config) (worker.Worker, error) { return expectWorker, nil }, }) getResource := dt.StubGetResource(dt.StubResources{ "api-caller": dt.StubResource{Output: &fakeCaller{}}, }) worker, err := manifold.Start(getResource) c.Check(err, jc.ErrorIsNil) c.Check(worker, gc.Equals, expectWorker) }
func (s *ManifoldSuite) TestStartWorkerError(c *gc.C) { expectFacade := &fakeFacade{} manifold := servicescaler.Manifold(servicescaler.ManifoldConfig{ APICallerName: "api-caller", NewFacade: func(_ base.APICaller) (servicescaler.Facade, error) { return expectFacade, nil }, NewWorker: func(config servicescaler.Config) (worker.Worker, error) { c.Check(config.Validate(), jc.ErrorIsNil) c.Check(config.Facade, gc.Equals, expectFacade) return nil, errors.New("splot") }, }) context := dt.StubContext(nil, map[string]interface{}{ "api-caller": &fakeCaller{}, }) worker, err := manifold.Start(context) c.Check(err, gc.ErrorMatches, "splot") c.Check(worker, gc.IsNil) }
func (s *ManifoldSuite) TestStartWorkerError(c *gc.C) { expectFacade := &fakeFacade{} manifold := servicescaler.Manifold(servicescaler.ManifoldConfig{ APICallerName: "api-caller", NewFacade: func(_ base.APICaller) (servicescaler.Facade, error) { return expectFacade, nil }, NewWorker: func(config servicescaler.Config) (worker.Worker, error) { c.Check(config.Validate(), jc.ErrorIsNil) c.Check(config.Facade, gc.Equals, expectFacade) return nil, errors.New("splot") }, }) getResource := dt.StubGetResource(dt.StubResources{ "api-caller": dt.StubResource{Output: &fakeCaller{}}, }) worker, err := manifold.Start(getResource) c.Check(err, gc.ErrorMatches, "splot") c.Check(worker, gc.IsNil) }
// Manifolds returns a set of interdependent dependency manifolds that will // run together to administer a model, as configured. func Manifolds(config ManifoldsConfig) dependency.Manifolds { modelTag := config.Agent.CurrentConfig().Model() return dependency.Manifolds{ // The first group are foundational; the agent and clock // which wrap those supplied in config, and the api-caller // through everything else communicates with the apiserver. agentName: agent.Manifold(config.Agent), clockName: clockManifold(config.Clock), apiCallerName: apicaller.Manifold(apicaller.ManifoldConfig{ AgentName: agentName, APIOpen: apicaller.APIOpen, NewConnection: apicaller.OnlyConnect, }), // The discover spaces gate is used to coordinate workers which // shouldn't do anything until the discoverspaces worker has completed // its first discovery attempt. discovertSpacesCheckGateName: gate.ManifoldEx(config.DiscoverSpacesCheckLock), // All other manifolds should depend on at least one of these // three, which handle all the tasks that are safe and sane // to run in *all* controller machines. notDeadFlagName: lifeflag.Manifold(lifeflag.ManifoldConfig{ APICallerName: apiCallerName, Entity: modelTag, Result: life.IsNotDead, Filter: lifeFilter, NewFacade: lifeflag.NewFacade, NewWorker: lifeflag.NewWorker, }), notAliveFlagName: lifeflag.Manifold(lifeflag.ManifoldConfig{ APICallerName: apiCallerName, Entity: modelTag, Result: life.IsNotAlive, Filter: lifeFilter, NewFacade: lifeflag.NewFacade, NewWorker: lifeflag.NewWorker, }), isResponsibleFlagName: singular.Manifold(singular.ManifoldConfig{ ClockName: clockName, AgentName: agentName, APICallerName: apiCallerName, Duration: config.RunFlagDuration, NewFacade: singular.NewFacade, NewWorker: singular.NewWorker, }), // Everything else should be wrapped in ifResponsible, // ifNotAlive, or ifNotDead, to ensure that only a single // controller is administering this model at a time. // // NOTE: not perfectly reliable at this stage? i.e. a worker // that ignores its stop signal for "too long" might continue // to take admin actions after the window of responsibility // closes. This *is* a pre-existing problem, but demands some // thought/care: e.g. should we make sure the apiserver also // closes any connections that lose responsibility..? can we // make sure all possible environ operations are either time- // bounded or interruptible? etc // // On the other hand, all workers *should* be written in the // expectation of dealing with a sucky infrastructure running // things in parallel unexpectedly, just because the universe // hates us and will engineer matters such that it happens // sometimes, even when we try to avoid it. // The environ tracker could/should be used by several other // workers (firewaller, provisioners, address-cleaner?). environTrackerName: ifResponsible(environ.Manifold(environ.ManifoldConfig{ APICallerName: apiCallerName, NewEnvironFunc: environs.New, })), // The undertaker is currently the only ifNotAlive worker. undertakerName: ifNotAlive(undertaker.Manifold(undertaker.ManifoldConfig{ APICallerName: apiCallerName, EnvironName: environTrackerName, ClockName: clockName, RemoveDelay: config.ModelRemoveDelay, NewFacade: undertaker.NewFacade, NewWorker: undertaker.NewWorker, })), // All the rest depend on ifNotDead. discoverSpacesName: ifNotDead(discoverspaces.Manifold(discoverspaces.ManifoldConfig{ EnvironName: environTrackerName, APICallerName: apiCallerName, UnlockerName: discovertSpacesCheckGateName, NewFacade: discoverspaces.NewFacade, NewWorker: discoverspaces.NewWorker, })), computeProvisionerName: ifNotDead(provisioner.Manifold(provisioner.ManifoldConfig{ AgentName: agentName, APICallerName: apiCallerName, })), storageProvisionerName: ifNotDead(storageprovisioner.ModelManifold(storageprovisioner.ModelManifoldConfig{ APICallerName: apiCallerName, ClockName: clockName, Scope: modelTag, })), firewallerName: ifNotDead(firewaller.Manifold(firewaller.ManifoldConfig{ APICallerName: apiCallerName, })), unitAssignerName: ifNotDead(unitassigner.Manifold(unitassigner.ManifoldConfig{ APICallerName: apiCallerName, })), serviceScalerName: ifNotDead(servicescaler.Manifold(servicescaler.ManifoldConfig{ APICallerName: apiCallerName, NewFacade: servicescaler.NewFacade, NewWorker: servicescaler.New, })), instancePollerName: ifNotDead(instancepoller.Manifold(instancepoller.ManifoldConfig{ APICallerName: apiCallerName, EnvironName: environTrackerName, })), charmRevisionUpdaterName: ifNotDead(charmrevisionmanifold.Manifold(charmrevisionmanifold.ManifoldConfig{ APICallerName: apiCallerName, ClockName: clockName, Period: config.CharmRevisionUpdateInterval, NewFacade: charmrevisionmanifold.NewAPIFacade, NewWorker: charmrevision.NewWorker, })), metricWorkerName: ifNotDead(metricworker.Manifold(metricworker.ManifoldConfig{ APICallerName: apiCallerName, })), stateCleanerName: ifNotDead(cleaner.Manifold(cleaner.ManifoldConfig{ APICallerName: apiCallerName, })), addressCleanerName: ifNotDead(addresser.Manifold(addresser.ManifoldConfig{ APICallerName: apiCallerName, })), statusHistoryPrunerName: ifNotDead(statushistorypruner.Manifold(statushistorypruner.ManifoldConfig{ APICallerName: apiCallerName, MaxLogsPerEntity: config.EntityStatusHistoryCount, PruneInterval: config.EntityStatusHistoryInterval, // TODO(fwereade): 2016-03-17 lp:1558657 NewTimer: worker.NewTimer, })), } }
func (s *ManifoldSuite) TestOutput(c *gc.C) { manifold := servicescaler.Manifold(servicescaler.ManifoldConfig{}) c.Check(manifold.Output, gc.IsNil) }
func (s *ManifoldSuite) TestInputs(c *gc.C) { manifold := servicescaler.Manifold(servicescaler.ManifoldConfig{ APICallerName: "washington the terrible", }) c.Check(manifold.Inputs, jc.DeepEquals, []string{"washington the terrible"}) }