// Manifold returns a dependency manifold that runs an upgrader // worker, using the resource names defined in the supplied config. func Manifold(config ManifoldConfig) dependency.Manifold { inputs := []string{ config.AgentName, config.APICallerName, } // The machine agent uses these but the unit agent doesn't. if config.UpgradeStepsGateName != "" { inputs = append(inputs, config.UpgradeStepsGateName) } if config.UpgradeCheckGateName != "" { inputs = append(inputs, config.UpgradeCheckGateName) } return dependency.Manifold{ Inputs: inputs, Start: func(context dependency.Context) (worker.Worker, error) { var agent agent.Agent if err := context.Get(config.AgentName, &agent); err != nil { return nil, err } currentConfig := agent.CurrentConfig() var apiCaller base.APICaller if err := context.Get(config.APICallerName, &apiCaller); err != nil { return nil, err } upgraderFacade := upgrader.NewState(apiCaller) var upgradeStepsWaiter gate.Waiter if config.UpgradeStepsGateName == "" { upgradeStepsWaiter = gate.NewLock() } else { if config.PreviousAgentVersion == version.Zero { return nil, errors.New("previous agent version not specified") } if err := context.Get(config.UpgradeStepsGateName, &upgradeStepsWaiter); err != nil { return nil, err } } var initialCheckUnlocker gate.Unlocker if config.UpgradeCheckGateName == "" { initialCheckUnlocker = gate.NewLock() } else { if err := context.Get(config.UpgradeCheckGateName, &initialCheckUnlocker); err != nil { return nil, err } } return NewAgentUpgrader( upgraderFacade, currentConfig, config.PreviousAgentVersion, upgradeStepsWaiter, initialCheckUnlocker, ) }, } }
func (s *ManifoldSuite) TestOutput(c *gc.C) { stepsLock := gate.NewLock() checkLock := gate.NewLock() getResource := dt.StubGetResource(dt.StubResources{ "steps-waiter": dt.StubResource{Output: stepsLock}, "check-waiter": dt.StubResource{Output: checkLock}, }) w, err := s.manifold.Start(getResource) c.Assert(err, jc.ErrorIsNil) // Upgrades not completed yet so output is false. s.assertOutputFalse(c, w) // Unlock one of the upgrade gates, output should still be false. stepsLock.Unlock() s.assertOutputFalse(c, w) // Unlock the other gate, output should now be true. checkLock.Unlock() s.assertOutputTrue(c, w) // .. and the worker should exit with ErrBounce. checkStopWithError(c, w, dependency.ErrBounce) // Restarting the worker should result in the output immediately // being true. w2, err := s.manifold.Start(getResource) c.Assert(err, jc.ErrorIsNil) s.assertOutputTrue(c, w) checkStop(c, w2) }
func (s *ManifoldSuite) TestStartSuccess(c *gc.C) { getResource := dt.StubGetResource(dt.StubResources{ "steps-waiter": dt.StubResource{Output: gate.NewLock()}, "check-waiter": dt.StubResource{Output: gate.NewLock()}, }) w, err := s.manifold.Start(getResource) c.Assert(err, jc.ErrorIsNil) checkStop(c, w) }
func (*ManifoldsSuite) TestUpgradeGates(c *gc.C) { upgradeStepsLock := gate.NewLock() upgradeCheckLock := gate.NewLock() manifolds := machine.Manifolds(machine.ManifoldsConfig{ UpgradeStepsLock: upgradeStepsLock, UpgradeCheckLock: upgradeCheckLock, }) assertGate(c, manifolds["upgrade-steps-gate"], upgradeStepsLock) assertGate(c, manifolds["upgrade-check-gate"], upgradeCheckLock) }
func (s *ManifoldSuite) TestOutputWithWrongType(c *gc.C) { getResource := dt.StubGetResource(dt.StubResources{ "steps-waiter": dt.StubResource{Output: gate.NewLock()}, "check-waiter": dt.StubResource{Output: gate.NewLock()}, }) w, err := s.manifold.Start(getResource) c.Assert(err, jc.ErrorIsNil) var foo int err = s.manifold.Output(w, &foo) c.Assert(err, gc.ErrorMatches, `out should be a \*bool;.+`) }
func (s *UpgraderSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) // s.machine needs to have IsManager() so that it can get the actual // current revision to upgrade to. s.state, s.machine = s.OpenAPIAsNewMachine(c, state.JobManageModel) // Capture the value of RetryAfter, and use that captured // value in the cleanup lambda. oldRetryAfter := *upgrader.RetryAfter s.AddCleanup(func(*gc.C) { *upgrader.RetryAfter = oldRetryAfter }) s.upgradeStepsComplete = gate.NewLock() s.initialCheckComplete = gate.NewLock() }
func (s *ManifoldSuite) TestOutputWithWrongWorker(c *gc.C) { getResource := dt.StubGetResource(dt.StubResources{ "steps-waiter": dt.StubResource{Output: gate.NewLock()}, "check-waiter": dt.StubResource{Output: gate.NewLock()}, }) _, err := s.manifold.Start(getResource) c.Assert(err, jc.ErrorIsNil) type dummyWorker struct { worker.Worker } var foo bool err = s.manifold.Output(new(dummyWorker), &foo) c.Assert(err, gc.ErrorMatches, `in should be a \*upgradeWaiter;.+`) }
// NewLock creates a gate.Lock to be used to synchronise workers which // need to start after upgrades have completed. If no upgrade steps // are required the Lock is unlocked and the version in agent's // configuration is updated to the currently running version. // // The returned Lock should be passed to NewWorker. func NewLock(a agent.Agent) (gate.Lock, error) { lock := gate.NewLock() if wrench.IsActive("machine-agent", "always-try-upgrade") { // Always enter upgrade mode. This allows test of upgrades // even when there's actually no upgrade steps to run. return lock, nil } err := a.ChangeConfig(func(agentConfig agent.ConfigSetter) error { if !upgrades.AreUpgradesDefined(agentConfig.UpgradedToVersion()) { logger.Infof("no upgrade steps required or upgrade steps for %v "+ "have already been run.", jujuversion.Current) lock.Unlock() // Even if no upgrade is required the version number in // the agent's config still needs to be bumped. agentConfig.SetUpgradedToVersion(jujuversion.Current) } return nil }) if err != nil { return nil, err } return lock, nil }
func (*FlagSuite) TestFlagLocked(c *gc.C) { lock := gate.NewLock() worker, err := gate.NewFlag(lock) c.Assert(err, jc.ErrorIsNil) defer workertest.CleanKill(c, worker) workertest.CheckAlive(c, worker) c.Check(worker.Check(), jc.IsFalse) }
func (s *ManifoldSuite) TestStartNoCheckWaiter(c *gc.C) { getResource := dt.StubGetResource(dt.StubResources{ "steps-waiter": dt.StubResource{Output: gate.NewLock()}, "check-waiter": dt.StubResource{Error: dependency.ErrMissing}, }) w, err := s.manifold.Start(getResource) c.Assert(w, gc.IsNil) c.Assert(err, gc.Equals, dependency.ErrMissing) }
func (*FlagSuite) TestFlagUnlockError(c *gc.C) { lock := gate.NewLock() worker, err := gate.NewFlag(lock) c.Assert(err, jc.ErrorIsNil) defer workertest.DirtyKill(c, worker) workertest.CheckAlive(c, worker) lock.Unlock() err = workertest.CheckKilled(c, worker) c.Check(err, gc.Equals, gate.ErrUnlocked) }
// Manifold returns a dependency.Manifold which aggregates the // upgradesteps lock and the upgrader's "initial check" lock into a // single boolean output. The output is false until both locks are // unlocked. To make it easy to depend on this manifold, the // manifold's worker restarts when the output value changes, causing // dependent workers to be restarted. func Manifold(config ManifoldConfig) dependency.Manifold { // This lock is unlocked when both the upgradesteps and upgrader // locks are unlocked. It exists outside of the start func and // worker code so that the state can be maintained beyond restart // of the manifold's worker. done := gate.NewLock() return dependency.Manifold{ Inputs: []string{ config.UpgradeStepsWaiterName, config.UpgradeCheckWaiterName, }, Start: func(getResource dependency.GetResourceFunc) (worker.Worker, error) { var stepsWaiter gate.Waiter if err := getResource(config.UpgradeStepsWaiterName, &stepsWaiter); err != nil { return nil, err } var checkWaiter gate.Waiter if err := getResource(config.UpgradeCheckWaiterName, &checkWaiter); err != nil { return nil, err } w := &upgradeWaiter{ done: done, stepsWaiter: stepsWaiter, checkWaiter: checkWaiter, } go func() { defer w.tomb.Done() w.tomb.Kill(w.wait()) }() return w, nil }, Output: func(in worker.Worker, out interface{}) error { inWorker, _ := in.(*upgradeWaiter) if inWorker == nil { return errors.Errorf("in should be a *upgradeWaiter; is %T", in) } switch outPointer := out.(type) { case *bool: *outPointer = done.IsUnlocked() default: return errors.Errorf("out should be a *bool; is %T", out) } return nil }, } }
func (s *WorkerSuite) startWorker(c *gc.C) (worker.Worker, gate.Lock) { // create fresh environ to see any injected broken-ness environ, err := stateenvirons.GetNewEnvironFunc(environs.New)(s.State) c.Assert(err, jc.ErrorIsNil) lock := gate.NewLock() worker, err := discoverspaces.NewWorker(discoverspaces.Config{ Facade: s.API, Environ: environ, NewName: network.ConvertSpaceName, Unlocker: lock, }) c.Assert(err, jc.ErrorIsNil) return worker, lock }
func (s *ManifoldSuite) TestManifoldEx(c *gc.C) { lock := gate.NewLock() manifold := gate.ManifoldEx(lock) var waiter1 gate.Waiter = lock var unlocker1 gate.Unlocker = lock worker, err := manifold.Start(nil) c.Assert(err, jc.ErrorIsNil) defer checkStop(c, worker) waiter2 := waiter(c, manifold, worker) assertLocked(c, waiter1) assertLocked(c, waiter2) unlocker1.Unlock() assertUnlocked(c, waiter1) assertUnlocked(c, waiter2) }
// NewMachineAgent instantiates a new MachineAgent. func NewMachineAgent( machineId string, agentConfWriter AgentConfigWriter, bufferedLogs logsender.LogRecordCh, runner worker.Runner, loopDeviceManager looputil.LoopDeviceManager, rootDir string, ) *MachineAgent { return &MachineAgent{ machineId: machineId, AgentConfigWriter: agentConfWriter, configChangedVal: voyeur.NewValue(true), bufferedLogs: bufferedLogs, workersStarted: make(chan struct{}), runner: runner, rootDir: rootDir, initialUpgradeCheckComplete: gate.NewLock(), loopDeviceManager: loopDeviceManager, } }