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  }