github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/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  	gc "gopkg.in/check.v1"
    16  
    17  	"github.com/juju/juju/constraints"
    18  	"github.com/juju/juju/container"
    19  	"github.com/juju/juju/container/kvm"
    20  	kvmtesting "github.com/juju/juju/container/kvm/testing"
    21  	containertesting "github.com/juju/juju/container/testing"
    22  	"github.com/juju/juju/environs/config"
    23  	"github.com/juju/juju/instance"
    24  	"github.com/juju/juju/juju/arch"
    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  			{MACAddress: "00:16:3e:20:b0:11"},
   111  		},
   112  	}
   113  	tempDir := c.MkDir()
   114  
   115  	templatePath := filepath.Join(tempDir, "kvm.xml")
   116  	err := kvm.WriteTemplate(templatePath, params)
   117  	c.Assert(err, jc.ErrorIsNil)
   118  	templateBytes, err := ioutil.ReadFile(templatePath)
   119  	c.Assert(err, jc.ErrorIsNil)
   120  
   121  	template := string(templateBytes)
   122  
   123  	c.Assert(template, jc.Contains, "<name>foo-bar</name>")
   124  	c.Assert(template, jc.Contains, "<mac address='00:16:3e:20:b0:11'/>")
   125  	c.Assert(template, jc.Contains, "<source bridge='br0'/>")
   126  	c.Assert(strings.Count(string(template), "<interface type='bridge'>"), gc.Equals, 1)
   127  }
   128  
   129  func (s *KVMSuite) TestCreateMachineUsesTemplate(c *gc.C) {
   130  	const uvtKvmBinName = "uvt-kvm"
   131  	testing.PatchExecutableAsEchoArgs(c, s, uvtKvmBinName)
   132  
   133  	tempDir := c.MkDir()
   134  	params := kvm.CreateMachineParams{
   135  		Hostname:      "foo-bar",
   136  		NetworkBridge: "br0",
   137  		Interfaces: []network.InterfaceInfo{
   138  			{MACAddress: "00:16:3e:20:b0:11"},
   139  		},
   140  		UserDataFile: filepath.Join(tempDir, "something"),
   141  	}
   142  
   143  	err := kvm.CreateMachine(params)
   144  	c.Assert(err, jc.ErrorIsNil)
   145  
   146  	expectedArgs := []string{
   147  		"create",
   148  		"--log-console-output",
   149  		"--user-data",
   150  		filepath.Join(tempDir, "something"),
   151  		"--template",
   152  		filepath.Join(tempDir, "kvm-template.xml"),
   153  		"foo-bar",
   154  	}
   155  
   156  	testing.AssertEchoArgs(c, uvtKvmBinName, expectedArgs...)
   157  }
   158  
   159  func (s *KVMSuite) TestDestroyContainer(c *gc.C) {
   160  	instance := containertesting.CreateContainer(c, s.manager, "1/lxc/0")
   161  
   162  	err := s.manager.DestroyContainer(instance.Id())
   163  	c.Assert(err, jc.ErrorIsNil)
   164  
   165  	name := string(instance.Id())
   166  	// Check that the container dir is no longer in the container dir
   167  	c.Assert(filepath.Join(s.ContainerDir, name), jc.DoesNotExist)
   168  	// but instead, in the removed container dir
   169  	c.Assert(filepath.Join(s.RemovedDir, name), jc.IsDirectory)
   170  }
   171  
   172  // Test that CreateContainer creates proper startParams.
   173  func (s *KVMSuite) TestCreateContainerUtilizesReleaseSimpleStream(c *gc.C) {
   174  
   175  	envCfg, err := config.New(
   176  		config.NoDefaults,
   177  		dummy.SampleConfig().Merge(
   178  			coretesting.Attrs{"image-stream": "released"},
   179  		),
   180  	)
   181  	c.Assert(err, jc.ErrorIsNil)
   182  
   183  	// Mock machineConfig with a mocked simple stream URL.
   184  	instanceConfig, err := containertesting.MockMachineConfig("1/kvm/0")
   185  	c.Assert(err, jc.ErrorIsNil)
   186  	instanceConfig.Config = envCfg
   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: "cpu-cores=4",
   270  		expected: kvm.StartParams{
   271  			Memory:   kvm.DefaultMemory,
   272  			CpuCores: 4,
   273  			RootDisk: kvm.DefaultDisk,
   274  		},
   275  	}, {
   276  		cons: "cpu-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=lxc",
   308  		expected: kvm.StartParams{
   309  			Memory:   kvm.DefaultMemory,
   310  			CpuCores: kvm.DefaultCpu,
   311  			RootDisk: kvm.DefaultDisk,
   312  		},
   313  		infoLog: []string{
   314  			`container constraint of "lxc" 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 cpu-cores=4 root-disk=20G arch=armhf cpu-power=100 container=lxc 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 "lxc" 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, loggo.DEBUG), 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  }