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