github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/container/kvm/kvm_test.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package kvm_test 5 6 import ( 7 "fmt" 8 "io/ioutil" 9 "path/filepath" 10 "strings" 11 12 "github.com/juju/loggo" 13 "github.com/juju/testing" 14 jc "github.com/juju/testing/checkers" 15 "github.com/juju/utils/arch" 16 gc "gopkg.in/check.v1" 17 18 "github.com/juju/juju/constraints" 19 "github.com/juju/juju/container" 20 "github.com/juju/juju/container/kvm" 21 kvmtesting "github.com/juju/juju/container/kvm/testing" 22 containertesting "github.com/juju/juju/container/testing" 23 "github.com/juju/juju/environs/config" 24 "github.com/juju/juju/instance" 25 "github.com/juju/juju/network" 26 "github.com/juju/juju/provider/dummy" 27 coretesting "github.com/juju/juju/testing" 28 ) 29 30 type KVMSuite struct { 31 kvmtesting.TestSuite 32 manager container.Manager 33 } 34 35 var _ = gc.Suite(&KVMSuite{}) 36 37 func (s *KVMSuite) SetUpTest(c *gc.C) { 38 s.TestSuite.SetUpTest(c) 39 var err error 40 s.manager, err = kvm.NewContainerManager(container.ManagerConfig{container.ConfigName: "test"}) 41 c.Assert(err, jc.ErrorIsNil) 42 } 43 44 func (*KVMSuite) TestManagerNameNeeded(c *gc.C) { 45 manager, err := kvm.NewContainerManager(container.ManagerConfig{container.ConfigName: ""}) 46 c.Assert(err, gc.ErrorMatches, "name is required") 47 c.Assert(manager, gc.IsNil) 48 } 49 50 func (*KVMSuite) TestManagerWarnsAboutUnknownOption(c *gc.C) { 51 _, err := kvm.NewContainerManager(container.ManagerConfig{ 52 container.ConfigName: "BillyBatson", 53 "shazam": "Captain Marvel", 54 }) 55 c.Assert(err, jc.ErrorIsNil) 56 c.Assert(c.GetTestLog(), jc.Contains, `WARNING juju.container unused config option: "shazam" -> "Captain Marvel"`) 57 } 58 59 func (s *KVMSuite) TestListInitiallyEmpty(c *gc.C) { 60 containers, err := s.manager.ListContainers() 61 c.Assert(err, jc.ErrorIsNil) 62 c.Assert(containers, gc.HasLen, 0) 63 } 64 65 func (s *KVMSuite) createRunningContainer(c *gc.C, name string) kvm.Container { 66 kvmContainer := s.ContainerFactory.New(name) 67 network := container.BridgeNetworkConfig("testbr0", 0, nil) 68 c.Assert(kvmContainer.Start(kvm.StartParams{ 69 Series: "quantal", 70 Arch: arch.HostArch(), 71 UserDataFile: "userdata.txt", 72 Network: network}), gc.IsNil) 73 return kvmContainer 74 } 75 76 func (s *KVMSuite) TestListMatchesManagerName(c *gc.C) { 77 s.createRunningContainer(c, "test-match1") 78 s.createRunningContainer(c, "test-match2") 79 s.createRunningContainer(c, "testNoMatch") 80 s.createRunningContainer(c, "other") 81 containers, err := s.manager.ListContainers() 82 c.Assert(err, jc.ErrorIsNil) 83 c.Assert(containers, gc.HasLen, 2) 84 expectedIds := []instance.Id{"test-match1", "test-match2"} 85 ids := []instance.Id{containers[0].Id(), containers[1].Id()} 86 c.Assert(ids, jc.SameContents, expectedIds) 87 } 88 89 func (s *KVMSuite) TestListMatchesRunningContainers(c *gc.C) { 90 running := s.createRunningContainer(c, "test-running") 91 s.ContainerFactory.New("test-stopped") 92 containers, err := s.manager.ListContainers() 93 c.Assert(err, jc.ErrorIsNil) 94 c.Assert(containers, gc.HasLen, 1) 95 c.Assert(string(containers[0].Id()), gc.Equals, running.Name()) 96 } 97 98 func (s *KVMSuite) TestCreateContainer(c *gc.C) { 99 instance := containertesting.CreateContainer(c, s.manager, "1/kvm/0") 100 name := string(instance.Id()) 101 cloudInitFilename := filepath.Join(s.ContainerDir, name, "cloud-init") 102 containertesting.AssertCloudInit(c, cloudInitFilename) 103 } 104 105 func (s *KVMSuite) TestWriteTemplate(c *gc.C) { 106 params := kvm.CreateMachineParams{ 107 Hostname: "foo-bar", 108 NetworkBridge: "br0", 109 Interfaces: []network.InterfaceInfo{ 110 { 111 MACAddress: "00:16:3e:20:b0:11", 112 ParentInterfaceName: "br-eth0.10", 113 }, 114 }, 115 } 116 tempDir := c.MkDir() 117 118 templatePath := filepath.Join(tempDir, "kvm.xml") 119 err := kvm.WriteTemplate(templatePath, params) 120 c.Assert(err, jc.ErrorIsNil) 121 templateBytes, err := ioutil.ReadFile(templatePath) 122 c.Assert(err, jc.ErrorIsNil) 123 124 template := string(templateBytes) 125 126 c.Assert(template, jc.Contains, "<name>foo-bar</name>") 127 c.Assert(template, jc.Contains, "<mac address='00:16:3e:20:b0:11'/>") 128 c.Assert(template, jc.Contains, "<source bridge='br-eth0.10'/>") 129 c.Assert(strings.Count(string(template), "<interface type='bridge'>"), gc.Equals, 1) 130 } 131 132 func (s *KVMSuite) TestCreateMachineUsesTemplate(c *gc.C) { 133 const uvtKvmBinName = "uvt-kvm" 134 testing.PatchExecutableAsEchoArgs(c, s, uvtKvmBinName) 135 136 tempDir := c.MkDir() 137 params := kvm.CreateMachineParams{ 138 Hostname: "foo-bar", 139 NetworkBridge: "br0", 140 Interfaces: []network.InterfaceInfo{ 141 {MACAddress: "00:16:3e:20:b0:11"}, 142 }, 143 UserDataFile: filepath.Join(tempDir, "something"), 144 } 145 146 err := kvm.CreateMachine(params) 147 c.Assert(err, jc.ErrorIsNil) 148 149 expectedArgs := []string{ 150 "create", 151 "--log-console-output", 152 "--user-data", 153 filepath.Join(tempDir, "something"), 154 "--template", 155 filepath.Join(tempDir, "kvm-template.xml"), 156 "foo-bar", 157 } 158 159 testing.AssertEchoArgs(c, uvtKvmBinName, expectedArgs...) 160 } 161 162 func (s *KVMSuite) TestDestroyContainer(c *gc.C) { 163 instance := containertesting.CreateContainer(c, s.manager, "1/lxc/0") 164 165 err := s.manager.DestroyContainer(instance.Id()) 166 c.Assert(err, jc.ErrorIsNil) 167 168 name := string(instance.Id()) 169 // Check that the container dir is no longer in the container dir 170 c.Assert(filepath.Join(s.ContainerDir, name), jc.DoesNotExist) 171 // but instead, in the removed container dir 172 c.Assert(filepath.Join(s.RemovedDir, name), jc.IsDirectory) 173 } 174 175 // Test that CreateContainer creates proper startParams. 176 func (s *KVMSuite) TestCreateContainerUtilizesReleaseSimpleStream(c *gc.C) { 177 178 envCfg, err := config.New( 179 config.NoDefaults, 180 dummy.SampleConfig().Merge( 181 coretesting.Attrs{"image-stream": "released"}, 182 ), 183 ) 184 c.Assert(err, jc.ErrorIsNil) 185 186 // Mock machineConfig with a mocked simple stream URL. 187 instanceConfig, err := containertesting.MockMachineConfig("1/kvm/0") 188 c.Assert(err, jc.ErrorIsNil) 189 instanceConfig.Config = envCfg 190 191 // CreateContainer sets TestStartParams internally; we call this 192 // purely for the side-effect. 193 containertesting.CreateContainerWithMachineConfig(c, s.manager, instanceConfig) 194 195 c.Assert(kvm.TestStartParams.ImageDownloadUrl, gc.Equals, "") 196 } 197 198 // Test that CreateContainer creates proper startParams. 199 func (s *KVMSuite) TestCreateContainerUtilizesDailySimpleStream(c *gc.C) { 200 201 // Mock machineConfig with a mocked simple stream URL. 202 instanceConfig, err := containertesting.MockMachineConfig("1/kvm/0") 203 c.Assert(err, jc.ErrorIsNil) 204 instanceConfig.ImageStream = "daily" 205 206 // CreateContainer sets TestStartParams internally; we call this 207 // purely for the side-effect. 208 containertesting.CreateContainerWithMachineConfig(c, s.manager, instanceConfig) 209 210 c.Assert(kvm.TestStartParams.ImageDownloadUrl, gc.Equals, "http://cloud-images.ubuntu.com/daily") 211 } 212 213 func (s *KVMSuite) TestStartContainerUtilizesSimpleStream(c *gc.C) { 214 215 const libvirtBinName = "uvt-simplestreams-libvirt" 216 testing.PatchExecutableAsEchoArgs(c, s, libvirtBinName) 217 218 startParams := kvm.StartParams{ 219 Series: "mocked-series", 220 Arch: "mocked-arch", 221 ImageDownloadUrl: "mocked-url", 222 } 223 mockedContainer := kvm.NewEmptyKvmContainer() 224 mockedContainer.Start(startParams) 225 226 expectedArgs := strings.Split( 227 fmt.Sprintf( 228 "sync arch=%s release=%s --source=%s", 229 startParams.Arch, 230 startParams.Series, 231 startParams.ImageDownloadUrl, 232 ), 233 " ", 234 ) 235 236 testing.AssertEchoArgs(c, libvirtBinName, expectedArgs...) 237 } 238 239 type ConstraintsSuite struct { 240 coretesting.BaseSuite 241 } 242 243 var _ = gc.Suite(&ConstraintsSuite{}) 244 245 func (s *ConstraintsSuite) TestDefaults(c *gc.C) { 246 247 for _, test := range []struct { 248 cons string 249 expected kvm.StartParams 250 infoLog []string 251 }{{ 252 expected: kvm.StartParams{ 253 Memory: kvm.DefaultMemory, 254 CpuCores: kvm.DefaultCpu, 255 RootDisk: kvm.DefaultDisk, 256 }, 257 }, { 258 cons: "mem=256M", 259 expected: kvm.StartParams{ 260 Memory: kvm.MinMemory, 261 CpuCores: kvm.DefaultCpu, 262 RootDisk: kvm.DefaultDisk, 263 }, 264 }, { 265 cons: "mem=4G", 266 expected: kvm.StartParams{ 267 Memory: 4 * 1024, 268 CpuCores: kvm.DefaultCpu, 269 RootDisk: kvm.DefaultDisk, 270 }, 271 }, { 272 cons: "cpu-cores=4", 273 expected: kvm.StartParams{ 274 Memory: kvm.DefaultMemory, 275 CpuCores: 4, 276 RootDisk: kvm.DefaultDisk, 277 }, 278 }, { 279 cons: "cpu-cores=0", 280 expected: kvm.StartParams{ 281 Memory: kvm.DefaultMemory, 282 CpuCores: kvm.MinCpu, 283 RootDisk: kvm.DefaultDisk, 284 }, 285 }, { 286 cons: "root-disk=512M", 287 expected: kvm.StartParams{ 288 Memory: kvm.DefaultMemory, 289 CpuCores: kvm.DefaultCpu, 290 RootDisk: kvm.MinDisk, 291 }, 292 }, { 293 cons: "root-disk=4G", 294 expected: kvm.StartParams{ 295 Memory: kvm.DefaultMemory, 296 CpuCores: kvm.DefaultCpu, 297 RootDisk: 4, 298 }, 299 }, { 300 cons: "arch=armhf", 301 expected: kvm.StartParams{ 302 Memory: kvm.DefaultMemory, 303 CpuCores: kvm.DefaultCpu, 304 RootDisk: kvm.DefaultDisk, 305 }, 306 infoLog: []string{ 307 `arch constraint of "armhf" being ignored as not supported`, 308 }, 309 }, { 310 cons: "container=lxc", 311 expected: kvm.StartParams{ 312 Memory: kvm.DefaultMemory, 313 CpuCores: kvm.DefaultCpu, 314 RootDisk: kvm.DefaultDisk, 315 }, 316 infoLog: []string{ 317 `container constraint of "lxc" being ignored as not supported`, 318 }, 319 }, { 320 cons: "cpu-power=100", 321 expected: kvm.StartParams{ 322 Memory: kvm.DefaultMemory, 323 CpuCores: kvm.DefaultCpu, 324 RootDisk: kvm.DefaultDisk, 325 }, 326 infoLog: []string{ 327 `cpu-power constraint of 100 being ignored as not supported`, 328 }, 329 }, { 330 cons: "tags=foo,bar", 331 expected: kvm.StartParams{ 332 Memory: kvm.DefaultMemory, 333 CpuCores: kvm.DefaultCpu, 334 RootDisk: kvm.DefaultDisk, 335 }, 336 infoLog: []string{ 337 `tags constraint of "foo,bar" being ignored as not supported`, 338 }, 339 }, { 340 cons: "mem=4G cpu-cores=4 root-disk=20G arch=armhf cpu-power=100 container=lxc tags=foo,bar", 341 expected: kvm.StartParams{ 342 Memory: 4 * 1024, 343 CpuCores: 4, 344 RootDisk: 20, 345 }, 346 infoLog: []string{ 347 `arch constraint of "armhf" being ignored as not supported`, 348 `container constraint of "lxc" being ignored as not supported`, 349 `cpu-power constraint of 100 being ignored as not supported`, 350 `tags constraint of "foo,bar" being ignored as not supported`, 351 }, 352 }} { 353 var tw loggo.TestWriter 354 c.Assert(loggo.RegisterWriter("constraint-tester", &tw, loggo.DEBUG), gc.IsNil) 355 cons := constraints.MustParse(test.cons) 356 params := kvm.ParseConstraintsToStartParams(cons) 357 c.Check(params, gc.DeepEquals, test.expected) 358 c.Check(tw.Log(), jc.LogMatches, test.infoLog) 359 loggo.RemoveWriter("constraint-tester") 360 } 361 } 362 363 // Test the output when no binary can be found. 364 func (s *KVMSuite) TestIsKVMSupportedKvmOkNotFound(c *gc.C) { 365 // With no path, and no backup directory, we should fail. 366 s.PatchEnvironment("PATH", "") 367 s.PatchValue(kvm.KVMPath, "") 368 369 supported, err := kvm.IsKVMSupported() 370 c.Check(supported, jc.IsFalse) 371 c.Assert(err, gc.ErrorMatches, "kvm-ok executable not found") 372 } 373 374 // Test the output when the binary is found, but errors out. 375 func (s *KVMSuite) TestIsKVMSupportedBinaryErrorsOut(c *gc.C) { 376 // Clear path so real binary is not found. 377 s.PatchEnvironment("PATH", "") 378 379 // Create mocked binary which returns an error and give the test access. 380 tmpDir := c.MkDir() 381 err := ioutil.WriteFile(filepath.Join(tmpDir, "kvm-ok"), []byte("#!/bin/bash\nexit 127"), 0777) 382 c.Assert(err, jc.ErrorIsNil) 383 s.PatchValue(kvm.KVMPath, tmpDir) 384 385 supported, err := kvm.IsKVMSupported() 386 c.Check(supported, jc.IsFalse) 387 c.Assert(err, gc.ErrorMatches, "exit status 127") 388 } 389 390 // Test the case where kvm-ok is not in the path, but is in the 391 // specified directory. 392 func (s *KVMSuite) TestIsKVMSupportedNoPath(c *gc.C) { 393 // Create a mocked binary so that this test does not fail for 394 // developers without kvm-ok. 395 s.PatchEnvironment("PATH", "") 396 tmpDir := c.MkDir() 397 err := ioutil.WriteFile(filepath.Join(tmpDir, "kvm-ok"), []byte("#!/bin/bash"), 0777) 398 c.Assert(err, jc.ErrorIsNil) 399 s.PatchValue(kvm.KVMPath, tmpDir) 400 401 supported, err := kvm.IsKVMSupported() 402 c.Check(supported, jc.IsTrue) 403 c.Assert(err, jc.ErrorIsNil) 404 } 405 406 // Test the case that kvm-ok is found in the path. 407 func (s *KVMSuite) TestIsKVMSupportedOnlyPath(c *gc.C) { 408 // Create a mocked binary so that this test does not fail for 409 // developers without kvm-ok. 410 tmpDir := c.MkDir() 411 err := ioutil.WriteFile(filepath.Join(tmpDir, "kvm-ok"), []byte("#!/bin/bash"), 0777) 412 s.PatchEnvironment("PATH", tmpDir) 413 414 supported, err := kvm.IsKVMSupported() 415 c.Check(supported, jc.IsTrue) 416 c.Assert(err, jc.ErrorIsNil) 417 } 418 419 func (s *KVMSuite) TestKVMPathIsCorrect(c *gc.C) { 420 c.Assert(*kvm.KVMPath, gc.Equals, "/usr/sbin") 421 }