func (s *minUnitsWorkerSuite) TestMinUnitsWorker(c *gc.C) { mu := minunitsworker.NewMinUnitsWorker(s.State) defer func() { c.Assert(worker.Stop(mu), gc.IsNil) }() // Set up services and units for later use. wordpress, err := s.State.AddService("wordpress", s.AddTestingCharm(c, "wordpress")) c.Assert(err, gc.IsNil) mysql, err := s.State.AddService("mysql", s.AddTestingCharm(c, "mysql")) c.Assert(err, gc.IsNil) unit, err := wordpress.AddUnit() c.Assert(err, gc.IsNil) _, err = wordpress.AddUnit() c.Assert(err, gc.IsNil) // Set up minimum units for services. err = wordpress.SetMinUnits(3) c.Assert(err, gc.IsNil) err = mysql.SetMinUnits(2) c.Assert(err, gc.IsNil) // Remove a unit for a service. err = unit.Destroy() c.Assert(err, gc.IsNil) timeout := time.After(coretesting.LongWait) for { s.State.StartSync() select { case <-time.After(coretesting.ShortWait): wordpressUnits, err := wordpress.AllUnits() c.Assert(err, gc.IsNil) mysqlUnits, err := mysql.AllUnits() c.Assert(err, gc.IsNil) wordpressCount := len(wordpressUnits) mysqlCount := len(mysqlUnits) if wordpressCount == 3 && mysqlCount == 2 { return } logger.Infof("wordpress units: %d; mysql units: %d", wordpressCount, mysqlCount) case <-timeout: c.Fatalf("timed out waiting for minunits events") } } }
// StateJobs returns a worker running all the workers that require // a *state.State connection. func (a *MachineAgent) StateWorker() (worker.Worker, error) { st, entity, err := openState(a.Conf.Conf, a) if err != nil { return nil, err } reportOpenedState(st) m := entity.(*state.Machine) // TODO(rog) use more discriminating test for errors // rather than taking everything down indiscriminately. dataDir := a.Conf.DataDir runner := worker.NewRunner(allFatal, moreImportant) // At this stage, since we don't embed lxc containers, just start an lxc // provisioner task for non-lxc containers. Since we have only LXC // containers and normal machines, this effectively means that we only // have an LXC provisioner when we have a normally provisioned machine // (through the environ-provisioner). With the upcoming advent of KVM // containers, it is likely that we will want an LXC provisioner on a KVM // machine, and once we get nested LXC containers, we can remove this // check. providerType := os.Getenv(osenv.JujuProviderType) if providerType != provider.Local && m.ContainerType() != instance.LXC { workerName := fmt.Sprintf("%s-provisioner", provisioner.LXC) runner.StartWorker(workerName, func() (worker.Worker, error) { return provisioner.NewProvisioner(provisioner.LXC, st, a.MachineId, dataDir), nil }) } // Take advantage of special knowledge here in that we will only ever want // the storage provider on one machine, and that is the "bootstrap" node. if providerType == provider.Local && m.Id() == bootstrapMachineId { runner.StartWorker("local-storage", func() (worker.Worker, error) { return localstorage.NewWorker(), nil }) } for _, job := range m.Jobs() { switch job { case state.JobHostUnits: // Implemented in APIWorker. case state.JobManageEnviron: runner.StartWorker("environ-provisioner", func() (worker.Worker, error) { return provisioner.NewProvisioner(provisioner.ENVIRON, st, a.MachineId, dataDir), nil }) runner.StartWorker("firewaller", func() (worker.Worker, error) { return firewaller.NewFirewaller(st), nil }) case state.JobManageState: runner.StartWorker("apiserver", func() (worker.Worker, error) { // If the configuration does not have the required information, // it is currently not a recoverable error, so we kill the whole // agent, potentially enabling human intervention to fix // the agent's configuration file. In the future, we may retrieve // the state server certificate and key from the state, and // this should then change. if len(a.Conf.StateServerCert) == 0 || len(a.Conf.StateServerKey) == 0 { return nil, &fatalError{"configuration does not have state server cert/key"} } return apiserver.NewServer(st, fmt.Sprintf(":%d", a.Conf.APIPort), a.Conf.StateServerCert, a.Conf.StateServerKey) }) runner.StartWorker("cleaner", func() (worker.Worker, error) { return cleaner.NewCleaner(st), nil }) runner.StartWorker("resumer", func() (worker.Worker, error) { // The action of resumer is so subtle that it is not tested, // because we can't figure out how to do so without brutalising // the transaction log. return resumer.NewResumer(st), nil }) runner.StartWorker("minunitsworker", func() (worker.Worker, error) { return minunitsworker.NewMinUnitsWorker(st), nil }) default: log.Warningf("ignoring unknown job %q", job) } } return newCloseWorker(runner, st), nil }