github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/cmd/juju/environment/ensureavailability_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package environment_test 5 6 import ( 7 "bytes" 8 "encoding/json" 9 "fmt" 10 "strings" 11 12 "github.com/juju/cmd" 13 jc "github.com/juju/testing/checkers" 14 gc "gopkg.in/check.v1" 15 goyaml "gopkg.in/yaml.v1" 16 17 "github.com/juju/juju/apiserver/params" 18 "github.com/juju/juju/cmd/envcmd" 19 "github.com/juju/juju/cmd/juju/environment" 20 "github.com/juju/juju/constraints" 21 coretesting "github.com/juju/juju/testing" 22 ) 23 24 type EnsureAvailabilitySuite struct { 25 coretesting.FakeJujuHomeSuite 26 fake *fakeHAClient 27 } 28 29 // Initialize numStateServers to an invalid number to validate 30 // that ensure-availability doesn't call into the API when its 31 // pre-checks fail 32 const invalidNumServers = -2 33 34 func (s *EnsureAvailabilitySuite) SetUpTest(c *gc.C) { 35 s.FakeJujuHomeSuite.SetUpTest(c) 36 s.fake = &fakeHAClient{numStateServers: invalidNumServers} 37 } 38 39 type fakeHAClient struct { 40 numStateServers int 41 cons constraints.Value 42 err error 43 series string 44 placement []string 45 result params.StateServersChanges 46 } 47 48 func (f *fakeHAClient) Close() error { 49 return nil 50 } 51 52 func (f *fakeHAClient) EnsureAvailability(numStateServers int, cons constraints.Value, 53 series string, placement []string) (params.StateServersChanges, error) { 54 55 f.numStateServers = numStateServers 56 f.cons = cons 57 f.series = series 58 f.placement = placement 59 60 if f.err != nil { 61 return f.result, f.err 62 } 63 64 if numStateServers == 1 { 65 return f.result, nil 66 } 67 68 // In the real HAClient, specifying a numStateServers value of 0 69 // indicates that the default value (3) should be used 70 if numStateServers == 0 { 71 numStateServers = 3 72 } 73 74 // If numStateServers > 1, we need to pretend that we added some machines 75 f.result.Maintained = append(f.result.Maintained, "machine-0") 76 for i := 1; i < numStateServers; i++ { 77 f.result.Added = append(f.result.Added, fmt.Sprintf("machine-%d", i)) 78 } 79 80 return f.result, nil 81 } 82 83 var _ = gc.Suite(&EnsureAvailabilitySuite{}) 84 85 func (s *EnsureAvailabilitySuite) runEnsureAvailability(c *gc.C, args ...string) (*cmd.Context, error) { 86 command := environment.NewEnsureAvailabilityCommand(s.fake) 87 return coretesting.RunCommand(c, envcmd.Wrap(command), args...) 88 } 89 90 func (s *EnsureAvailabilitySuite) TestEnsureAvailability(c *gc.C) { 91 ctx, err := s.runEnsureAvailability(c, "-n", "1") 92 c.Assert(err, jc.ErrorIsNil) 93 c.Assert(coretesting.Stdout(ctx), gc.Equals, "") 94 95 c.Assert(s.fake.numStateServers, gc.Equals, 1) 96 c.Assert(&s.fake.cons, jc.Satisfies, constraints.IsEmpty) 97 c.Assert(s.fake.series, gc.Equals, "") 98 c.Assert(len(s.fake.placement), gc.Equals, 0) 99 } 100 101 func (s *EnsureAvailabilitySuite) TestBlockEnsureAvailability(c *gc.C) { 102 s.fake.err = ¶ms.Error{Code: params.CodeOperationBlocked} 103 _, err := s.runEnsureAvailability(c, "-n", "1") 104 c.Assert(err, gc.ErrorMatches, cmd.ErrSilent.Error()) 105 106 // msg is logged 107 stripped := strings.Replace(c.GetTestLog(), "\n", "", -1) 108 c.Check(stripped, gc.Matches, ".*To unblock changes.*") 109 } 110 111 func (s *EnsureAvailabilitySuite) TestEnsureAvailabilityPlacementError(c *gc.C) { 112 _, err := s.runEnsureAvailability(c, "-n", "1", "--to", "1") 113 c.Assert(err, gc.ErrorMatches, `unsupported ensure-availability placement directive "1"`) 114 } 115 116 func (s *EnsureAvailabilitySuite) TestEnsureAvailabilityFormatYaml(c *gc.C) { 117 expected := map[string][]string{ 118 "maintained": {"0"}, 119 "added": {"1", "2"}, 120 } 121 122 ctx, err := s.runEnsureAvailability(c, "-n", "3", "--format", "yaml") 123 c.Assert(err, jc.ErrorIsNil) 124 125 c.Assert(s.fake.numStateServers, gc.Equals, 3) 126 c.Assert(&s.fake.cons, jc.Satisfies, constraints.IsEmpty) 127 c.Assert(s.fake.series, gc.Equals, "") 128 c.Assert(len(s.fake.placement), gc.Equals, 0) 129 130 var result map[string][]string 131 err = goyaml.Unmarshal(ctx.Stdout.(*bytes.Buffer).Bytes(), &result) 132 c.Assert(err, jc.ErrorIsNil) 133 c.Assert(result, gc.DeepEquals, expected) 134 } 135 136 func (s *EnsureAvailabilitySuite) TestEnsureAvailabilityFormatJson(c *gc.C) { 137 expected := map[string][]string{ 138 "maintained": {"0"}, 139 "added": {"1", "2"}, 140 } 141 142 ctx, err := s.runEnsureAvailability(c, "-n", "3", "--format", "json") 143 c.Assert(err, jc.ErrorIsNil) 144 145 c.Assert(s.fake.numStateServers, gc.Equals, 3) 146 c.Assert(&s.fake.cons, jc.Satisfies, constraints.IsEmpty) 147 c.Assert(s.fake.series, gc.Equals, "") 148 c.Assert(len(s.fake.placement), gc.Equals, 0) 149 150 var result map[string][]string 151 err = json.Unmarshal(ctx.Stdout.(*bytes.Buffer).Bytes(), &result) 152 c.Assert(err, jc.ErrorIsNil) 153 c.Assert(result, gc.DeepEquals, expected) 154 } 155 156 func (s *EnsureAvailabilitySuite) TestEnsureAvailabilityWithSeries(c *gc.C) { 157 // Also test with -n 5 to validate numbers other than 1 and 3 158 ctx, err := s.runEnsureAvailability(c, "--series", "series", "-n", "5") 159 c.Assert(err, jc.ErrorIsNil) 160 c.Assert(coretesting.Stdout(ctx), gc.Equals, 161 "maintaining machines: 0\n"+ 162 "adding machines: 1, 2, 3, 4\n\n") 163 164 c.Assert(s.fake.numStateServers, gc.Equals, 5) 165 c.Assert(&s.fake.cons, jc.Satisfies, constraints.IsEmpty) 166 c.Assert(s.fake.series, gc.Equals, "series") 167 c.Assert(len(s.fake.placement), gc.Equals, 0) 168 } 169 170 func (s *EnsureAvailabilitySuite) TestEnsureAvailabilityWithConstraints(c *gc.C) { 171 ctx, err := s.runEnsureAvailability(c, "--constraints", "mem=4G", "-n", "3") 172 c.Assert(err, jc.ErrorIsNil) 173 c.Assert(coretesting.Stdout(ctx), gc.Equals, 174 "maintaining machines: 0\n"+ 175 "adding machines: 1, 2\n\n") 176 177 c.Assert(s.fake.numStateServers, gc.Equals, 3) 178 expectedCons := constraints.MustParse("mem=4G") 179 c.Assert(s.fake.cons, gc.DeepEquals, expectedCons) 180 c.Assert(s.fake.series, gc.Equals, "") 181 c.Assert(len(s.fake.placement), gc.Equals, 0) 182 } 183 184 func (s *EnsureAvailabilitySuite) TestEnsureAvailabilityWithPlacement(c *gc.C) { 185 ctx, err := s.runEnsureAvailability(c, "--to", "valid", "-n", "3") 186 c.Assert(err, jc.ErrorIsNil) 187 c.Assert(coretesting.Stdout(ctx), gc.Equals, 188 "maintaining machines: 0\n"+ 189 "adding machines: 1, 2\n\n") 190 191 c.Assert(s.fake.numStateServers, gc.Equals, 3) 192 c.Assert(&s.fake.cons, jc.Satisfies, constraints.IsEmpty) 193 c.Assert(s.fake.series, gc.Equals, "") 194 expectedPlacement := []string{"valid"} 195 c.Assert(s.fake.placement, gc.DeepEquals, expectedPlacement) 196 } 197 198 func (s *EnsureAvailabilitySuite) TestEnsureAvailabilityErrors(c *gc.C) { 199 for _, n := range []int{-1, 2} { 200 _, err := s.runEnsureAvailability(c, "-n", fmt.Sprint(n)) 201 c.Assert(err, gc.ErrorMatches, "must specify a number of state servers odd and non-negative") 202 } 203 204 // Verify that ensure-availability didn't call into the API 205 c.Assert(s.fake.numStateServers, gc.Equals, invalidNumServers) 206 } 207 208 func (s *EnsureAvailabilitySuite) TestEnsureAvailabilityAllows0(c *gc.C) { 209 // If the number of state servers is specified as "0", the API will 210 // then use the default number of 3. 211 ctx, err := s.runEnsureAvailability(c, "-n", "0") 212 c.Assert(err, jc.ErrorIsNil) 213 c.Assert(coretesting.Stdout(ctx), gc.Equals, 214 "maintaining machines: 0\n"+ 215 "adding machines: 1, 2\n\n") 216 217 c.Assert(s.fake.numStateServers, gc.Equals, 0) 218 c.Assert(&s.fake.cons, jc.Satisfies, constraints.IsEmpty) 219 c.Assert(s.fake.series, gc.Equals, "") 220 c.Assert(len(s.fake.placement), gc.Equals, 0) 221 } 222 223 func (s *EnsureAvailabilitySuite) TestEnsureAvailabilityDefaultsTo0(c *gc.C) { 224 // If the number of state servers is not specified, we pass in 0 to the 225 // API. The API will then use the default number of 3. 226 ctx, err := s.runEnsureAvailability(c) 227 c.Assert(err, jc.ErrorIsNil) 228 c.Assert(coretesting.Stdout(ctx), gc.Equals, 229 "maintaining machines: 0\n"+ 230 "adding machines: 1, 2\n\n") 231 232 c.Assert(s.fake.numStateServers, gc.Equals, 0) 233 c.Assert(&s.fake.cons, jc.Satisfies, constraints.IsEmpty) 234 c.Assert(s.fake.series, gc.Equals, "") 235 c.Assert(len(s.fake.placement), gc.Equals, 0) 236 }