github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/provisioner/lxd-broker_test.go (about)

     1  // Copyright 2013-2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package provisioner_test
     5  
     6  import (
     7  	"fmt"
     8  	"runtime"
     9  
    10  	"github.com/golang/mock/gomock"
    11  	"github.com/juju/errors"
    12  	"github.com/juju/loggo"
    13  	gitjujutesting "github.com/juju/testing"
    14  	jc "github.com/juju/testing/checkers"
    15  	"github.com/juju/utils/arch"
    16  	"github.com/juju/version"
    17  	gc "gopkg.in/check.v1"
    18  	"gopkg.in/juju/charm.v6"
    19  	"gopkg.in/juju/names.v2"
    20  
    21  	"github.com/juju/juju/agent"
    22  	apiprovisioner "github.com/juju/juju/api/provisioner"
    23  	"github.com/juju/juju/cloudconfig/instancecfg"
    24  	"github.com/juju/juju/container"
    25  	"github.com/juju/juju/container/testing"
    26  	"github.com/juju/juju/core/instance"
    27  	"github.com/juju/juju/core/lxdprofile"
    28  	"github.com/juju/juju/environs"
    29  	"github.com/juju/juju/environs/context"
    30  	"github.com/juju/juju/network"
    31  	coretesting "github.com/juju/juju/testing"
    32  	coretools "github.com/juju/juju/tools"
    33  	jujuversion "github.com/juju/juju/version"
    34  	"github.com/juju/juju/worker/provisioner"
    35  	"github.com/juju/juju/worker/provisioner/mocks"
    36  )
    37  
    38  type lxdBrokerSuite struct {
    39  	coretesting.BaseSuite
    40  	agentConfig agent.ConfigSetterWriter
    41  	api         *fakeAPI
    42  	manager     *fakeContainerManager
    43  }
    44  
    45  var _ = gc.Suite(&lxdBrokerSuite{})
    46  
    47  func (s *lxdBrokerSuite) SetUpTest(c *gc.C) {
    48  	s.BaseSuite.SetUpTest(c)
    49  	if runtime.GOOS == "windows" {
    50  		c.Skip("Skipping lxd tests on windows")
    51  	}
    52  
    53  	// To isolate the tests from the host's architecture, we override it here.
    54  	s.PatchValue(&arch.HostArch, func() string { return arch.AMD64 })
    55  
    56  	s.PatchValue(&provisioner.GetMachineCloudInitData, func(_ string) (map[string]interface{}, error) {
    57  		return nil, nil
    58  	})
    59  
    60  	var err error
    61  	s.agentConfig, err = agent.NewAgentConfig(
    62  		agent.AgentConfigParams{
    63  			Paths:             agent.NewPathsWithDefaults(agent.Paths{DataDir: "/not/used/here"}),
    64  			Tag:               names.NewMachineTag("1"),
    65  			UpgradedToVersion: jujuversion.Current,
    66  			Password:          "dummy-secret",
    67  			Nonce:             "nonce",
    68  			APIAddresses:      []string{"10.0.0.1:1234"},
    69  			CACert:            coretesting.CACert,
    70  			Controller:        coretesting.ControllerTag,
    71  			Model:             coretesting.ModelTag,
    72  		})
    73  	c.Assert(err, jc.ErrorIsNil)
    74  	s.api = NewFakeAPI()
    75  	s.manager = &fakeContainerManager{}
    76  }
    77  
    78  func (s *lxdBrokerSuite) startInstance(c *gc.C, broker environs.InstanceBroker, machineId string) (*environs.StartInstanceResult, error) {
    79  	return callStartInstance(c, s, broker, machineId)
    80  }
    81  
    82  func (s *lxdBrokerSuite) newLXDBroker(c *gc.C) (environs.InstanceBroker, error) {
    83  	return provisioner.NewLXDBroker(s.api.PrepareHost, s.api, s.manager, s.agentConfig)
    84  }
    85  
    86  func (s *lxdBrokerSuite) TestStartInstanceWithoutHostNetworkChanges(c *gc.C) {
    87  	broker, brokerErr := s.newLXDBroker(c)
    88  	c.Assert(brokerErr, jc.ErrorIsNil)
    89  	machineId := "1/lxd/0"
    90  	containerTag := names.NewMachineTag("1-lxd-0")
    91  	s.startInstance(c, broker, machineId)
    92  	s.api.CheckCalls(c, []gitjujutesting.StubCall{{
    93  		FuncName: "ContainerConfig",
    94  	}, {
    95  		FuncName: "PrepareHost",
    96  		Args:     []interface{}{containerTag},
    97  	}, {
    98  		FuncName: "PrepareContainerInterfaceInfo",
    99  		Args:     []interface{}{names.NewMachineTag("1-lxd-0")},
   100  	}, {
   101  		FuncName: "GetContainerProfileInfo",
   102  		Args:     []interface{}{names.NewMachineTag("1-lxd-0")},
   103  	}})
   104  	s.manager.CheckCallNames(c, "CreateContainer")
   105  	call := s.manager.Calls()[0]
   106  	c.Assert(call.Args[0], gc.FitsTypeOf, &instancecfg.InstanceConfig{})
   107  	instanceConfig := call.Args[0].(*instancecfg.InstanceConfig)
   108  	c.Assert(instanceConfig.ToolsList(), gc.HasLen, 1)
   109  	c.Assert(instanceConfig.ToolsList().Arches(), jc.DeepEquals, []string{"amd64"})
   110  }
   111  
   112  func (s *lxdBrokerSuite) TestStartInstancePopulatesNetworkInfo(c *gc.C) {
   113  	broker, brokerErr := s.newLXDBroker(c)
   114  	c.Assert(brokerErr, jc.ErrorIsNil)
   115  
   116  	patchResolvConf(s, c)
   117  
   118  	result, err := s.startInstance(c, broker, "1/lxd/0")
   119  	c.Assert(err, jc.ErrorIsNil)
   120  	c.Assert(result.NetworkInfo, gc.HasLen, 1)
   121  	iface := result.NetworkInfo[0]
   122  	c.Assert(iface, jc.DeepEquals, network.InterfaceInfo{
   123  		DeviceIndex:         0,
   124  		CIDR:                "0.1.2.0/24",
   125  		InterfaceName:       "dummy0",
   126  		ParentInterfaceName: "lxdbr0",
   127  		MACAddress:          "aa:bb:cc:dd:ee:ff",
   128  		Address:             network.NewAddress("0.1.2.3"),
   129  		GatewayAddress:      network.NewAddress("0.1.2.1"),
   130  		DNSServers:          network.NewAddresses("ns1.dummy", "ns2.dummy"),
   131  		DNSSearchDomains:    []string{"dummy", "invalid"},
   132  	})
   133  }
   134  
   135  func (s *lxdBrokerSuite) TestStartInstancePopulatesFallbackNetworkInfo(c *gc.C) {
   136  	broker, brokerErr := s.newLXDBroker(c)
   137  	c.Assert(brokerErr, jc.ErrorIsNil)
   138  
   139  	patchResolvConf(s, c)
   140  
   141  	s.api.SetErrors(
   142  		nil, // ContainerConfig succeeds
   143  		nil, // HostChangesForContainer succeeds
   144  		errors.NotSupportedf("container address allocation"),
   145  	)
   146  	_, err := s.startInstance(c, broker, "1/lxd/0")
   147  	c.Assert(err, gc.ErrorMatches, "container address allocation not supported")
   148  }
   149  
   150  func (s *lxdBrokerSuite) TestStartInstanceNoHostArchTools(c *gc.C) {
   151  	broker, brokerErr := s.newLXDBroker(c)
   152  	c.Assert(brokerErr, jc.ErrorIsNil)
   153  
   154  	_, err := broker.StartInstance(context.NewCloudCallContext(), environs.StartInstanceParams{
   155  		Tools: coretools.List{{
   156  			// non-host-arch tools should be filtered out by StartInstance
   157  			Version: version.MustParseBinary("2.3.4-quantal-arm64"),
   158  			URL:     "http://tools.testing.invalid/2.3.4-quantal-arm64.tgz",
   159  		}},
   160  		InstanceConfig: makeInstanceConfig(c, s, "1/lxd/0"),
   161  	})
   162  	c.Assert(err, gc.ErrorMatches, `need agent binaries for arch amd64, only found \[arm64\]`)
   163  }
   164  
   165  func (s *lxdBrokerSuite) TestStartInstanceWithCloudInitUserData(c *gc.C) {
   166  	broker, brokerErr := s.newLXDBroker(c)
   167  	c.Assert(brokerErr, jc.ErrorIsNil)
   168  
   169  	_, err := s.startInstance(c, broker, "1/lxd/0")
   170  	c.Assert(err, jc.ErrorIsNil)
   171  
   172  	s.manager.CheckCallNames(c, "CreateContainer")
   173  	call := s.manager.Calls()[0]
   174  	c.Assert(call.Args[0], gc.FitsTypeOf, &instancecfg.InstanceConfig{})
   175  	instanceConfig := call.Args[0].(*instancecfg.InstanceConfig)
   176  	assertCloudInitUserData(instanceConfig.CloudInitUserData, map[string]interface{}{
   177  		"packages":        []interface{}{"python-keystoneclient", "python-glanceclient"},
   178  		"preruncmd":       []interface{}{"mkdir /tmp/preruncmd", "mkdir /tmp/preruncmd2"},
   179  		"postruncmd":      []interface{}{"mkdir /tmp/postruncmd", "mkdir /tmp/postruncmd2"},
   180  		"package_upgrade": false,
   181  	}, c)
   182  }
   183  
   184  func (s *lxdBrokerSuite) TestStartInstanceWithContainerInheritProperties(c *gc.C) {
   185  	s.PatchValue(&provisioner.GetMachineCloudInitData, func(_ string) (map[string]interface{}, error) {
   186  		return map[string]interface{}{
   187  			"packages":   []interface{}{"python-novaclient"},
   188  			"fake-entry": []interface{}{"testing-garbage"},
   189  			"apt": map[interface{}]interface{}{
   190  				"primary": []interface{}{
   191  					map[interface{}]interface{}{
   192  						"arches": []interface{}{"default"},
   193  						"uri":    "http://archive.ubuntu.com/ubuntu",
   194  					},
   195  				},
   196  				"security": []interface{}{
   197  					map[interface{}]interface{}{
   198  						"arches": []interface{}{"default"},
   199  						"uri":    "http://archive.ubuntu.com/ubuntu",
   200  					},
   201  				},
   202  			},
   203  			"ca-certs": map[interface{}]interface{}{
   204  				"remove-defaults": true,
   205  				"trusted":         []interface{}{"-----BEGIN CERTIFICATE-----\nYOUR-ORGS-TRUSTED-CA-CERT-HERE\n-----END CERTIFICATE-----\n"},
   206  			},
   207  		}, nil
   208  	})
   209  	s.api.fakeContainerConfig.ContainerInheritProperties = "ca-certs,apt-security"
   210  
   211  	broker, brokerErr := s.newLXDBroker(c)
   212  	c.Assert(brokerErr, jc.ErrorIsNil)
   213  	_, err := s.startInstance(c, broker, "1/lxd/0")
   214  	c.Assert(err, jc.ErrorIsNil)
   215  
   216  	s.manager.CheckCallNames(c, "CreateContainer")
   217  	call := s.manager.Calls()[0]
   218  	c.Assert(call.Args[0], gc.FitsTypeOf, &instancecfg.InstanceConfig{})
   219  	instanceConfig := call.Args[0].(*instancecfg.InstanceConfig)
   220  	assertCloudInitUserData(instanceConfig.CloudInitUserData, map[string]interface{}{
   221  		"packages":        []interface{}{"python-keystoneclient", "python-glanceclient"},
   222  		"preruncmd":       []interface{}{"mkdir /tmp/preruncmd", "mkdir /tmp/preruncmd2"},
   223  		"postruncmd":      []interface{}{"mkdir /tmp/postruncmd", "mkdir /tmp/postruncmd2"},
   224  		"package_upgrade": false,
   225  		"apt": map[string]interface{}{
   226  			"security": []interface{}{
   227  				map[interface{}]interface{}{
   228  					"arches": []interface{}{"default"},
   229  					"uri":    "http://archive.ubuntu.com/ubuntu",
   230  				},
   231  			},
   232  		},
   233  		"ca-certs": map[interface{}]interface{}{
   234  			"remove-defaults": true,
   235  			"trusted": []interface{}{
   236  				"-----BEGIN CERTIFICATE-----\nYOUR-ORGS-TRUSTED-CA-CERT-HERE\n-----END CERTIFICATE-----\n"},
   237  		},
   238  	}, c)
   239  }
   240  
   241  func (s *lxdBrokerSuite) TestStartInstanceWithLXDProfile(c *gc.C) {
   242  	ctlr := gomock.NewController(c)
   243  	defer ctlr.Finish()
   244  
   245  	machineId := "1/lxd/0"
   246  	containerTag := names.NewMachineTag("1-lxd-0")
   247  
   248  	mockApi := mocks.NewMockAPICalls(ctlr)
   249  	mockApi.EXPECT().PrepareContainerInterfaceInfo(gomock.Eq(containerTag)).Return([]network.InterfaceInfo{fakeInterfaceInfo}, nil)
   250  	mockApi.EXPECT().ContainerConfig().Return(fakeContainerConfig(), nil)
   251  
   252  	put := &charm.LXDProfile{
   253  		Config: map[string]string{
   254  			"security.nesting": "true",
   255  		},
   256  		Devices: map[string]map[string]string{
   257  			"bdisk": {
   258  				"source": "/dev/loop0",
   259  				"type":   "unix-block",
   260  			},
   261  		},
   262  	}
   263  	result := &apiprovisioner.LXDProfileResult{
   264  		Config:  put.Config,
   265  		Devices: put.Devices,
   266  		Name:    "juju-test-profile",
   267  	}
   268  	mockApi.EXPECT().GetContainerProfileInfo(gomock.Eq(containerTag)).Return([]*apiprovisioner.LXDProfileResult{result}, nil)
   269  
   270  	mockManager := testing.NewMockTestLXDManager(ctlr)
   271  	mockManager.EXPECT().MaybeWriteLXDProfile("juju-test-profile", gomock.Eq(put)).Return(nil)
   272  
   273  	inst := mockInstance{id: "testinst"}
   274  	arch := "testarch"
   275  	hw := instance.HardwareCharacteristics{Arch: &arch}
   276  	mockManager.EXPECT().CreateContainer(
   277  		gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(),
   278  	).Return(&inst, &hw, nil)
   279  
   280  	broker, err := provisioner.NewLXDBroker(
   281  		func(containerTag names.MachineTag, log loggo.Logger, abort <-chan struct{}) error { return nil },
   282  		mockApi, mockManager, s.agentConfig)
   283  	c.Assert(err, jc.ErrorIsNil)
   284  
   285  	s.startInstance(c, broker, machineId)
   286  }
   287  
   288  func (s *lxdBrokerSuite) TestStartInstanceWithNoNameLXDProfile(c *gc.C) {
   289  	ctlr := gomock.NewController(c)
   290  	defer ctlr.Finish()
   291  
   292  	machineId := "1/lxd/0"
   293  	containerTag := names.NewMachineTag("1-lxd-0")
   294  
   295  	mockApi := mocks.NewMockAPICalls(ctlr)
   296  	mockApi.EXPECT().PrepareContainerInterfaceInfo(gomock.Eq(containerTag)).Return([]network.InterfaceInfo{fakeInterfaceInfo}, nil)
   297  	mockApi.EXPECT().ContainerConfig().Return(fakeContainerConfig(), nil)
   298  
   299  	put := &charm.LXDProfile{
   300  		Config: map[string]string{
   301  			"security.nesting": "true",
   302  		},
   303  	}
   304  	result := &apiprovisioner.LXDProfileResult{
   305  		Config: put.Config,
   306  		Name:   "",
   307  	}
   308  	mockApi.EXPECT().GetContainerProfileInfo(gomock.Eq(containerTag)).Return([]*apiprovisioner.LXDProfileResult{result}, nil)
   309  
   310  	mockManager := testing.NewMockTestLXDManager(ctlr)
   311  
   312  	broker, err := provisioner.NewLXDBroker(
   313  		func(containerTag names.MachineTag, log loggo.Logger, abort <-chan struct{}) error { return nil },
   314  		mockApi, mockManager, s.agentConfig)
   315  	c.Assert(err, jc.ErrorIsNil)
   316  
   317  	_, err = s.startInstance(c, broker, machineId)
   318  	c.Assert(err, gc.ErrorMatches, fmt.Sprintf("cannot write charm profile: request to write LXD profile for machine %s with no profile name", machineId))
   319  }
   320  
   321  func (s *lxdBrokerSuite) TestStartInstanceWithLXDProfileReturnsLXDProfileNames(c *gc.C) {
   322  	ctrl := gomock.NewController(c)
   323  	defer ctrl.Finish()
   324  
   325  	containerTag := names.NewMachineTag("1-lxd-0")
   326  
   327  	mockApi := mocks.NewMockAPICalls(ctrl)
   328  	mockManager := testing.NewMockTestLXDManager(ctrl)
   329  	mockManager.EXPECT().LXDProfileNames(containerTag.Id()).Return([]string{
   330  		lxdprofile.Name("foo", "bar", 1),
   331  	}, nil)
   332  
   333  	broker, err := provisioner.NewLXDBroker(
   334  		func(containerTag names.MachineTag, log loggo.Logger, abort <-chan struct{}) error { return nil },
   335  		mockApi, mockManager, s.agentConfig)
   336  	c.Assert(err, jc.ErrorIsNil)
   337  
   338  	nameRetriever := broker.(container.LXDProfileNameRetriever)
   339  	profileNames, err := nameRetriever.LXDProfileNames(containerTag.Id())
   340  	c.Assert(err, jc.ErrorIsNil)
   341  	c.Assert(profileNames, jc.DeepEquals, []string{
   342  		lxdprofile.Name("foo", "bar", 1),
   343  	})
   344  }