github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/cmd/juju/service/addunit_test.go (about) 1 // Copyright 2012-2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package service_test 5 6 import ( 7 "strings" 8 9 "github.com/juju/errors" 10 jc "github.com/juju/testing/checkers" 11 gc "gopkg.in/check.v1" 12 13 "github.com/juju/juju/apiserver/common" 14 "github.com/juju/juju/apiserver/params" 15 "github.com/juju/juju/cmd/envcmd" 16 "github.com/juju/juju/cmd/juju/service" 17 "github.com/juju/juju/environs/config" 18 "github.com/juju/juju/instance" 19 "github.com/juju/juju/testing" 20 ) 21 22 type AddUnitSuite struct { 23 testing.FakeJujuHomeSuite 24 fake *fakeServiceAddUnitAPI 25 } 26 27 type fakeServiceAddUnitAPI struct { 28 envType string 29 service string 30 numUnits int 31 machineSpec string 32 placement []*instance.Placement 33 err error 34 newAPI bool 35 } 36 37 func (f *fakeServiceAddUnitAPI) Close() error { 38 return nil 39 } 40 41 func (f *fakeServiceAddUnitAPI) EnvironmentUUID() string { 42 return "fake-uuid" 43 } 44 45 func (f *fakeServiceAddUnitAPI) AddServiceUnits(service string, numUnits int, machineSpec string) ([]string, error) { 46 if f.err != nil { 47 return nil, f.err 48 } 49 50 if service != f.service { 51 return nil, errors.NotFoundf("service %q", service) 52 } 53 54 f.numUnits += numUnits 55 f.machineSpec = machineSpec 56 57 // The add-unit subcommand doesn't check the results, so we can just return nil 58 return nil, nil 59 } 60 61 func (f *fakeServiceAddUnitAPI) AddServiceUnitsWithPlacement(service string, numUnits int, placement []*instance.Placement) ([]string, error) { 62 if !f.newAPI { 63 return nil, ¶ms.Error{Code: params.CodeNotImplemented} 64 } 65 if service != f.service { 66 return nil, errors.NotFoundf("service %q", service) 67 } 68 69 f.numUnits += numUnits 70 f.placement = placement 71 return nil, nil 72 } 73 74 func (f *fakeServiceAddUnitAPI) EnvironmentGet() (map[string]interface{}, error) { 75 cfg, err := config.New(config.UseDefaults, map[string]interface{}{ 76 "type": f.envType, 77 "name": "dummy", 78 }) 79 if err != nil { 80 return nil, err 81 } 82 83 return cfg.AllAttrs(), nil 84 } 85 86 func (s *AddUnitSuite) SetUpTest(c *gc.C) { 87 s.FakeJujuHomeSuite.SetUpTest(c) 88 s.fake = &fakeServiceAddUnitAPI{service: "some-service-name", numUnits: 1, envType: "dummy"} 89 } 90 91 var _ = gc.Suite(&AddUnitSuite{}) 92 93 var initAddUnitErrorTests = []struct { 94 args []string 95 err string 96 }{ 97 { 98 args: []string{"some-service-name", "-n", "0"}, 99 err: `--num-units must be a positive integer`, 100 }, { 101 args: []string{}, 102 err: `no service specified`, 103 }, { 104 args: []string{"some-service-name", "--to", "1,#:foo"}, 105 err: `invalid --to parameter "#:foo"`, 106 }, 107 } 108 109 func (s *AddUnitSuite) TestInitErrors(c *gc.C) { 110 for i, t := range initAddUnitErrorTests { 111 c.Logf("test %d", i) 112 err := testing.InitCommand(envcmd.Wrap(service.NewAddUnitCommand(s.fake)), t.args) 113 c.Check(err, gc.ErrorMatches, t.err) 114 } 115 } 116 117 func (s *AddUnitSuite) runAddUnit(c *gc.C, args ...string) error { 118 _, err := testing.RunCommand(c, envcmd.Wrap(service.NewAddUnitCommand(s.fake)), args...) 119 return err 120 } 121 122 func (s *AddUnitSuite) TestInvalidToParamWithOlderServer(c *gc.C) { 123 err := s.runAddUnit(c, "some-service-name") 124 c.Assert(err, jc.ErrorIsNil) 125 c.Assert(s.fake.numUnits, gc.Equals, 2) 126 127 err = s.runAddUnit(c, "--to", "bigglesplop", "some-service-name") 128 c.Assert(err, gc.ErrorMatches, `unsupported --to parameter "bigglesplop"`) 129 } 130 131 func (s *AddUnitSuite) TestUnsupportedNumUnitsWithOlderServer(c *gc.C) { 132 err := s.runAddUnit(c, "some-service-name") 133 c.Assert(err, jc.ErrorIsNil) 134 c.Assert(s.fake.numUnits, gc.Equals, 2) 135 136 err = s.runAddUnit(c, "-n", "2", "--to", "123", "some-service-name") 137 c.Assert(err, gc.ErrorMatches, `this version of Juju does not support --num-units > 1 with --to`) 138 } 139 140 func (s *AddUnitSuite) TestAddUnit(c *gc.C) { 141 err := s.runAddUnit(c, "some-service-name") 142 c.Assert(err, jc.ErrorIsNil) 143 c.Assert(s.fake.numUnits, gc.Equals, 2) 144 145 err = s.runAddUnit(c, "--num-units", "2", "some-service-name") 146 c.Assert(err, jc.ErrorIsNil) 147 c.Assert(s.fake.numUnits, gc.Equals, 4) 148 } 149 150 func (s *AddUnitSuite) TestAddUnitWithPlacement(c *gc.C) { 151 s.fake.newAPI = true 152 err := s.runAddUnit(c, "some-service-name") 153 c.Assert(err, jc.ErrorIsNil) 154 c.Assert(s.fake.numUnits, gc.Equals, 2) 155 156 err = s.runAddUnit(c, "--num-units", "2", "--to", "123,lxc:1,1/lxc/2,foo", "some-service-name") 157 c.Assert(err, jc.ErrorIsNil) 158 c.Assert(s.fake.numUnits, gc.Equals, 4) 159 c.Assert(s.fake.placement, jc.DeepEquals, []*instance.Placement{ 160 {"#", "123"}, 161 {"lxc", "1"}, 162 {"#", "1/lxc/2"}, 163 {"fake-uuid", "foo"}, 164 }) 165 } 166 167 func (s *AddUnitSuite) TestBlockAddUnit(c *gc.C) { 168 // Block operation 169 s.fake.err = common.ErrOperationBlocked("TestBlockAddUnit") 170 s.runAddUnit(c, "some-service-name") 171 172 // msg is logged 173 stripped := strings.Replace(c.GetTestLog(), "\n", "", -1) 174 c.Check(stripped, gc.Matches, ".*TestBlockAddUnit.*") 175 } 176 177 func (s *AddUnitSuite) TestNonLocalCanHostUnits(c *gc.C) { 178 err := s.runAddUnit(c, "some-service-name", "--to", "0") 179 c.Assert(err, jc.ErrorIsNil) 180 } 181 182 func (s *AddUnitSuite) TestLocalCannotHostUnits(c *gc.C) { 183 s.fake.envType = "local" 184 err := s.runAddUnit(c, "some-service-name", "--to", "0") 185 c.Assert(err, gc.ErrorMatches, "machine 0 is the state server for a local environment and cannot host units") 186 err = s.runAddUnit(c, "some-service-name", "--to", "1,#:0") 187 c.Assert(err, gc.ErrorMatches, "machine 0 is the state server for a local environment and cannot host units") 188 } 189 190 func (s *AddUnitSuite) TestForceMachine(c *gc.C) { 191 err := s.runAddUnit(c, "some-service-name", "--to", "3") 192 c.Assert(err, jc.ErrorIsNil) 193 c.Assert(s.fake.numUnits, gc.Equals, 2) 194 c.Assert(s.fake.machineSpec, gc.Equals, "3") 195 196 err = s.runAddUnit(c, "some-service-name", "--to", "23") 197 c.Assert(err, jc.ErrorIsNil) 198 c.Assert(s.fake.numUnits, gc.Equals, 3) 199 c.Assert(s.fake.machineSpec, gc.Equals, "23") 200 } 201 202 func (s *AddUnitSuite) TestForceMachineNewContainer(c *gc.C) { 203 err := s.runAddUnit(c, "some-service-name", "--to", "lxc:1") 204 c.Assert(err, jc.ErrorIsNil) 205 c.Assert(s.fake.numUnits, gc.Equals, 2) 206 c.Assert(s.fake.machineSpec, gc.Equals, "lxc:1") 207 } 208 209 func (s *AddUnitSuite) TestNameChecks(c *gc.C) { 210 assertMachineOrNewContainer := func(s string, expect bool) { 211 c.Logf("%s -> %v", s, expect) 212 c.Assert(service.IsMachineOrNewContainer(s), gc.Equals, expect) 213 } 214 assertMachineOrNewContainer("0", true) 215 assertMachineOrNewContainer("00", false) 216 assertMachineOrNewContainer("1", true) 217 assertMachineOrNewContainer("0/lxc/0", true) 218 assertMachineOrNewContainer("lxc:0", true) 219 assertMachineOrNewContainer("lxc:lxc:0", false) 220 assertMachineOrNewContainer("kvm:0/lxc/1", true) 221 assertMachineOrNewContainer("lxc:", false) 222 assertMachineOrNewContainer(":lxc", false) 223 assertMachineOrNewContainer("0/lxc/", false) 224 assertMachineOrNewContainer("0/lxc", false) 225 assertMachineOrNewContainer("kvm:0/lxc", false) 226 assertMachineOrNewContainer("0/lxc/01", false) 227 assertMachineOrNewContainer("0/lxc/10", true) 228 assertMachineOrNewContainer("0/kvm/4", true) 229 }