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