github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/container/broker/lxd-broker_test.go (about)

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