github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/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  	"path/filepath"
     9  	"runtime"
    10  	"time"
    11  
    12  	"github.com/juju/errors"
    13  	gitjujutesting "github.com/juju/testing"
    14  	jc "github.com/juju/testing/checkers"
    15  	"github.com/juju/utils/arch"
    16  	"github.com/juju/utils/series"
    17  	gc "gopkg.in/check.v1"
    18  	"gopkg.in/juju/names.v2"
    19  
    20  	"github.com/juju/juju/agent"
    21  	"github.com/juju/juju/container"
    22  	"github.com/juju/juju/container/kvm/mock"
    23  	kvmtesting "github.com/juju/juju/container/kvm/testing"
    24  	"github.com/juju/juju/environs"
    25  	"github.com/juju/juju/instance"
    26  	"github.com/juju/juju/network"
    27  	"github.com/juju/juju/state"
    28  	coretesting "github.com/juju/juju/testing"
    29  	jujuversion "github.com/juju/juju/version"
    30  	"github.com/juju/juju/worker/provisioner"
    31  )
    32  
    33  type kvmSuite struct {
    34  	kvmtesting.TestSuite
    35  	events     chan mock.Event
    36  	eventsDone chan struct{}
    37  }
    38  
    39  type kvmBrokerSuite struct {
    40  	kvmSuite
    41  	broker      environs.InstanceBroker
    42  	agentConfig agent.Config
    43  	api         *fakeAPI
    44  }
    45  
    46  var _ = gc.Suite(&kvmBrokerSuite{})
    47  
    48  func (s *kvmSuite) SetUpTest(c *gc.C) {
    49  	if runtime.GOOS == "windows" {
    50  		c.Skip("Skipping kvm tests on windows")
    51  	}
    52  	s.TestSuite.SetUpTest(c)
    53  	s.events = make(chan mock.Event)
    54  	s.eventsDone = make(chan struct{})
    55  	go func() {
    56  		defer close(s.eventsDone)
    57  		for event := range s.events {
    58  			c.Output(3, fmt.Sprintf("kvm event: <%s, %s>", event.Action, event.InstanceId))
    59  		}
    60  	}()
    61  	s.TestSuite.ContainerFactory.AddListener(s.events)
    62  }
    63  
    64  func (s *kvmSuite) TearDownTest(c *gc.C) {
    65  	close(s.events)
    66  	<-s.eventsDone
    67  	s.TestSuite.TearDownTest(c)
    68  }
    69  
    70  func (s *kvmBrokerSuite) SetUpTest(c *gc.C) {
    71  	if runtime.GOOS == "windows" {
    72  		c.Skip("Skipping kvm tests on windows")
    73  	}
    74  	s.kvmSuite.SetUpTest(c)
    75  	var err error
    76  	s.agentConfig, err = agent.NewAgentConfig(
    77  		agent.AgentConfigParams{
    78  			Paths:             agent.NewPathsWithDefaults(agent.Paths{DataDir: "/not/used/here"}),
    79  			Tag:               names.NewUnitTag("ubuntu/1"),
    80  			UpgradedToVersion: jujuversion.Current,
    81  			Password:          "dummy-secret",
    82  			Nonce:             "nonce",
    83  			APIAddresses:      []string{"10.0.0.1:1234"},
    84  			CACert:            coretesting.CACert,
    85  			Controller:        coretesting.ControllerTag,
    86  			Model:             coretesting.ModelTag,
    87  		})
    88  	c.Assert(err, jc.ErrorIsNil)
    89  	s.api = NewFakeAPI()
    90  	managerConfig := container.ManagerConfig{container.ConfigModelUUID: coretesting.ModelTag.Id()}
    91  	s.broker, err = provisioner.NewKvmBroker(s.api, s.agentConfig, managerConfig)
    92  	c.Assert(err, jc.ErrorIsNil)
    93  }
    94  
    95  func (s *kvmBrokerSuite) startInstance(c *gc.C, machineId string) *environs.StartInstanceResult {
    96  	return callStartInstance(c, s, s.broker, machineId)
    97  }
    98  
    99  func (s *kvmBrokerSuite) maintainInstance(c *gc.C, machineId string) {
   100  	callMaintainInstance(c, s, s.broker, machineId)
   101  }
   102  
   103  func (s *kvmBrokerSuite) TestStartInstance(c *gc.C) {
   104  	machineId := "1/kvm/0"
   105  	result := s.startInstance(c, machineId)
   106  	s.api.CheckCalls(c, []gitjujutesting.StubCall{{
   107  		FuncName: "ContainerConfig",
   108  	}, {
   109  		FuncName: "PrepareContainerInterfaceInfo",
   110  		Args:     []interface{}{names.NewMachineTag("1-kvm-0")},
   111  	}})
   112  	c.Assert(result.Instance.Id(), gc.Equals, instance.Id("juju-06f00d-1-kvm-0"))
   113  	s.assertResults(c, result)
   114  }
   115  
   116  func (s *kvmBrokerSuite) TestMaintainInstanceAddress(c *gc.C) {
   117  	machineId := "1/kvm/0"
   118  	result := s.startInstance(c, machineId)
   119  	s.api.ResetCalls()
   120  
   121  	s.maintainInstance(c, machineId)
   122  	s.api.CheckCalls(c, []gitjujutesting.StubCall{})
   123  	c.Assert(result.Instance.Id(), gc.Equals, instance.Id("juju-06f00d-1-kvm-0"))
   124  	s.assertResults(c, result)
   125  }
   126  
   127  func (s *kvmBrokerSuite) TestStopInstance(c *gc.C) {
   128  	result0 := s.startInstance(c, "1/kvm/0")
   129  	result1 := s.startInstance(c, "1/kvm/1")
   130  	result2 := s.startInstance(c, "1/kvm/2")
   131  
   132  	err := s.broker.StopInstances(result0.Instance.Id())
   133  	c.Assert(err, jc.ErrorIsNil)
   134  	s.assertResults(c, result1, result2)
   135  	c.Assert(s.kvmContainerDir(result0), jc.DoesNotExist)
   136  	c.Assert(s.kvmRemovedContainerDir(result0), jc.IsDirectory)
   137  
   138  	err = s.broker.StopInstances(result1.Instance.Id(), result2.Instance.Id())
   139  	c.Assert(err, jc.ErrorIsNil)
   140  	s.assertNoResults(c)
   141  }
   142  
   143  func (s *kvmBrokerSuite) TestAllInstances(c *gc.C) {
   144  	result0 := s.startInstance(c, "1/kvm/0")
   145  	result1 := s.startInstance(c, "1/kvm/1")
   146  	s.assertResults(c, result0, result1)
   147  
   148  	err := s.broker.StopInstances(result1.Instance.Id())
   149  	c.Assert(err, jc.ErrorIsNil)
   150  	result2 := s.startInstance(c, "1/kvm/2")
   151  	s.assertResults(c, result0, result2)
   152  }
   153  
   154  func (s *kvmBrokerSuite) assertResults(c *gc.C, results ...*environs.StartInstanceResult) {
   155  	assertInstancesStarted(c, s.broker, results...)
   156  }
   157  
   158  func (s *kvmBrokerSuite) assertNoResults(c *gc.C) {
   159  	s.assertResults(c)
   160  }
   161  
   162  func (s *kvmBrokerSuite) kvmContainerDir(result *environs.StartInstanceResult) string {
   163  	inst := result.Instance
   164  	return filepath.Join(s.ContainerDir, string(inst.Id()))
   165  }
   166  
   167  func (s *kvmBrokerSuite) kvmRemovedContainerDir(result *environs.StartInstanceResult) string {
   168  	inst := result.Instance
   169  	return filepath.Join(s.RemovedDir, string(inst.Id()))
   170  }
   171  
   172  func (s *kvmBrokerSuite) TestStartInstancePopulatesNetworkInfo(c *gc.C) {
   173  	patchResolvConf(s, c)
   174  
   175  	result := s.startInstance(c, "1/kvm/42")
   176  	c.Assert(result.NetworkInfo, gc.HasLen, 1)
   177  	iface := result.NetworkInfo[0]
   178  	c.Assert(iface, jc.DeepEquals, network.InterfaceInfo{
   179  		DeviceIndex:         0,
   180  		CIDR:                "0.1.2.0/24",
   181  		InterfaceName:       "dummy0",
   182  		ParentInterfaceName: "virbr0",
   183  		MACAddress:          "aa:bb:cc:dd:ee:ff",
   184  		Address:             network.NewAddress("0.1.2.3"),
   185  		GatewayAddress:      network.NewAddress("0.1.2.1"),
   186  		DNSServers:          network.NewAddresses("ns1.dummy", "ns2.dummy"),
   187  		DNSSearchDomains:    []string{"dummy", "invalid"},
   188  	})
   189  }
   190  
   191  func (s *kvmBrokerSuite) TestStartInstancePopulatesFallbackNetworkInfo(c *gc.C) {
   192  	patchResolvConf(s, c)
   193  
   194  	s.api.SetErrors(
   195  		nil, // ContainerConfig succeeds
   196  		errors.NotSupportedf("container address allocation"),
   197  	)
   198  	result := s.startInstance(c, "1/kvm/2")
   199  
   200  	c.Assert(result.NetworkInfo, jc.DeepEquals, []network.InterfaceInfo{{
   201  		DeviceIndex:         0,
   202  		InterfaceName:       "eth0",
   203  		InterfaceType:       network.EthernetInterface,
   204  		ConfigType:          network.ConfigDHCP,
   205  		ParentInterfaceName: "virbr0",
   206  		DNSServers:          network.NewAddresses("ns1.dummy", "ns2.dummy"),
   207  		DNSSearchDomains:    []string{"dummy", "invalid"},
   208  	}})
   209  }
   210  
   211  type kvmProvisionerSuite struct {
   212  	CommonProvisionerSuite
   213  	kvmSuite
   214  	events chan mock.Event
   215  }
   216  
   217  var _ = gc.Suite(&kvmProvisionerSuite{})
   218  
   219  func (s *kvmProvisionerSuite) SetUpSuite(c *gc.C) {
   220  	if runtime.GOOS == "windows" {
   221  		c.Skip("Skipping kvm tests on windows")
   222  	}
   223  	s.CommonProvisionerSuite.SetUpSuite(c)
   224  	s.kvmSuite.SetUpSuite(c)
   225  }
   226  
   227  func (s *kvmProvisionerSuite) TearDownSuite(c *gc.C) {
   228  	s.kvmSuite.TearDownSuite(c)
   229  	s.CommonProvisionerSuite.TearDownSuite(c)
   230  }
   231  
   232  func (s *kvmProvisionerSuite) SetUpTest(c *gc.C) {
   233  	s.CommonProvisionerSuite.SetUpTest(c)
   234  	s.kvmSuite.SetUpTest(c)
   235  
   236  	s.events = make(chan mock.Event, 25)
   237  	s.ContainerFactory.AddListener(s.events)
   238  }
   239  
   240  func (s *kvmProvisionerSuite) nextEvent(c *gc.C) mock.Event {
   241  	select {
   242  	case event := <-s.events:
   243  		return event
   244  	case <-time.After(coretesting.LongWait):
   245  		c.Fatalf("no event arrived")
   246  	}
   247  	panic("not reachable")
   248  }
   249  
   250  func (s *kvmProvisionerSuite) expectStarted(c *gc.C, machine *state.Machine) string {
   251  	s.State.StartSync()
   252  	event := s.nextEvent(c)
   253  	c.Assert(event.Action, gc.Equals, mock.Started)
   254  	err := machine.Refresh()
   255  	c.Assert(err, jc.ErrorIsNil)
   256  	s.waitInstanceId(c, machine, instance.Id(event.InstanceId))
   257  	return event.InstanceId
   258  }
   259  
   260  func (s *kvmProvisionerSuite) expectStopped(c *gc.C, instId string) {
   261  	s.State.StartSync()
   262  	event := s.nextEvent(c)
   263  	c.Assert(event.Action, gc.Equals, mock.Stopped)
   264  	c.Assert(event.InstanceId, gc.Equals, instId)
   265  }
   266  
   267  func (s *kvmProvisionerSuite) expectNoEvents(c *gc.C) {
   268  	select {
   269  	case event := <-s.events:
   270  		c.Fatalf("unexpected event %#v", event)
   271  	case <-time.After(coretesting.ShortWait):
   272  		return
   273  	}
   274  }
   275  
   276  func (s *kvmProvisionerSuite) TearDownTest(c *gc.C) {
   277  	close(s.events)
   278  	s.kvmSuite.TearDownTest(c)
   279  	s.CommonProvisionerSuite.TearDownTest(c)
   280  }
   281  
   282  func (s *kvmProvisionerSuite) newKvmProvisioner(c *gc.C) provisioner.Provisioner {
   283  	machineTag := names.NewMachineTag("0")
   284  	agentConfig := s.AgentConfigForTag(c, machineTag)
   285  	managerConfig := container.ManagerConfig{container.ConfigModelUUID: coretesting.ModelTag.Id()}
   286  	broker, err := provisioner.NewKvmBroker(s.provisioner, agentConfig, managerConfig)
   287  	c.Assert(err, jc.ErrorIsNil)
   288  	toolsFinder := (*provisioner.GetToolsFinder)(s.provisioner)
   289  	w, err := provisioner.NewContainerProvisioner(instance.KVM, s.provisioner, agentConfig, broker, toolsFinder)
   290  	c.Assert(err, jc.ErrorIsNil)
   291  	return w
   292  }
   293  
   294  func (s *kvmProvisionerSuite) TestProvisionerStartStop(c *gc.C) {
   295  	p := s.newKvmProvisioner(c)
   296  	stop(c, p)
   297  }
   298  
   299  func (s *kvmProvisionerSuite) TestDoesNotStartEnvironMachines(c *gc.C) {
   300  	p := s.newKvmProvisioner(c)
   301  	defer stop(c, p)
   302  
   303  	// Check that an instance is not provisioned when the machine is created.
   304  	_, err := s.State.AddMachine(series.LatestLts(), state.JobHostUnits)
   305  	c.Assert(err, jc.ErrorIsNil)
   306  
   307  	s.expectNoEvents(c)
   308  }
   309  
   310  func (s *kvmProvisionerSuite) TestDoesNotHaveRetryWatcher(c *gc.C) {
   311  	p := s.newKvmProvisioner(c)
   312  	defer stop(c, p)
   313  
   314  	w, err := provisioner.GetRetryWatcher(p)
   315  	c.Assert(w, gc.IsNil)
   316  	c.Assert(err, jc.Satisfies, errors.IsNotImplemented)
   317  }
   318  
   319  func (s *kvmProvisionerSuite) addContainer(c *gc.C) *state.Machine {
   320  	template := state.MachineTemplate{
   321  		Series: series.LatestLts(),
   322  		Jobs:   []state.MachineJob{state.JobHostUnits},
   323  	}
   324  	container, err := s.State.AddMachineInsideMachine(template, "0", instance.KVM)
   325  	c.Assert(err, jc.ErrorIsNil)
   326  	return container
   327  }
   328  
   329  func (s *kvmProvisionerSuite) TestContainerStartedAndStopped(c *gc.C) {
   330  	if arch.NormaliseArch(runtime.GOARCH) != arch.AMD64 {
   331  		c.Skip("Test only enabled on amd64, see bug lp:1572145")
   332  	}
   333  	p := s.newKvmProvisioner(c)
   334  	defer stop(c, p)
   335  
   336  	container := s.addContainer(c)
   337  
   338  	instId := s.expectStarted(c, container)
   339  
   340  	// ...and removed, along with the machine, when the machine is Dead.
   341  	c.Assert(container.EnsureDead(), gc.IsNil)
   342  	s.expectStopped(c, instId)
   343  	s.waitForRemovalMark(c, container)
   344  }
   345  
   346  func (s *kvmProvisionerSuite) TestKVMProvisionerObservesConfigChanges(c *gc.C) {
   347  	p := s.newKvmProvisioner(c)
   348  	defer stop(c, p)
   349  	s.assertProvisionerObservesConfigChanges(c, p)
   350  }