github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/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  	jc "github.com/juju/testing/checkers"
    14  	gc "gopkg.in/check.v1"
    15  
    16  	"github.com/juju/juju/constraints"
    17  	"github.com/juju/juju/container"
    18  	"github.com/juju/juju/container/kvm"
    19  	kvmtesting "github.com/juju/juju/container/kvm/testing"
    20  	containertesting "github.com/juju/juju/container/testing"
    21  	"github.com/juju/juju/environs/config"
    22  	"github.com/juju/juju/instance"
    23  	"github.com/juju/juju/provider/dummy"
    24  	coretesting "github.com/juju/juju/testing"
    25  	"github.com/juju/juju/version"
    26  	"github.com/juju/testing"
    27  )
    28  
    29  type KVMSuite struct {
    30  	kvmtesting.TestSuite
    31  	manager container.Manager
    32  }
    33  
    34  var _ = gc.Suite(&KVMSuite{})
    35  
    36  func (s *KVMSuite) SetUpTest(c *gc.C) {
    37  	s.TestSuite.SetUpTest(c)
    38  	var err error
    39  	s.manager, err = kvm.NewContainerManager(container.ManagerConfig{container.ConfigName: "test"})
    40  	c.Assert(err, jc.ErrorIsNil)
    41  }
    42  
    43  func (*KVMSuite) TestManagerNameNeeded(c *gc.C) {
    44  	manager, err := kvm.NewContainerManager(container.ManagerConfig{container.ConfigName: ""})
    45  	c.Assert(err, gc.ErrorMatches, "name 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.ConfigName: "BillyBatson",
    52  		"shazam":             "Captain Marvel",
    53  	})
    54  	c.Assert(err, jc.ErrorIsNil)
    55  	c.Assert(c.GetTestLog(), jc.Contains, `WARNING 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", nil)
    67  	c.Assert(kvmContainer.Start(kvm.StartParams{
    68  		Series:       "quantal",
    69  		Arch:         version.Current.Arch,
    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, "test-match1")
    77  	s.createRunningContainer(c, "test-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{"test-match1", "test-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, "test-running")
    90  	s.ContainerFactory.New("test-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) TestDestroyContainer(c *gc.C) {
   105  	instance := containertesting.CreateContainer(c, s.manager, "1/lxc/0")
   106  
   107  	err := s.manager.DestroyContainer(instance.Id())
   108  	c.Assert(err, jc.ErrorIsNil)
   109  
   110  	name := string(instance.Id())
   111  	// Check that the container dir is no longer in the container dir
   112  	c.Assert(filepath.Join(s.ContainerDir, name), jc.DoesNotExist)
   113  	// but instead, in the removed container dir
   114  	c.Assert(filepath.Join(s.RemovedDir, name), jc.IsDirectory)
   115  }
   116  
   117  // Test that CreateContainer creates proper startParams.
   118  func (s *KVMSuite) TestCreateContainerUtilizesReleaseSimpleStream(c *gc.C) {
   119  
   120  	envCfg, err := config.New(
   121  		config.NoDefaults,
   122  		dummy.SampleConfig().Merge(
   123  			coretesting.Attrs{"image-stream": "released"},
   124  		),
   125  	)
   126  	c.Assert(err, jc.ErrorIsNil)
   127  
   128  	// Mock machineConfig with a mocked simple stream URL.
   129  	machineConfig, err := containertesting.MockMachineConfig("1/kvm/0")
   130  	c.Assert(err, jc.ErrorIsNil)
   131  	machineConfig.Config = envCfg
   132  
   133  	// CreateContainer sets TestStartParams internally; we call this
   134  	// purely for the side-effect.
   135  	containertesting.CreateContainerWithMachineConfig(c, s.manager, machineConfig)
   136  
   137  	c.Assert(kvm.TestStartParams.ImageDownloadUrl, gc.Equals, "")
   138  }
   139  
   140  // Test that CreateContainer creates proper startParams.
   141  func (s *KVMSuite) TestCreateContainerUtilizesDailySimpleStream(c *gc.C) {
   142  
   143  	// Mock machineConfig with a mocked simple stream URL.
   144  	machineConfig, err := containertesting.MockMachineConfig("1/kvm/0")
   145  	c.Assert(err, jc.ErrorIsNil)
   146  	machineConfig.ImageStream = "daily"
   147  
   148  	// CreateContainer sets TestStartParams internally; we call this
   149  	// purely for the side-effect.
   150  	containertesting.CreateContainerWithMachineConfig(c, s.manager, machineConfig)
   151  
   152  	c.Assert(kvm.TestStartParams.ImageDownloadUrl, gc.Equals, "http://cloud-images.ubuntu.com/daily")
   153  }
   154  
   155  func (s *KVMSuite) TestStartContainerUtilizesSimpleStream(c *gc.C) {
   156  
   157  	const libvirtBinName = "uvt-simplestreams-libvirt"
   158  	testing.PatchExecutableAsEchoArgs(c, s, libvirtBinName)
   159  
   160  	startParams := kvm.StartParams{
   161  		Series:           "mocked-series",
   162  		Arch:             "mocked-arch",
   163  		ImageDownloadUrl: "mocked-url",
   164  	}
   165  	mockedContainer := kvm.NewEmptyKvmContainer()
   166  	mockedContainer.Start(startParams)
   167  
   168  	expectedArgs := strings.Split(
   169  		fmt.Sprintf(
   170  			"sync arch=%s release=%s --source=%s",
   171  			startParams.Arch,
   172  			startParams.Series,
   173  			startParams.ImageDownloadUrl,
   174  		),
   175  		" ",
   176  	)
   177  
   178  	testing.AssertEchoArgs(c, libvirtBinName, expectedArgs...)
   179  }
   180  
   181  type ConstraintsSuite struct {
   182  	coretesting.BaseSuite
   183  }
   184  
   185  var _ = gc.Suite(&ConstraintsSuite{})
   186  
   187  func (s *ConstraintsSuite) TestDefaults(c *gc.C) {
   188  
   189  	for _, test := range []struct {
   190  		cons     string
   191  		expected kvm.StartParams
   192  		infoLog  []string
   193  	}{{
   194  		expected: kvm.StartParams{
   195  			Memory:   kvm.DefaultMemory,
   196  			CpuCores: kvm.DefaultCpu,
   197  			RootDisk: kvm.DefaultDisk,
   198  		},
   199  	}, {
   200  		cons: "mem=256M",
   201  		expected: kvm.StartParams{
   202  			Memory:   kvm.MinMemory,
   203  			CpuCores: kvm.DefaultCpu,
   204  			RootDisk: kvm.DefaultDisk,
   205  		},
   206  	}, {
   207  		cons: "mem=4G",
   208  		expected: kvm.StartParams{
   209  			Memory:   4 * 1024,
   210  			CpuCores: kvm.DefaultCpu,
   211  			RootDisk: kvm.DefaultDisk,
   212  		},
   213  	}, {
   214  		cons: "cpu-cores=4",
   215  		expected: kvm.StartParams{
   216  			Memory:   kvm.DefaultMemory,
   217  			CpuCores: 4,
   218  			RootDisk: kvm.DefaultDisk,
   219  		},
   220  	}, {
   221  		cons: "cpu-cores=0",
   222  		expected: kvm.StartParams{
   223  			Memory:   kvm.DefaultMemory,
   224  			CpuCores: kvm.MinCpu,
   225  			RootDisk: kvm.DefaultDisk,
   226  		},
   227  	}, {
   228  		cons: "root-disk=512M",
   229  		expected: kvm.StartParams{
   230  			Memory:   kvm.DefaultMemory,
   231  			CpuCores: kvm.DefaultCpu,
   232  			RootDisk: kvm.MinDisk,
   233  		},
   234  	}, {
   235  		cons: "root-disk=4G",
   236  		expected: kvm.StartParams{
   237  			Memory:   kvm.DefaultMemory,
   238  			CpuCores: kvm.DefaultCpu,
   239  			RootDisk: 4,
   240  		},
   241  	}, {
   242  		cons: "arch=armhf",
   243  		expected: kvm.StartParams{
   244  			Memory:   kvm.DefaultMemory,
   245  			CpuCores: kvm.DefaultCpu,
   246  			RootDisk: kvm.DefaultDisk,
   247  		},
   248  		infoLog: []string{
   249  			`arch constraint of "armhf" being ignored as not supported`,
   250  		},
   251  	}, {
   252  		cons: "container=lxc",
   253  		expected: kvm.StartParams{
   254  			Memory:   kvm.DefaultMemory,
   255  			CpuCores: kvm.DefaultCpu,
   256  			RootDisk: kvm.DefaultDisk,
   257  		},
   258  		infoLog: []string{
   259  			`container constraint of "lxc" being ignored as not supported`,
   260  		},
   261  	}, {
   262  		cons: "cpu-power=100",
   263  		expected: kvm.StartParams{
   264  			Memory:   kvm.DefaultMemory,
   265  			CpuCores: kvm.DefaultCpu,
   266  			RootDisk: kvm.DefaultDisk,
   267  		},
   268  		infoLog: []string{
   269  			`cpu-power constraint of 100 being ignored as not supported`,
   270  		},
   271  	}, {
   272  		cons: "tags=foo,bar",
   273  		expected: kvm.StartParams{
   274  			Memory:   kvm.DefaultMemory,
   275  			CpuCores: kvm.DefaultCpu,
   276  			RootDisk: kvm.DefaultDisk,
   277  		},
   278  		infoLog: []string{
   279  			`tags constraint of "foo,bar" being ignored as not supported`,
   280  		},
   281  	}, {
   282  		cons: "mem=4G cpu-cores=4 root-disk=20G arch=armhf cpu-power=100 container=lxc tags=foo,bar",
   283  		expected: kvm.StartParams{
   284  			Memory:   4 * 1024,
   285  			CpuCores: 4,
   286  			RootDisk: 20,
   287  		},
   288  		infoLog: []string{
   289  			`arch constraint of "armhf" being ignored as not supported`,
   290  			`container constraint of "lxc" being ignored as not supported`,
   291  			`cpu-power constraint of 100 being ignored as not supported`,
   292  			`tags constraint of "foo,bar" being ignored as not supported`,
   293  		},
   294  	}} {
   295  		var tw loggo.TestWriter
   296  		c.Assert(loggo.RegisterWriter("constraint-tester", &tw, loggo.DEBUG), gc.IsNil)
   297  		cons := constraints.MustParse(test.cons)
   298  		params := kvm.ParseConstraintsToStartParams(cons)
   299  		c.Check(params, gc.DeepEquals, test.expected)
   300  		c.Check(tw.Log(), jc.LogMatches, test.infoLog)
   301  		loggo.RemoveWriter("constraint-tester")
   302  	}
   303  }
   304  
   305  // Test the output when no binary can be found.
   306  func (s *KVMSuite) TestIsKVMSupportedKvmOkNotFound(c *gc.C) {
   307  	// With no path, and no backup directory, we should fail.
   308  	s.PatchEnvironment("PATH", "")
   309  	s.PatchValue(kvm.KVMPath, "")
   310  
   311  	supported, err := kvm.IsKVMSupported()
   312  	c.Check(supported, jc.IsFalse)
   313  	c.Assert(err, gc.ErrorMatches, "kvm-ok executable not found")
   314  }
   315  
   316  // Test the output when the binary is found, but errors out.
   317  func (s *KVMSuite) TestIsKVMSupportedBinaryErrorsOut(c *gc.C) {
   318  	// Clear path so real binary is not found.
   319  	s.PatchEnvironment("PATH", "")
   320  
   321  	// Create mocked binary which returns an error and give the test access.
   322  	tmpDir := c.MkDir()
   323  	err := ioutil.WriteFile(filepath.Join(tmpDir, "kvm-ok"), []byte("#!/bin/bash\nexit 127"), 0777)
   324  	c.Assert(err, jc.ErrorIsNil)
   325  	s.PatchValue(kvm.KVMPath, tmpDir)
   326  
   327  	supported, err := kvm.IsKVMSupported()
   328  	c.Check(supported, jc.IsFalse)
   329  	c.Assert(err, gc.ErrorMatches, "exit status 127")
   330  }
   331  
   332  // Test the case where kvm-ok is not in the path, but is in the
   333  // specified directory.
   334  func (s *KVMSuite) TestIsKVMSupportedNoPath(c *gc.C) {
   335  	// Create a mocked binary so that this test does not fail for
   336  	// developers without kvm-ok.
   337  	s.PatchEnvironment("PATH", "")
   338  	tmpDir := c.MkDir()
   339  	err := ioutil.WriteFile(filepath.Join(tmpDir, "kvm-ok"), []byte("#!/bin/bash"), 0777)
   340  	c.Assert(err, jc.ErrorIsNil)
   341  	s.PatchValue(kvm.KVMPath, tmpDir)
   342  
   343  	supported, err := kvm.IsKVMSupported()
   344  	c.Check(supported, jc.IsTrue)
   345  	c.Assert(err, jc.ErrorIsNil)
   346  }
   347  
   348  // Test the case that kvm-ok is found in the path.
   349  func (s *KVMSuite) TestIsKVMSupportedOnlyPath(c *gc.C) {
   350  	// Create a mocked binary so that this test does not fail for
   351  	// developers without kvm-ok.
   352  	tmpDir := c.MkDir()
   353  	err := ioutil.WriteFile(filepath.Join(tmpDir, "kvm-ok"), []byte("#!/bin/bash"), 0777)
   354  	s.PatchEnvironment("PATH", tmpDir)
   355  
   356  	supported, err := kvm.IsKVMSupported()
   357  	c.Check(supported, jc.IsTrue)
   358  	c.Assert(err, jc.ErrorIsNil)
   359  }
   360  
   361  func (s *KVMSuite) TestKVMPathIsCorrect(c *gc.C) {
   362  	c.Assert(*kvm.KVMPath, gc.Equals, "/usr/sbin")
   363  }