github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/provisioner/kvm-broker_test.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package provisioner_test 5 6 import ( 7 "fmt" 8 "path/filepath" 9 "runtime" 10 "time" 11 12 "github.com/juju/errors" 13 "github.com/juju/loggo" 14 gitjujutesting "github.com/juju/testing" 15 jc "github.com/juju/testing/checkers" 16 "github.com/juju/utils/arch" 17 gc "gopkg.in/check.v1" 18 "gopkg.in/juju/names.v2" 19 "gopkg.in/juju/worker.v1/workertest" 20 21 "github.com/juju/juju/agent" 22 "github.com/juju/juju/cloudconfig/instancecfg" 23 "github.com/juju/juju/container" 24 "github.com/juju/juju/container/kvm" 25 "github.com/juju/juju/container/kvm/mock" 26 kvmtesting "github.com/juju/juju/container/kvm/testing" 27 "github.com/juju/juju/core/instance" 28 "github.com/juju/juju/environs" 29 "github.com/juju/juju/environs/context" 30 supportedversion "github.com/juju/juju/juju/version" 31 "github.com/juju/juju/network" 32 "github.com/juju/juju/state" 33 coretesting "github.com/juju/juju/testing" 34 jujuversion "github.com/juju/juju/version" 35 "github.com/juju/juju/worker/provisioner" 36 ) 37 38 type kvmSuite struct { 39 kvmtesting.TestSuite 40 events chan mock.Event 41 eventsDone chan struct{} 42 } 43 44 type kvmBrokerSuite struct { 45 kvmSuite 46 agentConfig agent.Config 47 api *fakeAPI 48 manager *fakeContainerManager 49 } 50 51 var _ = gc.Suite(&kvmBrokerSuite{}) 52 53 func (s *kvmSuite) SetUpTest(c *gc.C) { 54 if runtime.GOOS == "windows" { 55 c.Skip("Skipping kvm tests on windows") 56 } 57 s.TestSuite.SetUpTest(c) 58 s.events = make(chan mock.Event) 59 s.eventsDone = make(chan struct{}) 60 go func() { 61 defer close(s.eventsDone) 62 for event := range s.events { 63 c.Output(3, fmt.Sprintf("kvm event: <%s, %s>", event.Action, event.InstanceId)) 64 } 65 }() 66 s.TestSuite.ContainerFactory.AddListener(s.events) 67 } 68 69 func (s *kvmSuite) TearDownTest(c *gc.C) { 70 close(s.events) 71 <-s.eventsDone 72 s.TestSuite.TearDownTest(c) 73 } 74 75 func (s *kvmBrokerSuite) SetUpTest(c *gc.C) { 76 if runtime.GOOS == "windows" { 77 c.Skip("Skipping kvm tests on windows") 78 } 79 s.kvmSuite.SetUpTest(c) 80 s.PatchValue(&provisioner.GetMachineCloudInitData, func(_ string) (map[string]interface{}, error) { 81 return nil, nil 82 }) 83 var err error 84 s.agentConfig, err = agent.NewAgentConfig( 85 agent.AgentConfigParams{ 86 Paths: agent.NewPathsWithDefaults(agent.Paths{DataDir: "/not/used/here"}), 87 Tag: names.NewUnitTag("ubuntu/1"), 88 UpgradedToVersion: jujuversion.Current, 89 Password: "dummy-secret", 90 Nonce: "nonce", 91 APIAddresses: []string{"10.0.0.1:1234"}, 92 CACert: coretesting.CACert, 93 Controller: coretesting.ControllerTag, 94 Model: coretesting.ModelTag, 95 }) 96 c.Assert(err, jc.ErrorIsNil) 97 s.api = NewFakeAPI() 98 s.manager = &fakeContainerManager{} 99 } 100 101 func (s *kvmBrokerSuite) startInstance(c *gc.C, broker environs.InstanceBroker, machineId string) (*environs.StartInstanceResult, error) { 102 return callStartInstance(c, s, broker, machineId) 103 } 104 105 func (s *kvmBrokerSuite) newKVMBroker(c *gc.C) (environs.InstanceBroker, error) { 106 managerConfig := container.ManagerConfig{container.ConfigModelUUID: coretesting.ModelTag.Id()} 107 manager, err := kvm.NewContainerManager(managerConfig) 108 c.Assert(err, jc.ErrorIsNil) 109 return provisioner.NewKVMBroker(s.api.PrepareHost, s.api, manager, s.agentConfig) 110 } 111 112 func (s *kvmBrokerSuite) newKVMBrokerFakeManager(c *gc.C) (environs.InstanceBroker, error) { 113 return provisioner.NewKVMBroker(s.api.PrepareHost, s.api, s.manager, s.agentConfig) 114 } 115 116 func (s *kvmBrokerSuite) maintainInstance(c *gc.C, broker environs.InstanceBroker, machineId string) { 117 callMaintainInstance(c, s, broker, machineId) 118 } 119 120 func (s *kvmBrokerSuite) TestStartInstanceWithoutNetworkChanges(c *gc.C) { 121 broker, brokerErr := s.newKVMBroker(c) 122 c.Assert(brokerErr, jc.ErrorIsNil) 123 124 machineId := "1/kvm/0" 125 result, err := s.startInstance(c, broker, machineId) 126 c.Assert(err, jc.ErrorIsNil) 127 s.api.CheckCalls(c, []gitjujutesting.StubCall{{ 128 FuncName: "ContainerConfig", 129 }, { 130 FuncName: "PrepareHost", 131 Args: []interface{}{names.NewMachineTag("1-kvm-0")}, 132 }, { 133 FuncName: "PrepareContainerInterfaceInfo", 134 Args: []interface{}{names.NewMachineTag("1-kvm-0")}, 135 }}) 136 c.Assert(result.Instance.Id(), gc.Equals, instance.Id("juju-06f00d-1-kvm-0")) 137 s.assertResults(c, broker, result) 138 } 139 140 func (s *kvmBrokerSuite) TestMaintainInstanceAddress(c *gc.C) { 141 broker, brokerErr := s.newKVMBroker(c) 142 c.Assert(brokerErr, jc.ErrorIsNil) 143 144 machineId := "1/kvm/0" 145 result, err := s.startInstance(c, broker, machineId) 146 c.Assert(err, jc.ErrorIsNil) 147 148 s.api.ResetCalls() 149 150 s.maintainInstance(c, broker, machineId) 151 s.api.CheckCalls(c, []gitjujutesting.StubCall{}) 152 c.Assert(result.Instance.Id(), gc.Equals, instance.Id("juju-06f00d-1-kvm-0")) 153 s.assertResults(c, broker, result) 154 } 155 156 func (s *kvmBrokerSuite) TestStopInstance(c *gc.C) { 157 broker, brokerErr := s.newKVMBroker(c) 158 c.Assert(brokerErr, jc.ErrorIsNil) 159 160 result0, err0 := s.startInstance(c, broker, "1/kvm/0") 161 c.Assert(err0, jc.ErrorIsNil) 162 163 result1, err1 := s.startInstance(c, broker, "1/kvm/1") 164 c.Assert(err1, jc.ErrorIsNil) 165 166 result2, err2 := s.startInstance(c, broker, "1/kvm/2") 167 c.Assert(err2, jc.ErrorIsNil) 168 169 callCtx := context.NewCloudCallContext() 170 err := broker.StopInstances(callCtx, result0.Instance.Id()) 171 c.Assert(err, jc.ErrorIsNil) 172 s.assertResults(c, broker, result1, result2) 173 c.Assert(s.kvmContainerDir(result0), jc.DoesNotExist) 174 c.Assert(s.kvmRemovedContainerDir(result0), jc.IsDirectory) 175 176 err = broker.StopInstances(callCtx, result1.Instance.Id(), result2.Instance.Id()) 177 c.Assert(err, jc.ErrorIsNil) 178 s.assertNoResults(c, broker) 179 } 180 181 func (s *kvmBrokerSuite) TestAllInstances(c *gc.C) { 182 broker, brokerErr := s.newKVMBroker(c) 183 c.Assert(brokerErr, jc.ErrorIsNil) 184 185 result0, err0 := s.startInstance(c, broker, "1/kvm/0") 186 c.Assert(err0, jc.ErrorIsNil) 187 188 result1, err1 := s.startInstance(c, broker, "1/kvm/1") 189 c.Assert(err1, jc.ErrorIsNil) 190 s.assertResults(c, broker, result0, result1) 191 192 err := broker.StopInstances(context.NewCloudCallContext(), result1.Instance.Id()) 193 c.Assert(err, jc.ErrorIsNil) 194 result2, err2 := s.startInstance(c, broker, "1/kvm/2") 195 c.Assert(err2, jc.ErrorIsNil) 196 s.assertResults(c, broker, result0, result2) 197 } 198 199 func (s *kvmBrokerSuite) assertResults(c *gc.C, broker environs.InstanceBroker, results ...*environs.StartInstanceResult) { 200 assertInstancesStarted(c, broker, results...) 201 } 202 203 func (s *kvmBrokerSuite) assertNoResults(c *gc.C, broker environs.InstanceBroker) { 204 s.assertResults(c, broker) 205 } 206 207 func (s *kvmBrokerSuite) kvmContainerDir(result *environs.StartInstanceResult) string { 208 inst := result.Instance 209 return filepath.Join(s.ContainerDir, string(inst.Id())) 210 } 211 212 func (s *kvmBrokerSuite) kvmRemovedContainerDir(result *environs.StartInstanceResult) string { 213 inst := result.Instance 214 return filepath.Join(s.RemovedDir, string(inst.Id())) 215 } 216 217 func (s *kvmBrokerSuite) TestStartInstancePopulatesNetworkInfo(c *gc.C) { 218 broker, brokerErr := s.newKVMBroker(c) 219 c.Assert(brokerErr, jc.ErrorIsNil) 220 221 patchResolvConf(s, c) 222 223 result, err := s.startInstance(c, broker, "1/kvm/42") 224 c.Assert(err, jc.ErrorIsNil) 225 226 c.Assert(result.NetworkInfo, gc.HasLen, 1) 227 iface := result.NetworkInfo[0] 228 c.Assert(iface, jc.DeepEquals, network.InterfaceInfo{ 229 DeviceIndex: 0, 230 CIDR: "0.1.2.0/24", 231 InterfaceName: "dummy0", 232 ParentInterfaceName: "virbr0", 233 MACAddress: "aa:bb:cc:dd:ee:ff", 234 Address: network.NewAddress("0.1.2.3"), 235 GatewayAddress: network.NewAddress("0.1.2.1"), 236 DNSServers: network.NewAddresses("ns1.dummy", "ns2.dummy"), 237 DNSSearchDomains: []string{"dummy", "invalid"}, 238 }) 239 } 240 241 func (s *kvmBrokerSuite) TestStartInstancePopulatesFallbackNetworkInfo(c *gc.C) { 242 broker, brokerErr := s.newKVMBroker(c) 243 c.Assert(brokerErr, jc.ErrorIsNil) 244 245 patchResolvConf(s, c) 246 247 s.api.SetErrors( 248 nil, // ContainerConfig succeeds 249 nil, // HostChangesForContainer succeeds 250 errors.NotSupportedf("container address allocation"), 251 ) 252 _, err := s.startInstance(c, broker, "1/kvm/2") 253 c.Assert(err, gc.ErrorMatches, "container address allocation not supported") 254 } 255 256 func (s *kvmBrokerSuite) TestStartInstanceWithCloudInitUserData(c *gc.C) { 257 broker, brokerErr := s.newKVMBrokerFakeManager(c) 258 c.Assert(brokerErr, jc.ErrorIsNil) 259 260 _, err := s.startInstance(c, broker, "1/kvm/0") 261 c.Assert(err, jc.ErrorIsNil) 262 263 s.manager.CheckCallNames(c, "CreateContainer") 264 call := s.manager.Calls()[0] 265 c.Assert(call.Args[0], gc.FitsTypeOf, &instancecfg.InstanceConfig{}) 266 instanceConfig := call.Args[0].(*instancecfg.InstanceConfig) 267 assertCloudInitUserData(instanceConfig.CloudInitUserData, map[string]interface{}{ 268 "packages": []interface{}{"python-keystoneclient", "python-glanceclient"}, 269 "preruncmd": []interface{}{"mkdir /tmp/preruncmd", "mkdir /tmp/preruncmd2"}, 270 "postruncmd": []interface{}{"mkdir /tmp/postruncmd", "mkdir /tmp/postruncmd2"}, 271 "package_upgrade": false, 272 }, c) 273 } 274 275 func (s *kvmBrokerSuite) TestStartInstanceWithContainerInheritProperties(c *gc.C) { 276 s.PatchValue(&provisioner.GetMachineCloudInitData, func(_ string) (map[string]interface{}, error) { 277 return map[string]interface{}{ 278 "packages": []interface{}{"python-novaclient"}, 279 "fake-entry": []interface{}{"testing-garbage"}, 280 "apt": map[interface{}]interface{}{ 281 "primary": []interface{}{ 282 map[interface{}]interface{}{ 283 "arches": []interface{}{"default"}, 284 "uri": "http://archive.ubuntu.com/ubuntu", 285 }, 286 }, 287 "security": []interface{}{ 288 map[interface{}]interface{}{ 289 "arches": []interface{}{"default"}, 290 "uri": "http://archive.ubuntu.com/ubuntu", 291 }, 292 }, 293 }, 294 "ca-certs": map[interface{}]interface{}{ 295 "remove-defaults": true, 296 "trusted": []interface{}{"-----BEGIN CERTIFICATE-----\nYOUR-ORGS-TRUSTED-CA-CERT-HERE\n-----END CERTIFICATE-----\n"}, 297 }, 298 }, nil 299 }) 300 s.api.fakeContainerConfig.ContainerInheritProperties = "ca-certs,apt-security" 301 302 broker, brokerErr := s.newKVMBrokerFakeManager(c) 303 c.Assert(brokerErr, jc.ErrorIsNil) 304 305 _, err := s.startInstance(c, broker, "1/kvm/0") 306 c.Assert(err, jc.ErrorIsNil) 307 308 s.manager.CheckCallNames(c, "CreateContainer") 309 call := s.manager.Calls()[0] 310 c.Assert(call.Args[0], gc.FitsTypeOf, &instancecfg.InstanceConfig{}) 311 instanceConfig := call.Args[0].(*instancecfg.InstanceConfig) 312 assertCloudInitUserData(instanceConfig.CloudInitUserData, map[string]interface{}{ 313 "packages": []interface{}{"python-keystoneclient", "python-glanceclient"}, 314 "preruncmd": []interface{}{"mkdir /tmp/preruncmd", "mkdir /tmp/preruncmd2"}, 315 "postruncmd": []interface{}{"mkdir /tmp/postruncmd", "mkdir /tmp/postruncmd2"}, 316 "package_upgrade": false, 317 "apt": map[string]interface{}{ 318 "security": []interface{}{ 319 map[interface{}]interface{}{ 320 "arches": []interface{}{"default"}, 321 "uri": "http://archive.ubuntu.com/ubuntu", 322 }, 323 }, 324 }, 325 "ca-certs": map[interface{}]interface{}{ 326 "remove-defaults": true, 327 "trusted": []interface{}{"-----BEGIN CERTIFICATE-----\nYOUR-ORGS-TRUSTED-CA-CERT-HERE\n-----END CERTIFICATE-----\n"}, 328 }, 329 }, c) 330 } 331 332 type kvmProvisionerSuite struct { 333 CommonProvisionerSuite 334 kvmSuite 335 events chan mock.Event 336 } 337 338 var _ = gc.Suite(&kvmProvisionerSuite{}) 339 340 func (s *kvmProvisionerSuite) SetUpSuite(c *gc.C) { 341 if runtime.GOOS == "windows" { 342 c.Skip("Skipping kvm tests on windows") 343 } 344 s.CommonProvisionerSuite.SetUpSuite(c) 345 s.kvmSuite.SetUpSuite(c) 346 } 347 348 func (s *kvmProvisionerSuite) TearDownSuite(c *gc.C) { 349 s.kvmSuite.TearDownSuite(c) 350 s.CommonProvisionerSuite.TearDownSuite(c) 351 } 352 353 func (s *kvmProvisionerSuite) SetUpTest(c *gc.C) { 354 s.CommonProvisionerSuite.SetUpTest(c) 355 s.kvmSuite.SetUpTest(c) 356 357 s.events = make(chan mock.Event, 25) 358 s.ContainerFactory.AddListener(s.events) 359 } 360 361 func (s *kvmProvisionerSuite) nextEvent(c *gc.C) mock.Event { 362 select { 363 case event := <-s.events: 364 return event 365 case <-time.After(coretesting.LongWait): 366 c.Fatalf("no event arrived") 367 } 368 panic("not reachable") 369 } 370 371 func (s *kvmProvisionerSuite) expectStarted(c *gc.C, machine *state.Machine) string { 372 s.State.StartSync() 373 event := s.nextEvent(c) 374 c.Assert(event.Action, gc.Equals, mock.Started) 375 err := machine.Refresh() 376 c.Assert(err, jc.ErrorIsNil) 377 s.waitInstanceId(c, machine, instance.Id(event.InstanceId)) 378 return event.InstanceId 379 } 380 381 func (s *kvmProvisionerSuite) expectStopped(c *gc.C, instId string) { 382 s.State.StartSync() 383 event := s.nextEvent(c) 384 c.Assert(event.Action, gc.Equals, mock.Stopped) 385 c.Assert(event.InstanceId, gc.Equals, instId) 386 } 387 388 func (s *kvmProvisionerSuite) expectNoEvents(c *gc.C) { 389 select { 390 case event := <-s.events: 391 c.Fatalf("unexpected event %#v", event) 392 case <-time.After(coretesting.ShortWait): 393 return 394 } 395 } 396 397 func (s *kvmProvisionerSuite) TearDownTest(c *gc.C) { 398 close(s.events) 399 s.kvmSuite.TearDownTest(c) 400 s.CommonProvisionerSuite.TearDownTest(c) 401 } 402 403 func noopPrepareHostFunc(names.MachineTag, loggo.Logger, <-chan struct{}) error { 404 return nil 405 } 406 407 func (s *kvmProvisionerSuite) newKvmProvisioner(c *gc.C) provisioner.Provisioner { 408 machineTag := names.NewMachineTag("0") 409 agentConfig := s.AgentConfigForTag(c, machineTag) 410 manager := &fakeContainerManager{} 411 broker, brokerErr := provisioner.NewKVMBroker(noopPrepareHostFunc, s.provisioner, manager, agentConfig) 412 c.Assert(brokerErr, jc.ErrorIsNil) 413 toolsFinder := (*provisioner.GetToolsFinder)(s.provisioner) 414 w, err := provisioner.NewContainerProvisioner(instance.KVM, s.provisioner, agentConfig, broker, 415 toolsFinder, &mockDistributionGroupFinder{}, &credentialAPIForTest{}) 416 c.Assert(err, jc.ErrorIsNil) 417 return w 418 } 419 420 func (s *kvmProvisionerSuite) TestProvisionerStartStop(c *gc.C) { 421 p := s.newKvmProvisioner(c) 422 workertest.CleanKill(c, p) 423 } 424 425 func (s *kvmProvisionerSuite) TestDoesNotStartEnvironMachines(c *gc.C) { 426 p := s.newKvmProvisioner(c) 427 defer workertest.CleanKill(c, p) 428 429 // Check that an instance is not provisioned when the machine is created. 430 _, err := s.State.AddMachine(supportedversion.SupportedLTS(), state.JobHostUnits) 431 c.Assert(err, jc.ErrorIsNil) 432 433 s.expectNoEvents(c) 434 } 435 436 func (s *kvmProvisionerSuite) TestDoesNotHaveRetryWatcher(c *gc.C) { 437 p := s.newKvmProvisioner(c) 438 defer workertest.CleanKill(c, p) 439 440 w, err := provisioner.GetRetryWatcher(p) 441 c.Assert(w, gc.IsNil) 442 c.Assert(err, jc.Satisfies, errors.IsNotImplemented) 443 } 444 445 func (s *kvmProvisionerSuite) addContainer(c *gc.C) *state.Machine { 446 template := state.MachineTemplate{ 447 Series: supportedversion.SupportedLTS(), 448 Jobs: []state.MachineJob{state.JobHostUnits}, 449 } 450 container, err := s.State.AddMachineInsideMachine(template, "0", instance.KVM) 451 c.Assert(err, jc.ErrorIsNil) 452 return container 453 } 454 455 func (s *kvmProvisionerSuite) TestContainerStartedAndStopped(c *gc.C) { 456 if arch.NormaliseArch(runtime.GOARCH) != arch.AMD64 { 457 c.Skip("Test only enabled on amd64, see bug lp:1572145") 458 } 459 p := s.newKvmProvisioner(c) 460 defer workertest.CleanKill(c, p) 461 462 container := s.addContainer(c) 463 464 // TODO(jam): 2016-12-22 recent changes to check for networking changes 465 // when starting a container cause this test to start failing, because 466 // the Dummy provider does not support Networking configuration. 467 _, _, err := s.provisioner.HostChangesForContainer(container.MachineTag()) 468 c.Assert(err, gc.ErrorMatches, "dummy provider network config not supported.*") 469 c.Skip("dummy provider doesn't support network config. https://pad.lv/1651974") 470 instId := s.expectStarted(c, container) 471 472 // ...and removed, along with the machine, when the machine is Dead. 473 c.Assert(container.EnsureDead(), gc.IsNil) 474 s.expectStopped(c, instId) 475 s.waitForRemovalMark(c, container) 476 } 477 478 func (s *kvmProvisionerSuite) TestKVMProvisionerObservesConfigChanges(c *gc.C) { 479 p := s.newKvmProvisioner(c) 480 defer workertest.CleanKill(c, p) 481 s.assertProvisionerObservesConfigChanges(c, p) 482 }