github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/provider/local/environ_test.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package local_test
     5  
     6  import (
     7  	"fmt"
     8  	"io/ioutil"
     9  	"os"
    10  	"path/filepath"
    11  	"strings"
    12  	"time"
    13  
    14  	jc "github.com/juju/testing/checkers"
    15  	gc "launchpad.net/gocheck"
    16  
    17  	"github.com/juju/juju/agent/mongo"
    18  	coreCloudinit "github.com/juju/juju/cloudinit"
    19  	"github.com/juju/juju/constraints"
    20  	"github.com/juju/juju/container"
    21  	"github.com/juju/juju/container/lxc"
    22  	containertesting "github.com/juju/juju/container/testing"
    23  	"github.com/juju/juju/environs"
    24  	"github.com/juju/juju/environs/cloudinit"
    25  	"github.com/juju/juju/environs/config"
    26  	"github.com/juju/juju/environs/jujutest"
    27  	envtesting "github.com/juju/juju/environs/testing"
    28  	"github.com/juju/juju/environs/tools"
    29  	"github.com/juju/juju/juju/arch"
    30  	"github.com/juju/juju/juju/osenv"
    31  	"github.com/juju/juju/provider/local"
    32  	"github.com/juju/juju/state/api/params"
    33  	coretesting "github.com/juju/juju/testing"
    34  	"github.com/juju/juju/upstart"
    35  )
    36  
    37  const echoCommandScript = "#!/bin/sh\necho $0 \"$@\" >> $0.args"
    38  
    39  type environSuite struct {
    40  	baseProviderSuite
    41  	envtesting.ToolsFixture
    42  }
    43  
    44  var _ = gc.Suite(&environSuite{})
    45  
    46  func (s *environSuite) SetUpTest(c *gc.C) {
    47  	s.baseProviderSuite.SetUpTest(c)
    48  	s.ToolsFixture.SetUpTest(c)
    49  }
    50  
    51  func (s *environSuite) TearDownTest(c *gc.C) {
    52  	s.ToolsFixture.TearDownTest(c)
    53  	s.baseProviderSuite.TearDownTest(c)
    54  }
    55  
    56  func (*environSuite) TestOpenFailsWithProtectedDirectories(c *gc.C) {
    57  	testConfig := minimalConfig(c)
    58  	testConfig, err := testConfig.Apply(map[string]interface{}{
    59  		"root-dir": "/usr/lib/juju",
    60  	})
    61  	c.Assert(err, gc.IsNil)
    62  
    63  	environ, err := local.Provider.Open(testConfig)
    64  	c.Assert(err, gc.ErrorMatches, "failure setting config: mkdir .* permission denied")
    65  	c.Assert(environ, gc.IsNil)
    66  }
    67  
    68  func (s *environSuite) TestNameAndStorage(c *gc.C) {
    69  	testConfig := minimalConfig(c)
    70  	environ, err := local.Provider.Open(testConfig)
    71  	c.Assert(err, gc.IsNil)
    72  	c.Assert(environ.Name(), gc.Equals, "test")
    73  	c.Assert(environ.Storage(), gc.NotNil)
    74  }
    75  
    76  func (s *environSuite) TestGetToolsMetadataSources(c *gc.C) {
    77  	testConfig := minimalConfig(c)
    78  	environ, err := local.Provider.Open(testConfig)
    79  	c.Assert(err, gc.IsNil)
    80  	sources, err := tools.GetMetadataSources(environ)
    81  	c.Assert(err, gc.IsNil)
    82  	c.Assert(len(sources), gc.Equals, 1)
    83  	url, err := sources[0].URL("")
    84  	c.Assert(err, gc.IsNil)
    85  	c.Assert(strings.Contains(url, "/tools"), jc.IsTrue)
    86  }
    87  
    88  func (*environSuite) TestSupportedArchitectures(c *gc.C) {
    89  	testConfig := minimalConfig(c)
    90  	environ, err := local.Provider.Open(testConfig)
    91  	c.Assert(err, gc.IsNil)
    92  	arches, err := environ.SupportedArchitectures()
    93  	c.Assert(err, gc.IsNil)
    94  	for _, a := range arches {
    95  		c.Assert(arch.IsSupportedArch(a), jc.IsTrue)
    96  	}
    97  }
    98  
    99  func (*environSuite) TestSupportNetworks(c *gc.C) {
   100  	testConfig := minimalConfig(c)
   101  	environ, err := local.Provider.Open(testConfig)
   102  	c.Assert(err, gc.IsNil)
   103  	c.Assert(environ.SupportNetworks(), jc.IsFalse)
   104  }
   105  
   106  type localJujuTestSuite struct {
   107  	baseProviderSuite
   108  	jujutest.Tests
   109  	oldUpstartLocation string
   110  	testPath           string
   111  	fakesudo           string
   112  }
   113  
   114  func (s *localJujuTestSuite) SetUpTest(c *gc.C) {
   115  	s.baseProviderSuite.SetUpTest(c)
   116  	// Construct the directories first.
   117  	err := local.CreateDirs(c, minimalConfig(c))
   118  	c.Assert(err, gc.IsNil)
   119  	s.testPath = c.MkDir()
   120  	s.fakesudo = filepath.Join(s.testPath, "sudo")
   121  	s.PatchEnvPathPrepend(s.testPath)
   122  	s.PatchValue(&lxc.TemplateLockDir, c.MkDir())
   123  	s.PatchValue(&lxc.TemplateStopTimeout, 500*time.Millisecond)
   124  
   125  	// Write a fake "sudo" which records its args to sudo.args.
   126  	err = ioutil.WriteFile(s.fakesudo, []byte(echoCommandScript), 0755)
   127  	c.Assert(err, gc.IsNil)
   128  
   129  	// Add in an admin secret
   130  	s.Tests.TestConfig["admin-secret"] = "sekrit"
   131  	s.PatchValue(local.CheckIfRoot, func() bool { return false })
   132  	s.Tests.SetUpTest(c)
   133  
   134  	s.PatchValue(local.FinishBootstrap, func(mcfg *cloudinit.MachineConfig, cloudcfg *coreCloudinit.Config, ctx environs.BootstrapContext) error {
   135  		return nil
   136  	})
   137  }
   138  
   139  func (s *localJujuTestSuite) TearDownTest(c *gc.C) {
   140  	s.Tests.TearDownTest(c)
   141  	s.baseProviderSuite.TearDownTest(c)
   142  }
   143  
   144  func (s *localJujuTestSuite) MakeTool(c *gc.C, name, script string) {
   145  	path := filepath.Join(s.testPath, name)
   146  	script = "#!/bin/bash\n" + script
   147  	err := ioutil.WriteFile(path, []byte(script), 0755)
   148  	c.Assert(err, gc.IsNil)
   149  }
   150  
   151  func (s *localJujuTestSuite) StoppedStatus(c *gc.C) {
   152  	s.MakeTool(c, "status", `echo "some-service stop/waiting"`)
   153  }
   154  
   155  func (s *localJujuTestSuite) RunningStatus(c *gc.C) {
   156  	s.MakeTool(c, "status", `echo "some-service start/running, process 123"`)
   157  }
   158  
   159  var _ = gc.Suite(&localJujuTestSuite{
   160  	Tests: jujutest.Tests{
   161  		TestConfig: minimalConfigValues(),
   162  	},
   163  })
   164  
   165  func (s *localJujuTestSuite) TestStartStop(c *gc.C) {
   166  	c.Skip("StartInstance not implemented yet.")
   167  }
   168  
   169  func (s *localJujuTestSuite) testBootstrap(c *gc.C, cfg *config.Config) (env environs.Environ) {
   170  	ctx := coretesting.Context(c)
   171  	environ, err := local.Provider.Prepare(ctx, cfg)
   172  	c.Assert(err, gc.IsNil)
   173  	envtesting.UploadFakeTools(c, environ.Storage())
   174  	defer environ.Storage().RemoveAll()
   175  	err = environ.Bootstrap(ctx, environs.BootstrapParams{})
   176  	c.Assert(err, gc.IsNil)
   177  	return environ
   178  }
   179  
   180  func (s *localJujuTestSuite) TestBootstrap(c *gc.C) {
   181  	s.PatchValue(local.FinishBootstrap, func(mcfg *cloudinit.MachineConfig, cloudcfg *coreCloudinit.Config, ctx environs.BootstrapContext) error {
   182  		c.Assert(cloudcfg.AptUpdate(), jc.IsFalse)
   183  		c.Assert(cloudcfg.AptUpgrade(), jc.IsFalse)
   184  		c.Assert(cloudcfg.Packages(), gc.HasLen, 0)
   185  		c.Assert(mcfg.AgentEnvironment, gc.Not(gc.IsNil))
   186  		// local does not allow machine-0 to host units
   187  		c.Assert(mcfg.Jobs, gc.DeepEquals, []params.MachineJob{params.JobManageEnviron})
   188  		return nil
   189  	})
   190  	s.testBootstrap(c, minimalConfig(c))
   191  }
   192  
   193  func (s *localJujuTestSuite) TestDestroy(c *gc.C) {
   194  	env := s.testBootstrap(c, minimalConfig(c))
   195  	err := env.Destroy()
   196  	// Succeeds because there's no "agents" directory,
   197  	// so destroy will just return without attempting
   198  	// sudo or anything.
   199  	c.Assert(err, gc.IsNil)
   200  	c.Assert(s.fakesudo+".args", jc.DoesNotExist)
   201  }
   202  
   203  func (s *localJujuTestSuite) makeAgentsDir(c *gc.C, env environs.Environ) {
   204  	rootDir := env.Config().AllAttrs()["root-dir"].(string)
   205  	agentsDir := filepath.Join(rootDir, "agents")
   206  	err := os.Mkdir(agentsDir, 0755)
   207  	c.Assert(err, gc.IsNil)
   208  }
   209  
   210  func (s *localJujuTestSuite) TestDestroyCallSudo(c *gc.C) {
   211  	env := s.testBootstrap(c, minimalConfig(c))
   212  	s.makeAgentsDir(c, env)
   213  	err := env.Destroy()
   214  	c.Assert(err, gc.IsNil)
   215  	data, err := ioutil.ReadFile(s.fakesudo + ".args")
   216  	c.Assert(err, gc.IsNil)
   217  	expected := []string{
   218  		s.fakesudo,
   219  		"env",
   220  		"JUJU_HOME=" + osenv.JujuHome(),
   221  		os.Args[0],
   222  		"destroy-environment",
   223  		"-y",
   224  		"--force",
   225  		env.Config().Name(),
   226  	}
   227  	c.Assert(string(data), gc.Equals, strings.Join(expected, " ")+"\n")
   228  }
   229  
   230  func (s *localJujuTestSuite) makeFakeUpstartScripts(c *gc.C, env environs.Environ,
   231  ) (mongoService *upstart.Service, machineAgent *upstart.Service) {
   232  	upstartDir := c.MkDir()
   233  	s.PatchValue(&upstart.InitDir, upstartDir)
   234  	s.MakeTool(c, "start", `echo "some-service start/running, process 123"`)
   235  
   236  	namespace := env.Config().AllAttrs()["namespace"].(string)
   237  	mongoService = upstart.NewService(mongo.ServiceName(namespace))
   238  	mongoConf := upstart.Conf{
   239  		Service: *mongoService,
   240  		Desc:    "fake mongo",
   241  		Cmd:     "echo FAKE",
   242  	}
   243  	err := mongoConf.Install()
   244  	c.Assert(err, gc.IsNil)
   245  	c.Assert(mongoService.Installed(), jc.IsTrue)
   246  
   247  	machineAgent = upstart.NewService(fmt.Sprintf("juju-agent-%s", namespace))
   248  	agentConf := upstart.Conf{
   249  		Service: *machineAgent,
   250  		Desc:    "fake agent",
   251  		Cmd:     "echo FAKE",
   252  	}
   253  	err = agentConf.Install()
   254  	c.Assert(err, gc.IsNil)
   255  	c.Assert(machineAgent.Installed(), jc.IsTrue)
   256  
   257  	return mongoService, machineAgent
   258  }
   259  
   260  func (s *localJujuTestSuite) TestDestroyRemovesUpstartServices(c *gc.C) {
   261  	env := s.testBootstrap(c, minimalConfig(c))
   262  	s.makeAgentsDir(c, env)
   263  	mongo, machineAgent := s.makeFakeUpstartScripts(c, env)
   264  	s.PatchValue(local.CheckIfRoot, func() bool { return true })
   265  
   266  	err := env.Destroy()
   267  	c.Assert(err, gc.IsNil)
   268  
   269  	c.Assert(mongo.Installed(), jc.IsFalse)
   270  	c.Assert(machineAgent.Installed(), jc.IsFalse)
   271  }
   272  
   273  func (s *localJujuTestSuite) TestDestroyRemovesContainers(c *gc.C) {
   274  	env := s.testBootstrap(c, minimalConfig(c))
   275  	s.makeAgentsDir(c, env)
   276  	s.PatchValue(local.CheckIfRoot, func() bool { return true })
   277  
   278  	namespace := env.Config().AllAttrs()["namespace"].(string)
   279  	manager, err := lxc.NewContainerManager(container.ManagerConfig{
   280  		container.ConfigName:   namespace,
   281  		container.ConfigLogDir: "logdir",
   282  		"use-clone":            "false",
   283  	})
   284  	c.Assert(err, gc.IsNil)
   285  
   286  	machine1 := containertesting.CreateContainer(c, manager, "1")
   287  
   288  	err = env.Destroy()
   289  	c.Assert(err, gc.IsNil)
   290  
   291  	container := s.Factory.New(string(machine1.Id()))
   292  	c.Assert(container.IsConstructed(), jc.IsFalse)
   293  }
   294  
   295  func (s *localJujuTestSuite) TestBootstrapRemoveLeftovers(c *gc.C) {
   296  	cfg := minimalConfig(c)
   297  	rootDir := cfg.AllAttrs()["root-dir"].(string)
   298  
   299  	// Create a dir inside local/log that should be removed by Bootstrap.
   300  	logThings := filepath.Join(rootDir, "log", "things")
   301  	err := os.MkdirAll(logThings, 0755)
   302  	c.Assert(err, gc.IsNil)
   303  
   304  	// Create a cloud-init-output.log in root-dir that should be
   305  	// removed/truncated by Bootstrap.
   306  	cloudInitOutputLog := filepath.Join(rootDir, "cloud-init-output.log")
   307  	err = ioutil.WriteFile(cloudInitOutputLog, []byte("ohai"), 0644)
   308  	c.Assert(err, gc.IsNil)
   309  
   310  	s.testBootstrap(c, cfg)
   311  	c.Assert(logThings, jc.DoesNotExist)
   312  	c.Assert(cloudInitOutputLog, jc.DoesNotExist)
   313  	c.Assert(filepath.Join(rootDir, "log"), jc.IsSymlink)
   314  }
   315  
   316  func (s *localJujuTestSuite) TestConstraintsValidator(c *gc.C) {
   317  	ctx := coretesting.Context(c)
   318  	env, err := local.Provider.Prepare(ctx, minimalConfig(c))
   319  	c.Assert(err, gc.IsNil)
   320  	validator, err := env.ConstraintsValidator()
   321  	c.Assert(err, gc.IsNil)
   322  	hostArch := arch.HostArch()
   323  	cons := constraints.MustParse(fmt.Sprintf("arch=%s instance-type=foo tags=bar cpu-power=10 cpu-cores=2", hostArch))
   324  	unsupported, err := validator.Validate(cons)
   325  	c.Assert(err, gc.IsNil)
   326  	c.Assert(unsupported, jc.SameContents, []string{"cpu-cores", "cpu-power", "instance-type", "tags"})
   327  }
   328  
   329  func (s *localJujuTestSuite) TestConstraintsValidatorVocab(c *gc.C) {
   330  	env := s.Prepare(c)
   331  	validator, err := env.ConstraintsValidator()
   332  	c.Assert(err, gc.IsNil)
   333  
   334  	hostArch := arch.HostArch()
   335  	var invalidArch string
   336  	for _, a := range arch.AllSupportedArches {
   337  		if a != hostArch {
   338  			invalidArch = a
   339  			break
   340  		}
   341  	}
   342  	cons := constraints.MustParse(fmt.Sprintf("arch=%s", invalidArch))
   343  	_, err = validator.Validate(cons)
   344  	c.Assert(err, gc.ErrorMatches, "invalid constraint value: arch="+invalidArch+"\nvalid values are:.*")
   345  }