github.com/mwhudson/juju@v0.0.0-20160512215208-90ff01f3497f/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 "io/ioutil" 9 "net" 10 "path/filepath" 11 "runtime" 12 "time" 13 14 "github.com/juju/errors" 15 "github.com/juju/names" 16 gitjujutesting "github.com/juju/testing" 17 jc "github.com/juju/testing/checkers" 18 "github.com/juju/utils/arch" 19 "github.com/juju/utils/series" 20 "github.com/juju/version" 21 gc "gopkg.in/check.v1" 22 23 "github.com/juju/juju/agent" 24 "github.com/juju/juju/cloudconfig/instancecfg" 25 "github.com/juju/juju/constraints" 26 "github.com/juju/juju/container" 27 "github.com/juju/juju/container/kvm/mock" 28 kvmtesting "github.com/juju/juju/container/kvm/testing" 29 "github.com/juju/juju/environs" 30 "github.com/juju/juju/feature" 31 "github.com/juju/juju/instance" 32 instancetest "github.com/juju/juju/instance/testing" 33 jujutesting "github.com/juju/juju/juju/testing" 34 "github.com/juju/juju/network" 35 "github.com/juju/juju/state" 36 "github.com/juju/juju/status" 37 coretesting "github.com/juju/juju/testing" 38 coretools "github.com/juju/juju/tools" 39 jujuversion "github.com/juju/juju/version" 40 "github.com/juju/juju/worker/provisioner" 41 ) 42 43 type kvmSuite struct { 44 kvmtesting.TestSuite 45 events chan mock.Event 46 eventsDone chan struct{} 47 } 48 49 type kvmBrokerSuite struct { 50 kvmSuite 51 broker environs.InstanceBroker 52 agentConfig agent.Config 53 api *fakeAPI 54 } 55 56 var _ = gc.Suite(&kvmBrokerSuite{}) 57 58 func (s *kvmSuite) SetUpTest(c *gc.C) { 59 if runtime.GOOS == "windows" { 60 c.Skip("Skipping kvm tests on windows") 61 } 62 s.TestSuite.SetUpTest(c) 63 s.events = make(chan mock.Event) 64 s.eventsDone = make(chan struct{}) 65 go func() { 66 defer close(s.eventsDone) 67 for event := range s.events { 68 c.Output(3, fmt.Sprintf("kvm event: <%s, %s>", event.Action, event.InstanceId)) 69 } 70 }() 71 s.TestSuite.ContainerFactory.AddListener(s.events) 72 } 73 74 func (s *kvmSuite) TearDownTest(c *gc.C) { 75 close(s.events) 76 <-s.eventsDone 77 s.TestSuite.TearDownTest(c) 78 } 79 80 func (s *kvmBrokerSuite) SetUpTest(c *gc.C) { 81 if runtime.GOOS == "windows" { 82 c.Skip("Skipping kvm tests on windows") 83 } 84 s.kvmSuite.SetUpTest(c) 85 var err error 86 s.agentConfig, err = agent.NewAgentConfig( 87 agent.AgentConfigParams{ 88 Paths: agent.NewPathsWithDefaults(agent.Paths{DataDir: "/not/used/here"}), 89 Tag: names.NewUnitTag("ubuntu/1"), 90 UpgradedToVersion: jujuversion.Current, 91 Password: "dummy-secret", 92 Nonce: "nonce", 93 APIAddresses: []string{"10.0.0.1:1234"}, 94 CACert: coretesting.CACert, 95 Model: coretesting.ModelTag, 96 }) 97 c.Assert(err, jc.ErrorIsNil) 98 s.api = NewFakeAPI() 99 managerConfig := container.ManagerConfig{container.ConfigName: "juju"} 100 s.broker, err = provisioner.NewKvmBroker(s.api, s.agentConfig, managerConfig, false) 101 c.Assert(err, jc.ErrorIsNil) 102 } 103 104 func (s *kvmBrokerSuite) instanceConfig(c *gc.C, machineId string) *instancecfg.InstanceConfig { 105 machineNonce := "fake-nonce" 106 // To isolate the tests from the host's architecture, we override it here. 107 s.PatchValue(&arch.HostArch, func() string { return arch.AMD64 }) 108 stateInfo := jujutesting.FakeStateInfo(machineId) 109 apiInfo := jujutesting.FakeAPIInfo(machineId) 110 instanceConfig, err := instancecfg.NewInstanceConfig(machineId, machineNonce, "released", "quantal", "", true, stateInfo, apiInfo) 111 c.Assert(err, jc.ErrorIsNil) 112 return instanceConfig 113 } 114 115 func (s *kvmBrokerSuite) startInstance(c *gc.C, machineId string) instance.Instance { 116 instanceConfig := s.instanceConfig(c, machineId) 117 cons := constraints.Value{} 118 possibleTools := coretools.List{&coretools.Tools{ 119 Version: version.MustParseBinary("2.3.4-quantal-amd64"), 120 URL: "http://tools.testing.invalid/2.3.4-quantal-amd64.tgz", 121 }, { 122 // non-host-arch tools should be filtered out by StartInstance 123 Version: version.MustParseBinary("2.3.4-quantal-arm64"), 124 URL: "http://tools.testing.invalid/2.3.4-quantal-arm64.tgz", 125 }} 126 callback := func(settableStatus status.Status, info string, data map[string]interface{}) error { 127 return nil 128 } 129 result, err := s.broker.StartInstance(environs.StartInstanceParams{ 130 Constraints: cons, 131 Tools: possibleTools, 132 InstanceConfig: instanceConfig, 133 StatusCallback: callback, 134 }) 135 c.Assert(err, jc.ErrorIsNil) 136 return result.Instance 137 } 138 139 func (s *kvmBrokerSuite) maintainInstance(c *gc.C, machineId string) { 140 machineNonce := "fake-nonce" 141 stateInfo := jujutesting.FakeStateInfo(machineId) 142 apiInfo := jujutesting.FakeAPIInfo(machineId) 143 instanceConfig, err := instancecfg.NewInstanceConfig(machineId, machineNonce, "released", "quantal", "", true, stateInfo, apiInfo) 144 c.Assert(err, jc.ErrorIsNil) 145 cons := constraints.Value{} 146 possibleTools := coretools.List{&coretools.Tools{ 147 Version: version.MustParseBinary("2.3.4-quantal-amd64"), 148 URL: "http://tools.testing.invalid/2.3.4-quantal-amd64.tgz", 149 }} 150 callback := func(settableStatus status.Status, info string, data map[string]interface{}) error { 151 return nil 152 } 153 err = s.broker.MaintainInstance(environs.StartInstanceParams{ 154 Constraints: cons, 155 Tools: possibleTools, 156 InstanceConfig: instanceConfig, 157 StatusCallback: callback, 158 }) 159 c.Assert(err, jc.ErrorIsNil) 160 } 161 162 func (s *kvmBrokerSuite) TestStartInstance(c *gc.C) { 163 machineId := "1/kvm/0" 164 s.SetFeatureFlags(feature.AddressAllocation) 165 kvm := s.startInstance(c, machineId) 166 s.api.CheckCalls(c, []gitjujutesting.StubCall{{ 167 FuncName: "ContainerConfig", 168 }, { 169 FuncName: "PrepareContainerInterfaceInfo", 170 Args: []interface{}{names.NewMachineTag("1-kvm-0")}, 171 }}) 172 c.Assert(kvm.Id(), gc.Equals, instance.Id("juju-machine-1-kvm-0")) 173 s.assertInstances(c, kvm) 174 } 175 176 func (s *kvmBrokerSuite) TestStartInstanceAddressAllocationDisabled(c *gc.C) { 177 machineId := "1/kvm/0" 178 kvm := s.startInstance(c, machineId) 179 s.api.CheckCalls(c, []gitjujutesting.StubCall{{ 180 FuncName: "ContainerConfig", 181 }, { 182 FuncName: "PrepareContainerInterfaceInfo", 183 Args: []interface{}{names.NewMachineTag("1-kvm-0")}, 184 }}) 185 c.Assert(kvm.Id(), gc.Equals, instance.Id("juju-machine-1-kvm-0")) 186 s.assertInstances(c, kvm) 187 } 188 189 func (s *kvmBrokerSuite) TestMaintainInstance(c *gc.C) { 190 machineId := "1/kvm/0" 191 s.SetFeatureFlags(feature.AddressAllocation) 192 kvm := s.startInstance(c, machineId) 193 s.api.ResetCalls() 194 195 s.maintainInstance(c, machineId) 196 s.api.CheckCalls(c, []gitjujutesting.StubCall{{ 197 FuncName: "GetContainerInterfaceInfo", 198 Args: []interface{}{names.NewMachineTag("1-kvm-0")}, 199 }}) 200 c.Assert(kvm.Id(), gc.Equals, instance.Id("juju-machine-1-kvm-0")) 201 s.assertInstances(c, kvm) 202 } 203 204 func (s *kvmBrokerSuite) TestMaintainInstanceAddressAllocationDisabled(c *gc.C) { 205 machineId := "1/kvm/0" 206 kvm := s.startInstance(c, machineId) 207 s.api.ResetCalls() 208 209 s.maintainInstance(c, machineId) 210 s.api.CheckCalls(c, []gitjujutesting.StubCall{}) 211 c.Assert(kvm.Id(), gc.Equals, instance.Id("juju-machine-1-kvm-0")) 212 s.assertInstances(c, kvm) 213 } 214 215 func (s *kvmBrokerSuite) TestStopInstance(c *gc.C) { 216 kvm0 := s.startInstance(c, "1/kvm/0") 217 kvm1 := s.startInstance(c, "1/kvm/1") 218 kvm2 := s.startInstance(c, "1/kvm/2") 219 220 err := s.broker.StopInstances(kvm0.Id()) 221 c.Assert(err, jc.ErrorIsNil) 222 s.assertInstances(c, kvm1, kvm2) 223 c.Assert(s.kvmContainerDir(kvm0), jc.DoesNotExist) 224 c.Assert(s.kvmRemovedContainerDir(kvm0), jc.IsDirectory) 225 226 err = s.broker.StopInstances(kvm1.Id(), kvm2.Id()) 227 c.Assert(err, jc.ErrorIsNil) 228 s.assertInstances(c) 229 } 230 231 func (s *kvmBrokerSuite) TestAllInstances(c *gc.C) { 232 kvm0 := s.startInstance(c, "1/kvm/0") 233 kvm1 := s.startInstance(c, "1/kvm/1") 234 s.assertInstances(c, kvm0, kvm1) 235 236 err := s.broker.StopInstances(kvm1.Id()) 237 c.Assert(err, jc.ErrorIsNil) 238 kvm2 := s.startInstance(c, "1/kvm/2") 239 s.assertInstances(c, kvm0, kvm2) 240 } 241 242 func (s *kvmBrokerSuite) assertInstances(c *gc.C, inst ...instance.Instance) { 243 results, err := s.broker.AllInstances() 244 c.Assert(err, jc.ErrorIsNil) 245 instancetest.MatchInstances(c, results, inst...) 246 } 247 248 func (s *kvmBrokerSuite) kvmContainerDir(inst instance.Instance) string { 249 return filepath.Join(s.ContainerDir, string(inst.Id())) 250 } 251 252 func (s *kvmBrokerSuite) kvmRemovedContainerDir(inst instance.Instance) string { 253 return filepath.Join(s.RemovedDir, string(inst.Id())) 254 } 255 256 func (s *kvmBrokerSuite) TestStartInstancePopulatesNetworkInfo(c *gc.C) { 257 s.SetFeatureFlags(feature.AddressAllocation) 258 s.PatchValue(provisioner.InterfaceAddrs, func(i *net.Interface) ([]net.Addr, error) { 259 return []net.Addr{&fakeAddr{"0.1.2.1/24"}}, nil 260 }) 261 fakeResolvConf := filepath.Join(c.MkDir(), "resolv.conf") 262 err := ioutil.WriteFile(fakeResolvConf, []byte("nameserver ns1.dummy\n"), 0644) 263 c.Assert(err, jc.ErrorIsNil) 264 s.PatchValue(provisioner.ResolvConf, fakeResolvConf) 265 266 instanceConfig := s.instanceConfig(c, "42") 267 possibleTools := coretools.List{&coretools.Tools{ 268 Version: version.MustParseBinary("2.3.4-quantal-amd64"), 269 URL: "http://tools.testing.invalid/2.3.4-quantal-amd64.tgz", 270 }} 271 callback := func(settableStatus status.Status, info string, data map[string]interface{}) error { 272 return nil 273 } 274 result, err := s.broker.StartInstance(environs.StartInstanceParams{ 275 Constraints: constraints.Value{}, 276 Tools: possibleTools, 277 InstanceConfig: instanceConfig, 278 StatusCallback: callback, 279 }) 280 c.Assert(err, jc.ErrorIsNil) 281 c.Assert(result.NetworkInfo, gc.HasLen, 1) 282 iface := result.NetworkInfo[0] 283 c.Assert(err, jc.ErrorIsNil) 284 c.Assert(iface, jc.DeepEquals, network.InterfaceInfo{ 285 DeviceIndex: 0, 286 CIDR: "0.1.2.0/24", 287 ConfigType: network.ConfigStatic, 288 InterfaceName: "eth0", // generated from the device index. 289 DNSServers: network.NewAddresses("ns1.dummy"), 290 DNSSearchDomains: []string{""}, 291 MACAddress: "aa:bb:cc:dd:ee:ff", 292 Address: network.NewAddress("0.1.2.3"), 293 GatewayAddress: network.NewAddress("0.1.2.1"), 294 }) 295 } 296 297 type kvmProvisionerSuite struct { 298 CommonProvisionerSuite 299 kvmSuite 300 events chan mock.Event 301 } 302 303 var _ = gc.Suite(&kvmProvisionerSuite{}) 304 305 func (s *kvmProvisionerSuite) SetUpSuite(c *gc.C) { 306 if runtime.GOOS == "windows" { 307 c.Skip("Skipping kvm tests on windows") 308 } 309 s.CommonProvisionerSuite.SetUpSuite(c) 310 s.kvmSuite.SetUpSuite(c) 311 } 312 313 func (s *kvmProvisionerSuite) TearDownSuite(c *gc.C) { 314 s.kvmSuite.TearDownSuite(c) 315 s.CommonProvisionerSuite.TearDownSuite(c) 316 } 317 318 func (s *kvmProvisionerSuite) SetUpTest(c *gc.C) { 319 s.CommonProvisionerSuite.SetUpTest(c) 320 s.kvmSuite.SetUpTest(c) 321 322 s.events = make(chan mock.Event, 25) 323 s.ContainerFactory.AddListener(s.events) 324 } 325 326 func (s *kvmProvisionerSuite) nextEvent(c *gc.C) mock.Event { 327 select { 328 case event := <-s.events: 329 return event 330 case <-time.After(coretesting.LongWait): 331 c.Fatalf("no event arrived") 332 } 333 panic("not reachable") 334 } 335 336 func (s *kvmProvisionerSuite) expectStarted(c *gc.C, machine *state.Machine) string { 337 s.State.StartSync() 338 event := s.nextEvent(c) 339 c.Assert(event.Action, gc.Equals, mock.Started) 340 err := machine.Refresh() 341 c.Assert(err, jc.ErrorIsNil) 342 s.waitInstanceId(c, machine, instance.Id(event.InstanceId)) 343 return event.InstanceId 344 } 345 346 func (s *kvmProvisionerSuite) expectStopped(c *gc.C, instId string) { 347 s.State.StartSync() 348 event := s.nextEvent(c) 349 c.Assert(event.Action, gc.Equals, mock.Stopped) 350 c.Assert(event.InstanceId, gc.Equals, instId) 351 } 352 353 func (s *kvmProvisionerSuite) expectNoEvents(c *gc.C) { 354 select { 355 case event := <-s.events: 356 c.Fatalf("unexpected event %#v", event) 357 case <-time.After(coretesting.ShortWait): 358 return 359 } 360 } 361 362 func (s *kvmProvisionerSuite) TearDownTest(c *gc.C) { 363 close(s.events) 364 s.kvmSuite.TearDownTest(c) 365 s.CommonProvisionerSuite.TearDownTest(c) 366 } 367 368 func (s *kvmProvisionerSuite) newKvmProvisioner(c *gc.C) provisioner.Provisioner { 369 machineTag := names.NewMachineTag("0") 370 agentConfig := s.AgentConfigForTag(c, machineTag) 371 managerConfig := container.ManagerConfig{container.ConfigName: "juju"} 372 broker, err := provisioner.NewKvmBroker(s.provisioner, agentConfig, managerConfig, false) 373 c.Assert(err, jc.ErrorIsNil) 374 toolsFinder := (*provisioner.GetToolsFinder)(s.provisioner) 375 w, err := provisioner.NewContainerProvisioner(instance.KVM, s.provisioner, agentConfig, broker, toolsFinder) 376 c.Assert(err, jc.ErrorIsNil) 377 return w 378 } 379 380 func (s *kvmProvisionerSuite) TestProvisionerStartStop(c *gc.C) { 381 p := s.newKvmProvisioner(c) 382 stop(c, p) 383 } 384 385 func (s *kvmProvisionerSuite) TestDoesNotStartEnvironMachines(c *gc.C) { 386 p := s.newKvmProvisioner(c) 387 defer stop(c, p) 388 389 // Check that an instance is not provisioned when the machine is created. 390 _, err := s.State.AddMachine(series.LatestLts(), state.JobHostUnits) 391 c.Assert(err, jc.ErrorIsNil) 392 393 s.expectNoEvents(c) 394 } 395 396 func (s *kvmProvisionerSuite) TestDoesNotHaveRetryWatcher(c *gc.C) { 397 p := s.newKvmProvisioner(c) 398 defer stop(c, p) 399 400 w, err := provisioner.GetRetryWatcher(p) 401 c.Assert(w, gc.IsNil) 402 c.Assert(err, jc.Satisfies, errors.IsNotImplemented) 403 } 404 405 func (s *kvmProvisionerSuite) addContainer(c *gc.C) *state.Machine { 406 template := state.MachineTemplate{ 407 Series: series.LatestLts(), 408 Jobs: []state.MachineJob{state.JobHostUnits}, 409 } 410 container, err := s.State.AddMachineInsideMachine(template, "0", instance.KVM) 411 c.Assert(err, jc.ErrorIsNil) 412 return container 413 } 414 415 func (s *kvmProvisionerSuite) TestContainerStartedAndStopped(c *gc.C) { 416 if arch.NormaliseArch(runtime.GOARCH) != arch.AMD64 { 417 c.Skip("Test only enabled on amd64, see bug lp:1572145") 418 } 419 p := s.newKvmProvisioner(c) 420 defer stop(c, p) 421 422 container := s.addContainer(c) 423 424 instId := s.expectStarted(c, container) 425 426 // ...and removed, along with the machine, when the machine is Dead. 427 c.Assert(container.EnsureDead(), gc.IsNil) 428 s.expectStopped(c, instId) 429 s.waitRemoved(c, container) 430 } 431 432 func (s *kvmProvisionerSuite) TestKVMProvisionerObservesConfigChanges(c *gc.C) { 433 p := s.newKvmProvisioner(c) 434 defer stop(c, p) 435 s.assertProvisionerObservesConfigChanges(c, p) 436 }