github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/worker/provisioner/provisioner_test.go (about)

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