github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/worker/provisioner/kvm-broker_test.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package provisioner_test
     5  
     6  import (
     7  	"fmt"
     8  	"io/ioutil"
     9  	"net"
    10  	"path/filepath"
    11  	"runtime"
    12  	"time"
    13  
    14  	"github.com/juju/errors"
    15  	"github.com/juju/names"
    16  	gitjujutesting "github.com/juju/testing"
    17  	jc "github.com/juju/testing/checkers"
    18  	"github.com/juju/utils/arch"
    19  	gc "gopkg.in/check.v1"
    20  
    21  	"github.com/juju/juju/agent"
    22  	"github.com/juju/juju/cloudconfig/instancecfg"
    23  	"github.com/juju/juju/constraints"
    24  	"github.com/juju/juju/container"
    25  	"github.com/juju/juju/container/kvm/mock"
    26  	kvmtesting "github.com/juju/juju/container/kvm/testing"
    27  	"github.com/juju/juju/environs"
    28  	"github.com/juju/juju/feature"
    29  	"github.com/juju/juju/instance"
    30  	instancetest "github.com/juju/juju/instance/testing"
    31  	jujutesting "github.com/juju/juju/juju/testing"
    32  	"github.com/juju/juju/network"
    33  	"github.com/juju/juju/state"
    34  	coretesting "github.com/juju/juju/testing"
    35  	coretools "github.com/juju/juju/tools"
    36  	"github.com/juju/juju/version"
    37  	"github.com/juju/juju/worker/provisioner"
    38  )
    39  
    40  type kvmSuite struct {
    41  	kvmtesting.TestSuite
    42  	events     chan mock.Event
    43  	eventsDone chan struct{}
    44  }
    45  
    46  type kvmBrokerSuite struct {
    47  	kvmSuite
    48  	broker      environs.InstanceBroker
    49  	agentConfig agent.Config
    50  	api         *fakeAPI
    51  }
    52  
    53  var _ = gc.Suite(&kvmBrokerSuite{})
    54  
    55  func (s *kvmSuite) SetUpTest(c *gc.C) {
    56  	if runtime.GOOS == "windows" {
    57  		c.Skip("Skipping kvm tests on windows")
    58  	}
    59  	s.TestSuite.SetUpTest(c)
    60  	s.events = make(chan mock.Event)
    61  	s.eventsDone = make(chan struct{})
    62  	go func() {
    63  		defer close(s.eventsDone)
    64  		for event := range s.events {
    65  			c.Output(3, fmt.Sprintf("kvm event: <%s, %s>", event.Action, event.InstanceId))
    66  		}
    67  	}()
    68  	s.TestSuite.ContainerFactory.AddListener(s.events)
    69  }
    70  
    71  func (s *kvmSuite) TearDownTest(c *gc.C) {
    72  	close(s.events)
    73  	<-s.eventsDone
    74  	s.TestSuite.TearDownTest(c)
    75  }
    76  
    77  func (s *kvmBrokerSuite) SetUpTest(c *gc.C) {
    78  	if runtime.GOOS == "windows" {
    79  		c.Skip("Skipping kvm tests on windows")
    80  	}
    81  	s.kvmSuite.SetUpTest(c)
    82  	var err error
    83  	s.agentConfig, err = agent.NewAgentConfig(
    84  		agent.AgentConfigParams{
    85  			Paths:             agent.NewPathsWithDefaults(agent.Paths{DataDir: "/not/used/here"}),
    86  			Tag:               names.NewUnitTag("ubuntu/1"),
    87  			UpgradedToVersion: version.Current.Number,
    88  			Password:          "dummy-secret",
    89  			Nonce:             "nonce",
    90  			APIAddresses:      []string{"10.0.0.1:1234"},
    91  			CACert:            coretesting.CACert,
    92  			Environment:       coretesting.EnvironmentTag,
    93  		})
    94  	c.Assert(err, jc.ErrorIsNil)
    95  	s.api = NewFakeAPI()
    96  	managerConfig := container.ManagerConfig{container.ConfigName: "juju"}
    97  	s.broker, err = provisioner.NewKvmBroker(s.api, s.agentConfig, managerConfig, false)
    98  	c.Assert(err, jc.ErrorIsNil)
    99  }
   100  
   101  func (s *kvmBrokerSuite) instanceConfig(c *gc.C, machineId string) *instancecfg.InstanceConfig {
   102  	machineNonce := "fake-nonce"
   103  	// To isolate the tests from the host's architecture, we override it here.
   104  	s.PatchValue(&arch.HostArch, func() string { return arch.AMD64 })
   105  	stateInfo := jujutesting.FakeStateInfo(machineId)
   106  	apiInfo := jujutesting.FakeAPIInfo(machineId)
   107  	instanceConfig, err := instancecfg.NewInstanceConfig(machineId, machineNonce, "released", "quantal", true, nil, stateInfo, apiInfo)
   108  	c.Assert(err, jc.ErrorIsNil)
   109  	return instanceConfig
   110  }
   111  
   112  func (s *kvmBrokerSuite) startInstance(c *gc.C, machineId string) instance.Instance {
   113  	instanceConfig := s.instanceConfig(c, machineId)
   114  	cons := constraints.Value{}
   115  	possibleTools := coretools.List{&coretools.Tools{
   116  		Version: version.MustParseBinary("2.3.4-quantal-amd64"),
   117  		URL:     "http://tools.testing.invalid/2.3.4-quantal-amd64.tgz",
   118  	}}
   119  	result, err := s.broker.StartInstance(environs.StartInstanceParams{
   120  		Constraints:    cons,
   121  		Tools:          possibleTools,
   122  		InstanceConfig: instanceConfig,
   123  	})
   124  	c.Assert(err, jc.ErrorIsNil)
   125  	return result.Instance
   126  }
   127  
   128  func (s *kvmBrokerSuite) maintainInstance(c *gc.C, machineId string) {
   129  	machineNonce := "fake-nonce"
   130  	stateInfo := jujutesting.FakeStateInfo(machineId)
   131  	apiInfo := jujutesting.FakeAPIInfo(machineId)
   132  	instanceConfig, err := instancecfg.NewInstanceConfig(machineId, machineNonce, "released", "quantal", true, nil, stateInfo, apiInfo)
   133  	c.Assert(err, jc.ErrorIsNil)
   134  	cons := constraints.Value{}
   135  	possibleTools := coretools.List{&coretools.Tools{
   136  		Version: version.MustParseBinary("2.3.4-quantal-amd64"),
   137  		URL:     "http://tools.testing.invalid/2.3.4-quantal-amd64.tgz",
   138  	}}
   139  	err = s.broker.MaintainInstance(environs.StartInstanceParams{
   140  		Constraints:    cons,
   141  		Tools:          possibleTools,
   142  		InstanceConfig: instanceConfig,
   143  	})
   144  	c.Assert(err, jc.ErrorIsNil)
   145  }
   146  
   147  func (s *kvmBrokerSuite) TestStartInstance(c *gc.C) {
   148  	machineId := "1/kvm/0"
   149  	s.SetFeatureFlags(feature.AddressAllocation)
   150  	kvm := s.startInstance(c, machineId)
   151  	s.api.CheckCalls(c, []gitjujutesting.StubCall{{
   152  		FuncName: "PrepareContainerInterfaceInfo",
   153  		Args:     []interface{}{names.NewMachineTag("1-kvm-0")},
   154  	}, {
   155  		FuncName: "ContainerConfig",
   156  	}})
   157  	c.Assert(kvm.Id(), gc.Equals, instance.Id("juju-machine-1-kvm-0"))
   158  	s.assertInstances(c, kvm)
   159  }
   160  
   161  func (s *kvmBrokerSuite) TestStartInstanceAddressAllocationDisabled(c *gc.C) {
   162  	machineId := "1/kvm/0"
   163  	kvm := s.startInstance(c, machineId)
   164  	s.api.CheckCalls(c, []gitjujutesting.StubCall{{
   165  		FuncName: "ContainerConfig",
   166  	}})
   167  	c.Assert(kvm.Id(), gc.Equals, instance.Id("juju-machine-1-kvm-0"))
   168  	s.assertInstances(c, kvm)
   169  }
   170  
   171  func (s *kvmBrokerSuite) TestMaintainInstance(c *gc.C) {
   172  	machineId := "1/kvm/0"
   173  	s.SetFeatureFlags(feature.AddressAllocation)
   174  	kvm := s.startInstance(c, machineId)
   175  	s.api.ResetCalls()
   176  
   177  	s.maintainInstance(c, machineId)
   178  	s.api.CheckCalls(c, []gitjujutesting.StubCall{{
   179  		FuncName: "GetContainerInterfaceInfo",
   180  		Args:     []interface{}{names.NewMachineTag("1-kvm-0")},
   181  	}})
   182  	c.Assert(kvm.Id(), gc.Equals, instance.Id("juju-machine-1-kvm-0"))
   183  	s.assertInstances(c, kvm)
   184  }
   185  
   186  func (s *kvmBrokerSuite) TestMaintainInstanceAddressAllocationDisabled(c *gc.C) {
   187  	machineId := "1/kvm/0"
   188  	kvm := s.startInstance(c, machineId)
   189  	s.api.ResetCalls()
   190  
   191  	s.maintainInstance(c, machineId)
   192  	s.api.CheckCalls(c, []gitjujutesting.StubCall{})
   193  	c.Assert(kvm.Id(), gc.Equals, instance.Id("juju-machine-1-kvm-0"))
   194  	s.assertInstances(c, kvm)
   195  }
   196  
   197  func (s *kvmBrokerSuite) TestStopInstance(c *gc.C) {
   198  	kvm0 := s.startInstance(c, "1/kvm/0")
   199  	kvm1 := s.startInstance(c, "1/kvm/1")
   200  	kvm2 := s.startInstance(c, "1/kvm/2")
   201  
   202  	err := s.broker.StopInstances(kvm0.Id())
   203  	c.Assert(err, jc.ErrorIsNil)
   204  	s.assertInstances(c, kvm1, kvm2)
   205  	c.Assert(s.kvmContainerDir(kvm0), jc.DoesNotExist)
   206  	c.Assert(s.kvmRemovedContainerDir(kvm0), jc.IsDirectory)
   207  
   208  	err = s.broker.StopInstances(kvm1.Id(), kvm2.Id())
   209  	c.Assert(err, jc.ErrorIsNil)
   210  	s.assertInstances(c)
   211  }
   212  
   213  func (s *kvmBrokerSuite) TestAllInstances(c *gc.C) {
   214  	kvm0 := s.startInstance(c, "1/kvm/0")
   215  	kvm1 := s.startInstance(c, "1/kvm/1")
   216  	s.assertInstances(c, kvm0, kvm1)
   217  
   218  	err := s.broker.StopInstances(kvm1.Id())
   219  	c.Assert(err, jc.ErrorIsNil)
   220  	kvm2 := s.startInstance(c, "1/kvm/2")
   221  	s.assertInstances(c, kvm0, kvm2)
   222  }
   223  
   224  func (s *kvmBrokerSuite) assertInstances(c *gc.C, inst ...instance.Instance) {
   225  	results, err := s.broker.AllInstances()
   226  	c.Assert(err, jc.ErrorIsNil)
   227  	instancetest.MatchInstances(c, results, inst...)
   228  }
   229  
   230  func (s *kvmBrokerSuite) kvmContainerDir(inst instance.Instance) string {
   231  	return filepath.Join(s.ContainerDir, string(inst.Id()))
   232  }
   233  
   234  func (s *kvmBrokerSuite) kvmRemovedContainerDir(inst instance.Instance) string {
   235  	return filepath.Join(s.RemovedDir, string(inst.Id()))
   236  }
   237  
   238  func (s *kvmBrokerSuite) TestStartInstancePopulatesNetworkInfo(c *gc.C) {
   239  	s.SetFeatureFlags(feature.AddressAllocation)
   240  	s.PatchValue(provisioner.InterfaceAddrs, func(i *net.Interface) ([]net.Addr, error) {
   241  		return []net.Addr{&fakeAddr{"0.1.2.1/24"}}, nil
   242  	})
   243  	fakeResolvConf := filepath.Join(c.MkDir(), "resolv.conf")
   244  	err := ioutil.WriteFile(fakeResolvConf, []byte("nameserver ns1.dummy\n"), 0644)
   245  	c.Assert(err, jc.ErrorIsNil)
   246  	s.PatchValue(provisioner.ResolvConf, fakeResolvConf)
   247  
   248  	instanceConfig := s.instanceConfig(c, "42")
   249  	possibleTools := coretools.List{&coretools.Tools{
   250  		Version: version.MustParseBinary("2.3.4-quantal-amd64"),
   251  		URL:     "http://tools.testing.invalid/2.3.4-quantal-amd64.tgz",
   252  	}}
   253  	result, err := s.broker.StartInstance(environs.StartInstanceParams{
   254  		Constraints:    constraints.Value{},
   255  		Tools:          possibleTools,
   256  		InstanceConfig: instanceConfig,
   257  	})
   258  	c.Assert(err, jc.ErrorIsNil)
   259  	c.Assert(result.NetworkInfo, gc.HasLen, 1)
   260  	iface := result.NetworkInfo[0]
   261  	c.Assert(err, jc.ErrorIsNil)
   262  	c.Assert(iface, jc.DeepEquals, network.InterfaceInfo{
   263  		DeviceIndex:    0,
   264  		CIDR:           "0.1.2.0/24",
   265  		ConfigType:     network.ConfigStatic,
   266  		InterfaceName:  "eth0", // generated from the device index.
   267  		MACAddress:     "aa:bb:cc:dd:ee:ff",
   268  		DNSServers:     network.NewAddresses("ns1.dummy"),
   269  		Address:        network.NewAddress("0.1.2.3"),
   270  		GatewayAddress: network.NewAddress("0.1.2.1"),
   271  		NetworkName:    network.DefaultPrivate,
   272  		ProviderId:     network.DefaultProviderId,
   273  	})
   274  }
   275  
   276  type kvmProvisionerSuite struct {
   277  	CommonProvisionerSuite
   278  	kvmSuite
   279  	events chan mock.Event
   280  }
   281  
   282  var _ = gc.Suite(&kvmProvisionerSuite{})
   283  
   284  func (s *kvmProvisionerSuite) SetUpSuite(c *gc.C) {
   285  	if runtime.GOOS == "windows" {
   286  		c.Skip("Skipping kvm tests on windows")
   287  	}
   288  	s.CommonProvisionerSuite.SetUpSuite(c)
   289  	s.kvmSuite.SetUpSuite(c)
   290  }
   291  
   292  func (s *kvmProvisionerSuite) TearDownSuite(c *gc.C) {
   293  	s.kvmSuite.TearDownSuite(c)
   294  	s.CommonProvisionerSuite.TearDownSuite(c)
   295  }
   296  
   297  func (s *kvmProvisionerSuite) SetUpTest(c *gc.C) {
   298  	s.CommonProvisionerSuite.SetUpTest(c)
   299  	s.kvmSuite.SetUpTest(c)
   300  
   301  	s.events = make(chan mock.Event, 25)
   302  	s.ContainerFactory.AddListener(s.events)
   303  }
   304  
   305  func (s *kvmProvisionerSuite) nextEvent(c *gc.C) mock.Event {
   306  	select {
   307  	case event := <-s.events:
   308  		return event
   309  	case <-time.After(coretesting.LongWait):
   310  		c.Fatalf("no event arrived")
   311  	}
   312  	panic("not reachable")
   313  }
   314  
   315  func (s *kvmProvisionerSuite) expectStarted(c *gc.C, machine *state.Machine) string {
   316  	// This check in particular leads to tests just hanging
   317  	// indefinitely quite often on i386.
   318  	coretesting.SkipIfI386(c, "lp:1425569")
   319  
   320  	s.State.StartSync()
   321  	event := s.nextEvent(c)
   322  	c.Assert(event.Action, gc.Equals, mock.Started)
   323  	err := machine.Refresh()
   324  	c.Assert(err, jc.ErrorIsNil)
   325  	s.waitInstanceId(c, machine, instance.Id(event.InstanceId))
   326  	return event.InstanceId
   327  }
   328  
   329  func (s *kvmProvisionerSuite) expectStopped(c *gc.C, instId string) {
   330  	// This check in particular leads to tests just hanging
   331  	// indefinitely quite often on i386.
   332  	coretesting.SkipIfI386(c, "lp:1425569")
   333  
   334  	s.State.StartSync()
   335  	event := s.nextEvent(c)
   336  	c.Assert(event.Action, gc.Equals, mock.Stopped)
   337  	c.Assert(event.InstanceId, gc.Equals, instId)
   338  }
   339  
   340  func (s *kvmProvisionerSuite) expectNoEvents(c *gc.C) {
   341  	select {
   342  	case event := <-s.events:
   343  		c.Fatalf("unexpected event %#v", event)
   344  	case <-time.After(coretesting.ShortWait):
   345  		return
   346  	}
   347  }
   348  
   349  func (s *kvmProvisionerSuite) TearDownTest(c *gc.C) {
   350  	close(s.events)
   351  	s.kvmSuite.TearDownTest(c)
   352  	s.CommonProvisionerSuite.TearDownTest(c)
   353  }
   354  
   355  func (s *kvmProvisionerSuite) newKvmProvisioner(c *gc.C) provisioner.Provisioner {
   356  	machineTag := names.NewMachineTag("0")
   357  	agentConfig := s.AgentConfigForTag(c, machineTag)
   358  	managerConfig := container.ManagerConfig{container.ConfigName: "juju"}
   359  	broker, err := provisioner.NewKvmBroker(s.provisioner, agentConfig, managerConfig, false)
   360  	c.Assert(err, jc.ErrorIsNil)
   361  	toolsFinder := (*provisioner.GetToolsFinder)(s.provisioner)
   362  	return provisioner.NewContainerProvisioner(instance.KVM, s.provisioner, agentConfig, broker, toolsFinder)
   363  }
   364  
   365  func (s *kvmProvisionerSuite) TestProvisionerStartStop(c *gc.C) {
   366  	p := s.newKvmProvisioner(c)
   367  	c.Assert(p.Stop(), gc.IsNil)
   368  }
   369  
   370  func (s *kvmProvisionerSuite) TestDoesNotStartEnvironMachines(c *gc.C) {
   371  	p := s.newKvmProvisioner(c)
   372  	defer stop(c, p)
   373  
   374  	// Check that an instance is not provisioned when the machine is created.
   375  	_, err := s.State.AddMachine(coretesting.FakeDefaultSeries, state.JobHostUnits)
   376  	c.Assert(err, jc.ErrorIsNil)
   377  
   378  	s.expectNoEvents(c)
   379  }
   380  
   381  func (s *kvmProvisionerSuite) TestDoesNotHaveRetryWatcher(c *gc.C) {
   382  	p := s.newKvmProvisioner(c)
   383  	defer stop(c, p)
   384  
   385  	w, err := provisioner.GetRetryWatcher(p)
   386  	c.Assert(w, gc.IsNil)
   387  	c.Assert(err, jc.Satisfies, errors.IsNotImplemented)
   388  }
   389  
   390  func (s *kvmProvisionerSuite) addContainer(c *gc.C) *state.Machine {
   391  	template := state.MachineTemplate{
   392  		Series: coretesting.FakeDefaultSeries,
   393  		Jobs:   []state.MachineJob{state.JobHostUnits},
   394  	}
   395  	container, err := s.State.AddMachineInsideMachine(template, "0", instance.KVM)
   396  	c.Assert(err, jc.ErrorIsNil)
   397  	return container
   398  }
   399  
   400  func (s *kvmProvisionerSuite) TestContainerStartedAndStopped(c *gc.C) {
   401  	coretesting.SkipIfI386(c, "lp:1425569")
   402  
   403  	p := s.newKvmProvisioner(c)
   404  	defer stop(c, p)
   405  
   406  	container := s.addContainer(c)
   407  
   408  	instId := s.expectStarted(c, container)
   409  
   410  	// ...and removed, along with the machine, when the machine is Dead.
   411  	c.Assert(container.EnsureDead(), gc.IsNil)
   412  	s.expectStopped(c, instId)
   413  	s.waitRemoved(c, container)
   414  }
   415  
   416  func (s *kvmProvisionerSuite) TestKVMProvisionerObservesConfigChanges(c *gc.C) {
   417  	p := s.newKvmProvisioner(c)
   418  	defer stop(c, p)
   419  	s.assertProvisionerObservesConfigChanges(c, p)
   420  }