github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/container/lxd/manager_test.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package lxd_test 5 6 import ( 7 "errors" 8 stdtesting "testing" 9 10 "github.com/golang/mock/gomock" 11 "github.com/juju/proxy" 12 jc "github.com/juju/testing/checkers" 13 "github.com/juju/version" 14 lxdclient "github.com/lxc/lxd/client" 15 lxdapi "github.com/lxc/lxd/shared/api" 16 gc "gopkg.in/check.v1" 17 "gopkg.in/juju/names.v2" 18 19 "github.com/juju/juju/api" 20 "github.com/juju/juju/cloudconfig/instancecfg" 21 "github.com/juju/juju/container" 22 "github.com/juju/juju/container/lxd" 23 lxdtesting "github.com/juju/juju/container/lxd/testing" 24 "github.com/juju/juju/core/constraints" 25 "github.com/juju/juju/core/status" 26 "github.com/juju/juju/environs/config" 27 "github.com/juju/juju/environs/context" 28 "github.com/juju/juju/network" 29 coretesting "github.com/juju/juju/testing" 30 coretools "github.com/juju/juju/tools" 31 "gopkg.in/juju/charm.v6" 32 ) 33 34 func Test(t *stdtesting.T) { 35 gc.TestingT(t) 36 } 37 38 type managerSuite struct { 39 lxdtesting.BaseSuite 40 } 41 42 var _ = gc.Suite(&managerSuite{}) 43 44 func (s *managerSuite) patch(svr lxdclient.ImageServer) { 45 lxd.PatchConnectRemote(s, map[string]lxdclient.ImageServer{"cloud-images.ubuntu.com": svr}) 46 lxd.PatchGenerateVirtualMACAddress(s) 47 } 48 49 func (s *managerSuite) makeManager(c *gc.C, svr lxdclient.ContainerServer) container.Manager { 50 return s.makeManagerForConfig(c, getBaseConfig(), svr) 51 } 52 53 func (s *managerSuite) makeManagerForConfig( 54 c *gc.C, cfg container.ManagerConfig, cSvr lxdclient.ContainerServer, 55 ) container.Manager { 56 svr, err := lxd.NewServer(cSvr) 57 c.Assert(err, jc.ErrorIsNil) 58 59 manager, err := lxd.NewContainerManager(cfg, svr) 60 c.Assert(err, jc.ErrorIsNil) 61 return manager 62 } 63 64 func getBaseConfig() container.ManagerConfig { 65 return container.ManagerConfig{ 66 container.ConfigModelUUID: coretesting.ModelTag.Id(), 67 container.ConfigAvailabilityZone: "test-availability-zone", 68 config.ContainerImageStreamKey: "released", 69 } 70 } 71 72 func prepInstanceConfig(c *gc.C) *instancecfg.InstanceConfig { 73 apiInfo := &api.Info{ 74 Addrs: []string{"127.0.0.1:1337"}, 75 Password: "password", 76 Nonce: "nonce", 77 Tag: names.NewMachineTag("123"), 78 ModelTag: names.NewModelTag("3fe11b2c-ae46-4cd1-96ad-d34239f70daf"), 79 CACert: "foo", 80 } 81 icfg, err := instancecfg.NewInstanceConfig( 82 names.NewControllerTag("4e29484b-4ff3-417f-97c3-09d1bec98d5b"), 83 "123", 84 "nonce", 85 "imagestream", 86 "xenial", 87 apiInfo, 88 ) 89 c.Assert(err, jc.ErrorIsNil) 90 instancecfg.PopulateInstanceConfig( 91 icfg, 92 "lxd", 93 "", 94 false, 95 proxy.Settings{}, 96 proxy.Settings{}, 97 proxy.Settings{}, 98 "", 99 false, 100 false, 101 nil, 102 nil, 103 ) 104 list := coretools.List{ 105 &coretools.Tools{Version: version.MustParseBinary("2.3.4-trusty-amd64")}, 106 } 107 err = icfg.SetTools(list) 108 c.Assert(err, jc.ErrorIsNil) 109 return icfg 110 } 111 112 func prepNetworkConfig() *container.NetworkConfig { 113 return container.BridgeNetworkConfig("eth0", 1500, []network.InterfaceInfo{{ 114 InterfaceName: "eth0", 115 InterfaceType: network.EthernetInterface, 116 ConfigType: network.ConfigDHCP, 117 ParentInterfaceName: "eth0", 118 }}) 119 } 120 121 func (s *managerSuite) TestContainerCreateDestroy(c *gc.C) { 122 ctrl := gomock.NewController(c) 123 defer ctrl.Finish() 124 cSvr := s.NewMockServer(ctrl) 125 s.patch(cSvr) 126 127 manager := s.makeManager(c, cSvr) 128 iCfg := prepInstanceConfig(c) 129 hostName, err := manager.Namespace().Hostname(iCfg.MachineId) 130 c.Assert(err, jc.ErrorIsNil) 131 132 // Operation arrangements. 133 startOp := lxdtesting.NewMockOperation(ctrl) 134 startOp.EXPECT().Wait().Return(nil) 135 136 stopOp := lxdtesting.NewMockOperation(ctrl) 137 stopOp.EXPECT().Wait().Return(nil) 138 139 deleteOp := lxdtesting.NewMockOperation(ctrl) 140 deleteOp.EXPECT().Wait().Return(nil) 141 142 exp := cSvr.EXPECT() 143 144 // Arrangements for the container creation. 145 expectCreateContainer(ctrl, cSvr, "juju/xenial/"+s.Arch(), "foo-target") 146 exp.UpdateContainerState(hostName, lxdapi.ContainerStatePut{Action: "start", Timeout: -1}, "").Return(startOp, nil) 147 148 exp.GetContainerState(hostName).Return( 149 &lxdapi.ContainerState{StatusCode: lxdapi.Running}, lxdtesting.ETag, nil).Times(2) 150 151 exp.GetContainer(hostName).Return(&lxdapi.Container{Name: hostName}, lxdtesting.ETag, nil) 152 153 // Arrangements for the container destruction. 154 stopReq := lxdapi.ContainerStatePut{ 155 Action: "stop", 156 Timeout: -1, 157 Stateful: false, 158 Force: true, 159 } 160 gomock.InOrder( 161 exp.UpdateContainerState(hostName, stopReq, lxdtesting.ETag).Return(stopOp, nil), 162 exp.DeleteContainer(hostName).Return(deleteOp, nil), 163 ) 164 165 instance, hc, err := manager.CreateContainer( 166 iCfg, constraints.Value{}, "xenial", prepNetworkConfig(), &container.StorageConfig{}, lxdtesting.NoOpCallback, 167 ) 168 c.Assert(err, jc.ErrorIsNil) 169 170 instanceId := instance.Id() 171 c.Check(string(instanceId), gc.Equals, hostName) 172 173 instanceStatus := instance.Status(context.NewCloudCallContext()) 174 c.Check(instanceStatus.Status, gc.Equals, status.Running) 175 c.Check(*hc.AvailabilityZone, gc.Equals, "test-availability-zone") 176 177 err = manager.DestroyContainer(instanceId) 178 c.Assert(err, jc.ErrorIsNil) 179 } 180 181 func (s *managerSuite) TestContainerCreateUpdateIPv4Network(c *gc.C) { 182 ctrl := gomock.NewController(c) 183 defer ctrl.Finish() 184 cSvr := s.NewMockServerWithExtensions(ctrl, "network") 185 s.patch(cSvr) 186 187 manager := s.makeManager(c, cSvr) 188 iCfg := prepInstanceConfig(c) 189 hostName, err := manager.Namespace().Hostname(iCfg.MachineId) 190 c.Assert(err, jc.ErrorIsNil) 191 192 exp := cSvr.EXPECT() 193 194 req := lxdapi.NetworkPut{ 195 Config: map[string]string{ 196 "ipv4.address": "auto", 197 "ipv4.nat": "true", 198 }, 199 } 200 gomock.InOrder( 201 exp.GetNetwork(network.DefaultLXDBridge).Return(&lxdapi.Network{}, lxdtesting.ETag, nil), 202 exp.UpdateNetwork(network.DefaultLXDBridge, req, lxdtesting.ETag).Return(nil), 203 ) 204 205 expectCreateContainer(ctrl, cSvr, "juju/xenial/"+s.Arch(), "foo-target") 206 207 startOp := lxdtesting.NewMockOperation(ctrl) 208 startOp.EXPECT().Wait().Return(nil) 209 210 exp.UpdateContainerState(hostName, lxdapi.ContainerStatePut{Action: "start", Timeout: -1}, "").Return(startOp, nil) 211 exp.GetContainer(hostName).Return(&lxdapi.Container{Name: hostName}, lxdtesting.ETag, nil) 212 213 // Supplying config for a single device with default bridge and without a 214 // CIDR will cause the default bridge to be updated with IPv4 config. 215 netConfig := container.BridgeNetworkConfig("eth0", 1500, []network.InterfaceInfo{{ 216 InterfaceName: "eth0", 217 InterfaceType: network.EthernetInterface, 218 ConfigType: network.ConfigDHCP, 219 ParentInterfaceName: network.DefaultLXDBridge, 220 }}) 221 _, _, err = manager.CreateContainer( 222 iCfg, constraints.Value{}, "xenial", netConfig, &container.StorageConfig{}, lxdtesting.NoOpCallback, 223 ) 224 c.Assert(err, jc.ErrorIsNil) 225 } 226 227 func (s *managerSuite) TestCreateContainerCreateFailed(c *gc.C) { 228 ctrl := gomock.NewController(c) 229 defer ctrl.Finish() 230 cSvr := s.NewMockServer(ctrl) 231 232 createRemoteOp := lxdtesting.NewMockRemoteOperation(ctrl) 233 createRemoteOp.EXPECT().Wait().Return(nil).AnyTimes() 234 createRemoteOp.EXPECT().GetTarget().Return(&lxdapi.Operation{StatusCode: lxdapi.Failure, Err: "create failed"}, nil) 235 236 exp := cSvr.EXPECT() 237 238 alias := &lxdapi.ImageAliasesEntry{ImageAliasesEntryPut: lxdapi.ImageAliasesEntryPut{Target: "foo-target"}} 239 image := lxdapi.Image{Filename: "this-is-our-image"} 240 gomock.InOrder( 241 exp.GetImageAlias("juju/xenial/"+s.Arch()).Return(alias, lxdtesting.ETag, nil), 242 exp.GetImage("foo-target").Return(&image, lxdtesting.ETag, nil), 243 exp.CreateContainerFromImage(cSvr, image, gomock.Any()).Return(createRemoteOp, nil), 244 ) 245 246 _, _, err := s.makeManager(c, cSvr).CreateContainer( 247 prepInstanceConfig(c), 248 constraints.Value{}, 249 "xenial", 250 prepNetworkConfig(), 251 &container.StorageConfig{}, 252 lxdtesting.NoOpCallback, 253 ) 254 c.Assert(err, gc.ErrorMatches, ".*create failed") 255 } 256 257 func (s *managerSuite) TestCreateContainerSpecCreationError(c *gc.C) { 258 ctrl := gomock.NewController(c) 259 defer ctrl.Finish() 260 cSvr := s.NewMockServer(ctrl) 261 262 // When the local image acquisition fails, this will cause the remote 263 // connection attempt to fail. 264 // This is our error condition exit from manager.getContainerSpec. 265 lxd.PatchConnectRemote(s, map[string]lxdclient.ImageServer{}) 266 267 exp := cSvr.EXPECT() 268 269 alias := &lxdapi.ImageAliasesEntry{ImageAliasesEntryPut: lxdapi.ImageAliasesEntryPut{Target: "foo-target"}} 270 image := lxdapi.Image{Filename: "this-is-our-image"} 271 gomock.InOrder( 272 exp.GetImageAlias("juju/xenial/"+s.Arch()).Return(alias, lxdtesting.ETag, nil), 273 exp.GetImage("foo-target").Return(&image, lxdtesting.ETag, errors.New("not here")), 274 ) 275 276 _, _, err := s.makeManager(c, cSvr).CreateContainer( 277 prepInstanceConfig(c), 278 constraints.Value{}, 279 "xenial", 280 prepNetworkConfig(), 281 &container.StorageConfig{}, 282 lxdtesting.NoOpCallback, 283 ) 284 c.Assert(err, gc.ErrorMatches, ".*unrecognized remote server") 285 } 286 287 func (s *managerSuite) TestCreateContainerStartFailed(c *gc.C) { 288 ctrl := gomock.NewController(c) 289 defer ctrl.Finish() 290 cSvr := s.NewMockServer(ctrl) 291 s.patch(cSvr) 292 293 manager := s.makeManager(c, cSvr) 294 iCfg := prepInstanceConfig(c) 295 hostName, err := manager.Namespace().Hostname(iCfg.MachineId) 296 c.Assert(err, jc.ErrorIsNil) 297 298 updateOp := lxdtesting.NewMockOperation(ctrl) 299 updateOp.EXPECT().Wait().Return(errors.New("start failed")) 300 301 deleteOp := lxdtesting.NewMockOperation(ctrl) 302 deleteOp.EXPECT().Wait().Return(nil).AnyTimes() 303 304 exp := cSvr.EXPECT() 305 306 expectCreateContainer(ctrl, cSvr, "juju/xenial/"+s.Arch(), "foo-target") 307 gomock.InOrder( 308 exp.UpdateContainerState( 309 hostName, lxdapi.ContainerStatePut{Action: "start", Timeout: -1}, "").Return(updateOp, nil), 310 exp.GetContainerState(hostName).Return(&lxdapi.ContainerState{StatusCode: lxdapi.Stopped}, lxdtesting.ETag, nil), 311 exp.DeleteContainer(hostName).Return(deleteOp, nil), 312 ) 313 314 _, _, err = manager.CreateContainer( 315 iCfg, 316 constraints.Value{}, 317 "xenial", 318 prepNetworkConfig(), 319 &container.StorageConfig{}, 320 lxdtesting.NoOpCallback, 321 ) 322 c.Assert(err, gc.ErrorMatches, ".*start failed") 323 } 324 325 // expectCreateContainer is a convenience function for the expectations 326 // concerning a successful container creation based on a cached local 327 // image. 328 func expectCreateContainer(ctrl *gomock.Controller, svr *lxdtesting.MockContainerServer, aliasName, target string) { 329 createRemoteOp := lxdtesting.NewMockRemoteOperation(ctrl) 330 createRemoteOp.EXPECT().Wait().Return(nil).AnyTimes() 331 createRemoteOp.EXPECT().GetTarget().Return(&lxdapi.Operation{StatusCode: lxdapi.Success}, nil) 332 333 exp := svr.EXPECT() 334 335 alias := &lxdapi.ImageAliasesEntry{ImageAliasesEntryPut: lxdapi.ImageAliasesEntryPut{Target: target}} 336 exp.GetImageAlias(aliasName).Return(alias, lxdtesting.ETag, nil) 337 338 image := lxdapi.Image{Filename: "this-is-our-image"} 339 exp.GetImage("foo-target").Return(&image, lxdtesting.ETag, nil) 340 exp.CreateContainerFromImage(svr, image, gomock.Any()).Return(createRemoteOp, nil) 341 } 342 343 func (s *managerSuite) TestListContainers(c *gc.C) { 344 ctrl := gomock.NewController(c) 345 defer ctrl.Finish() 346 cSvr := s.NewMockServer(ctrl) 347 manager := s.makeManager(c, cSvr) 348 349 prefix := manager.Namespace().Prefix() 350 wrongPrefix := prefix[:len(prefix)-1] + "j" 351 352 containers := []lxdapi.Container{ 353 {Name: "foobar"}, 354 {Name: "definitely-not-a-juju-container"}, 355 {Name: wrongPrefix + "-0"}, 356 {Name: prefix + "-0"}, 357 {Name: "please-disperse"}, 358 {Name: prefix + "-1"}, 359 {Name: "nothing-to-see-here-please"}, 360 } 361 362 cSvr.EXPECT().GetContainers().Return(containers, nil) 363 364 result, err := manager.ListContainers() 365 c.Assert(err, jc.ErrorIsNil) 366 c.Check(result, gc.HasLen, 2) 367 c.Check(string(result[0].Id()), gc.Equals, prefix+"-0") 368 c.Check(string(result[1].Id()), gc.Equals, prefix+"-1") 369 } 370 371 func (s *managerSuite) TestIsInitialized(c *gc.C) { 372 ctrl := gomock.NewController(c) 373 defer ctrl.Finish() 374 cSvr := s.NewMockServer(ctrl) 375 376 manager := s.makeManager(c, cSvr) 377 c.Check(manager.IsInitialized(), gc.Equals, true) 378 } 379 380 func (s *managerSuite) TestNetworkDevicesFromConfigWithEmptyParentDevice(c *gc.C) { 381 ctrl := gomock.NewController(c) 382 defer ctrl.Finish() 383 cSvr := s.NewMockServer(ctrl) 384 385 interfaces := []network.InterfaceInfo{{ 386 InterfaceName: "eth1", 387 InterfaceType: "ethernet", 388 MACAddress: "aa:bb:cc:dd:ee:f1", 389 MTU: 9000, 390 }} 391 392 result, _, err := lxd.NetworkDevicesFromConfig(s.makeManager(c, cSvr), &container.NetworkConfig{ 393 Interfaces: interfaces, 394 }) 395 396 c.Assert(err, gc.ErrorMatches, "parent interface name is empty") 397 c.Assert(result, gc.IsNil) 398 } 399 400 func (s *managerSuite) TestNetworkDevicesFromConfigWithParentDevice(c *gc.C) { 401 ctrl := gomock.NewController(c) 402 defer ctrl.Finish() 403 cSvr := s.NewMockServer(ctrl) 404 405 interfaces := []network.InterfaceInfo{{ 406 ParentInterfaceName: "br-eth0", 407 InterfaceName: "eth0", 408 InterfaceType: "ethernet", 409 CIDR: "10.10.0.0/24", 410 MACAddress: "aa:bb:cc:dd:ee:f0", 411 }} 412 413 expected := map[string]map[string]string{ 414 "eth0": { 415 "hwaddr": "aa:bb:cc:dd:ee:f0", 416 "name": "eth0", 417 "nictype": "bridged", 418 "parent": "br-eth0", 419 "type": "nic", 420 }, 421 } 422 423 result, unknown, err := lxd.NetworkDevicesFromConfig(s.makeManager(c, cSvr), &container.NetworkConfig{ 424 Device: "lxdbr0", 425 Interfaces: interfaces, 426 }) 427 428 c.Assert(err, jc.ErrorIsNil) 429 c.Check(result, jc.DeepEquals, expected) 430 c.Check(unknown, gc.HasLen, 0) 431 } 432 433 func (s *managerSuite) TestNetworkDevicesFromConfigUnknownCIDR(c *gc.C) { 434 ctrl := gomock.NewController(c) 435 defer ctrl.Finish() 436 cSvr := s.NewMockServer(ctrl) 437 438 interfaces := []network.InterfaceInfo{{ 439 ParentInterfaceName: "br-eth0", 440 InterfaceName: "eth0", 441 InterfaceType: "ethernet", 442 MACAddress: "aa:bb:cc:dd:ee:f0", 443 }} 444 445 _, unknown, err := lxd.NetworkDevicesFromConfig(s.makeManager(c, cSvr), &container.NetworkConfig{ 446 Device: "lxdbr0", 447 Interfaces: interfaces, 448 }) 449 450 c.Assert(err, jc.ErrorIsNil) 451 c.Check(unknown, gc.DeepEquals, []string{"br-eth0"}) 452 } 453 454 func (s *managerSuite) TestNetworkDevicesFromConfigNoInputGetsProfileNICs(c *gc.C) { 455 ctrl := gomock.NewController(c) 456 defer ctrl.Finish() 457 cSvr := s.NewMockServer(ctrl) 458 s.patch(cSvr) 459 460 cSvr.EXPECT().GetProfile("default").Return(defaultProfileWithNIC(), lxdtesting.ETag, nil) 461 462 result, _, err := lxd.NetworkDevicesFromConfig(s.makeManager(c, cSvr), &container.NetworkConfig{}) 463 c.Assert(err, jc.ErrorIsNil) 464 465 exp := map[string]map[string]string{ 466 "eth0": { 467 "parent": network.DefaultLXDBridge, 468 "type": "nic", 469 "nictype": "bridged", 470 "hwaddr": "00:16:3e:00:00:00", 471 }, 472 } 473 474 c.Check(result, gc.DeepEquals, exp) 475 } 476 477 func (s *managerSuite) TestGetImageSourcesDefaultConfig(c *gc.C) { 478 ctrl := gomock.NewController(c) 479 defer ctrl.Finish() 480 cSvr := s.NewMockServer(ctrl) 481 482 mgr := s.makeManager(c, cSvr) 483 484 sources, err := lxd.GetImageSources(mgr) 485 c.Assert(err, jc.ErrorIsNil) 486 c.Check(sources, gc.DeepEquals, []lxd.ServerSpec{lxd.CloudImagesRemote, lxd.CloudImagesDailyRemote}) 487 } 488 489 func (s *managerSuite) TestGetImageSourcesNonStandardStreamDefaultConfig(c *gc.C) { 490 ctrl := gomock.NewController(c) 491 defer ctrl.Finish() 492 cSvr := s.NewMockServer(ctrl) 493 494 cfg := getBaseConfig() 495 cfg[config.ContainerImageStreamKey] = "nope" 496 mgr := s.makeManagerForConfig(c, cfg, cSvr) 497 498 sources, err := lxd.GetImageSources(mgr) 499 c.Assert(err, jc.ErrorIsNil) 500 c.Check(sources, gc.DeepEquals, []lxd.ServerSpec{lxd.CloudImagesRemote, lxd.CloudImagesDailyRemote}) 501 } 502 503 func (s *managerSuite) TestGetImageSourcesDailyOnly(c *gc.C) { 504 ctrl := gomock.NewController(c) 505 defer ctrl.Finish() 506 cSvr := s.NewMockServer(ctrl) 507 508 cfg := getBaseConfig() 509 cfg[config.ContainerImageStreamKey] = "daily" 510 mgr := s.makeManagerForConfig(c, cfg, cSvr) 511 512 sources, err := lxd.GetImageSources(mgr) 513 c.Assert(err, jc.ErrorIsNil) 514 c.Check(sources, gc.DeepEquals, []lxd.ServerSpec{lxd.CloudImagesDailyRemote}) 515 } 516 517 func (s *managerSuite) TestGetImageSourcesImageMetadataURLExpectedHTTPSSources(c *gc.C) { 518 ctrl := gomock.NewController(c) 519 defer ctrl.Finish() 520 cSvr := s.NewMockServer(ctrl) 521 522 cfg := getBaseConfig() 523 cfg[config.ContainerImageMetadataURLKey] = "http://special.container.sauce" 524 mgr := s.makeManagerForConfig(c, cfg, cSvr) 525 526 sources, err := lxd.GetImageSources(mgr) 527 c.Assert(err, jc.ErrorIsNil) 528 529 expectedSources := []lxd.ServerSpec{ 530 { 531 Name: "special.container.sauce", 532 Host: "https://special.container.sauce", 533 Protocol: lxd.SimpleStreamsProtocol, 534 }, 535 lxd.CloudImagesRemote, 536 lxd.CloudImagesDailyRemote, 537 } 538 c.Check(sources, gc.DeepEquals, expectedSources) 539 } 540 541 func (s *managerSuite) TestGetImageSourcesImageMetadataURLDailyStream(c *gc.C) { 542 ctrl := gomock.NewController(c) 543 defer ctrl.Finish() 544 cSvr := s.NewMockServer(ctrl) 545 546 cfg := getBaseConfig() 547 cfg[config.ContainerImageMetadataURLKey] = "http://special.container.sauce" 548 cfg[config.ContainerImageStreamKey] = "daily" 549 mgr := s.makeManagerForConfig(c, cfg, cSvr) 550 551 sources, err := lxd.GetImageSources(mgr) 552 c.Assert(err, jc.ErrorIsNil) 553 554 expectedSources := []lxd.ServerSpec{ 555 { 556 Name: "special.container.sauce", 557 Host: "https://special.container.sauce", 558 Protocol: lxd.SimpleStreamsProtocol, 559 }, 560 lxd.CloudImagesDailyRemote, 561 } 562 c.Check(sources, gc.DeepEquals, expectedSources) 563 } 564 565 func (s *managerSuite) TestMaybeWriteLXDProfile(c *gc.C) { 566 ctrl := gomock.NewController(c) 567 defer ctrl.Finish() 568 cSvr := s.NewMockServer(ctrl) 569 570 mgr := s.makeManager(c, cSvr) 571 proMgr, ok := mgr.(container.LXDProfileManager) 572 c.Assert(ok, jc.IsTrue) 573 574 put := charm.LXDProfile{ 575 Config: map[string]string{ 576 "security.nesting": "true", 577 "security.privileged": "true", 578 }, 579 Description: "lxd profile for testing", 580 Devices: map[string]map[string]string{ 581 "tun": { 582 "path": "/dev/net/tun", 583 "type": "unix-char", 584 }, 585 }, 586 } 587 post := lxdapi.ProfilesPost{ 588 ProfilePut: lxdapi.ProfilePut(put), 589 Name: "juju-default-lxd-0", 590 } 591 cSvr.EXPECT().CreateProfile(post).Return(nil).Times(1) 592 cSvr.EXPECT().GetProfileNames().Return([]string{"default", "custom"}, nil).Times(1) 593 594 err := proMgr.MaybeWriteLXDProfile("juju-default-lxd-0", &put) 595 c.Assert(err, jc.ErrorIsNil) 596 } 597 598 func (s *managerSuite) TestReplaceOrAddInstanceProfile(c *gc.C) { 599 ctrl := gomock.NewController(c) 600 defer ctrl.Finish() 601 cSvr := s.NewMockServer(ctrl) 602 // Operation arrangements. 603 updateOp := lxdtesting.NewMockOperation(ctrl) 604 updateOp.EXPECT().Wait().Return(nil) 605 updateOp.EXPECT().Get().Return(lxdapi.Operation{Description: "Updating ontainer"}) 606 607 instId := "testme" 608 old := "old-profile" 609 oldProfiles := []string{"default", "juju-default", old} 610 new := "new-profile" 611 newProfiles := []string{"default", "juju-default", new} 612 put := charm.LXDProfile{ 613 Config: map[string]string{ 614 "security.nesting": "true", 615 }, 616 Description: "test profile", 617 } 618 post := lxdapi.ProfilesPost{ 619 ProfilePut: lxdapi.ProfilePut(put), 620 Name: new, 621 } 622 cExp := cSvr.EXPECT() 623 gomock.InOrder( 624 cExp.GetProfileNames().Return(oldProfiles, nil), 625 cExp.CreateProfile(post).Return(nil), 626 cExp.GetContainer(instId).Return( 627 &lxdapi.Container{ 628 ContainerPut: lxdapi.ContainerPut{ 629 Profiles: oldProfiles, 630 }, 631 }, "", nil), 632 cExp.UpdateContainer(instId, gomock.Any(), gomock.Any()).Return(updateOp, nil), 633 cExp.DeleteProfile(old).Return(nil), 634 cExp.GetContainer(instId).Return( 635 &lxdapi.Container{ 636 ContainerPut: lxdapi.ContainerPut{ 637 Profiles: newProfiles, 638 }, 639 }, "", nil), 640 ) 641 642 mgr := s.makeManager(c, cSvr) 643 proMgr, ok := mgr.(container.LXDProfileManager) 644 c.Assert(ok, jc.IsTrue) 645 646 obtained, err := proMgr.ReplaceOrAddInstanceProfile(instId, old, new, &put) 647 c.Assert(err, jc.ErrorIsNil) 648 c.Assert(obtained, gc.DeepEquals, newProfiles) 649 }