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 }