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 }