github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/cmd/juju/deploy_test.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package main 5 6 import ( 7 "strings" 8 9 "github.com/juju/cmd" 10 "github.com/juju/errors" 11 jc "github.com/juju/testing/checkers" 12 "github.com/juju/utils" 13 "github.com/juju/utils/featureflag" 14 gc "gopkg.in/check.v1" 15 "gopkg.in/juju/charm.v4" 16 17 "github.com/juju/juju/api" 18 "github.com/juju/juju/cmd/envcmd" 19 "github.com/juju/juju/constraints" 20 "github.com/juju/juju/environs/config" 21 "github.com/juju/juju/instance" 22 "github.com/juju/juju/juju/osenv" 23 "github.com/juju/juju/juju/testing" 24 "github.com/juju/juju/state" 25 "github.com/juju/juju/testcharms" 26 coretesting "github.com/juju/juju/testing" 27 ) 28 29 type DeploySuite struct { 30 testing.RepoSuite 31 } 32 33 var _ = gc.Suite(&DeploySuite{}) 34 35 func runDeploy(c *gc.C, args ...string) error { 36 _, err := coretesting.RunCommand(c, envcmd.Wrap(&DeployCommand{}), args...) 37 return err 38 } 39 40 var initErrorTests = []struct { 41 args []string 42 err string 43 }{ 44 { 45 args: nil, 46 err: `no charm specified`, 47 }, { 48 args: []string{"charm-name", "service-name", "hotdog"}, 49 err: `unrecognized args: \["hotdog"\]`, 50 }, { 51 args: []string{"craz~ness"}, 52 err: `invalid charm name "craz~ness"`, 53 }, { 54 args: []string{"craziness", "burble-1"}, 55 err: `invalid service name "burble-1"`, 56 }, { 57 args: []string{"craziness", "burble1", "-n", "0"}, 58 err: `--num-units must be a positive integer`, 59 }, { 60 args: []string{"craziness", "burble1", "--to", "bigglesplop"}, 61 err: `invalid --to parameter "bigglesplop"`, 62 }, { 63 args: []string{"craziness", "burble1", "-n", "2", "--to", "123"}, 64 err: `cannot use --num-units > 1 with --to`, 65 }, { 66 args: []string{"craziness", "burble1", "--constraints", "gibber=plop"}, 67 err: `invalid value "gibber=plop" for flag --constraints: unknown constraint "gibber"`, 68 }, 69 } 70 71 func (s *DeploySuite) TestInitErrors(c *gc.C) { 72 for i, t := range initErrorTests { 73 c.Logf("test %d", i) 74 err := coretesting.InitCommand(envcmd.Wrap(&DeployCommand{}), t.args) 75 c.Assert(err, gc.ErrorMatches, t.err) 76 } 77 } 78 79 func (s *DeploySuite) TestNoCharm(c *gc.C) { 80 err := runDeploy(c, "local:unknown-123") 81 c.Assert(err, gc.ErrorMatches, `charm not found in ".*": local:trusty/unknown-123`) 82 } 83 84 func (s *DeploySuite) TestBlockDeploy(c *gc.C) { 85 // Block operation 86 s.AssertConfigParameterUpdated(c, "block-all-changes", true) 87 testcharms.Repo.CharmArchivePath(s.SeriesPath, "dummy") 88 err := runDeploy(c, "local:dummy", "some-service-name") 89 c.Assert(err, gc.ErrorMatches, cmd.ErrSilent.Error()) 90 91 // msg is logged 92 stripped := strings.Replace(c.GetTestLog(), "\n", "", -1) 93 c.Check(stripped, gc.Matches, ".*To unblock changes.*") 94 } 95 96 func (s *DeploySuite) TestCharmDir(c *gc.C) { 97 testcharms.Repo.ClonedDirPath(s.SeriesPath, "dummy") 98 err := runDeploy(c, "local:dummy") 99 c.Assert(err, jc.ErrorIsNil) 100 curl := charm.MustParseURL("local:trusty/dummy-1") 101 s.AssertService(c, "dummy", curl, 1, 0) 102 } 103 104 func (s *DeploySuite) TestUpgradeReportsDeprecated(c *gc.C) { 105 testcharms.Repo.ClonedDirPath(s.SeriesPath, "dummy") 106 ctx, err := coretesting.RunCommand(c, envcmd.Wrap(&DeployCommand{}), "local:dummy", "-u") 107 c.Assert(err, jc.ErrorIsNil) 108 109 c.Assert(coretesting.Stdout(ctx), gc.Equals, "") 110 output := strings.Split(coretesting.Stderr(ctx), "\n") 111 c.Check(output[0], gc.Matches, `Added charm ".*" to the environment.`) 112 c.Check(output[1], gc.Equals, "--upgrade (or -u) is deprecated and ignored; charms are always deployed with a unique revision.") 113 } 114 115 func (s *DeploySuite) TestUpgradeCharmDir(c *gc.C) { 116 // Add the charm, so the url will exist and a new revision will be 117 // picked in ServiceDeploy. 118 dummyCharm := s.AddTestingCharm(c, "dummy") 119 120 dirPath := testcharms.Repo.ClonedDirPath(s.SeriesPath, "dummy") 121 err := runDeploy(c, "local:quantal/dummy") 122 c.Assert(err, jc.ErrorIsNil) 123 upgradedRev := dummyCharm.Revision() + 1 124 curl := dummyCharm.URL().WithRevision(upgradedRev) 125 s.AssertService(c, "dummy", curl, 1, 0) 126 // Check the charm dir was left untouched. 127 ch, err := charm.ReadCharmDir(dirPath) 128 c.Assert(err, jc.ErrorIsNil) 129 c.Assert(ch.Revision(), gc.Equals, 1) 130 } 131 132 func (s *DeploySuite) TestCharmBundle(c *gc.C) { 133 testcharms.Repo.CharmArchivePath(s.SeriesPath, "dummy") 134 err := runDeploy(c, "local:dummy", "some-service-name") 135 c.Assert(err, jc.ErrorIsNil) 136 curl := charm.MustParseURL("local:trusty/dummy-1") 137 s.AssertService(c, "some-service-name", curl, 1, 0) 138 } 139 140 func (s *DeploySuite) TestSubordinateCharm(c *gc.C) { 141 testcharms.Repo.CharmArchivePath(s.SeriesPath, "logging") 142 err := runDeploy(c, "local:logging") 143 c.Assert(err, jc.ErrorIsNil) 144 curl := charm.MustParseURL("local:trusty/logging-1") 145 s.AssertService(c, "logging", curl, 0, 0) 146 } 147 148 func (s *DeploySuite) TestConfig(c *gc.C) { 149 testcharms.Repo.CharmArchivePath(s.SeriesPath, "dummy") 150 path := setupConfigFile(c, c.MkDir()) 151 err := runDeploy(c, "local:dummy", "dummy-service", "--config", path) 152 c.Assert(err, jc.ErrorIsNil) 153 service, err := s.State.Service("dummy-service") 154 c.Assert(err, jc.ErrorIsNil) 155 settings, err := service.ConfigSettings() 156 c.Assert(err, jc.ErrorIsNil) 157 c.Assert(settings, gc.DeepEquals, charm.Settings{ 158 "skill-level": int64(9000), 159 "username": "admin001", 160 }) 161 } 162 163 func (s *DeploySuite) TestRelativeConfigPath(c *gc.C) { 164 testcharms.Repo.CharmArchivePath(s.SeriesPath, "dummy") 165 // Putting a config file in home is okay as $HOME is set to a tempdir 166 setupConfigFile(c, utils.Home()) 167 err := runDeploy(c, "local:dummy", "dummy-service", "--config", "~/testconfig.yaml") 168 c.Assert(err, jc.ErrorIsNil) 169 } 170 171 func (s *DeploySuite) TestConfigError(c *gc.C) { 172 testcharms.Repo.CharmArchivePath(s.SeriesPath, "dummy") 173 path := setupConfigFile(c, c.MkDir()) 174 err := runDeploy(c, "local:dummy", "other-service", "--config", path) 175 c.Assert(err, gc.ErrorMatches, `no settings found for "other-service"`) 176 _, err = s.State.Service("other-service") 177 c.Assert(err, jc.Satisfies, errors.IsNotFound) 178 } 179 180 func (s *DeploySuite) TestConstraints(c *gc.C) { 181 testcharms.Repo.CharmArchivePath(s.SeriesPath, "dummy") 182 err := runDeploy(c, "local:dummy", "--constraints", "mem=2G cpu-cores=2 networks=net1,^net2") 183 c.Assert(err, jc.ErrorIsNil) 184 curl := charm.MustParseURL("local:trusty/dummy-1") 185 service, _ := s.AssertService(c, "dummy", curl, 1, 0) 186 cons, err := service.Constraints() 187 c.Assert(err, jc.ErrorIsNil) 188 c.Assert(cons, jc.DeepEquals, constraints.MustParse("mem=2G cpu-cores=2 networks=net1,^net2")) 189 } 190 191 func (s *DeploySuite) TestNetworks(c *gc.C) { 192 testcharms.Repo.CharmArchivePath(s.SeriesPath, "dummy") 193 err := runDeploy(c, "local:dummy", "--networks", ", net1, net2 , ", "--constraints", "mem=2G cpu-cores=2 networks=net1,net0,^net3,^net4") 194 c.Assert(err, jc.ErrorIsNil) 195 curl := charm.MustParseURL("local:trusty/dummy-1") 196 service, _ := s.AssertService(c, "dummy", curl, 1, 0) 197 networks, err := service.Networks() 198 c.Assert(err, jc.ErrorIsNil) 199 c.Assert(networks, jc.DeepEquals, []string{"net1", "net2"}) 200 cons, err := service.Constraints() 201 c.Assert(err, jc.ErrorIsNil) 202 c.Assert(cons, jc.DeepEquals, constraints.MustParse("mem=2G cpu-cores=2 networks=net1,net0,^net3,^net4")) 203 } 204 205 func (s *DeploySuite) TestStorageWithoutFeatureFlag(c *gc.C) { 206 err := runDeploy(c, "local:storage-block", "--storage", "data=1G") 207 c.Assert(err, gc.ErrorMatches, "flag provided but not defined: --storage") 208 } 209 210 func (s *DeploySuite) TestStorage(c *gc.C) { 211 s.PatchEnvironment(osenv.JujuFeatureFlagEnvKey, "storage") 212 featureflag.SetFlagsFromEnvironment(osenv.JujuFeatureFlagEnvKey) 213 214 testcharms.Repo.CharmArchivePath(s.SeriesPath, "storage-block") 215 err := runDeploy(c, "local:storage-block", "--storage", "data=1G") 216 c.Assert(err, jc.ErrorIsNil) 217 curl := charm.MustParseURL("local:trusty/storage-block-1") 218 service, _ := s.AssertService(c, "storage-block", curl, 1, 0) 219 220 cons, err := service.StorageConstraints() 221 c.Assert(err, jc.ErrorIsNil) 222 c.Assert(cons, jc.DeepEquals, map[string]state.StorageConstraints{ 223 "data": { 224 Count: 1, 225 Size: 1024, 226 }, 227 }) 228 } 229 230 func (s *DeploySuite) TestSubordinateConstraints(c *gc.C) { 231 testcharms.Repo.CharmArchivePath(s.SeriesPath, "logging") 232 err := runDeploy(c, "local:logging", "--constraints", "mem=1G") 233 c.Assert(err, gc.ErrorMatches, "cannot use --constraints with subordinate service") 234 } 235 236 func (s *DeploySuite) TestNumUnits(c *gc.C) { 237 testcharms.Repo.CharmArchivePath(s.SeriesPath, "dummy") 238 err := runDeploy(c, "local:dummy", "-n", "13") 239 c.Assert(err, jc.ErrorIsNil) 240 curl := charm.MustParseURL("local:trusty/dummy-1") 241 s.AssertService(c, "dummy", curl, 13, 0) 242 } 243 244 func (s *DeploySuite) TestNumUnitsSubordinate(c *gc.C) { 245 testcharms.Repo.CharmArchivePath(s.SeriesPath, "logging") 246 err := runDeploy(c, "--num-units", "3", "local:logging") 247 c.Assert(err, gc.ErrorMatches, "cannot use --num-units or --to with subordinate service") 248 _, err = s.State.Service("dummy") 249 c.Assert(err, gc.ErrorMatches, `service "dummy" not found`) 250 } 251 252 func (s *DeploySuite) assertForceMachine(c *gc.C, machineId string) { 253 svc, err := s.State.Service("portlandia") 254 c.Assert(err, jc.ErrorIsNil) 255 units, err := svc.AllUnits() 256 c.Assert(err, jc.ErrorIsNil) 257 c.Assert(units, gc.HasLen, 1) 258 mid, err := units[0].AssignedMachineId() 259 c.Assert(err, jc.ErrorIsNil) 260 c.Assert(mid, gc.Equals, machineId) 261 } 262 263 func (s *DeploySuite) TestForceMachine(c *gc.C) { 264 testcharms.Repo.CharmArchivePath(s.SeriesPath, "dummy") 265 machine, err := s.State.AddMachine(coretesting.FakeDefaultSeries, state.JobHostUnits) 266 c.Assert(err, jc.ErrorIsNil) 267 err = runDeploy(c, "--to", machine.Id(), "local:dummy", "portlandia") 268 c.Assert(err, jc.ErrorIsNil) 269 s.assertForceMachine(c, machine.Id()) 270 } 271 272 func (s *DeploySuite) TestForceMachineExistingContainer(c *gc.C) { 273 testcharms.Repo.CharmArchivePath(s.SeriesPath, "dummy") 274 template := state.MachineTemplate{ 275 Series: coretesting.FakeDefaultSeries, 276 Jobs: []state.MachineJob{state.JobHostUnits}, 277 } 278 container, err := s.State.AddMachineInsideNewMachine(template, template, instance.LXC) 279 c.Assert(err, jc.ErrorIsNil) 280 err = runDeploy(c, "--to", container.Id(), "local:dummy", "portlandia") 281 c.Assert(err, jc.ErrorIsNil) 282 s.assertForceMachine(c, container.Id()) 283 machines, err := s.State.AllMachines() 284 c.Assert(err, jc.ErrorIsNil) 285 c.Assert(machines, gc.HasLen, 2) 286 } 287 288 func (s *DeploySuite) TestForceMachineNewContainer(c *gc.C) { 289 testcharms.Repo.CharmArchivePath(s.SeriesPath, "dummy") 290 machine, err := s.State.AddMachine(coretesting.FakeDefaultSeries, state.JobHostUnits) 291 c.Assert(err, jc.ErrorIsNil) 292 err = runDeploy(c, "--to", "lxc:"+machine.Id(), "local:dummy", "portlandia") 293 c.Assert(err, jc.ErrorIsNil) 294 s.assertForceMachine(c, machine.Id()+"/lxc/0") 295 machines, err := s.State.AllMachines() 296 c.Assert(err, jc.ErrorIsNil) 297 c.Assert(machines, gc.HasLen, 2) 298 } 299 300 func (s *DeploySuite) TestForceMachineNotFound(c *gc.C) { 301 testcharms.Repo.CharmArchivePath(s.SeriesPath, "dummy") 302 err := runDeploy(c, "--to", "42", "local:dummy", "portlandia") 303 c.Assert(err, gc.ErrorMatches, `cannot deploy "portlandia" to machine 42: machine 42 not found`) 304 _, err = s.State.Service("portlandia") 305 c.Assert(err, gc.ErrorMatches, `service "portlandia" not found`) 306 } 307 308 func (s *DeploySuite) TestForceMachineSubordinate(c *gc.C) { 309 machine, err := s.State.AddMachine(coretesting.FakeDefaultSeries, state.JobHostUnits) 310 c.Assert(err, jc.ErrorIsNil) 311 testcharms.Repo.CharmArchivePath(s.SeriesPath, "logging") 312 err = runDeploy(c, "--to", machine.Id(), "local:logging") 313 c.Assert(err, gc.ErrorMatches, "cannot use --num-units or --to with subordinate service") 314 _, err = s.State.Service("dummy") 315 c.Assert(err, gc.ErrorMatches, `service "dummy" not found`) 316 } 317 318 func (s *DeploySuite) TestNonLocalCannotHostUnits(c *gc.C) { 319 err := runDeploy(c, "--to", "0", "local:dummy", "portlandia") 320 c.Assert(err, gc.Not(gc.ErrorMatches), "machine 0 is the state server for a local environment and cannot host units") 321 } 322 323 type DeployLocalSuite struct { 324 testing.RepoSuite 325 } 326 327 var _ = gc.Suite(&DeployLocalSuite{}) 328 329 func (s *DeployLocalSuite) SetUpTest(c *gc.C) { 330 s.RepoSuite.SetUpTest(c) 331 332 // override provider type 333 s.PatchValue(&getClientConfig, func(client *api.Client) (*config.Config, error) { 334 attrs, err := client.EnvironmentGet() 335 if err != nil { 336 return nil, err 337 } 338 attrs["type"] = "local" 339 340 return config.New(config.NoDefaults, attrs) 341 }) 342 } 343 344 func (s *DeployLocalSuite) TestLocalCannotHostUnits(c *gc.C) { 345 err := runDeploy(c, "--to", "0", "local:dummy", "portlandia") 346 c.Assert(err, gc.ErrorMatches, "machine 0 is the state server for a local environment and cannot host units") 347 }