github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/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  	"strconv"
     9  	"strings"
    10  	"time"
    11  
    12  	"github.com/juju/errors"
    13  	"github.com/juju/names"
    14  	jc "github.com/juju/testing/checkers"
    15  	"github.com/juju/utils"
    16  	"github.com/juju/utils/set"
    17  	gc "gopkg.in/check.v1"
    18  
    19  	"github.com/juju/juju/agent"
    20  	"github.com/juju/juju/api"
    21  	apiprovisioner "github.com/juju/juju/api/provisioner"
    22  	"github.com/juju/juju/apiserver/params"
    23  	apiserverprovisioner "github.com/juju/juju/apiserver/provisioner"
    24  	"github.com/juju/juju/constraints"
    25  	"github.com/juju/juju/environmentserver/authentication"
    26  	"github.com/juju/juju/environs"
    27  	"github.com/juju/juju/environs/config"
    28  	"github.com/juju/juju/environs/filestorage"
    29  	"github.com/juju/juju/environs/imagemetadata"
    30  	envtesting "github.com/juju/juju/environs/testing"
    31  	"github.com/juju/juju/environs/tools"
    32  	"github.com/juju/juju/instance"
    33  	"github.com/juju/juju/juju/arch"
    34  	"github.com/juju/juju/juju/testing"
    35  	"github.com/juju/juju/mongo"
    36  	"github.com/juju/juju/network"
    37  	"github.com/juju/juju/provider/dummy"
    38  	"github.com/juju/juju/state"
    39  	"github.com/juju/juju/state/multiwatcher"
    40  	"github.com/juju/juju/storage"
    41  	"github.com/juju/juju/storage/poolmanager"
    42  	dummystorage "github.com/juju/juju/storage/provider/dummy"
    43  	"github.com/juju/juju/storage/provider/registry"
    44  	coretesting "github.com/juju/juju/testing"
    45  	coretools "github.com/juju/juju/tools"
    46  	"github.com/juju/juju/version"
    47  	"github.com/juju/juju/worker/provisioner"
    48  )
    49  
    50  type CommonProvisionerSuite struct {
    51  	testing.JujuConnSuite
    52  	op  <-chan dummy.Operation
    53  	cfg *config.Config
    54  	// defaultConstraints are used when adding a machine and then later in test assertions.
    55  	defaultConstraints constraints.Value
    56  
    57  	st          api.Connection
    58  	provisioner *apiprovisioner.State
    59  }
    60  
    61  func (s *CommonProvisionerSuite) assertProvisionerObservesConfigChanges(c *gc.C, p provisioner.Provisioner) {
    62  	// Inject our observer into the provisioner
    63  	cfgObserver := make(chan *config.Config, 1)
    64  	provisioner.SetObserver(p, cfgObserver)
    65  
    66  	// Switch to reaping on All machines.
    67  	attrs := map[string]interface{}{
    68  		config.ProvisionerHarvestModeKey: config.HarvestAll.String(),
    69  	}
    70  	err := s.State.UpdateEnvironConfig(attrs, nil, nil)
    71  	c.Assert(err, jc.ErrorIsNil)
    72  
    73  	s.BackingState.StartSync()
    74  
    75  	// Wait for the PA to load the new configuration. We wait for the change we expect
    76  	// like this because sometimes we pick up the initial harvest config (destroyed)
    77  	// rather than the one we change to (all).
    78  	received := []string{}
    79  	for {
    80  		select {
    81  		case newCfg := <-cfgObserver:
    82  			if newCfg.ProvisionerHarvestMode().String() == config.HarvestAll.String() {
    83  				return
    84  			}
    85  			received = append(received, newCfg.ProvisionerHarvestMode().String())
    86  		case <-time.After(coretesting.LongWait):
    87  			if len(received) == 0 {
    88  				c.Fatalf("PA did not action config change")
    89  			} else {
    90  				c.Fatalf("timed out waiting for config to change to '%s', received %+v",
    91  					config.HarvestAll.String(), received)
    92  			}
    93  		}
    94  	}
    95  }
    96  
    97  type ProvisionerSuite struct {
    98  	CommonProvisionerSuite
    99  }
   100  
   101  var _ = gc.Suite(&ProvisionerSuite{})
   102  
   103  var veryShortAttempt = utils.AttemptStrategy{
   104  	Total: 1 * time.Second,
   105  	Delay: 80 * time.Millisecond,
   106  }
   107  
   108  func (s *CommonProvisionerSuite) SetUpSuite(c *gc.C) {
   109  	s.JujuConnSuite.SetUpSuite(c)
   110  	s.defaultConstraints = constraints.MustParse("arch=amd64 mem=4G cpu-cores=1 root-disk=8G")
   111  }
   112  
   113  func (s *CommonProvisionerSuite) SetUpTest(c *gc.C) {
   114  	// Disable the default state policy, because the
   115  	// provisioner needs to be able to test pathological
   116  	// scenarios where a machine exists in state with
   117  	// invalid environment config.
   118  	dummy.SetStatePolicy(nil)
   119  
   120  	s.JujuConnSuite.SetUpTest(c)
   121  
   122  	// Create the operations channel with more than enough space
   123  	// for those tests that don't listen on it.
   124  	op := make(chan dummy.Operation, 500)
   125  	dummy.Listen(op)
   126  	s.op = op
   127  
   128  	cfg, err := s.State.EnvironConfig()
   129  	c.Assert(err, jc.ErrorIsNil)
   130  	s.cfg = cfg
   131  
   132  	// Create a machine for the dummy bootstrap instance,
   133  	// so the provisioner doesn't destroy it.
   134  	insts, err := s.Environ.Instances([]instance.Id{dummy.BootstrapInstanceId})
   135  	c.Assert(err, jc.ErrorIsNil)
   136  	addrs, err := insts[0].Addresses()
   137  	c.Assert(err, jc.ErrorIsNil)
   138  	machine, err := s.State.AddOneMachine(state.MachineTemplate{
   139  		Addresses:  addrs,
   140  		Series:     "quantal",
   141  		Nonce:      agent.BootstrapNonce,
   142  		InstanceId: dummy.BootstrapInstanceId,
   143  		Jobs:       []state.MachineJob{state.JobManageEnviron},
   144  	})
   145  	c.Assert(err, jc.ErrorIsNil)
   146  	c.Assert(machine.Id(), gc.Equals, "0")
   147  
   148  	err = machine.SetAgentVersion(version.Current)
   149  	c.Assert(err, jc.ErrorIsNil)
   150  
   151  	password, err := utils.RandomPassword()
   152  	c.Assert(err, jc.ErrorIsNil)
   153  	err = machine.SetPassword(password)
   154  	c.Assert(err, jc.ErrorIsNil)
   155  
   156  	s.st = s.OpenAPIAsMachine(c, machine.Tag(), password, agent.BootstrapNonce)
   157  	c.Assert(s.st, gc.NotNil)
   158  	c.Logf("API: login as %q successful", machine.Tag())
   159  	s.provisioner = s.st.Provisioner()
   160  	c.Assert(s.provisioner, gc.NotNil)
   161  }
   162  
   163  // breakDummyProvider changes the environment config in state in a way
   164  // that causes the given environMethod of the dummy provider to return
   165  // an error, which is also returned as a message to be checked.
   166  func breakDummyProvider(c *gc.C, st *state.State, environMethod string) string {
   167  	attrs := map[string]interface{}{"broken": environMethod}
   168  	err := st.UpdateEnvironConfig(attrs, nil, nil)
   169  	c.Assert(err, jc.ErrorIsNil)
   170  	return fmt.Sprintf("dummy.%s is broken", environMethod)
   171  }
   172  
   173  // invalidateEnvironment alters the environment configuration
   174  // so the Settings returned from the watcher will not pass
   175  // validation.
   176  func (s *CommonProvisionerSuite) invalidateEnvironment(c *gc.C) {
   177  	st, err := state.Open(s.State.EnvironTag(), s.MongoInfo(c), mongo.DefaultDialOpts(), state.Policy(nil))
   178  	c.Assert(err, jc.ErrorIsNil)
   179  	defer st.Close()
   180  	attrs := map[string]interface{}{"type": "unknown"}
   181  	err = st.UpdateEnvironConfig(attrs, nil, nil)
   182  	c.Assert(err, jc.ErrorIsNil)
   183  }
   184  
   185  // fixEnvironment undoes the work of invalidateEnvironment.
   186  func (s *CommonProvisionerSuite) fixEnvironment(c *gc.C) error {
   187  	st, err := state.Open(s.State.EnvironTag(), s.MongoInfo(c), mongo.DefaultDialOpts(), state.Policy(nil))
   188  	c.Assert(err, jc.ErrorIsNil)
   189  	defer st.Close()
   190  	attrs := map[string]interface{}{"type": s.cfg.AllAttrs()["type"]}
   191  	return st.UpdateEnvironConfig(attrs, nil, nil)
   192  }
   193  
   194  // stopper is stoppable.
   195  type stopper interface {
   196  	Stop() error
   197  }
   198  
   199  // stop stops a stopper.
   200  func stop(c *gc.C, s stopper) {
   201  	c.Assert(s.Stop(), jc.ErrorIsNil)
   202  }
   203  
   204  func (s *CommonProvisionerSuite) startUnknownInstance(c *gc.C, id string) instance.Instance {
   205  	instance, _ := testing.AssertStartInstance(c, s.Environ, id)
   206  	select {
   207  	case o := <-s.op:
   208  		switch o := o.(type) {
   209  		case dummy.OpStartInstance:
   210  		default:
   211  			c.Fatalf("unexpected operation %#v", o)
   212  		}
   213  	case <-time.After(coretesting.LongWait):
   214  		c.Fatalf("timed out waiting for startinstance operation")
   215  	}
   216  	return instance
   217  }
   218  
   219  func (s *CommonProvisionerSuite) checkStartInstance(c *gc.C, m *state.Machine) instance.Instance {
   220  	return s.checkStartInstanceCustom(c, m, "pork", s.defaultConstraints, nil, nil, nil, nil, true, nil, true)
   221  }
   222  
   223  func (s *CommonProvisionerSuite) checkStartInstanceNoSecureConnection(c *gc.C, m *state.Machine) instance.Instance {
   224  	return s.checkStartInstanceCustom(c, m, "pork", s.defaultConstraints, nil, nil, nil, nil, false, nil, true)
   225  }
   226  
   227  func (s *CommonProvisionerSuite) checkStartInstanceCustom(
   228  	c *gc.C, m *state.Machine,
   229  	secret string, cons constraints.Value,
   230  	networks []string, networkInfo []network.InterfaceInfo,
   231  	subnetsToZones map[network.Id][]string,
   232  	volumes []storage.Volume,
   233  	secureServerConnection bool,
   234  	checkPossibleTools coretools.List,
   235  	waitInstanceId bool,
   236  ) (
   237  	inst instance.Instance,
   238  ) {
   239  	s.BackingState.StartSync()
   240  	for {
   241  		select {
   242  		case o := <-s.op:
   243  			switch o := o.(type) {
   244  			case dummy.OpStartInstance:
   245  				inst = o.Instance
   246  				if waitInstanceId {
   247  					s.waitInstanceId(c, m, inst.Id())
   248  				}
   249  
   250  				// Check the instance was started with the expected params.
   251  				c.Assert(o.MachineId, gc.Equals, m.Id())
   252  				nonceParts := strings.SplitN(o.MachineNonce, ":", 2)
   253  				c.Assert(nonceParts, gc.HasLen, 2)
   254  				c.Assert(nonceParts[0], gc.Equals, names.NewMachineTag("0").String())
   255  				c.Assert(nonceParts[1], jc.Satisfies, utils.IsValidUUIDString)
   256  				c.Assert(o.Secret, gc.Equals, secret)
   257  				c.Assert(o.SubnetsToZones, jc.DeepEquals, subnetsToZones)
   258  				c.Assert(o.Networks, jc.DeepEquals, networks)
   259  				c.Assert(o.NetworkInfo, jc.DeepEquals, networkInfo)
   260  				c.Assert(o.Volumes, jc.DeepEquals, volumes)
   261  				c.Assert(o.AgentEnvironment["SECURE_STATESERVER_CONNECTION"], gc.Equals, strconv.FormatBool(secureServerConnection))
   262  
   263  				var jobs []multiwatcher.MachineJob
   264  				for _, job := range m.Jobs() {
   265  					jobs = append(jobs, job.ToParams())
   266  				}
   267  				c.Assert(o.Jobs, jc.SameContents, jobs)
   268  
   269  				if checkPossibleTools != nil {
   270  					for _, t := range o.PossibleTools {
   271  						url := fmt.Sprintf("https://%s/environment/%s/tools/%s",
   272  							s.st.Addr(), coretesting.EnvironmentTag.Id(), t.Version)
   273  						c.Check(t.URL, gc.Equals, url)
   274  						t.URL = ""
   275  					}
   276  					for _, t := range checkPossibleTools {
   277  						t.URL = ""
   278  					}
   279  					c.Assert(o.PossibleTools, gc.DeepEquals, checkPossibleTools)
   280  				}
   281  
   282  				// All provisioned machines in this test suite have
   283  				// their hardware characteristics attributes set to
   284  				// the same values as the constraints due to the dummy
   285  				// environment being used.
   286  				if !constraints.IsEmpty(&cons) {
   287  					c.Assert(o.Constraints, gc.DeepEquals, cons)
   288  					hc, err := m.HardwareCharacteristics()
   289  					c.Assert(err, jc.ErrorIsNil)
   290  					c.Assert(*hc, gc.DeepEquals, instance.HardwareCharacteristics{
   291  						Arch:     cons.Arch,
   292  						Mem:      cons.Mem,
   293  						RootDisk: cons.RootDisk,
   294  						CpuCores: cons.CpuCores,
   295  						CpuPower: cons.CpuPower,
   296  						Tags:     cons.Tags,
   297  					})
   298  				}
   299  				return
   300  			default:
   301  				c.Logf("ignoring unexpected operation %#v", o)
   302  			}
   303  		case <-time.After(2 * time.Second):
   304  			c.Fatalf("provisioner did not start an instance")
   305  			return
   306  		}
   307  	}
   308  }
   309  
   310  // checkNoOperations checks that the environ was not operated upon.
   311  func (s *CommonProvisionerSuite) checkNoOperations(c *gc.C) {
   312  	s.BackingState.StartSync()
   313  	select {
   314  	case o := <-s.op:
   315  		c.Fatalf("unexpected operation %+v", o)
   316  	case <-time.After(coretesting.ShortWait):
   317  		return
   318  	}
   319  }
   320  
   321  // checkStopInstances checks that an instance has been stopped.
   322  func (s *CommonProvisionerSuite) checkStopInstances(c *gc.C, instances ...instance.Instance) {
   323  	s.checkStopSomeInstances(c, instances, nil)
   324  }
   325  
   326  // checkStopSomeInstances checks that instancesToStop are stopped while instancesToKeep are not.
   327  func (s *CommonProvisionerSuite) checkStopSomeInstances(c *gc.C,
   328  	instancesToStop []instance.Instance, instancesToKeep []instance.Instance) {
   329  
   330  	s.BackingState.StartSync()
   331  	instanceIdsToStop := set.NewStrings()
   332  	for _, instance := range instancesToStop {
   333  		instanceIdsToStop.Add(string(instance.Id()))
   334  	}
   335  	instanceIdsToKeep := set.NewStrings()
   336  	for _, instance := range instancesToKeep {
   337  		instanceIdsToKeep.Add(string(instance.Id()))
   338  	}
   339  	// Continue checking for stop instance calls until all the instances we
   340  	// are waiting on to finish, actually finish, or we time out.
   341  	for !instanceIdsToStop.IsEmpty() {
   342  		select {
   343  		case o := <-s.op:
   344  			switch o := o.(type) {
   345  			case dummy.OpStopInstances:
   346  				for _, id := range o.Ids {
   347  					instId := string(id)
   348  					instanceIdsToStop.Remove(instId)
   349  					if instanceIdsToKeep.Contains(instId) {
   350  						c.Errorf("provisioner unexpectedly stopped instance %s", instId)
   351  					}
   352  				}
   353  			default:
   354  				c.Fatalf("unexpected operation %#v", o)
   355  				return
   356  			}
   357  		case <-time.After(2 * time.Second):
   358  			c.Fatalf("provisioner did not stop an instance")
   359  			return
   360  		}
   361  	}
   362  }
   363  
   364  func (s *CommonProvisionerSuite) waitMachine(c *gc.C, m *state.Machine, check func() bool) {
   365  	// TODO(jam): We need to grow a new method on NotifyWatcherC
   366  	// that calls StartSync while waiting for changes, then
   367  	// waitMachine and waitHardwareCharacteristics can use that
   368  	// instead
   369  	w := m.Watch()
   370  	defer stop(c, w)
   371  	timeout := time.After(coretesting.LongWait)
   372  	resync := time.After(0)
   373  	for {
   374  		select {
   375  		case <-w.Changes():
   376  			if check() {
   377  				return
   378  			}
   379  		case <-resync:
   380  			resync = time.After(coretesting.ShortWait)
   381  			s.BackingState.StartSync()
   382  		case <-timeout:
   383  			c.Fatalf("machine %v wait timed out", m)
   384  		}
   385  	}
   386  }
   387  
   388  func (s *CommonProvisionerSuite) waitHardwareCharacteristics(c *gc.C, m *state.Machine, check func() bool) {
   389  	w := m.WatchHardwareCharacteristics()
   390  	defer stop(c, w)
   391  	timeout := time.After(coretesting.LongWait)
   392  	resync := time.After(0)
   393  	for {
   394  		select {
   395  		case <-w.Changes():
   396  			if check() {
   397  				return
   398  			}
   399  		case <-resync:
   400  			resync = time.After(coretesting.ShortWait)
   401  			s.BackingState.StartSync()
   402  		case <-timeout:
   403  			c.Fatalf("hardware characteristics for machine %v wait timed out", m)
   404  		}
   405  	}
   406  }
   407  
   408  // waitRemoved waits for the supplied machine to be removed from state.
   409  func (s *CommonProvisionerSuite) waitRemoved(c *gc.C, m *state.Machine) {
   410  	s.waitMachine(c, m, func() bool {
   411  		err := m.Refresh()
   412  		if errors.IsNotFound(err) {
   413  			return true
   414  		}
   415  		c.Assert(err, jc.ErrorIsNil)
   416  		c.Logf("machine %v is still %s", m, m.Life())
   417  		return false
   418  	})
   419  }
   420  
   421  // waitInstanceId waits until the supplied machine has an instance id, then
   422  // asserts it is as expected.
   423  func (s *CommonProvisionerSuite) waitInstanceId(c *gc.C, m *state.Machine, expect instance.Id) {
   424  	s.waitHardwareCharacteristics(c, m, func() bool {
   425  		if actual, err := m.InstanceId(); err == nil {
   426  			c.Assert(actual, gc.Equals, expect)
   427  			return true
   428  		} else if !errors.IsNotProvisioned(err) {
   429  			// We don't expect any errors.
   430  			panic(err)
   431  		}
   432  		c.Logf("machine %v is still unprovisioned", m)
   433  		return false
   434  	})
   435  }
   436  
   437  func (s *CommonProvisionerSuite) newEnvironProvisioner(c *gc.C) provisioner.Provisioner {
   438  	machineTag := names.NewMachineTag("0")
   439  	agentConfig := s.AgentConfigForTag(c, machineTag)
   440  	return provisioner.NewEnvironProvisioner(s.provisioner, agentConfig)
   441  }
   442  
   443  func (s *CommonProvisionerSuite) addMachine() (*state.Machine, error) {
   444  	return s.addMachineWithConstraints(s.defaultConstraints)
   445  }
   446  
   447  func (s *CommonProvisionerSuite) addMachineWithConstraints(cons constraints.Value) (*state.Machine, error) {
   448  	return s.BackingState.AddOneMachine(state.MachineTemplate{
   449  		Series:      coretesting.FakeDefaultSeries,
   450  		Jobs:        []state.MachineJob{state.JobHostUnits},
   451  		Constraints: cons,
   452  	})
   453  }
   454  
   455  func (s *CommonProvisionerSuite) ensureAvailability(c *gc.C, n int) []*state.Machine {
   456  	changes, err := s.BackingState.EnsureAvailability(n, s.defaultConstraints, coretesting.FakeDefaultSeries, nil)
   457  	c.Assert(err, jc.ErrorIsNil)
   458  	added := make([]*state.Machine, len(changes.Added))
   459  	for i, mid := range changes.Added {
   460  		m, err := s.BackingState.Machine(mid)
   461  		c.Assert(err, jc.ErrorIsNil)
   462  		added[i] = m
   463  	}
   464  	return added
   465  }
   466  
   467  func (s *ProvisionerSuite) TestProvisionerStartStop(c *gc.C) {
   468  	p := s.newEnvironProvisioner(c)
   469  	c.Assert(p.Stop(), jc.ErrorIsNil)
   470  }
   471  
   472  func (s *ProvisionerSuite) TestSimple(c *gc.C) {
   473  	p := s.newEnvironProvisioner(c)
   474  	defer stop(c, p)
   475  
   476  	// Check that an instance is provisioned when the machine is created...
   477  	m, err := s.addMachine()
   478  	c.Assert(err, jc.ErrorIsNil)
   479  	instance := s.checkStartInstanceNoSecureConnection(c, m)
   480  
   481  	// ...and removed, along with the machine, when the machine is Dead.
   482  	c.Assert(m.EnsureDead(), gc.IsNil)
   483  	s.checkStopInstances(c, instance)
   484  	s.waitRemoved(c, m)
   485  }
   486  
   487  func (s *ProvisionerSuite) TestConstraints(c *gc.C) {
   488  	// Create a machine with non-standard constraints.
   489  	m, err := s.addMachine()
   490  	c.Assert(err, jc.ErrorIsNil)
   491  	cons := constraints.MustParse("mem=8G arch=amd64 cpu-cores=2 root-disk=10G")
   492  	err = m.SetConstraints(cons)
   493  	c.Assert(err, jc.ErrorIsNil)
   494  
   495  	// Start a provisioner and check those constraints are used.
   496  	p := s.newEnvironProvisioner(c)
   497  	defer stop(c, p)
   498  	s.checkStartInstanceCustom(c, m, "pork", cons, nil, nil, nil, nil, false, nil, true)
   499  }
   500  
   501  func (s *ProvisionerSuite) TestPossibleTools(c *gc.C) {
   502  
   503  	storageDir := c.MkDir()
   504  	s.PatchValue(&tools.DefaultBaseURL, storageDir)
   505  	stor, err := filestorage.NewFileStorageWriter(storageDir)
   506  	c.Assert(err, jc.ErrorIsNil)
   507  
   508  	// Set a current version that does not match the
   509  	// agent-version in the environ config.
   510  	currentVersion := version.MustParseBinary("1.2.3-quantal-arm64")
   511  	s.PatchValue(&version.Current, currentVersion)
   512  
   513  	// Upload some plausible matches, and some that should be filtered out.
   514  	compatibleVersion := version.MustParseBinary("1.2.3-quantal-amd64")
   515  	ignoreVersion1 := version.MustParseBinary("1.2.4-quantal-arm64")
   516  	ignoreVersion2 := version.MustParseBinary("1.2.3-precise-arm64")
   517  	availableVersions := []version.Binary{
   518  		currentVersion, compatibleVersion, ignoreVersion1, ignoreVersion2,
   519  	}
   520  	envtesting.AssertUploadFakeToolsVersions(c, stor, s.cfg.AgentStream(), s.cfg.AgentStream(), availableVersions...)
   521  
   522  	// Extract the tools that we expect to actually match.
   523  	expectedList, err := tools.FindTools(s.Environ, -1, -1, s.cfg.AgentStream(), coretools.Filter{
   524  		Number: currentVersion.Number,
   525  		Series: currentVersion.Series,
   526  	})
   527  	c.Assert(err, jc.ErrorIsNil)
   528  
   529  	// Create the machine and check the tools that get passed into StartInstance.
   530  	machine, err := s.BackingState.AddOneMachine(state.MachineTemplate{
   531  		Series: "quantal",
   532  		Jobs:   []state.MachineJob{state.JobHostUnits},
   533  	})
   534  	c.Assert(err, jc.ErrorIsNil)
   535  
   536  	provisioner := s.newEnvironProvisioner(c)
   537  	defer stop(c, provisioner)
   538  	s.checkStartInstanceCustom(
   539  		c, machine, "pork", constraints.Value{},
   540  		nil, nil, nil, nil, false, expectedList, true,
   541  	)
   542  }
   543  
   544  func (s *ProvisionerSuite) TestProvisionerSetsErrorStatusWhenNoToolsAreAvailable(c *gc.C) {
   545  	p := s.newEnvironProvisioner(c)
   546  	defer stop(c, p)
   547  
   548  	// Check that an instance is not provisioned when the machine is created...
   549  	m, err := s.BackingState.AddOneMachine(state.MachineTemplate{
   550  		// We need a valid series that has no tools uploaded
   551  		Series:      "raring",
   552  		Jobs:        []state.MachineJob{state.JobHostUnits},
   553  		Constraints: s.defaultConstraints,
   554  	})
   555  	c.Assert(err, jc.ErrorIsNil)
   556  	s.checkNoOperations(c)
   557  
   558  	t0 := time.Now()
   559  	for time.Since(t0) < coretesting.LongWait {
   560  		// And check the machine status is set to error.
   561  		statusInfo, err := m.Status()
   562  		c.Assert(err, jc.ErrorIsNil)
   563  		if statusInfo.Status == state.StatusPending {
   564  			time.Sleep(coretesting.ShortWait)
   565  			continue
   566  		}
   567  		c.Assert(statusInfo.Status, gc.Equals, state.StatusError)
   568  		c.Assert(statusInfo.Message, gc.Equals, "no matching tools available")
   569  		break
   570  	}
   571  
   572  	// Restart the PA to make sure the machine is skipped again.
   573  	stop(c, p)
   574  	p = s.newEnvironProvisioner(c)
   575  	defer stop(c, p)
   576  	s.checkNoOperations(c)
   577  }
   578  
   579  func (s *ProvisionerSuite) TestProvisionerSetsErrorStatusWhenStartInstanceFailed(c *gc.C) {
   580  	brokenMsg := breakDummyProvider(c, s.State, "StartInstance")
   581  	p := s.newEnvironProvisioner(c)
   582  	defer stop(c, p)
   583  
   584  	// Check that an instance is not provisioned when the machine is created...
   585  	m, err := s.addMachine()
   586  	c.Assert(err, jc.ErrorIsNil)
   587  	s.checkNoOperations(c)
   588  
   589  	t0 := time.Now()
   590  	for time.Since(t0) < coretesting.LongWait {
   591  		// And check the machine status is set to error.
   592  		statusInfo, err := m.Status()
   593  		c.Assert(err, jc.ErrorIsNil)
   594  		if statusInfo.Status == state.StatusPending {
   595  			time.Sleep(coretesting.ShortWait)
   596  			continue
   597  		}
   598  		c.Assert(statusInfo.Status, gc.Equals, state.StatusError)
   599  		c.Assert(statusInfo.Message, gc.Equals, brokenMsg)
   600  		break
   601  	}
   602  
   603  	// Unbreak the environ config.
   604  	err = s.fixEnvironment(c)
   605  	c.Assert(err, jc.ErrorIsNil)
   606  
   607  	// Restart the PA to make sure the machine is skipped again.
   608  	stop(c, p)
   609  	p = s.newEnvironProvisioner(c)
   610  	defer stop(c, p)
   611  	s.checkNoOperations(c)
   612  }
   613  
   614  func (s *ProvisionerSuite) TestProvisionerFailedStartInstanceWithInjectedCreationError(c *gc.C) {
   615  	// create the error injection channel
   616  	errorInjectionChannel := make(chan error, 2)
   617  
   618  	p := s.newEnvironProvisioner(c)
   619  	defer stop(c, p)
   620  
   621  	// patch the dummy provider error injection channel
   622  	cleanup := dummy.PatchTransientErrorInjectionChannel(errorInjectionChannel)
   623  	defer cleanup()
   624  
   625  	retryableError := instance.NewRetryableCreationError("container failed to start and was destroyed")
   626  	destroyError := errors.New("container failed to start and failed to destroy: manual cleanup of containers needed")
   627  	// send the error message TWICE, because the provisioner will retry only ONCE
   628  	errorInjectionChannel <- retryableError
   629  	errorInjectionChannel <- destroyError
   630  
   631  	m, err := s.addMachine()
   632  	c.Assert(err, jc.ErrorIsNil)
   633  	s.checkNoOperations(c)
   634  
   635  	t0 := time.Now()
   636  	for time.Since(t0) < coretesting.LongWait {
   637  		// And check the machine status is set to error.
   638  		statusInfo, err := m.Status()
   639  		c.Assert(err, jc.ErrorIsNil)
   640  		if statusInfo.Status == state.StatusPending {
   641  			time.Sleep(coretesting.ShortWait)
   642  			continue
   643  		}
   644  		c.Assert(statusInfo.Status, gc.Equals, state.StatusError)
   645  		// check that the status matches the error message
   646  		c.Assert(statusInfo.Message, gc.Equals, destroyError.Error())
   647  		break
   648  	}
   649  
   650  }
   651  
   652  func (s *ProvisionerSuite) TestProvisionerSucceedStartInstanceWithInjectedRetryableCreationError(c *gc.C) {
   653  	// create the error injection channel
   654  	errorInjectionChannel := make(chan error, 1)
   655  	c.Assert(errorInjectionChannel, gc.NotNil)
   656  
   657  	p := s.newEnvironProvisioner(c)
   658  	defer stop(c, p)
   659  
   660  	// patch the dummy provider error injection channel
   661  	cleanup := dummy.PatchTransientErrorInjectionChannel(errorInjectionChannel)
   662  	defer cleanup()
   663  
   664  	// send the error message once
   665  	// - instance creation should succeed
   666  	retryableError := instance.NewRetryableCreationError("container failed to start and was destroyed")
   667  	errorInjectionChannel <- retryableError
   668  
   669  	m, err := s.addMachine()
   670  	c.Assert(err, jc.ErrorIsNil)
   671  	s.checkStartInstanceNoSecureConnection(c, m)
   672  }
   673  
   674  func (s *ProvisionerSuite) TestProvisionerSucceedStartInstanceWithInjectedWrappedRetryableCreationError(c *gc.C) {
   675  	// create the error injection channel
   676  	errorInjectionChannel := make(chan error, 1)
   677  	c.Assert(errorInjectionChannel, gc.NotNil)
   678  
   679  	p := s.newEnvironProvisioner(c)
   680  	defer stop(c, p)
   681  
   682  	// patch the dummy provider error injection channel
   683  	cleanup := dummy.PatchTransientErrorInjectionChannel(errorInjectionChannel)
   684  	defer cleanup()
   685  
   686  	// send the error message once
   687  	// - instance creation should succeed
   688  	retryableError := errors.Wrap(errors.New(""), instance.NewRetryableCreationError("container failed to start and was destroyed"))
   689  	errorInjectionChannel <- retryableError
   690  
   691  	m, err := s.addMachine()
   692  	c.Assert(err, jc.ErrorIsNil)
   693  	s.checkStartInstanceNoSecureConnection(c, m)
   694  }
   695  
   696  func (s *ProvisionerSuite) TestProvisionerFailStartInstanceWithInjectedNonRetryableCreationError(c *gc.C) {
   697  	// create the error injection channel
   698  	errorInjectionChannel := make(chan error, 1)
   699  	c.Assert(errorInjectionChannel, gc.NotNil)
   700  
   701  	p := s.newEnvironProvisioner(c)
   702  	defer stop(c, p)
   703  
   704  	// patch the dummy provider error injection channel
   705  	cleanup := dummy.PatchTransientErrorInjectionChannel(errorInjectionChannel)
   706  	defer cleanup()
   707  
   708  	// send the error message once
   709  	// - instance creation should succeed
   710  	nonRetryableError := errors.New("some nonretryable error")
   711  	errorInjectionChannel <- nonRetryableError
   712  
   713  	m, err := s.addMachine()
   714  	c.Assert(err, jc.ErrorIsNil)
   715  	s.checkNoOperations(c)
   716  
   717  	t0 := time.Now()
   718  	for time.Since(t0) < coretesting.LongWait {
   719  		// And check the machine status is set to error.
   720  		statusInfo, err := m.Status()
   721  		c.Assert(err, jc.ErrorIsNil)
   722  		if statusInfo.Status == state.StatusPending {
   723  			time.Sleep(coretesting.ShortWait)
   724  			continue
   725  		}
   726  		c.Assert(statusInfo.Status, gc.Equals, state.StatusError)
   727  		// check that the status matches the error message
   728  		c.Assert(statusInfo.Message, gc.Equals, nonRetryableError.Error())
   729  		break
   730  	}
   731  }
   732  
   733  func (s *ProvisionerSuite) TestProvisioningDoesNotOccurForLXC(c *gc.C) {
   734  	p := s.newEnvironProvisioner(c)
   735  	defer stop(c, p)
   736  
   737  	// create a machine to host the container.
   738  	m, err := s.addMachine()
   739  	c.Assert(err, jc.ErrorIsNil)
   740  	inst := s.checkStartInstanceNoSecureConnection(c, m)
   741  
   742  	// make a container on the machine we just created
   743  	template := state.MachineTemplate{
   744  		Series: coretesting.FakeDefaultSeries,
   745  		Jobs:   []state.MachineJob{state.JobHostUnits},
   746  	}
   747  	container, err := s.State.AddMachineInsideMachine(template, m.Id(), instance.LXC)
   748  	c.Assert(err, jc.ErrorIsNil)
   749  
   750  	// the PA should not attempt to create it
   751  	s.checkNoOperations(c)
   752  
   753  	// cleanup
   754  	c.Assert(container.EnsureDead(), gc.IsNil)
   755  	c.Assert(container.Remove(), gc.IsNil)
   756  	c.Assert(m.EnsureDead(), gc.IsNil)
   757  	s.checkStopInstances(c, inst)
   758  	s.waitRemoved(c, m)
   759  }
   760  
   761  func (s *ProvisionerSuite) TestProvisioningDoesNotOccurForKVM(c *gc.C) {
   762  	p := s.newEnvironProvisioner(c)
   763  	defer stop(c, p)
   764  
   765  	// create a machine to host the container.
   766  	m, err := s.addMachine()
   767  	c.Assert(err, jc.ErrorIsNil)
   768  	inst := s.checkStartInstanceNoSecureConnection(c, m)
   769  
   770  	// make a container on the machine we just created
   771  	template := state.MachineTemplate{
   772  		Series: coretesting.FakeDefaultSeries,
   773  		Jobs:   []state.MachineJob{state.JobHostUnits},
   774  	}
   775  	container, err := s.State.AddMachineInsideMachine(template, m.Id(), instance.KVM)
   776  	c.Assert(err, jc.ErrorIsNil)
   777  
   778  	// the PA should not attempt to create it
   779  	s.checkNoOperations(c)
   780  
   781  	// cleanup
   782  	c.Assert(container.EnsureDead(), gc.IsNil)
   783  	c.Assert(container.Remove(), gc.IsNil)
   784  	c.Assert(m.EnsureDead(), gc.IsNil)
   785  	s.checkStopInstances(c, inst)
   786  	s.waitRemoved(c, m)
   787  }
   788  
   789  type MachineClassifySuite struct {
   790  }
   791  
   792  var _ = gc.Suite(&MachineClassifySuite{})
   793  
   794  type MockMachine struct {
   795  	life          params.Life
   796  	status        params.Status
   797  	id            string
   798  	idErr         error
   799  	ensureDeadErr error
   800  	statusErr     error
   801  }
   802  
   803  func (m *MockMachine) Life() params.Life {
   804  	return m.life
   805  }
   806  
   807  func (m *MockMachine) InstanceId() (instance.Id, error) {
   808  	return instance.Id(m.id), m.idErr
   809  }
   810  
   811  func (m *MockMachine) EnsureDead() error {
   812  	return m.ensureDeadErr
   813  }
   814  
   815  func (m *MockMachine) Status() (params.Status, string, error) {
   816  	return m.status, "", m.statusErr
   817  }
   818  
   819  func (m *MockMachine) Id() string {
   820  	return m.id
   821  }
   822  
   823  type machineClassificationTest struct {
   824  	description    string
   825  	life           params.Life
   826  	status         params.Status
   827  	idErr          string
   828  	ensureDeadErr  string
   829  	expectErrCode  string
   830  	expectErrFmt   string
   831  	statusErr      string
   832  	classification provisioner.MachineClassification
   833  }
   834  
   835  var machineClassificationTests = []machineClassificationTest{{
   836  	description:    "Dead machine is dead",
   837  	life:           params.Dead,
   838  	status:         params.StatusStarted,
   839  	classification: provisioner.Dead,
   840  }, {
   841  	description:    "Dying machine can carry on dying",
   842  	life:           params.Dying,
   843  	status:         params.StatusStarted,
   844  	classification: provisioner.None,
   845  }, {
   846  	description:    "Dying unprovisioned machine is ensured dead",
   847  	life:           params.Dying,
   848  	status:         params.StatusStarted,
   849  	classification: provisioner.Dead,
   850  	idErr:          params.CodeNotProvisioned,
   851  }, {
   852  	description:    "Can't load provisioned dying machine",
   853  	life:           params.Dying,
   854  	status:         params.StatusStarted,
   855  	classification: provisioner.None,
   856  	idErr:          params.CodeNotFound,
   857  	expectErrCode:  params.CodeNotFound,
   858  	expectErrFmt:   "failed to load dying machine id:%s.*",
   859  }, {
   860  	description:    "Alive machine is not provisioned - pending",
   861  	life:           params.Alive,
   862  	status:         params.StatusPending,
   863  	classification: provisioner.Pending,
   864  	idErr:          params.CodeNotProvisioned,
   865  	expectErrFmt:   "found machine pending provisioning id:%s.*",
   866  }, {
   867  	description:    "Alive, pending machine not found",
   868  	life:           params.Alive,
   869  	status:         params.StatusPending,
   870  	classification: provisioner.None,
   871  	idErr:          params.CodeNotFound,
   872  	expectErrCode:  params.CodeNotFound,
   873  	expectErrFmt:   "failed to load machine id:%s.*",
   874  }, {
   875  	description:    "Cannot get unprovisioned machine status",
   876  	life:           params.Alive,
   877  	classification: provisioner.None,
   878  	statusErr:      params.CodeNotFound,
   879  	idErr:          params.CodeNotProvisioned,
   880  }, {
   881  	description:    "Dying machine fails to ensure dead",
   882  	life:           params.Dying,
   883  	status:         params.StatusStarted,
   884  	classification: provisioner.None,
   885  	idErr:          params.CodeNotProvisioned,
   886  	expectErrCode:  params.CodeNotFound,
   887  	ensureDeadErr:  params.CodeNotFound,
   888  	expectErrFmt:   "failed to ensure machine dead id:%s.*",
   889  }}
   890  
   891  var machineClassificationTestsRequireMaintenance = machineClassificationTest{
   892  	description:    "Machine needs maintaining",
   893  	life:           params.Alive,
   894  	status:         params.StatusStarted,
   895  	classification: provisioner.Maintain,
   896  }
   897  
   898  var machineClassificationTestsNoMaintenance = machineClassificationTest{
   899  	description:    "Machine doesn't need maintaining",
   900  	life:           params.Alive,
   901  	status:         params.StatusStarted,
   902  	classification: provisioner.None,
   903  }
   904  
   905  func (s *MachineClassifySuite) TestMachineClassification(c *gc.C) {
   906  	test := func(t machineClassificationTest, id string) {
   907  		// Run a sub-test from the test table
   908  		s2e := func(s string) error {
   909  			// Little helper to turn a non-empty string into a useful error for "ErrorMaches"
   910  			if s != "" {
   911  				return &params.Error{Code: s}
   912  			}
   913  			return nil
   914  		}
   915  
   916  		c.Logf("%s: %s", id, t.description)
   917  		machine := MockMachine{t.life, t.status, id, s2e(t.idErr), s2e(t.ensureDeadErr), s2e(t.statusErr)}
   918  		classification, err := provisioner.ClassifyMachine(&machine)
   919  		if err != nil {
   920  			c.Assert(err, gc.ErrorMatches, fmt.Sprintf(t.expectErrFmt, machine.Id()))
   921  		} else {
   922  			c.Assert(err, gc.Equals, s2e(t.expectErrCode))
   923  		}
   924  		c.Assert(classification, gc.Equals, t.classification)
   925  	}
   926  
   927  	machineIds := []string{"0/lxc/0", "0/kvm/0", "0"}
   928  	for _, id := range machineIds {
   929  		tests := machineClassificationTests
   930  		if id == "0" {
   931  			tests = append(tests, machineClassificationTestsNoMaintenance)
   932  		} else {
   933  			tests = append(tests, machineClassificationTestsRequireMaintenance)
   934  		}
   935  		for _, t := range tests {
   936  			test(t, id)
   937  		}
   938  	}
   939  }
   940  
   941  func (s *ProvisionerSuite) TestProvisioningMachinesWithSpacesSuccess(c *gc.C) {
   942  	p := s.newEnvironProvisioner(c)
   943  	defer p.Stop()
   944  
   945  	// Add the spaces used in constraints.
   946  	_, err := s.State.AddSpace("space1", nil, false)
   947  	c.Assert(err, jc.ErrorIsNil)
   948  	_, err = s.State.AddSpace("space2", nil, false)
   949  	c.Assert(err, jc.ErrorIsNil)
   950  
   951  	// Add and provision a machine with spaces specified.
   952  	cons := constraints.MustParse(
   953  		s.defaultConstraints.String(), "spaces=space2,^space1",
   954  	)
   955  	// The dummy provider simulates 2 subnets per included space.
   956  	expectedSubnetsToZones := map[network.Id][]string{
   957  		"subnet-0": []string{"zone0"},
   958  		"subnet-1": []string{"zone1"},
   959  	}
   960  	m, err := s.addMachineWithConstraints(cons)
   961  	c.Assert(err, jc.ErrorIsNil)
   962  	inst := s.checkStartInstanceCustom(
   963  		c, m, "pork", cons,
   964  		nil, nil,
   965  		expectedSubnetsToZones,
   966  		nil, false, nil, true,
   967  	)
   968  
   969  	// Cleanup.
   970  	c.Assert(m.EnsureDead(), gc.IsNil)
   971  	s.checkStopInstances(c, inst)
   972  	s.waitRemoved(c, m)
   973  }
   974  
   975  func (s *ProvisionerSuite) TestProvisioningMachinesFailsWithUnknownSpaces(c *gc.C) {
   976  	cons := constraints.MustParse(
   977  		s.defaultConstraints.String(), "spaces=missing,ignored,^ignored-too",
   978  	)
   979  	m, err := s.addMachineWithConstraints(cons)
   980  	c.Assert(err, jc.ErrorIsNil)
   981  
   982  	// Start the PA.
   983  	p := s.newEnvironProvisioner(c)
   984  	defer stop(c, p)
   985  
   986  	// Expect StartInstance to fail.
   987  	s.checkNoOperations(c)
   988  
   989  	// Ensure machine error status was set.
   990  	t0 := time.Now()
   991  	for time.Since(t0) < coretesting.LongWait {
   992  		// And check the machine status is set to error.
   993  		statusInfo, err := m.Status()
   994  		c.Assert(err, jc.ErrorIsNil)
   995  		if statusInfo.Status == state.StatusPending {
   996  			time.Sleep(coretesting.ShortWait)
   997  			continue
   998  		}
   999  		c.Assert(statusInfo.Status, gc.Equals, state.StatusError)
  1000  		c.Assert(statusInfo.Message, gc.Equals, `cannot match subnets to zones: space "missing" not found`)
  1001  		break
  1002  	}
  1003  
  1004  	// Make sure the task didn't stop with an error
  1005  	died := make(chan error)
  1006  	go func() {
  1007  		died <- p.Wait()
  1008  	}()
  1009  	select {
  1010  	case <-time.After(coretesting.ShortWait):
  1011  	case err := <-died:
  1012  		c.Fatalf("provisioner task died unexpectedly with err: %v", err)
  1013  	}
  1014  
  1015  	// Restart the PA to make sure the machine is not retried.
  1016  	stop(c, p)
  1017  	p = s.newEnvironProvisioner(c)
  1018  	defer stop(c, p)
  1019  
  1020  	s.checkNoOperations(c)
  1021  }
  1022  
  1023  func (s *CommonProvisionerSuite) addMachineWithRequestedVolumes(volumes []state.MachineVolumeParams, cons constraints.Value) (*state.Machine, error) {
  1024  	return s.BackingState.AddOneMachine(state.MachineTemplate{
  1025  		Series:      coretesting.FakeDefaultSeries,
  1026  		Jobs:        []state.MachineJob{state.JobHostUnits},
  1027  		Constraints: cons,
  1028  		Volumes:     volumes,
  1029  	})
  1030  }
  1031  
  1032  func (s *ProvisionerSuite) TestProvisioningMachinesWithRequestedVolumes(c *gc.C) {
  1033  	// Set up a persistent pool.
  1034  	registry.RegisterProvider("static", &dummystorage.StorageProvider{IsDynamic: false})
  1035  	registry.RegisterEnvironStorageProviders("dummy", "static")
  1036  	defer registry.RegisterProvider("static", nil)
  1037  	poolManager := poolmanager.New(state.NewStateSettings(s.State))
  1038  	_, err := poolManager.Create("persistent-pool", "static", map[string]interface{}{"persistent": true})
  1039  	c.Assert(err, jc.ErrorIsNil)
  1040  
  1041  	p := s.newEnvironProvisioner(c)
  1042  	defer p.Stop()
  1043  
  1044  	// Add and provision a machine with volumes specified.
  1045  	requestedVolumes := []state.MachineVolumeParams{{
  1046  		Volume:     state.VolumeParams{Pool: "static", Size: 1024},
  1047  		Attachment: state.VolumeAttachmentParams{},
  1048  	}, {
  1049  		Volume:     state.VolumeParams{Pool: "persistent-pool", Size: 2048},
  1050  		Attachment: state.VolumeAttachmentParams{},
  1051  	}}
  1052  	expectVolumeInfo := []storage.Volume{{
  1053  		names.NewVolumeTag("1"),
  1054  		storage.VolumeInfo{
  1055  			Size: 1024,
  1056  		},
  1057  	}, {
  1058  		names.NewVolumeTag("2"),
  1059  		storage.VolumeInfo{
  1060  			Size:       2048,
  1061  			Persistent: true,
  1062  		},
  1063  	}}
  1064  	m, err := s.addMachineWithRequestedVolumes(requestedVolumes, s.defaultConstraints)
  1065  	c.Assert(err, jc.ErrorIsNil)
  1066  	inst := s.checkStartInstanceCustom(
  1067  		c, m, "pork", s.defaultConstraints,
  1068  		nil, nil, nil,
  1069  		expectVolumeInfo, false,
  1070  		nil, true,
  1071  	)
  1072  
  1073  	// Cleanup.
  1074  	c.Assert(m.EnsureDead(), gc.IsNil)
  1075  	s.checkStopInstances(c, inst)
  1076  	s.waitRemoved(c, m)
  1077  }
  1078  
  1079  func (s *ProvisionerSuite) TestProvisioningDoesNotOccurWithAnInvalidEnvironment(c *gc.C) {
  1080  	s.invalidateEnvironment(c)
  1081  
  1082  	p := s.newEnvironProvisioner(c)
  1083  	defer stop(c, p)
  1084  
  1085  	// try to create a machine
  1086  	_, err := s.addMachine()
  1087  	c.Assert(err, jc.ErrorIsNil)
  1088  
  1089  	// the PA should not create it
  1090  	s.checkNoOperations(c)
  1091  }
  1092  
  1093  func (s *ProvisionerSuite) TestProvisioningOccursWithFixedEnvironment(c *gc.C) {
  1094  	s.invalidateEnvironment(c)
  1095  
  1096  	p := s.newEnvironProvisioner(c)
  1097  	defer stop(c, p)
  1098  
  1099  	// try to create a machine
  1100  	m, err := s.addMachine()
  1101  	c.Assert(err, jc.ErrorIsNil)
  1102  
  1103  	// the PA should not create it
  1104  	s.checkNoOperations(c)
  1105  
  1106  	err = s.fixEnvironment(c)
  1107  	c.Assert(err, jc.ErrorIsNil)
  1108  
  1109  	s.checkStartInstanceNoSecureConnection(c, m)
  1110  }
  1111  
  1112  func (s *ProvisionerSuite) TestProvisioningDoesOccurAfterInvalidEnvironmentPublished(c *gc.C) {
  1113  	s.PatchValue(provisioner.GetToolsFinder, func(*apiprovisioner.State) provisioner.ToolsFinder {
  1114  		return mockToolsFinder{}
  1115  	})
  1116  	p := s.newEnvironProvisioner(c)
  1117  	defer stop(c, p)
  1118  
  1119  	// place a new machine into the state
  1120  	m, err := s.addMachine()
  1121  	c.Assert(err, jc.ErrorIsNil)
  1122  
  1123  	s.checkStartInstanceNoSecureConnection(c, m)
  1124  
  1125  	s.invalidateEnvironment(c)
  1126  
  1127  	// create a second machine
  1128  	m, err = s.addMachine()
  1129  	c.Assert(err, jc.ErrorIsNil)
  1130  
  1131  	// the PA should create it using the old environment
  1132  	s.checkStartInstanceNoSecureConnection(c, m)
  1133  }
  1134  
  1135  func (s *ProvisionerSuite) TestProvisioningDoesNotProvisionTheSameMachineAfterRestart(c *gc.C) {
  1136  	p := s.newEnvironProvisioner(c)
  1137  	defer stop(c, p)
  1138  
  1139  	// create a machine
  1140  	m, err := s.addMachine()
  1141  	c.Assert(err, jc.ErrorIsNil)
  1142  	s.checkStartInstanceNoSecureConnection(c, m)
  1143  
  1144  	// restart the PA
  1145  	stop(c, p)
  1146  	p = s.newEnvironProvisioner(c)
  1147  	defer stop(c, p)
  1148  
  1149  	// check that there is only one machine provisioned.
  1150  	machines, err := s.State.AllMachines()
  1151  	c.Assert(err, jc.ErrorIsNil)
  1152  	c.Check(len(machines), gc.Equals, 2)
  1153  	c.Check(machines[0].Id(), gc.Equals, "0")
  1154  	c.Check(machines[1].CheckProvisioned("fake_nonce"), jc.IsFalse)
  1155  
  1156  	// the PA should not create it a second time
  1157  	s.checkNoOperations(c)
  1158  }
  1159  
  1160  func (s *ProvisionerSuite) TestDyingMachines(c *gc.C) {
  1161  	p := s.newEnvironProvisioner(c)
  1162  	defer stop(c, p)
  1163  
  1164  	// provision a machine
  1165  	m0, err := s.addMachine()
  1166  	c.Assert(err, jc.ErrorIsNil)
  1167  	s.checkStartInstanceNoSecureConnection(c, m0)
  1168  
  1169  	// stop the provisioner and make the machine dying
  1170  	stop(c, p)
  1171  	err = m0.Destroy()
  1172  	c.Assert(err, jc.ErrorIsNil)
  1173  
  1174  	// add a new, dying, unprovisioned machine
  1175  	m1, err := s.addMachine()
  1176  	c.Assert(err, jc.ErrorIsNil)
  1177  	err = m1.Destroy()
  1178  	c.Assert(err, jc.ErrorIsNil)
  1179  
  1180  	// start the provisioner and wait for it to reap the useless machine
  1181  	p = s.newEnvironProvisioner(c)
  1182  	defer stop(c, p)
  1183  	s.checkNoOperations(c)
  1184  	s.waitRemoved(c, m1)
  1185  
  1186  	// verify the other one's still fine
  1187  	err = m0.Refresh()
  1188  	c.Assert(err, jc.ErrorIsNil)
  1189  	c.Assert(m0.Life(), gc.Equals, state.Dying)
  1190  }
  1191  
  1192  func (s *ProvisionerSuite) TestProvisioningRecoversAfterInvalidEnvironmentPublished(c *gc.C) {
  1193  	s.PatchValue(provisioner.GetToolsFinder, func(*apiprovisioner.State) provisioner.ToolsFinder {
  1194  		return mockToolsFinder{}
  1195  	})
  1196  	p := s.newEnvironProvisioner(c)
  1197  	defer stop(c, p)
  1198  
  1199  	// place a new machine into the state
  1200  	m, err := s.addMachine()
  1201  	c.Assert(err, jc.ErrorIsNil)
  1202  	s.checkStartInstanceNoSecureConnection(c, m)
  1203  
  1204  	s.invalidateEnvironment(c)
  1205  	s.BackingState.StartSync()
  1206  
  1207  	// create a second machine
  1208  	m, err = s.addMachine()
  1209  	c.Assert(err, jc.ErrorIsNil)
  1210  
  1211  	// the PA should create it using the old environment
  1212  	s.checkStartInstanceNoSecureConnection(c, m)
  1213  
  1214  	err = s.fixEnvironment(c)
  1215  	c.Assert(err, jc.ErrorIsNil)
  1216  
  1217  	// insert our observer
  1218  	cfgObserver := make(chan *config.Config, 1)
  1219  	provisioner.SetObserver(p, cfgObserver)
  1220  
  1221  	err = s.State.UpdateEnvironConfig(map[string]interface{}{"secret": "beef"}, nil, nil)
  1222  	c.Assert(err, jc.ErrorIsNil)
  1223  
  1224  	s.BackingState.StartSync()
  1225  
  1226  	// wait for the PA to load the new configuration
  1227  	select {
  1228  	case <-cfgObserver:
  1229  	case <-time.After(coretesting.LongWait):
  1230  		c.Fatalf("PA did not action config change")
  1231  	}
  1232  
  1233  	// create a third machine
  1234  	m, err = s.addMachine()
  1235  	c.Assert(err, jc.ErrorIsNil)
  1236  
  1237  	// the PA should create it using the new environment
  1238  	s.checkStartInstanceCustom(c, m, "beef", s.defaultConstraints, nil, nil, nil, nil, false, nil, true)
  1239  }
  1240  
  1241  type mockMachineGetter struct{}
  1242  
  1243  func (*mockMachineGetter) Machine(names.MachineTag) (*apiprovisioner.Machine, error) {
  1244  	return nil, fmt.Errorf("error")
  1245  }
  1246  
  1247  func (*mockMachineGetter) MachinesWithTransientErrors() ([]*apiprovisioner.Machine, []params.StatusResult, error) {
  1248  	return nil, nil, fmt.Errorf("error")
  1249  }
  1250  
  1251  func (s *ProvisionerSuite) TestMachineErrorsRetainInstances(c *gc.C) {
  1252  	task := s.newProvisionerTask(c, config.HarvestAll, s.Environ, s.provisioner, mockToolsFinder{})
  1253  	defer stop(c, task)
  1254  
  1255  	// create a machine
  1256  	m0, err := s.addMachine()
  1257  	c.Assert(err, jc.ErrorIsNil)
  1258  	s.checkStartInstance(c, m0)
  1259  
  1260  	// create an instance out of band
  1261  	s.startUnknownInstance(c, "999")
  1262  
  1263  	// start the provisioner and ensure it doesn't kill any instances if there are error getting machines
  1264  	task = s.newProvisionerTask(
  1265  		c,
  1266  		config.HarvestAll,
  1267  		s.Environ,
  1268  		&mockMachineGetter{},
  1269  		&mockToolsFinder{},
  1270  	)
  1271  	defer func() {
  1272  		err := task.Stop()
  1273  		c.Assert(err, gc.ErrorMatches, ".*failed to get machine.*")
  1274  	}()
  1275  	s.checkNoOperations(c)
  1276  }
  1277  
  1278  func (s *ProvisionerSuite) TestEnvironProvisionerObservesConfigChanges(c *gc.C) {
  1279  	p := s.newEnvironProvisioner(c)
  1280  	defer stop(c, p)
  1281  	s.assertProvisionerObservesConfigChanges(c, p)
  1282  }
  1283  
  1284  func (s *ProvisionerSuite) newProvisionerTask(
  1285  	c *gc.C,
  1286  	harvestingMethod config.HarvestMode,
  1287  	broker environs.InstanceBroker,
  1288  	machineGetter provisioner.MachineGetter,
  1289  	toolsFinder provisioner.ToolsFinder,
  1290  ) provisioner.ProvisionerTask {
  1291  
  1292  	machineWatcher, err := s.provisioner.WatchEnvironMachines()
  1293  	c.Assert(err, jc.ErrorIsNil)
  1294  	retryWatcher, err := s.provisioner.WatchMachineErrorRetry()
  1295  	c.Assert(err, jc.ErrorIsNil)
  1296  	auth, err := authentication.NewAPIAuthenticator(s.provisioner)
  1297  	c.Assert(err, jc.ErrorIsNil)
  1298  
  1299  	return provisioner.NewProvisionerTask(
  1300  		names.NewMachineTag("0"),
  1301  		harvestingMethod,
  1302  		machineGetter,
  1303  		toolsFinder,
  1304  		machineWatcher,
  1305  		retryWatcher,
  1306  		broker,
  1307  		auth,
  1308  		imagemetadata.ReleasedStream,
  1309  		true,
  1310  	)
  1311  }
  1312  
  1313  func (s *ProvisionerSuite) TestHarvestNoneReapsNothing(c *gc.C) {
  1314  
  1315  	task := s.newProvisionerTask(c, config.HarvestDestroyed, s.Environ, s.provisioner, mockToolsFinder{})
  1316  	defer stop(c, task)
  1317  	task.SetHarvestMode(config.HarvestNone)
  1318  
  1319  	// Create a machine and an unknown instance.
  1320  	m0, err := s.addMachine()
  1321  	c.Assert(err, jc.ErrorIsNil)
  1322  	s.checkStartInstance(c, m0)
  1323  	s.startUnknownInstance(c, "999")
  1324  
  1325  	// Mark the first machine as dead.
  1326  	c.Assert(m0.EnsureDead(), gc.IsNil)
  1327  
  1328  	// Ensure we're doing nothing.
  1329  	s.checkNoOperations(c)
  1330  }
  1331  
  1332  func (s *ProvisionerSuite) TestHarvestUnknownReapsOnlyUnknown(c *gc.C) {
  1333  
  1334  	task := s.newProvisionerTask(c,
  1335  		config.HarvestDestroyed,
  1336  		s.Environ,
  1337  		s.provisioner,
  1338  		mockToolsFinder{},
  1339  	)
  1340  	defer stop(c, task)
  1341  	task.SetHarvestMode(config.HarvestUnknown)
  1342  
  1343  	// Create a machine and an unknown instance.
  1344  	m0, err := s.addMachine()
  1345  	c.Assert(err, jc.ErrorIsNil)
  1346  	i0 := s.checkStartInstance(c, m0)
  1347  	i1 := s.startUnknownInstance(c, "999")
  1348  
  1349  	// Mark the first machine as dead.
  1350  	c.Assert(m0.EnsureDead(), gc.IsNil)
  1351  
  1352  	// When only harvesting unknown machines, only one of the machines
  1353  	// is stopped.
  1354  	s.checkStopSomeInstances(c, []instance.Instance{i1}, []instance.Instance{i0})
  1355  	s.waitRemoved(c, m0)
  1356  }
  1357  
  1358  func (s *ProvisionerSuite) TestHarvestDestroyedReapsOnlyDestroyed(c *gc.C) {
  1359  
  1360  	task := s.newProvisionerTask(
  1361  		c,
  1362  		config.HarvestDestroyed,
  1363  		s.Environ,
  1364  		s.provisioner,
  1365  		mockToolsFinder{},
  1366  	)
  1367  	defer stop(c, task)
  1368  
  1369  	// Create a machine and an unknown instance.
  1370  	m0, err := s.addMachine()
  1371  	c.Assert(err, jc.ErrorIsNil)
  1372  	i0 := s.checkStartInstance(c, m0)
  1373  	i1 := s.startUnknownInstance(c, "999")
  1374  
  1375  	// Mark the first machine as dead.
  1376  	c.Assert(m0.EnsureDead(), gc.IsNil)
  1377  
  1378  	// When only harvesting destroyed machines, only one of the
  1379  	// machines is stopped.
  1380  	s.checkStopSomeInstances(c, []instance.Instance{i0}, []instance.Instance{i1})
  1381  	s.waitRemoved(c, m0)
  1382  }
  1383  
  1384  func (s *ProvisionerSuite) TestHarvestAllReapsAllTheThings(c *gc.C) {
  1385  
  1386  	task := s.newProvisionerTask(c,
  1387  		config.HarvestDestroyed,
  1388  		s.Environ,
  1389  		s.provisioner,
  1390  		mockToolsFinder{},
  1391  	)
  1392  	defer stop(c, task)
  1393  	task.SetHarvestMode(config.HarvestAll)
  1394  
  1395  	// Create a machine and an unknown instance.
  1396  	m0, err := s.addMachine()
  1397  	c.Assert(err, jc.ErrorIsNil)
  1398  	i0 := s.checkStartInstance(c, m0)
  1399  	i1 := s.startUnknownInstance(c, "999")
  1400  
  1401  	// Mark the first machine as dead.
  1402  	c.Assert(m0.EnsureDead(), gc.IsNil)
  1403  
  1404  	// Everything must die!
  1405  	s.checkStopSomeInstances(c, []instance.Instance{i0, i1}, []instance.Instance{})
  1406  	s.waitRemoved(c, m0)
  1407  }
  1408  
  1409  func (s *ProvisionerSuite) TestProvisionerRetriesTransientErrors(c *gc.C) {
  1410  	s.PatchValue(&apiserverprovisioner.ErrorRetryWaitDelay, 5*time.Millisecond)
  1411  	e := &mockBroker{Environ: s.Environ, retryCount: make(map[string]int)}
  1412  	task := s.newProvisionerTask(c, config.HarvestAll, e, s.provisioner, mockToolsFinder{})
  1413  	defer stop(c, task)
  1414  
  1415  	// Provision some machines, some will be started first time,
  1416  	// another will require retries.
  1417  	m1, err := s.addMachine()
  1418  	c.Assert(err, jc.ErrorIsNil)
  1419  	s.checkStartInstance(c, m1)
  1420  	m2, err := s.addMachine()
  1421  	c.Assert(err, jc.ErrorIsNil)
  1422  	s.checkStartInstance(c, m2)
  1423  	m3, err := s.addMachine()
  1424  	c.Assert(err, jc.ErrorIsNil)
  1425  	m4, err := s.addMachine()
  1426  	c.Assert(err, jc.ErrorIsNil)
  1427  
  1428  	// mockBroker will fail to start machine-3 several times;
  1429  	// keep setting the transient flag to retry until the
  1430  	// instance has started.
  1431  	thatsAllFolks := make(chan struct{})
  1432  	go func() {
  1433  		for {
  1434  			select {
  1435  			case <-thatsAllFolks:
  1436  				return
  1437  			case <-time.After(coretesting.ShortWait):
  1438  				err := m3.SetStatus(state.StatusError, "info", map[string]interface{}{"transient": true})
  1439  				c.Assert(err, jc.ErrorIsNil)
  1440  			}
  1441  		}
  1442  	}()
  1443  	s.checkStartInstance(c, m3)
  1444  	close(thatsAllFolks)
  1445  
  1446  	// Machine 4 is never provisioned.
  1447  	statusInfo, err := m4.Status()
  1448  	c.Assert(err, jc.ErrorIsNil)
  1449  	c.Assert(statusInfo.Status, gc.Equals, state.StatusError)
  1450  	_, err = m4.InstanceId()
  1451  	c.Assert(err, jc.Satisfies, errors.IsNotProvisioned)
  1452  }
  1453  
  1454  func (s *ProvisionerSuite) TestProvisionerObservesMachineJobs(c *gc.C) {
  1455  	s.PatchValue(&apiserverprovisioner.ErrorRetryWaitDelay, 5*time.Millisecond)
  1456  	broker := &mockBroker{Environ: s.Environ, retryCount: make(map[string]int)}
  1457  	task := s.newProvisionerTask(c, config.HarvestAll, broker, s.provisioner, mockToolsFinder{})
  1458  	defer stop(c, task)
  1459  
  1460  	added := s.ensureAvailability(c, 3)
  1461  	c.Assert(added, gc.HasLen, 2)
  1462  	byId := make(map[string]*state.Machine)
  1463  	for _, m := range added {
  1464  		byId[m.Id()] = m
  1465  	}
  1466  	for _, id := range broker.ids {
  1467  		s.checkStartInstance(c, byId[id])
  1468  	}
  1469  }
  1470  
  1471  type mockBroker struct {
  1472  	environs.Environ
  1473  	retryCount map[string]int
  1474  	ids        []string
  1475  }
  1476  
  1477  func (b *mockBroker) StartInstance(args environs.StartInstanceParams) (*environs.StartInstanceResult, error) {
  1478  	// All machines except machines 3, 4 are provisioned successfully the first time.
  1479  	// Machines 3 is provisioned after some attempts have been made.
  1480  	// Machine 4 is never provisioned.
  1481  	id := args.InstanceConfig.MachineId
  1482  	// record ids so we can call checkStartInstance in the appropriate order.
  1483  	b.ids = append(b.ids, id)
  1484  	retries := b.retryCount[id]
  1485  	if (id != "3" && id != "4") || retries > 2 {
  1486  		return b.Environ.StartInstance(args)
  1487  	} else {
  1488  		b.retryCount[id] = retries + 1
  1489  	}
  1490  	return nil, fmt.Errorf("error: some error")
  1491  }
  1492  
  1493  type mockToolsFinder struct {
  1494  }
  1495  
  1496  func (f mockToolsFinder) FindTools(number version.Number, series string, a string) (coretools.List, error) {
  1497  	v, err := version.ParseBinary(fmt.Sprintf("%s-%s-%s", number, series, arch.HostArch()))
  1498  	if err != nil {
  1499  		return nil, err
  1500  	}
  1501  	if a != "" {
  1502  		v.Arch = a
  1503  	}
  1504  	return coretools.List{&coretools.Tools{Version: v}}, nil
  1505  }