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 }