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