github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/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  	"github.com/juju/loggo"
    14  	gitjujutesting "github.com/juju/testing"
    15  	jc "github.com/juju/testing/checkers"
    16  	"github.com/juju/utils/arch"
    17  	gc "gopkg.in/check.v1"
    18  	"gopkg.in/juju/names.v2"
    19  	"gopkg.in/juju/worker.v1/workertest"
    20  
    21  	"github.com/juju/juju/agent"
    22  	"github.com/juju/juju/cloudconfig/instancecfg"
    23  	"github.com/juju/juju/container"
    24  	"github.com/juju/juju/container/kvm"
    25  	"github.com/juju/juju/container/kvm/mock"
    26  	kvmtesting "github.com/juju/juju/container/kvm/testing"
    27  	"github.com/juju/juju/core/instance"
    28  	"github.com/juju/juju/environs"
    29  	"github.com/juju/juju/environs/context"
    30  	supportedversion "github.com/juju/juju/juju/version"
    31  	"github.com/juju/juju/network"
    32  	"github.com/juju/juju/state"
    33  	coretesting "github.com/juju/juju/testing"
    34  	jujuversion "github.com/juju/juju/version"
    35  	"github.com/juju/juju/worker/provisioner"
    36  )
    37  
    38  type kvmSuite struct {
    39  	kvmtesting.TestSuite
    40  	events     chan mock.Event
    41  	eventsDone chan struct{}
    42  }
    43  
    44  type kvmBrokerSuite struct {
    45  	kvmSuite
    46  	agentConfig agent.Config
    47  	api         *fakeAPI
    48  	manager     *fakeContainerManager
    49  }
    50  
    51  var _ = gc.Suite(&kvmBrokerSuite{})
    52  
    53  func (s *kvmSuite) SetUpTest(c *gc.C) {
    54  	if runtime.GOOS == "windows" {
    55  		c.Skip("Skipping kvm tests on windows")
    56  	}
    57  	s.TestSuite.SetUpTest(c)
    58  	s.events = make(chan mock.Event)
    59  	s.eventsDone = make(chan struct{})
    60  	go func() {
    61  		defer close(s.eventsDone)
    62  		for event := range s.events {
    63  			c.Output(3, fmt.Sprintf("kvm event: <%s, %s>", event.Action, event.InstanceId))
    64  		}
    65  	}()
    66  	s.TestSuite.ContainerFactory.AddListener(s.events)
    67  }
    68  
    69  func (s *kvmSuite) TearDownTest(c *gc.C) {
    70  	close(s.events)
    71  	<-s.eventsDone
    72  	s.TestSuite.TearDownTest(c)
    73  }
    74  
    75  func (s *kvmBrokerSuite) SetUpTest(c *gc.C) {
    76  	if runtime.GOOS == "windows" {
    77  		c.Skip("Skipping kvm tests on windows")
    78  	}
    79  	s.kvmSuite.SetUpTest(c)
    80  	s.PatchValue(&provisioner.GetMachineCloudInitData, func(_ string) (map[string]interface{}, error) {
    81  		return nil, nil
    82  	})
    83  	var err error
    84  	s.agentConfig, err = agent.NewAgentConfig(
    85  		agent.AgentConfigParams{
    86  			Paths:             agent.NewPathsWithDefaults(agent.Paths{DataDir: "/not/used/here"}),
    87  			Tag:               names.NewUnitTag("ubuntu/1"),
    88  			UpgradedToVersion: jujuversion.Current,
    89  			Password:          "dummy-secret",
    90  			Nonce:             "nonce",
    91  			APIAddresses:      []string{"10.0.0.1:1234"},
    92  			CACert:            coretesting.CACert,
    93  			Controller:        coretesting.ControllerTag,
    94  			Model:             coretesting.ModelTag,
    95  		})
    96  	c.Assert(err, jc.ErrorIsNil)
    97  	s.api = NewFakeAPI()
    98  	s.manager = &fakeContainerManager{}
    99  }
   100  
   101  func (s *kvmBrokerSuite) startInstance(c *gc.C, broker environs.InstanceBroker, machineId string) (*environs.StartInstanceResult, error) {
   102  	return callStartInstance(c, s, broker, machineId)
   103  }
   104  
   105  func (s *kvmBrokerSuite) newKVMBroker(c *gc.C) (environs.InstanceBroker, error) {
   106  	managerConfig := container.ManagerConfig{container.ConfigModelUUID: coretesting.ModelTag.Id()}
   107  	manager, err := kvm.NewContainerManager(managerConfig)
   108  	c.Assert(err, jc.ErrorIsNil)
   109  	return provisioner.NewKVMBroker(s.api.PrepareHost, s.api, manager, s.agentConfig)
   110  }
   111  
   112  func (s *kvmBrokerSuite) newKVMBrokerFakeManager(c *gc.C) (environs.InstanceBroker, error) {
   113  	return provisioner.NewKVMBroker(s.api.PrepareHost, s.api, s.manager, s.agentConfig)
   114  }
   115  
   116  func (s *kvmBrokerSuite) maintainInstance(c *gc.C, broker environs.InstanceBroker, machineId string) {
   117  	callMaintainInstance(c, s, broker, machineId)
   118  }
   119  
   120  func (s *kvmBrokerSuite) TestStartInstanceWithoutNetworkChanges(c *gc.C) {
   121  	broker, brokerErr := s.newKVMBroker(c)
   122  	c.Assert(brokerErr, jc.ErrorIsNil)
   123  
   124  	machineId := "1/kvm/0"
   125  	result, err := s.startInstance(c, broker, machineId)
   126  	c.Assert(err, jc.ErrorIsNil)
   127  	s.api.CheckCalls(c, []gitjujutesting.StubCall{{
   128  		FuncName: "ContainerConfig",
   129  	}, {
   130  		FuncName: "PrepareHost",
   131  		Args:     []interface{}{names.NewMachineTag("1-kvm-0")},
   132  	}, {
   133  		FuncName: "PrepareContainerInterfaceInfo",
   134  		Args:     []interface{}{names.NewMachineTag("1-kvm-0")},
   135  	}})
   136  	c.Assert(result.Instance.Id(), gc.Equals, instance.Id("juju-06f00d-1-kvm-0"))
   137  	s.assertResults(c, broker, result)
   138  }
   139  
   140  func (s *kvmBrokerSuite) TestMaintainInstanceAddress(c *gc.C) {
   141  	broker, brokerErr := s.newKVMBroker(c)
   142  	c.Assert(brokerErr, jc.ErrorIsNil)
   143  
   144  	machineId := "1/kvm/0"
   145  	result, err := s.startInstance(c, broker, machineId)
   146  	c.Assert(err, jc.ErrorIsNil)
   147  
   148  	s.api.ResetCalls()
   149  
   150  	s.maintainInstance(c, broker, machineId)
   151  	s.api.CheckCalls(c, []gitjujutesting.StubCall{})
   152  	c.Assert(result.Instance.Id(), gc.Equals, instance.Id("juju-06f00d-1-kvm-0"))
   153  	s.assertResults(c, broker, result)
   154  }
   155  
   156  func (s *kvmBrokerSuite) TestStopInstance(c *gc.C) {
   157  	broker, brokerErr := s.newKVMBroker(c)
   158  	c.Assert(brokerErr, jc.ErrorIsNil)
   159  
   160  	result0, err0 := s.startInstance(c, broker, "1/kvm/0")
   161  	c.Assert(err0, jc.ErrorIsNil)
   162  
   163  	result1, err1 := s.startInstance(c, broker, "1/kvm/1")
   164  	c.Assert(err1, jc.ErrorIsNil)
   165  
   166  	result2, err2 := s.startInstance(c, broker, "1/kvm/2")
   167  	c.Assert(err2, jc.ErrorIsNil)
   168  
   169  	callCtx := context.NewCloudCallContext()
   170  	err := broker.StopInstances(callCtx, result0.Instance.Id())
   171  	c.Assert(err, jc.ErrorIsNil)
   172  	s.assertResults(c, broker, result1, result2)
   173  	c.Assert(s.kvmContainerDir(result0), jc.DoesNotExist)
   174  	c.Assert(s.kvmRemovedContainerDir(result0), jc.IsDirectory)
   175  
   176  	err = broker.StopInstances(callCtx, result1.Instance.Id(), result2.Instance.Id())
   177  	c.Assert(err, jc.ErrorIsNil)
   178  	s.assertNoResults(c, broker)
   179  }
   180  
   181  func (s *kvmBrokerSuite) TestAllInstances(c *gc.C) {
   182  	broker, brokerErr := s.newKVMBroker(c)
   183  	c.Assert(brokerErr, jc.ErrorIsNil)
   184  
   185  	result0, err0 := s.startInstance(c, broker, "1/kvm/0")
   186  	c.Assert(err0, jc.ErrorIsNil)
   187  
   188  	result1, err1 := s.startInstance(c, broker, "1/kvm/1")
   189  	c.Assert(err1, jc.ErrorIsNil)
   190  	s.assertResults(c, broker, result0, result1)
   191  
   192  	err := broker.StopInstances(context.NewCloudCallContext(), result1.Instance.Id())
   193  	c.Assert(err, jc.ErrorIsNil)
   194  	result2, err2 := s.startInstance(c, broker, "1/kvm/2")
   195  	c.Assert(err2, jc.ErrorIsNil)
   196  	s.assertResults(c, broker, result0, result2)
   197  }
   198  
   199  func (s *kvmBrokerSuite) assertResults(c *gc.C, broker environs.InstanceBroker, results ...*environs.StartInstanceResult) {
   200  	assertInstancesStarted(c, broker, results...)
   201  }
   202  
   203  func (s *kvmBrokerSuite) assertNoResults(c *gc.C, broker environs.InstanceBroker) {
   204  	s.assertResults(c, broker)
   205  }
   206  
   207  func (s *kvmBrokerSuite) kvmContainerDir(result *environs.StartInstanceResult) string {
   208  	inst := result.Instance
   209  	return filepath.Join(s.ContainerDir, string(inst.Id()))
   210  }
   211  
   212  func (s *kvmBrokerSuite) kvmRemovedContainerDir(result *environs.StartInstanceResult) string {
   213  	inst := result.Instance
   214  	return filepath.Join(s.RemovedDir, string(inst.Id()))
   215  }
   216  
   217  func (s *kvmBrokerSuite) TestStartInstancePopulatesNetworkInfo(c *gc.C) {
   218  	broker, brokerErr := s.newKVMBroker(c)
   219  	c.Assert(brokerErr, jc.ErrorIsNil)
   220  
   221  	patchResolvConf(s, c)
   222  
   223  	result, err := s.startInstance(c, broker, "1/kvm/42")
   224  	c.Assert(err, jc.ErrorIsNil)
   225  
   226  	c.Assert(result.NetworkInfo, gc.HasLen, 1)
   227  	iface := result.NetworkInfo[0]
   228  	c.Assert(iface, jc.DeepEquals, network.InterfaceInfo{
   229  		DeviceIndex:         0,
   230  		CIDR:                "0.1.2.0/24",
   231  		InterfaceName:       "dummy0",
   232  		ParentInterfaceName: "virbr0",
   233  		MACAddress:          "aa:bb:cc:dd:ee:ff",
   234  		Address:             network.NewAddress("0.1.2.3"),
   235  		GatewayAddress:      network.NewAddress("0.1.2.1"),
   236  		DNSServers:          network.NewAddresses("ns1.dummy", "ns2.dummy"),
   237  		DNSSearchDomains:    []string{"dummy", "invalid"},
   238  	})
   239  }
   240  
   241  func (s *kvmBrokerSuite) TestStartInstancePopulatesFallbackNetworkInfo(c *gc.C) {
   242  	broker, brokerErr := s.newKVMBroker(c)
   243  	c.Assert(brokerErr, jc.ErrorIsNil)
   244  
   245  	patchResolvConf(s, c)
   246  
   247  	s.api.SetErrors(
   248  		nil, // ContainerConfig succeeds
   249  		nil, // HostChangesForContainer succeeds
   250  		errors.NotSupportedf("container address allocation"),
   251  	)
   252  	_, err := s.startInstance(c, broker, "1/kvm/2")
   253  	c.Assert(err, gc.ErrorMatches, "container address allocation not supported")
   254  }
   255  
   256  func (s *kvmBrokerSuite) TestStartInstanceWithCloudInitUserData(c *gc.C) {
   257  	broker, brokerErr := s.newKVMBrokerFakeManager(c)
   258  	c.Assert(brokerErr, jc.ErrorIsNil)
   259  
   260  	_, err := s.startInstance(c, broker, "1/kvm/0")
   261  	c.Assert(err, jc.ErrorIsNil)
   262  
   263  	s.manager.CheckCallNames(c, "CreateContainer")
   264  	call := s.manager.Calls()[0]
   265  	c.Assert(call.Args[0], gc.FitsTypeOf, &instancecfg.InstanceConfig{})
   266  	instanceConfig := call.Args[0].(*instancecfg.InstanceConfig)
   267  	assertCloudInitUserData(instanceConfig.CloudInitUserData, map[string]interface{}{
   268  		"packages":        []interface{}{"python-keystoneclient", "python-glanceclient"},
   269  		"preruncmd":       []interface{}{"mkdir /tmp/preruncmd", "mkdir /tmp/preruncmd2"},
   270  		"postruncmd":      []interface{}{"mkdir /tmp/postruncmd", "mkdir /tmp/postruncmd2"},
   271  		"package_upgrade": false,
   272  	}, c)
   273  }
   274  
   275  func (s *kvmBrokerSuite) TestStartInstanceWithContainerInheritProperties(c *gc.C) {
   276  	s.PatchValue(&provisioner.GetMachineCloudInitData, func(_ string) (map[string]interface{}, error) {
   277  		return map[string]interface{}{
   278  			"packages":   []interface{}{"python-novaclient"},
   279  			"fake-entry": []interface{}{"testing-garbage"},
   280  			"apt": map[interface{}]interface{}{
   281  				"primary": []interface{}{
   282  					map[interface{}]interface{}{
   283  						"arches": []interface{}{"default"},
   284  						"uri":    "http://archive.ubuntu.com/ubuntu",
   285  					},
   286  				},
   287  				"security": []interface{}{
   288  					map[interface{}]interface{}{
   289  						"arches": []interface{}{"default"},
   290  						"uri":    "http://archive.ubuntu.com/ubuntu",
   291  					},
   292  				},
   293  			},
   294  			"ca-certs": map[interface{}]interface{}{
   295  				"remove-defaults": true,
   296  				"trusted":         []interface{}{"-----BEGIN CERTIFICATE-----\nYOUR-ORGS-TRUSTED-CA-CERT-HERE\n-----END CERTIFICATE-----\n"},
   297  			},
   298  		}, nil
   299  	})
   300  	s.api.fakeContainerConfig.ContainerInheritProperties = "ca-certs,apt-security"
   301  
   302  	broker, brokerErr := s.newKVMBrokerFakeManager(c)
   303  	c.Assert(brokerErr, jc.ErrorIsNil)
   304  
   305  	_, err := s.startInstance(c, broker, "1/kvm/0")
   306  	c.Assert(err, jc.ErrorIsNil)
   307  
   308  	s.manager.CheckCallNames(c, "CreateContainer")
   309  	call := s.manager.Calls()[0]
   310  	c.Assert(call.Args[0], gc.FitsTypeOf, &instancecfg.InstanceConfig{})
   311  	instanceConfig := call.Args[0].(*instancecfg.InstanceConfig)
   312  	assertCloudInitUserData(instanceConfig.CloudInitUserData, map[string]interface{}{
   313  		"packages":        []interface{}{"python-keystoneclient", "python-glanceclient"},
   314  		"preruncmd":       []interface{}{"mkdir /tmp/preruncmd", "mkdir /tmp/preruncmd2"},
   315  		"postruncmd":      []interface{}{"mkdir /tmp/postruncmd", "mkdir /tmp/postruncmd2"},
   316  		"package_upgrade": false,
   317  		"apt": map[string]interface{}{
   318  			"security": []interface{}{
   319  				map[interface{}]interface{}{
   320  					"arches": []interface{}{"default"},
   321  					"uri":    "http://archive.ubuntu.com/ubuntu",
   322  				},
   323  			},
   324  		},
   325  		"ca-certs": map[interface{}]interface{}{
   326  			"remove-defaults": true,
   327  			"trusted":         []interface{}{"-----BEGIN CERTIFICATE-----\nYOUR-ORGS-TRUSTED-CA-CERT-HERE\n-----END CERTIFICATE-----\n"},
   328  		},
   329  	}, c)
   330  }
   331  
   332  type kvmProvisionerSuite struct {
   333  	CommonProvisionerSuite
   334  	kvmSuite
   335  	events chan mock.Event
   336  }
   337  
   338  var _ = gc.Suite(&kvmProvisionerSuite{})
   339  
   340  func (s *kvmProvisionerSuite) SetUpSuite(c *gc.C) {
   341  	if runtime.GOOS == "windows" {
   342  		c.Skip("Skipping kvm tests on windows")
   343  	}
   344  	s.CommonProvisionerSuite.SetUpSuite(c)
   345  	s.kvmSuite.SetUpSuite(c)
   346  }
   347  
   348  func (s *kvmProvisionerSuite) TearDownSuite(c *gc.C) {
   349  	s.kvmSuite.TearDownSuite(c)
   350  	s.CommonProvisionerSuite.TearDownSuite(c)
   351  }
   352  
   353  func (s *kvmProvisionerSuite) SetUpTest(c *gc.C) {
   354  	s.CommonProvisionerSuite.SetUpTest(c)
   355  	s.kvmSuite.SetUpTest(c)
   356  
   357  	s.events = make(chan mock.Event, 25)
   358  	s.ContainerFactory.AddListener(s.events)
   359  }
   360  
   361  func (s *kvmProvisionerSuite) nextEvent(c *gc.C) mock.Event {
   362  	select {
   363  	case event := <-s.events:
   364  		return event
   365  	case <-time.After(coretesting.LongWait):
   366  		c.Fatalf("no event arrived")
   367  	}
   368  	panic("not reachable")
   369  }
   370  
   371  func (s *kvmProvisionerSuite) expectStarted(c *gc.C, machine *state.Machine) string {
   372  	s.State.StartSync()
   373  	event := s.nextEvent(c)
   374  	c.Assert(event.Action, gc.Equals, mock.Started)
   375  	err := machine.Refresh()
   376  	c.Assert(err, jc.ErrorIsNil)
   377  	s.waitInstanceId(c, machine, instance.Id(event.InstanceId))
   378  	return event.InstanceId
   379  }
   380  
   381  func (s *kvmProvisionerSuite) expectStopped(c *gc.C, instId string) {
   382  	s.State.StartSync()
   383  	event := s.nextEvent(c)
   384  	c.Assert(event.Action, gc.Equals, mock.Stopped)
   385  	c.Assert(event.InstanceId, gc.Equals, instId)
   386  }
   387  
   388  func (s *kvmProvisionerSuite) expectNoEvents(c *gc.C) {
   389  	select {
   390  	case event := <-s.events:
   391  		c.Fatalf("unexpected event %#v", event)
   392  	case <-time.After(coretesting.ShortWait):
   393  		return
   394  	}
   395  }
   396  
   397  func (s *kvmProvisionerSuite) TearDownTest(c *gc.C) {
   398  	close(s.events)
   399  	s.kvmSuite.TearDownTest(c)
   400  	s.CommonProvisionerSuite.TearDownTest(c)
   401  }
   402  
   403  func noopPrepareHostFunc(names.MachineTag, loggo.Logger, <-chan struct{}) error {
   404  	return nil
   405  }
   406  
   407  func (s *kvmProvisionerSuite) newKvmProvisioner(c *gc.C) provisioner.Provisioner {
   408  	machineTag := names.NewMachineTag("0")
   409  	agentConfig := s.AgentConfigForTag(c, machineTag)
   410  	manager := &fakeContainerManager{}
   411  	broker, brokerErr := provisioner.NewKVMBroker(noopPrepareHostFunc, s.provisioner, manager, agentConfig)
   412  	c.Assert(brokerErr, jc.ErrorIsNil)
   413  	toolsFinder := (*provisioner.GetToolsFinder)(s.provisioner)
   414  	w, err := provisioner.NewContainerProvisioner(instance.KVM, s.provisioner, agentConfig, broker,
   415  		toolsFinder, &mockDistributionGroupFinder{}, &credentialAPIForTest{})
   416  	c.Assert(err, jc.ErrorIsNil)
   417  	return w
   418  }
   419  
   420  func (s *kvmProvisionerSuite) TestProvisionerStartStop(c *gc.C) {
   421  	p := s.newKvmProvisioner(c)
   422  	workertest.CleanKill(c, p)
   423  }
   424  
   425  func (s *kvmProvisionerSuite) TestDoesNotStartEnvironMachines(c *gc.C) {
   426  	p := s.newKvmProvisioner(c)
   427  	defer workertest.CleanKill(c, p)
   428  
   429  	// Check that an instance is not provisioned when the machine is created.
   430  	_, err := s.State.AddMachine(supportedversion.SupportedLTS(), state.JobHostUnits)
   431  	c.Assert(err, jc.ErrorIsNil)
   432  
   433  	s.expectNoEvents(c)
   434  }
   435  
   436  func (s *kvmProvisionerSuite) TestDoesNotHaveRetryWatcher(c *gc.C) {
   437  	p := s.newKvmProvisioner(c)
   438  	defer workertest.CleanKill(c, p)
   439  
   440  	w, err := provisioner.GetRetryWatcher(p)
   441  	c.Assert(w, gc.IsNil)
   442  	c.Assert(err, jc.Satisfies, errors.IsNotImplemented)
   443  }
   444  
   445  func (s *kvmProvisionerSuite) addContainer(c *gc.C) *state.Machine {
   446  	template := state.MachineTemplate{
   447  		Series: supportedversion.SupportedLTS(),
   448  		Jobs:   []state.MachineJob{state.JobHostUnits},
   449  	}
   450  	container, err := s.State.AddMachineInsideMachine(template, "0", instance.KVM)
   451  	c.Assert(err, jc.ErrorIsNil)
   452  	return container
   453  }
   454  
   455  func (s *kvmProvisionerSuite) TestContainerStartedAndStopped(c *gc.C) {
   456  	if arch.NormaliseArch(runtime.GOARCH) != arch.AMD64 {
   457  		c.Skip("Test only enabled on amd64, see bug lp:1572145")
   458  	}
   459  	p := s.newKvmProvisioner(c)
   460  	defer workertest.CleanKill(c, p)
   461  
   462  	container := s.addContainer(c)
   463  
   464  	// TODO(jam): 2016-12-22 recent changes to check for networking changes
   465  	// when starting a container cause this test to start failing, because
   466  	// the Dummy provider does not support Networking configuration.
   467  	_, _, err := s.provisioner.HostChangesForContainer(container.MachineTag())
   468  	c.Assert(err, gc.ErrorMatches, "dummy provider network config not supported.*")
   469  	c.Skip("dummy provider doesn't support network config. https://pad.lv/1651974")
   470  	instId := s.expectStarted(c, container)
   471  
   472  	// ...and removed, along with the machine, when the machine is Dead.
   473  	c.Assert(container.EnsureDead(), gc.IsNil)
   474  	s.expectStopped(c, instId)
   475  	s.waitForRemovalMark(c, container)
   476  }
   477  
   478  func (s *kvmProvisionerSuite) TestKVMProvisionerObservesConfigChanges(c *gc.C) {
   479  	p := s.newKvmProvisioner(c)
   480  	defer workertest.CleanKill(c, p)
   481  	s.assertProvisionerObservesConfigChanges(c, p)
   482  }