github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/worker/provisioner/provisioner_test.go (about)

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