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 }