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