github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/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  	"github.com/juju/utils/arch"
    16  	gc "gopkg.in/check.v1"
    17  
    18  	"github.com/juju/juju/agent"
    19  	"github.com/juju/juju/cloudconfig/cloudinit"
    20  	"github.com/juju/juju/cloudconfig/instancecfg"
    21  	"github.com/juju/juju/constraints"
    22  	"github.com/juju/juju/container"
    23  	"github.com/juju/juju/container/lxc"
    24  	containertesting "github.com/juju/juju/container/testing"
    25  	"github.com/juju/juju/environs"
    26  	"github.com/juju/juju/environs/config"
    27  	"github.com/juju/juju/environs/jujutest"
    28  	envtesting "github.com/juju/juju/environs/testing"
    29  	"github.com/juju/juju/environs/tools"
    30  	"github.com/juju/juju/instance"
    31  	"github.com/juju/juju/juju/osenv"
    32  	"github.com/juju/juju/mongo"
    33  	"github.com/juju/juju/provider/local"
    34  	"github.com/juju/juju/service/common"
    35  	svctesting "github.com/juju/juju/service/common/testing"
    36  	"github.com/juju/juju/state/multiwatcher"
    37  	"github.com/juju/juju/testing"
    38  	coretools "github.com/juju/juju/tools"
    39  	"github.com/juju/juju/version"
    40  )
    41  
    42  const echoCommandScript = "#!/bin/sh\necho $0 \"$@\" >> $0.args"
    43  
    44  type environSuite struct {
    45  	baseProviderSuite
    46  	envtesting.ToolsFixture
    47  }
    48  
    49  var _ = gc.Suite(&environSuite{})
    50  
    51  func (s *environSuite) SetUpTest(c *gc.C) {
    52  	s.baseProviderSuite.SetUpTest(c)
    53  	s.ToolsFixture.SetUpTest(c)
    54  }
    55  
    56  func (s *environSuite) TearDownTest(c *gc.C) {
    57  	s.ToolsFixture.TearDownTest(c)
    58  	s.baseProviderSuite.TearDownTest(c)
    59  }
    60  
    61  func (*environSuite) TestOpenFailsWithProtectedDirectories(c *gc.C) {
    62  	testConfig := minimalConfig(c)
    63  	testConfig, err := testConfig.Apply(map[string]interface{}{
    64  		"root-dir": "/usr/lib/juju",
    65  	})
    66  	c.Assert(err, jc.ErrorIsNil)
    67  
    68  	environ, err := local.Provider.Open(testConfig)
    69  	c.Assert(err, gc.ErrorMatches, "failure setting config: mkdir .* permission denied")
    70  	c.Assert(environ, gc.IsNil)
    71  }
    72  
    73  func (s *environSuite) TestName(c *gc.C) {
    74  	testConfig := minimalConfig(c)
    75  	environ, err := local.Provider.Open(testConfig)
    76  	c.Assert(err, jc.ErrorIsNil)
    77  	c.Assert(environ.Config().Name(), gc.Equals, "test")
    78  }
    79  
    80  func (s *environSuite) TestGetToolsMetadataSources(c *gc.C) {
    81  	testConfig := minimalConfig(c)
    82  	environ, err := local.Provider.Open(testConfig)
    83  	c.Assert(err, jc.ErrorIsNil)
    84  	sources, err := tools.GetMetadataSources(environ)
    85  	c.Assert(err, jc.ErrorIsNil)
    86  	c.Assert(sources, gc.HasLen, 0)
    87  }
    88  
    89  func (*environSuite) TestSupportedArchitectures(c *gc.C) {
    90  	testConfig := minimalConfig(c)
    91  	environ, err := local.Provider.Open(testConfig)
    92  	c.Assert(err, jc.ErrorIsNil)
    93  	arches, err := environ.SupportedArchitectures()
    94  	c.Assert(err, jc.ErrorIsNil)
    95  	for _, a := range arches {
    96  		c.Assert(arch.IsSupportedArch(a), jc.IsTrue)
    97  	}
    98  }
    99  
   100  func (*environSuite) TestSupportsNetworking(c *gc.C) {
   101  	testConfig := minimalConfig(c)
   102  	environ, err := local.Provider.Open(testConfig)
   103  	c.Assert(err, jc.ErrorIsNil)
   104  	_, ok := environs.SupportsNetworking(environ)
   105  	c.Assert(ok, jc.IsFalse)
   106  }
   107  
   108  type localJujuTestSuite struct {
   109  	baseProviderSuite
   110  	jujutest.Tests
   111  	testPath string
   112  	fakesudo string
   113  	svcData  *svctesting.FakeServiceData
   114  }
   115  
   116  func (s *localJujuTestSuite) SetUpTest(c *gc.C) {
   117  	s.baseProviderSuite.SetUpTest(c)
   118  	s.PatchValue(&version.Current.Number, testing.FakeVersionNumber)
   119  	// Construct the directories first.
   120  	err := local.CreateDirs(c, minimalConfig(c))
   121  	c.Assert(err, jc.ErrorIsNil)
   122  	s.testPath = c.MkDir()
   123  	s.fakesudo = filepath.Join(s.testPath, "sudo")
   124  	s.PatchEnvPathPrepend(s.testPath)
   125  	s.PatchValue(&lxc.TemplateLockDir, c.MkDir())
   126  	s.PatchValue(&lxc.TemplateStopTimeout, 500*time.Millisecond)
   127  
   128  	// Write a fake "sudo" which records its args to sudo.args.
   129  	err = ioutil.WriteFile(s.fakesudo, []byte(echoCommandScript), 0755)
   130  	c.Assert(err, jc.ErrorIsNil)
   131  
   132  	// Add in an admin secret
   133  	s.Tests.TestConfig["admin-secret"] = "sekrit"
   134  	s.PatchValue(local.CheckIfRoot, func() bool { return false })
   135  	s.Tests.SetUpTest(c)
   136  
   137  	s.PatchValue(local.ExecuteCloudConfig, func(environs.BootstrapContext, *instancecfg.InstanceConfig, cloudinit.CloudConfig) error {
   138  		return nil
   139  	})
   140  
   141  	s.svcData = svctesting.NewFakeServiceData()
   142  	local.PatchServices(s.PatchValue, s.svcData)
   143  }
   144  
   145  func (s *localJujuTestSuite) TearDownTest(c *gc.C) {
   146  	s.Tests.TearDownTest(c)
   147  	s.baseProviderSuite.TearDownTest(c)
   148  }
   149  
   150  func (s *localJujuTestSuite) MakeTool(c *gc.C, name, script string) {
   151  	path := filepath.Join(s.testPath, name)
   152  	script = "#!/bin/bash\n" + script
   153  	err := ioutil.WriteFile(path, []byte(script), 0755)
   154  	c.Assert(err, jc.ErrorIsNil)
   155  }
   156  
   157  func (s *localJujuTestSuite) StoppedStatus(c *gc.C) {
   158  	s.MakeTool(c, "status", `echo "some-service stop/waiting"`)
   159  }
   160  
   161  func (s *localJujuTestSuite) RunningStatus(c *gc.C) {
   162  	s.MakeTool(c, "status", `echo "some-service start/running, process 123"`)
   163  }
   164  
   165  var _ = gc.Suite(&localJujuTestSuite{
   166  	Tests: jujutest.Tests{
   167  		TestConfig: minimalConfigValues(),
   168  	},
   169  })
   170  
   171  func (s *localJujuTestSuite) TestStartStop(c *gc.C) {
   172  	c.Skip("StartInstance not implemented yet.")
   173  }
   174  
   175  func (s *localJujuTestSuite) testBootstrap(c *gc.C, cfg *config.Config) environs.Environ {
   176  	ctx := envtesting.BootstrapContext(c)
   177  	environ, err := local.Provider.PrepareForBootstrap(ctx, cfg)
   178  	c.Assert(err, jc.ErrorIsNil)
   179  	availableTools := coretools.List{&coretools.Tools{
   180  		Version: version.Current,
   181  		URL:     "http://testing.invalid/tools.tar.gz",
   182  	}}
   183  	_, _, finalizer, err := environ.Bootstrap(ctx, environs.BootstrapParams{
   184  		AvailableTools: availableTools,
   185  	})
   186  	c.Assert(err, jc.ErrorIsNil)
   187  	icfg, err := instancecfg.NewBootstrapInstanceConfig(constraints.Value{}, "quantal")
   188  	c.Assert(err, jc.ErrorIsNil)
   189  	icfg.Tools = availableTools[0]
   190  	err = finalizer(ctx, icfg)
   191  	c.Assert(err, jc.ErrorIsNil)
   192  	return environ
   193  }
   194  
   195  func (s *localJujuTestSuite) TestBootstrap(c *gc.C) {
   196  
   197  	minCfg := minimalConfig(c)
   198  
   199  	mockFinish := func(ctx environs.BootstrapContext, icfg *instancecfg.InstanceConfig, cloudcfg cloudinit.CloudConfig) error {
   200  
   201  		envCfgAttrs := minCfg.AllAttrs()
   202  		if val, ok := envCfgAttrs["enable-os-refresh-update"]; !ok {
   203  			c.Check(cloudcfg.SystemUpdate(), jc.IsFalse)
   204  		} else {
   205  			c.Check(cloudcfg.SystemUpdate(), gc.Equals, val)
   206  		}
   207  
   208  		if val, ok := envCfgAttrs["enable-os-upgrade"]; !ok {
   209  			c.Check(cloudcfg.SystemUpgrade(), jc.IsFalse)
   210  		} else {
   211  			c.Check(cloudcfg.SystemUpgrade(), gc.Equals, val)
   212  		}
   213  
   214  		c.Assert(icfg.AgentEnvironment, gc.Not(gc.IsNil))
   215  		c.Assert(icfg.AgentEnvironment[agent.LxcBridge], gc.Not(gc.Equals), "")
   216  		// local does not allow machine-0 to host units
   217  		c.Assert(icfg.Jobs, gc.DeepEquals, []multiwatcher.MachineJob{multiwatcher.JobManageEnviron})
   218  		return nil
   219  	}
   220  	s.PatchValue(local.ExecuteCloudConfig, mockFinish)
   221  
   222  	// Test that defaults are correct.
   223  	s.testBootstrap(c, minCfg)
   224  
   225  	// Test that overrides work.
   226  	minCfg, err := minCfg.Apply(map[string]interface{}{
   227  		"enable-os-refresh-update": true,
   228  		"enable-os-upgrade":        true,
   229  	})
   230  	c.Assert(err, jc.ErrorIsNil)
   231  	s.testBootstrap(c, minCfg)
   232  }
   233  
   234  func (s *localJujuTestSuite) TestDestroy(c *gc.C) {
   235  	env := s.testBootstrap(c, minimalConfig(c))
   236  	err := env.Destroy()
   237  	// Succeeds because there's no "agents" directory,
   238  	// so destroy will just return without attempting
   239  	// sudo or anything.
   240  	c.Assert(err, jc.ErrorIsNil)
   241  	c.Assert(s.fakesudo+".args", jc.DoesNotExist)
   242  }
   243  
   244  func (s *localJujuTestSuite) makeAgentsDir(c *gc.C, env environs.Environ) {
   245  	rootDir := env.Config().AllAttrs()["root-dir"].(string)
   246  	agentsDir := filepath.Join(rootDir, "agents")
   247  	err := os.Mkdir(agentsDir, 0755)
   248  	c.Assert(err, jc.ErrorIsNil)
   249  }
   250  
   251  func (s *localJujuTestSuite) TestDestroyCallSudo(c *gc.C) {
   252  	env := s.testBootstrap(c, minimalConfig(c))
   253  	s.makeAgentsDir(c, env)
   254  	err := env.Destroy()
   255  	c.Assert(err, jc.ErrorIsNil)
   256  	data, err := ioutil.ReadFile(s.fakesudo + ".args")
   257  	c.Assert(err, jc.ErrorIsNil)
   258  	expected := []string{
   259  		s.fakesudo,
   260  		"env",
   261  		"JUJU_HOME=" + osenv.JujuHome(),
   262  		os.Args[0],
   263  		"destroy-environment",
   264  		"-y",
   265  		"--force",
   266  		env.Config().Name(),
   267  	}
   268  	c.Assert(string(data), gc.Equals, strings.Join(expected, " ")+"\n")
   269  }
   270  
   271  type installable interface {
   272  	Install() error
   273  	Installed() (bool, error)
   274  }
   275  
   276  func (s *localJujuTestSuite) makeFakeInitScripts(c *gc.C, env environs.Environ) (installable, installable) {
   277  	s.MakeTool(c, "start", `echo "some-service start/running, process 123"`)
   278  	namespace := env.Config().AllAttrs()["namespace"].(string)
   279  
   280  	// Mongo first...
   281  	mongoName := mongo.ServiceName(namespace)
   282  	mongoConf := common.Conf{
   283  		Desc:      "fake mongo",
   284  		ExecStart: "echo FAKE",
   285  	}
   286  	mongoService := local.NewService(mongoName, mongoConf, s.svcData)
   287  	s.svcData.SetStatus(mongoName, "installed")
   288  	installed, err := mongoService.Installed()
   289  	c.Assert(err, jc.ErrorIsNil)
   290  	c.Check(installed, jc.IsTrue)
   291  
   292  	// ...then the machine agent
   293  	agentName := fmt.Sprintf("juju-agent-%s", namespace)
   294  	agentConf := common.Conf{
   295  		Desc:      "fake agent",
   296  		ExecStart: "echo FAKE",
   297  	}
   298  	agentService := local.NewService(agentName, agentConf, s.svcData)
   299  	s.svcData.SetStatus(agentName, "installed")
   300  	installed, err = agentService.Installed()
   301  	c.Assert(err, jc.ErrorIsNil)
   302  	c.Check(installed, jc.IsTrue)
   303  
   304  	return mongoService, agentService
   305  }
   306  
   307  func (s *localJujuTestSuite) TestDestroyRemovesInitServices(c *gc.C) {
   308  	env := s.testBootstrap(c, minimalConfig(c))
   309  	s.makeAgentsDir(c, env)
   310  	mongoService, agentService := s.makeFakeInitScripts(c, env)
   311  	s.PatchValue(local.CheckIfRoot, func() bool { return true })
   312  
   313  	err := env.Destroy()
   314  	c.Assert(err, jc.ErrorIsNil)
   315  
   316  	installed, err := mongoService.Installed()
   317  	c.Assert(err, jc.ErrorIsNil)
   318  	c.Check(installed, jc.IsFalse)
   319  	installed, err = agentService.Installed()
   320  	c.Assert(err, jc.ErrorIsNil)
   321  	c.Check(installed, jc.IsFalse)
   322  }
   323  
   324  func (s *localJujuTestSuite) TestDestroyRemovesContainers(c *gc.C) {
   325  	env := s.testBootstrap(c, minimalConfig(c))
   326  	s.makeAgentsDir(c, env)
   327  	s.PatchValue(local.CheckIfRoot, func() bool { return true })
   328  
   329  	namespace := env.Config().AllAttrs()["namespace"].(string)
   330  	manager, err := lxc.NewContainerManager(container.ManagerConfig{
   331  		container.ConfigName:   namespace,
   332  		container.ConfigLogDir: "logdir",
   333  		"use-clone":            "false",
   334  	}, nil, nil)
   335  	c.Assert(err, jc.ErrorIsNil)
   336  
   337  	machine1 := containertesting.CreateContainer(c, manager, "1")
   338  
   339  	err = env.Destroy()
   340  	c.Assert(err, jc.ErrorIsNil)
   341  
   342  	container := s.ContainerFactory.New(string(machine1.Id()))
   343  	c.Assert(container.IsConstructed(), jc.IsFalse)
   344  }
   345  
   346  func (s *localJujuTestSuite) TestBootstrapRemoveLeftovers(c *gc.C) {
   347  	cfg := minimalConfig(c)
   348  	rootDir := cfg.AllAttrs()["root-dir"].(string)
   349  
   350  	// Create a dir inside local/log that should be removed by Bootstrap.
   351  	logThings := filepath.Join(rootDir, "log", "things")
   352  	err := os.MkdirAll(logThings, 0755)
   353  	c.Assert(err, jc.ErrorIsNil)
   354  
   355  	// Create a cloud-init-output.log in root-dir that should be
   356  	// removed/truncated by Bootstrap.
   357  	cloudInitOutputLog := filepath.Join(rootDir, "cloud-init-output.log")
   358  	err = ioutil.WriteFile(cloudInitOutputLog, []byte("ohai"), 0644)
   359  	c.Assert(err, jc.ErrorIsNil)
   360  
   361  	s.testBootstrap(c, cfg)
   362  	c.Assert(logThings, jc.DoesNotExist)
   363  	c.Assert(cloudInitOutputLog, jc.DoesNotExist)
   364  	c.Assert(filepath.Join(rootDir, "log"), jc.IsSymlink)
   365  }
   366  
   367  func (s *localJujuTestSuite) TestConstraintsValidator(c *gc.C) {
   368  	ctx := envtesting.BootstrapContext(c)
   369  	env, err := local.Provider.PrepareForBootstrap(ctx, minimalConfig(c))
   370  	c.Assert(err, jc.ErrorIsNil)
   371  	validator, err := env.ConstraintsValidator()
   372  	c.Assert(err, jc.ErrorIsNil)
   373  	hostArch := arch.HostArch()
   374  	cons := constraints.MustParse(fmt.Sprintf("arch=%s instance-type=foo tags=bar cpu-power=10 cpu-cores=2", hostArch))
   375  	unsupported, err := validator.Validate(cons)
   376  	c.Assert(err, jc.ErrorIsNil)
   377  	c.Assert(unsupported, jc.SameContents, []string{"cpu-cores", "cpu-power", "instance-type", "tags"})
   378  }
   379  
   380  func (s *localJujuTestSuite) TestConstraintsValidatorVocab(c *gc.C) {
   381  	env := s.Prepare(c)
   382  	validator, err := env.ConstraintsValidator()
   383  	c.Assert(err, jc.ErrorIsNil)
   384  
   385  	hostArch := arch.HostArch()
   386  	var invalidArch string
   387  	for _, a := range arch.AllSupportedArches {
   388  		if a != hostArch {
   389  			invalidArch = a
   390  			break
   391  		}
   392  	}
   393  	cons := constraints.MustParse(fmt.Sprintf("arch=%s", invalidArch))
   394  	_, err = validator.Validate(cons)
   395  	c.Assert(err, gc.ErrorMatches, "invalid constraint value: arch="+invalidArch+"\nvalid values are:.*")
   396  }
   397  
   398  func (s *localJujuTestSuite) TestStateServerInstances(c *gc.C) {
   399  	env := s.testBootstrap(c, minimalConfig(c))
   400  
   401  	instances, err := env.StateServerInstances()
   402  	c.Assert(err, gc.Equals, environs.ErrNotBootstrapped)
   403  	c.Assert(instances, gc.HasLen, 0)
   404  
   405  	s.makeAgentsDir(c, env)
   406  	instances, err = env.StateServerInstances()
   407  	c.Assert(err, jc.ErrorIsNil)
   408  	c.Assert(instances, gc.DeepEquals, []instance.Id{"localhost"})
   409  }