github.com/cloudbase/juju-core@v0.0.0-20140504232958-a7271ac7912f/worker/provisioner/provisioner_test.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package provisioner_test
     5  
     6  import (
     7  	"fmt"
     8  	"strings"
     9  	"time"
    10  
    11  	gc "launchpad.net/gocheck"
    12  
    13  	"launchpad.net/juju-core/constraints"
    14  	"launchpad.net/juju-core/environs"
    15  	"launchpad.net/juju-core/environs/config"
    16  	"launchpad.net/juju-core/errors"
    17  	"launchpad.net/juju-core/instance"
    18  	"launchpad.net/juju-core/juju/testing"
    19  	"launchpad.net/juju-core/names"
    20  	"launchpad.net/juju-core/provider/dummy"
    21  	"launchpad.net/juju-core/state"
    22  	"launchpad.net/juju-core/state/api"
    23  	"launchpad.net/juju-core/state/api/params"
    24  	apiprovisioner "launchpad.net/juju-core/state/api/provisioner"
    25  	coretesting "launchpad.net/juju-core/testing"
    26  	jc "launchpad.net/juju-core/testing/checkers"
    27  	"launchpad.net/juju-core/utils"
    28  	"launchpad.net/juju-core/utils/set"
    29  	"launchpad.net/juju-core/worker/provisioner"
    30  )
    31  
    32  type CommonProvisionerSuite struct {
    33  	testing.JujuConnSuite
    34  	op  <-chan dummy.Operation
    35  	cfg *config.Config
    36  	//  // defaultConstraints are used when adding a machine and then later in test assertions.
    37  	defaultConstraints constraints.Value
    38  
    39  	st          *api.State
    40  	provisioner *apiprovisioner.State
    41  }
    42  
    43  type ProvisionerSuite struct {
    44  	CommonProvisionerSuite
    45  }
    46  
    47  var _ = gc.Suite(&ProvisionerSuite{})
    48  
    49  var veryShortAttempt = utils.AttemptStrategy{
    50  	Total: 1 * time.Second,
    51  	Delay: 80 * time.Millisecond,
    52  }
    53  
    54  func (s *CommonProvisionerSuite) SetUpSuite(c *gc.C) {
    55  	s.JujuConnSuite.SetUpSuite(c)
    56  	s.defaultConstraints = constraints.MustParse("arch=amd64 mem=4G cpu-cores=1 root-disk=8G")
    57  }
    58  
    59  func (s *CommonProvisionerSuite) SetUpTest(c *gc.C) {
    60  	// Disable the default state policy, because the
    61  	// provisioner needs to be able to test pathological
    62  	// scenarios where a machine exists in state with
    63  	// invalid environment config.
    64  	dummy.SetStatePolicy(nil)
    65  
    66  	s.JujuConnSuite.SetUpTest(c)
    67  	// Create the operations channel with more than enough space
    68  	// for those tests that don't listen on it.
    69  	op := make(chan dummy.Operation, 500)
    70  	dummy.Listen(op)
    71  	s.op = op
    72  
    73  	cfg, err := s.State.EnvironConfig()
    74  	c.Assert(err, gc.IsNil)
    75  	s.cfg = cfg
    76  }
    77  
    78  func (s *CommonProvisionerSuite) APILogin(c *gc.C, machine *state.Machine) {
    79  	if s.st != nil {
    80  		c.Assert(s.st.Close(), gc.IsNil)
    81  	}
    82  	password, err := utils.RandomPassword()
    83  	c.Assert(err, gc.IsNil)
    84  	err = machine.SetPassword(password)
    85  	c.Assert(err, gc.IsNil)
    86  	err = machine.SetProvisioned("i-fake", "fake_nonce", nil)
    87  	c.Assert(err, gc.IsNil)
    88  	s.st = s.OpenAPIAsMachine(c, machine.Tag(), password, "fake_nonce")
    89  	c.Assert(s.st, gc.NotNil)
    90  	c.Logf("API: login as %q successful", machine.Tag())
    91  	s.provisioner = s.st.Provisioner()
    92  	c.Assert(s.provisioner, gc.NotNil)
    93  }
    94  
    95  // breakDummyProvider changes the environment config in state in a way
    96  // that causes the given environMethod of the dummy provider to return
    97  // an error, which is also returned as a message to be checked.
    98  func breakDummyProvider(c *gc.C, st *state.State, environMethod string) string {
    99  	oldCfg, err := st.EnvironConfig()
   100  	c.Assert(err, gc.IsNil)
   101  	cfg, err := oldCfg.Apply(map[string]interface{}{"broken": environMethod})
   102  	c.Assert(err, gc.IsNil)
   103  	err = st.SetEnvironConfig(cfg, oldCfg)
   104  	c.Assert(err, gc.IsNil)
   105  	return fmt.Sprintf("dummy.%s is broken", environMethod)
   106  }
   107  
   108  // setupEnvironment adds an environment manager machine and login to the API.
   109  func (s *CommonProvisionerSuite) setupEnvironmentManager(c *gc.C) {
   110  	machine, err := s.State.AddMachine("quantal", state.JobManageEnviron)
   111  	c.Assert(err, gc.IsNil)
   112  	c.Assert(machine.Id(), gc.Equals, "0")
   113  	err = machine.SetAddresses([]instance.Address{
   114  		instance.NewAddress("0.1.2.3"),
   115  	})
   116  	c.Assert(err, gc.IsNil)
   117  	s.APILogin(c, machine)
   118  }
   119  
   120  // invalidateEnvironment alters the environment configuration
   121  // so the Settings returned from the watcher will not pass
   122  // validation.
   123  func (s *CommonProvisionerSuite) invalidateEnvironment(c *gc.C) {
   124  	attrs := s.cfg.AllAttrs()
   125  	attrs["type"] = "unknown"
   126  	invalidCfg, err := config.New(config.NoDefaults, attrs)
   127  	c.Assert(err, gc.IsNil)
   128  	err = s.State.SetEnvironConfig(invalidCfg, s.cfg)
   129  	c.Assert(err, gc.IsNil)
   130  }
   131  
   132  // fixEnvironment undoes the work of invalidateEnvironment.
   133  func (s *CommonProvisionerSuite) fixEnvironment() error {
   134  	cfg, err := s.State.EnvironConfig()
   135  	if err != nil {
   136  		return err
   137  	}
   138  	return s.State.SetEnvironConfig(s.cfg, cfg)
   139  }
   140  
   141  // stopper is stoppable.
   142  type stopper interface {
   143  	Stop() error
   144  }
   145  
   146  // stop stops a stopper.
   147  func stop(c *gc.C, s stopper) {
   148  	c.Assert(s.Stop(), gc.IsNil)
   149  }
   150  
   151  func (s *CommonProvisionerSuite) startUnknownInstance(c *gc.C, id string) instance.Instance {
   152  	instance, _ := testing.AssertStartInstance(c, s.Conn.Environ, id)
   153  	select {
   154  	case o := <-s.op:
   155  		switch o := o.(type) {
   156  		case dummy.OpStartInstance:
   157  		default:
   158  			c.Fatalf("unexpected operation %#v", o)
   159  		}
   160  	case <-time.After(coretesting.LongWait):
   161  		c.Fatalf("timed out waiting for startinstance operation")
   162  	}
   163  	return instance
   164  }
   165  
   166  func (s *CommonProvisionerSuite) checkStartInstance(c *gc.C, m *state.Machine) instance.Instance {
   167  	return s.checkStartInstanceCustom(c, m, "pork", s.defaultConstraints)
   168  }
   169  
   170  func (s *CommonProvisionerSuite) checkStartInstanceCustom(c *gc.C, m *state.Machine, secret string, cons constraints.Value) (inst instance.Instance) {
   171  	s.BackingState.StartSync()
   172  	for {
   173  		select {
   174  		case o := <-s.op:
   175  			switch o := o.(type) {
   176  			case dummy.OpStartInstance:
   177  				inst = o.Instance
   178  				s.waitInstanceId(c, m, inst.Id())
   179  
   180  				// Check the instance was started with the expected params.
   181  				c.Assert(o.MachineId, gc.Equals, m.Id())
   182  				nonceParts := strings.SplitN(o.MachineNonce, ":", 2)
   183  				c.Assert(nonceParts, gc.HasLen, 2)
   184  				c.Assert(nonceParts[0], gc.Equals, names.MachineTag("0"))
   185  				c.Assert(nonceParts[1], jc.Satisfies, utils.IsValidUUIDString)
   186  				c.Assert(o.Secret, gc.Equals, secret)
   187  				c.Assert(o.Constraints, gc.DeepEquals, cons)
   188  
   189  				// All provisioned machines in this test suite have their hardware characteristics
   190  				// attributes set to the same values as the constraints due to the dummy environment being used.
   191  				hc, err := m.HardwareCharacteristics()
   192  				c.Assert(err, gc.IsNil)
   193  				c.Assert(*hc, gc.DeepEquals, instance.HardwareCharacteristics{
   194  					Arch:     cons.Arch,
   195  					Mem:      cons.Mem,
   196  					RootDisk: cons.RootDisk,
   197  					CpuCores: cons.CpuCores,
   198  					CpuPower: cons.CpuPower,
   199  					Tags:     cons.Tags,
   200  				})
   201  				return
   202  			default:
   203  				c.Logf("ignoring unexpected operation %#v", o)
   204  			}
   205  		case <-time.After(2 * time.Second):
   206  			c.Fatalf("provisioner did not start an instance")
   207  			return
   208  		}
   209  	}
   210  	return
   211  }
   212  
   213  // checkNoOperations checks that the environ was not operated upon.
   214  func (s *CommonProvisionerSuite) checkNoOperations(c *gc.C) {
   215  	s.BackingState.StartSync()
   216  	select {
   217  	case o := <-s.op:
   218  		c.Fatalf("unexpected operation %#v", o)
   219  	case <-time.After(coretesting.ShortWait):
   220  		return
   221  	}
   222  }
   223  
   224  // checkStopInstances checks that an instance has been stopped.
   225  func (s *CommonProvisionerSuite) checkStopInstances(c *gc.C, instances ...instance.Instance) {
   226  	s.checkStopSomeInstances(c, instances, nil)
   227  }
   228  
   229  // checkStopSomeInstances checks that instancesToStop are stopped while instancesToKeep are not.
   230  func (s *CommonProvisionerSuite) checkStopSomeInstances(c *gc.C,
   231  	instancesToStop []instance.Instance, instancesToKeep []instance.Instance) {
   232  
   233  	s.BackingState.StartSync()
   234  	instanceIdsToStop := set.NewStrings()
   235  	for _, instance := range instancesToStop {
   236  		instanceIdsToStop.Add(string(instance.Id()))
   237  	}
   238  	instanceIdsToKeep := set.NewStrings()
   239  	for _, instance := range instancesToKeep {
   240  		instanceIdsToKeep.Add(string(instance.Id()))
   241  	}
   242  	// Continue checking for stop instance calls until all the instances we
   243  	// are waiting on to finish, actually finish, or we time out.
   244  	for !instanceIdsToStop.IsEmpty() {
   245  		select {
   246  		case o := <-s.op:
   247  			switch o := o.(type) {
   248  			case dummy.OpStopInstances:
   249  				for _, stoppedInstance := range o.Instances {
   250  					instId := string(stoppedInstance.Id())
   251  					instanceIdsToStop.Remove(instId)
   252  					if instanceIdsToKeep.Contains(instId) {
   253  						c.Errorf("provisioner unexpectedly stopped instance %s", instId)
   254  					}
   255  				}
   256  			default:
   257  				c.Fatalf("unexpected operation %#v", o)
   258  				return
   259  			}
   260  		case <-time.After(2 * time.Second):
   261  			c.Fatalf("provisioner did not stop an instance")
   262  			return
   263  		}
   264  	}
   265  }
   266  
   267  func (s *CommonProvisionerSuite) waitMachine(c *gc.C, m *state.Machine, check func() bool) {
   268  	// TODO(jam): We need to grow a new method on NotifyWatcherC
   269  	// that calls StartSync while waiting for changes, then
   270  	// waitMachine and waitHardwareCharacteristics can use that
   271  	// instead
   272  	w := m.Watch()
   273  	defer stop(c, w)
   274  	timeout := time.After(coretesting.LongWait)
   275  	resync := time.After(0)
   276  	for {
   277  		select {
   278  		case <-w.Changes():
   279  			if check() {
   280  				return
   281  			}
   282  		case <-resync:
   283  			resync = time.After(coretesting.ShortWait)
   284  			s.BackingState.StartSync()
   285  		case <-timeout:
   286  			c.Fatalf("machine %v wait timed out", m)
   287  		}
   288  	}
   289  }
   290  
   291  func (s *CommonProvisionerSuite) waitHardwareCharacteristics(c *gc.C, m *state.Machine, check func() bool) {
   292  	w := m.WatchHardwareCharacteristics()
   293  	defer stop(c, w)
   294  	timeout := time.After(coretesting.LongWait)
   295  	resync := time.After(0)
   296  	for {
   297  		select {
   298  		case <-w.Changes():
   299  			if check() {
   300  				return
   301  			}
   302  		case <-resync:
   303  			resync = time.After(coretesting.ShortWait)
   304  			s.BackingState.StartSync()
   305  		case <-timeout:
   306  			c.Fatalf("hardware characteristics for machine %v wait timed out", m)
   307  		}
   308  	}
   309  }
   310  
   311  // waitRemoved waits for the supplied machine to be removed from state.
   312  func (s *CommonProvisionerSuite) waitRemoved(c *gc.C, m *state.Machine) {
   313  	s.waitMachine(c, m, func() bool {
   314  		err := m.Refresh()
   315  		if errors.IsNotFoundError(err) {
   316  			return true
   317  		}
   318  		c.Assert(err, gc.IsNil)
   319  		c.Logf("machine %v is still %s", m, m.Life())
   320  		return false
   321  	})
   322  }
   323  
   324  // waitInstanceId waits until the supplied machine has an instance id, then
   325  // asserts it is as expected.
   326  func (s *CommonProvisionerSuite) waitInstanceId(c *gc.C, m *state.Machine, expect instance.Id) {
   327  	s.waitHardwareCharacteristics(c, m, func() bool {
   328  		if actual, err := m.InstanceId(); err == nil {
   329  			c.Assert(actual, gc.Equals, expect)
   330  			return true
   331  		} else if !state.IsNotProvisionedError(err) {
   332  			// We don't expect any errors.
   333  			panic(err)
   334  		}
   335  		c.Logf("machine %v is still unprovisioned", m)
   336  		return false
   337  	})
   338  }
   339  
   340  func (s *ProvisionerSuite) SetUpTest(c *gc.C) {
   341  	s.CommonProvisionerSuite.SetUpTest(c)
   342  	s.CommonProvisionerSuite.setupEnvironmentManager(c)
   343  }
   344  
   345  func (s *ProvisionerSuite) newEnvironProvisioner(c *gc.C) provisioner.Provisioner {
   346  	machineTag := "machine-0"
   347  	agentConfig := s.AgentConfigForTag(c, machineTag)
   348  	return provisioner.NewEnvironProvisioner(s.provisioner, agentConfig)
   349  }
   350  
   351  func (s *ProvisionerSuite) TestProvisionerStartStop(c *gc.C) {
   352  	p := s.newEnvironProvisioner(c)
   353  	c.Assert(p.Stop(), gc.IsNil)
   354  }
   355  
   356  func (s *ProvisionerSuite) addMachine() (*state.Machine, error) {
   357  	return s.BackingState.AddOneMachine(state.MachineTemplate{
   358  		Series:      config.DefaultSeries,
   359  		Jobs:        []state.MachineJob{state.JobHostUnits},
   360  		Constraints: s.defaultConstraints,
   361  	})
   362  }
   363  
   364  func (s *ProvisionerSuite) TestSimple(c *gc.C) {
   365  	p := s.newEnvironProvisioner(c)
   366  	defer stop(c, p)
   367  
   368  	// Check that an instance is provisioned when the machine is created...
   369  	m, err := s.addMachine()
   370  	c.Assert(err, gc.IsNil)
   371  	instance := s.checkStartInstance(c, m)
   372  
   373  	// ...and removed, along with the machine, when the machine is Dead.
   374  	c.Assert(m.EnsureDead(), gc.IsNil)
   375  	s.checkStopInstances(c, instance)
   376  	s.waitRemoved(c, m)
   377  }
   378  
   379  func (s *ProvisionerSuite) TestConstraints(c *gc.C) {
   380  	// Create a machine with non-standard constraints.
   381  	m, err := s.addMachine()
   382  	c.Assert(err, gc.IsNil)
   383  	cons := constraints.MustParse("mem=8G arch=amd64 cpu-cores=2 root-disk=10G")
   384  	err = m.SetConstraints(cons)
   385  	c.Assert(err, gc.IsNil)
   386  
   387  	// Start a provisioner and check those constraints are used.
   388  	p := s.newEnvironProvisioner(c)
   389  	defer stop(c, p)
   390  	s.checkStartInstanceCustom(c, m, "pork", cons)
   391  }
   392  
   393  func (s *ProvisionerSuite) TestProvisionerSetsErrorStatusWhenStartInstanceFailed(c *gc.C) {
   394  	brokenMsg := breakDummyProvider(c, s.State, "StartInstance")
   395  	p := s.newEnvironProvisioner(c)
   396  	defer stop(c, p)
   397  
   398  	// Check that an instance is not provisioned when the machine is created...
   399  	m, err := s.addMachine()
   400  	c.Assert(err, gc.IsNil)
   401  	s.checkNoOperations(c)
   402  
   403  	t0 := time.Now()
   404  	for time.Since(t0) < coretesting.LongWait {
   405  		// And check the machine status is set to error.
   406  		status, info, _, err := m.Status()
   407  		c.Assert(err, gc.IsNil)
   408  		if status == params.StatusPending {
   409  			time.Sleep(coretesting.ShortWait)
   410  			continue
   411  		}
   412  		c.Assert(status, gc.Equals, params.StatusError)
   413  		c.Assert(info, gc.Equals, brokenMsg)
   414  		break
   415  	}
   416  
   417  	// Unbreak the environ config.
   418  	err = s.fixEnvironment()
   419  	c.Assert(err, gc.IsNil)
   420  
   421  	// Restart the PA to make sure the machine is skipped again.
   422  	stop(c, p)
   423  	p = s.newEnvironProvisioner(c)
   424  	defer stop(c, p)
   425  	s.checkNoOperations(c)
   426  }
   427  
   428  func (s *ProvisionerSuite) TestProvisioningDoesNotOccurForContainers(c *gc.C) {
   429  	p := s.newEnvironProvisioner(c)
   430  	defer stop(c, p)
   431  
   432  	// create a machine to host the container.
   433  	m, err := s.addMachine()
   434  	c.Assert(err, gc.IsNil)
   435  	inst := s.checkStartInstance(c, m)
   436  
   437  	// make a container on the machine we just created
   438  	template := state.MachineTemplate{
   439  		Series: config.DefaultSeries,
   440  		Jobs:   []state.MachineJob{state.JobHostUnits},
   441  	}
   442  	container, err := s.State.AddMachineInsideMachine(template, m.Id(), instance.LXC)
   443  	c.Assert(err, gc.IsNil)
   444  
   445  	// the PA should not attempt to create it
   446  	s.checkNoOperations(c)
   447  
   448  	// cleanup
   449  	c.Assert(container.EnsureDead(), gc.IsNil)
   450  	c.Assert(container.Remove(), gc.IsNil)
   451  	c.Assert(m.EnsureDead(), gc.IsNil)
   452  	s.checkStopInstances(c, inst)
   453  	s.waitRemoved(c, m)
   454  }
   455  
   456  func (s *ProvisionerSuite) TestProvisioningDoesNotOccurWithAnInvalidEnvironment(c *gc.C) {
   457  	s.invalidateEnvironment(c)
   458  
   459  	p := s.newEnvironProvisioner(c)
   460  	defer stop(c, p)
   461  
   462  	// try to create a machine
   463  	_, err := s.addMachine()
   464  	c.Assert(err, gc.IsNil)
   465  
   466  	// the PA should not create it
   467  	s.checkNoOperations(c)
   468  }
   469  
   470  func (s *ProvisionerSuite) TestProvisioningOccursWithFixedEnvironment(c *gc.C) {
   471  	s.invalidateEnvironment(c)
   472  
   473  	p := s.newEnvironProvisioner(c)
   474  	defer stop(c, p)
   475  
   476  	// try to create a machine
   477  	m, err := s.addMachine()
   478  	c.Assert(err, gc.IsNil)
   479  
   480  	// the PA should not create it
   481  	s.checkNoOperations(c)
   482  
   483  	err = s.fixEnvironment()
   484  	c.Assert(err, gc.IsNil)
   485  
   486  	s.checkStartInstance(c, m)
   487  }
   488  
   489  func (s *ProvisionerSuite) TestProvisioningDoesOccurAfterInvalidEnvironmentPublished(c *gc.C) {
   490  	p := s.newEnvironProvisioner(c)
   491  	defer stop(c, p)
   492  
   493  	// place a new machine into the state
   494  	m, err := s.addMachine()
   495  	c.Assert(err, gc.IsNil)
   496  
   497  	s.checkStartInstance(c, m)
   498  
   499  	s.invalidateEnvironment(c)
   500  
   501  	// create a second machine
   502  	m, err = s.addMachine()
   503  	c.Assert(err, gc.IsNil)
   504  
   505  	// the PA should create it using the old environment
   506  	s.checkStartInstance(c, m)
   507  }
   508  
   509  func (s *ProvisionerSuite) TestProvisioningDoesNotProvisionTheSameMachineAfterRestart(c *gc.C) {
   510  	p := s.newEnvironProvisioner(c)
   511  	defer stop(c, p)
   512  
   513  	// create a machine
   514  	m, err := s.addMachine()
   515  	c.Assert(err, gc.IsNil)
   516  	s.checkStartInstance(c, m)
   517  
   518  	// restart the PA
   519  	stop(c, p)
   520  	p = s.newEnvironProvisioner(c)
   521  	defer stop(c, p)
   522  
   523  	// check that there is only one machine provisioned.
   524  	machines, err := s.State.AllMachines()
   525  	c.Assert(err, gc.IsNil)
   526  	c.Check(len(machines), gc.Equals, 2)
   527  	c.Check(machines[0].Id(), gc.Equals, "0")
   528  	c.Check(machines[1].CheckProvisioned("fake_nonce"), jc.IsFalse)
   529  
   530  	// the PA should not create it a second time
   531  	s.checkNoOperations(c)
   532  }
   533  
   534  func (s *ProvisionerSuite) TestProvisioningStopsInstances(c *gc.C) {
   535  	p := s.newEnvironProvisioner(c)
   536  	defer stop(c, p)
   537  
   538  	// create a machine
   539  	m0, err := s.addMachine()
   540  	c.Assert(err, gc.IsNil)
   541  	i0 := s.checkStartInstance(c, m0)
   542  
   543  	// create a second machine
   544  	m1, err := s.addMachine()
   545  	c.Assert(err, gc.IsNil)
   546  	i1 := s.checkStartInstance(c, m1)
   547  	stop(c, p)
   548  
   549  	// mark the first machine as dead
   550  	c.Assert(m0.EnsureDead(), gc.IsNil)
   551  
   552  	// remove the second machine entirely
   553  	c.Assert(m1.EnsureDead(), gc.IsNil)
   554  	c.Assert(m1.Remove(), gc.IsNil)
   555  
   556  	// start a new provisioner to shut them both down
   557  	p = s.newEnvironProvisioner(c)
   558  	defer stop(c, p)
   559  	s.checkStopInstances(c, i0, i1)
   560  	s.waitRemoved(c, m0)
   561  }
   562  
   563  func (s *ProvisionerSuite) TestDyingMachines(c *gc.C) {
   564  	p := s.newEnvironProvisioner(c)
   565  	defer stop(c, p)
   566  
   567  	// provision a machine
   568  	m0, err := s.addMachine()
   569  	c.Assert(err, gc.IsNil)
   570  	s.checkStartInstance(c, m0)
   571  
   572  	// stop the provisioner and make the machine dying
   573  	stop(c, p)
   574  	err = m0.Destroy()
   575  	c.Assert(err, gc.IsNil)
   576  
   577  	// add a new, dying, unprovisioned machine
   578  	m1, err := s.addMachine()
   579  	c.Assert(err, gc.IsNil)
   580  	err = m1.Destroy()
   581  	c.Assert(err, gc.IsNil)
   582  
   583  	// start the provisioner and wait for it to reap the useless machine
   584  	p = s.newEnvironProvisioner(c)
   585  	defer stop(c, p)
   586  	s.checkNoOperations(c)
   587  	s.waitRemoved(c, m1)
   588  
   589  	// verify the other one's still fine
   590  	err = m0.Refresh()
   591  	c.Assert(err, gc.IsNil)
   592  	c.Assert(m0.Life(), gc.Equals, state.Dying)
   593  }
   594  
   595  func (s *ProvisionerSuite) TestProvisioningRecoversAfterInvalidEnvironmentPublished(c *gc.C) {
   596  	p := s.newEnvironProvisioner(c)
   597  	defer stop(c, p)
   598  
   599  	// place a new machine into the state
   600  	m, err := s.addMachine()
   601  	c.Assert(err, gc.IsNil)
   602  	s.checkStartInstance(c, m)
   603  
   604  	s.invalidateEnvironment(c)
   605  	s.BackingState.StartSync()
   606  
   607  	// create a second machine
   608  	m, err = s.addMachine()
   609  	c.Assert(err, gc.IsNil)
   610  
   611  	// the PA should create it using the old environment
   612  	s.checkStartInstance(c, m)
   613  
   614  	err = s.fixEnvironment()
   615  	c.Assert(err, gc.IsNil)
   616  
   617  	// insert our observer
   618  	cfgObserver := make(chan *config.Config, 1)
   619  	provisioner.SetObserver(p, cfgObserver)
   620  
   621  	oldcfg, err := s.State.EnvironConfig()
   622  	c.Assert(err, gc.IsNil)
   623  	attrs := oldcfg.AllAttrs()
   624  	attrs["secret"] = "beef"
   625  	cfg, err := config.New(config.NoDefaults, attrs)
   626  	c.Assert(err, gc.IsNil)
   627  	err = s.State.SetEnvironConfig(cfg, oldcfg)
   628  
   629  	s.BackingState.StartSync()
   630  
   631  	// wait for the PA to load the new configuration
   632  	select {
   633  	case <-cfgObserver:
   634  	case <-time.After(coretesting.LongWait):
   635  		c.Fatalf("PA did not action config change")
   636  	}
   637  
   638  	// create a third machine
   639  	m, err = s.addMachine()
   640  	c.Assert(err, gc.IsNil)
   641  
   642  	// the PA should create it using the new environment
   643  	s.checkStartInstanceCustom(c, m, "beef", s.defaultConstraints)
   644  }
   645  
   646  func (s *ProvisionerSuite) TestProvisioningSafeMode(c *gc.C) {
   647  	p := s.newEnvironProvisioner(c)
   648  	defer stop(c, p)
   649  
   650  	// create a machine
   651  	m0, err := s.addMachine()
   652  	c.Assert(err, gc.IsNil)
   653  	i0 := s.checkStartInstance(c, m0)
   654  
   655  	// create a second machine
   656  	m1, err := s.addMachine()
   657  	c.Assert(err, gc.IsNil)
   658  	i1 := s.checkStartInstance(c, m1)
   659  	stop(c, p)
   660  
   661  	// mark the first machine as dead
   662  	c.Assert(m0.EnsureDead(), gc.IsNil)
   663  
   664  	// remove the second machine entirely from state
   665  	c.Assert(m1.EnsureDead(), gc.IsNil)
   666  	c.Assert(m1.Remove(), gc.IsNil)
   667  
   668  	// turn on safe mode
   669  	oldcfg, err := s.State.EnvironConfig()
   670  	c.Assert(err, gc.IsNil)
   671  	attrs := oldcfg.AllAttrs()
   672  	attrs["provisioner-safe-mode"] = true
   673  	cfg, err := config.New(config.NoDefaults, attrs)
   674  	c.Assert(err, gc.IsNil)
   675  	err = s.State.SetEnvironConfig(cfg, oldcfg)
   676  
   677  	// start a new provisioner to shut down only the machine still in state.
   678  	p = s.newEnvironProvisioner(c)
   679  	defer stop(c, p)
   680  	s.checkStopSomeInstances(c, []instance.Instance{i0}, []instance.Instance{i1})
   681  	s.waitRemoved(c, m0)
   682  }
   683  
   684  func (s *ProvisionerSuite) TestProvisioningSafeModeChange(c *gc.C) {
   685  	p := s.newEnvironProvisioner(c)
   686  	defer stop(c, p)
   687  
   688  	// First check that safe mode is initially off.
   689  
   690  	// create a machine
   691  	m0, err := s.addMachine()
   692  	c.Assert(err, gc.IsNil)
   693  	i0 := s.checkStartInstance(c, m0)
   694  
   695  	// create a second machine
   696  	m1, err := s.addMachine()
   697  	c.Assert(err, gc.IsNil)
   698  	i1 := s.checkStartInstance(c, m1)
   699  
   700  	// mark the first machine as dead
   701  	c.Assert(m0.EnsureDead(), gc.IsNil)
   702  
   703  	// remove the second machine entirely from state
   704  	c.Assert(m1.EnsureDead(), gc.IsNil)
   705  	c.Assert(m1.Remove(), gc.IsNil)
   706  
   707  	s.checkStopInstances(c, i0, i1)
   708  	s.waitRemoved(c, m0)
   709  
   710  	// insert our observer
   711  	cfgObserver := make(chan *config.Config, 1)
   712  	provisioner.SetObserver(p, cfgObserver)
   713  
   714  	// turn on safe mode
   715  	oldcfg, err := s.State.EnvironConfig()
   716  	c.Assert(err, gc.IsNil)
   717  	attrs := oldcfg.AllAttrs()
   718  	attrs["provisioner-safe-mode"] = true
   719  	cfg, err := config.New(config.NoDefaults, attrs)
   720  	c.Assert(err, gc.IsNil)
   721  	err = s.State.SetEnvironConfig(cfg, oldcfg)
   722  
   723  	s.BackingState.StartSync()
   724  
   725  	// wait for the PA to load the new configuration
   726  	select {
   727  	case <-cfgObserver:
   728  	case <-time.After(coretesting.LongWait):
   729  		c.Fatalf("PA did not action config change")
   730  	}
   731  
   732  	// Now check that the provisioner has noticed safe mode is on.
   733  
   734  	// create a machine
   735  	m3, err := s.addMachine()
   736  	c.Assert(err, gc.IsNil)
   737  	i3 := s.checkStartInstance(c, m3)
   738  
   739  	// create an instance out of band
   740  	i4 := s.startUnknownInstance(c, "999")
   741  
   742  	// mark the machine as dead
   743  	c.Assert(m3.EnsureDead(), gc.IsNil)
   744  
   745  	// check the machine's instance is stopped, and the other isn't
   746  	s.checkStopSomeInstances(c, []instance.Instance{i3}, []instance.Instance{i4})
   747  	s.waitRemoved(c, m3)
   748  }
   749  
   750  func (s *ProvisionerSuite) newProvisionerTask(c *gc.C, safeMode bool) provisioner.ProvisionerTask {
   751  	env := s.APIConn.Environ
   752  	watcher, err := s.provisioner.WatchEnvironMachines()
   753  	c.Assert(err, gc.IsNil)
   754  	auth, err := environs.NewAPIAuthenticator(s.provisioner)
   755  	c.Assert(err, gc.IsNil)
   756  	return provisioner.NewProvisionerTask("machine-0", safeMode, s.provisioner, watcher, env, auth)
   757  }
   758  
   759  func (s *ProvisionerSuite) TestTurningOffSafeModeReapsUnknownInstances(c *gc.C) {
   760  	task := s.newProvisionerTask(c, true)
   761  	defer stop(c, task)
   762  
   763  	// Initially create a machine, and an unknown instance, with safe mode on.
   764  	m0, err := s.addMachine()
   765  	c.Assert(err, gc.IsNil)
   766  	i0 := s.checkStartInstance(c, m0)
   767  	i1 := s.startUnknownInstance(c, "999")
   768  
   769  	// mark the first machine as dead
   770  	c.Assert(m0.EnsureDead(), gc.IsNil)
   771  
   772  	// with safe mode on, only one of the machines is stopped.
   773  	s.checkStopSomeInstances(c, []instance.Instance{i0}, []instance.Instance{i1})
   774  	s.waitRemoved(c, m0)
   775  
   776  	// turn off safe mode and check that the other machine is now stopped also.
   777  	task.SetSafeMode(false)
   778  	s.checkStopInstances(c, i1)
   779  }