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