github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/api/controller/controller_test.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package controller_test 5 6 import ( 7 "encoding/json" 8 9 "github.com/juju/errors" 10 jujutesting "github.com/juju/testing" 11 jc "github.com/juju/testing/checkers" 12 "github.com/juju/utils" 13 gc "gopkg.in/check.v1" 14 "gopkg.in/juju/names.v2" 15 "gopkg.in/macaroon.v2-unstable" 16 17 "github.com/juju/juju/api/base" 18 apitesting "github.com/juju/juju/api/base/testing" 19 "github.com/juju/juju/api/controller" 20 "github.com/juju/juju/apiserver/common" 21 "github.com/juju/juju/apiserver/params" 22 "github.com/juju/juju/environs" 23 coretesting "github.com/juju/juju/testing" 24 ) 25 26 type Suite struct { 27 jujutesting.IsolationSuite 28 } 29 30 var _ = gc.Suite(&Suite{}) 31 32 func (s *Suite) TestDestroyControllerAPIVersion(c *gc.C) { 33 apiCaller := apitesting.BestVersionCaller{BestVersion: 3} 34 client := controller.NewClient(apiCaller) 35 for _, destroyStorage := range []*bool{nil, new(bool)} { 36 err := client.DestroyController(controller.DestroyControllerParams{ 37 DestroyStorage: destroyStorage, 38 }) 39 c.Assert(err, gc.ErrorMatches, "this Juju controller requires DestroyStorage to be true") 40 } 41 42 } 43 44 func (s *Suite) TestDestroyController(c *gc.C) { 45 var stub jujutesting.Stub 46 apiCaller := apitesting.BestVersionCaller{ 47 BestVersion: 4, 48 APICallerFunc: func(objType string, version int, id, request string, arg, result interface{}) error { 49 stub.AddCall(objType+"."+request, arg) 50 return stub.NextErr() 51 }, 52 } 53 client := controller.NewClient(apiCaller) 54 55 destroyStorage := true 56 err := client.DestroyController(controller.DestroyControllerParams{ 57 DestroyModels: true, 58 DestroyStorage: &destroyStorage, 59 }) 60 c.Assert(err, jc.ErrorIsNil) 61 62 stub.CheckCalls(c, []jujutesting.StubCall{ 63 {"Controller.DestroyController", []interface{}{params.DestroyControllerArgs{ 64 DestroyModels: true, 65 DestroyStorage: &destroyStorage, 66 }}}, 67 }) 68 } 69 70 func (s *Suite) TestDestroyControllerError(c *gc.C) { 71 apiCaller := apitesting.BestVersionCaller{ 72 BestVersion: 4, 73 APICallerFunc: func(objType string, version int, id, request string, arg, result interface{}) error { 74 return errors.New("nope") 75 }, 76 } 77 client := controller.NewClient(apiCaller) 78 err := client.DestroyController(controller.DestroyControllerParams{}) 79 c.Assert(err, gc.ErrorMatches, "nope") 80 } 81 82 func (s *Suite) TestInitiateMigration(c *gc.C) { 83 s.checkInitiateMigration(c, makeSpec()) 84 } 85 86 func (s *Suite) TestInitiateMigrationEmptyCACert(c *gc.C) { 87 spec := makeSpec() 88 spec.TargetCACert = "" 89 s.checkInitiateMigration(c, spec) 90 } 91 92 func (s *Suite) checkInitiateMigration(c *gc.C, spec controller.MigrationSpec) { 93 client, stub := makeInitiateMigrationClient(params.InitiateMigrationResults{ 94 Results: []params.InitiateMigrationResult{{ 95 MigrationId: "id", 96 }}, 97 }) 98 id, err := client.InitiateMigration(spec) 99 c.Assert(err, jc.ErrorIsNil) 100 c.Check(id, gc.Equals, "id") 101 stub.CheckCalls(c, []jujutesting.StubCall{ 102 {"Controller.InitiateMigration", []interface{}{specToArgs(spec)}}, 103 }) 104 } 105 106 func specToArgs(spec controller.MigrationSpec) params.InitiateMigrationArgs { 107 var macsJSON []byte 108 if len(spec.TargetMacaroons) > 0 { 109 var err error 110 macsJSON, err = json.Marshal(spec.TargetMacaroons) 111 if err != nil { 112 panic(err) 113 } 114 } 115 return params.InitiateMigrationArgs{ 116 Specs: []params.MigrationSpec{{ 117 ModelTag: names.NewModelTag(spec.ModelUUID).String(), 118 TargetInfo: params.MigrationTargetInfo{ 119 ControllerTag: names.NewControllerTag(spec.TargetControllerUUID).String(), 120 Addrs: spec.TargetAddrs, 121 CACert: spec.TargetCACert, 122 AuthTag: names.NewUserTag(spec.TargetUser).String(), 123 Password: spec.TargetPassword, 124 Macaroons: string(macsJSON), 125 }, 126 }}, 127 } 128 } 129 130 func (s *Suite) TestInitiateMigrationError(c *gc.C) { 131 client, _ := makeInitiateMigrationClient(params.InitiateMigrationResults{ 132 Results: []params.InitiateMigrationResult{{ 133 Error: common.ServerError(errors.New("boom")), 134 }}, 135 }) 136 id, err := client.InitiateMigration(makeSpec()) 137 c.Check(id, gc.Equals, "") 138 c.Check(err, gc.ErrorMatches, "boom") 139 } 140 141 func (s *Suite) TestInitiateMigrationResultMismatch(c *gc.C) { 142 client, _ := makeInitiateMigrationClient(params.InitiateMigrationResults{ 143 Results: []params.InitiateMigrationResult{ 144 {MigrationId: "id"}, 145 {MigrationId: "wtf"}, 146 }, 147 }) 148 id, err := client.InitiateMigration(makeSpec()) 149 c.Check(id, gc.Equals, "") 150 c.Check(err, gc.ErrorMatches, "unexpected number of results returned") 151 } 152 153 func (s *Suite) TestInitiateMigrationCallError(c *gc.C) { 154 apiCaller := apitesting.APICallerFunc(func(string, int, string, string, interface{}, interface{}) error { 155 return errors.New("boom") 156 }) 157 client := controller.NewClient(apiCaller) 158 id, err := client.InitiateMigration(makeSpec()) 159 c.Check(id, gc.Equals, "") 160 c.Check(err, gc.ErrorMatches, "boom") 161 } 162 163 func (s *Suite) TestInitiateMigrationValidationError(c *gc.C) { 164 client, stub := makeInitiateMigrationClient(params.InitiateMigrationResults{}) 165 spec := makeSpec() 166 spec.ModelUUID = "not-a-uuid" 167 id, err := client.InitiateMigration(spec) 168 c.Check(id, gc.Equals, "") 169 c.Check(err, gc.ErrorMatches, "client-side validation failed: model UUID not valid") 170 c.Check(stub.Calls(), gc.HasLen, 0) // API call shouldn't have happened 171 } 172 173 func (s *Suite) TestHostedModelConfigs_CallError(c *gc.C) { 174 apiCaller := apitesting.APICallerFunc(func(string, int, string, string, interface{}, interface{}) error { 175 return errors.New("boom") 176 }) 177 client := controller.NewClient(apiCaller) 178 config, err := client.HostedModelConfigs() 179 c.Check(config, gc.HasLen, 0) 180 c.Check(err, gc.ErrorMatches, "boom") 181 } 182 183 func (s *Suite) TestHostedModelConfigs_FormatResults(c *gc.C) { 184 apiCaller := apitesting.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error { 185 c.Assert(objType, gc.Equals, "Controller") 186 c.Assert(request, gc.Equals, "HostedModelConfigs") 187 c.Assert(arg, gc.IsNil) 188 out := result.(*params.HostedModelConfigsResults) 189 c.Assert(out, gc.NotNil) 190 *out = params.HostedModelConfigsResults{ 191 Models: []params.HostedModelConfig{ 192 { 193 Name: "first", 194 OwnerTag: "user-foo@bar", 195 Config: map[string]interface{}{ 196 "name": "first", 197 }, 198 CloudSpec: ¶ms.CloudSpec{ 199 Type: "magic", 200 Name: "first", 201 }, 202 }, { 203 Name: "second", 204 OwnerTag: "bad-tag", 205 }, { 206 Name: "third", 207 OwnerTag: "user-foo@bar", 208 Config: map[string]interface{}{ 209 "name": "third", 210 }, 211 CloudSpec: ¶ms.CloudSpec{ 212 Name: "third", 213 }, 214 }, 215 }, 216 } 217 return nil 218 }) 219 client := controller.NewClient(apiCaller) 220 config, err := client.HostedModelConfigs() 221 c.Assert(config, gc.HasLen, 3) 222 c.Assert(err, jc.ErrorIsNil) 223 first := config[0] 224 c.Assert(first.Name, gc.Equals, "first") 225 c.Assert(first.Owner, gc.Equals, names.NewUserTag("foo@bar")) 226 c.Assert(first.Config, gc.DeepEquals, map[string]interface{}{ 227 "name": "first", 228 }) 229 c.Assert(first.CloudSpec, gc.DeepEquals, environs.CloudSpec{ 230 Type: "magic", 231 Name: "first", 232 }) 233 second := config[1] 234 c.Assert(second.Name, gc.Equals, "second") 235 c.Assert(second.Error.Error(), gc.Equals, `"bad-tag" is not a valid tag`) 236 third := config[2] 237 c.Assert(third.Name, gc.Equals, "third") 238 c.Assert(third.Error.Error(), gc.Equals, "validating CloudSpec: empty Type not valid") 239 } 240 241 func makeInitiateMigrationClient(results params.InitiateMigrationResults) ( 242 *controller.Client, *jujutesting.Stub, 243 ) { 244 var stub jujutesting.Stub 245 apiCaller := apitesting.APICallerFunc( 246 func(objType string, version int, id, request string, arg, result interface{}) error { 247 stub.AddCall(objType+"."+request, arg) 248 out := result.(*params.InitiateMigrationResults) 249 *out = results 250 return nil 251 }, 252 ) 253 client := controller.NewClient(apiCaller) 254 return client, &stub 255 } 256 257 func makeSpec() controller.MigrationSpec { 258 mac, err := macaroon.New([]byte("secret"), []byte("id"), "location") 259 if err != nil { 260 panic(err) 261 } 262 return controller.MigrationSpec{ 263 ModelUUID: randomUUID(), 264 TargetControllerUUID: randomUUID(), 265 TargetAddrs: []string{"1.2.3.4:5"}, 266 TargetCACert: "cert", 267 TargetUser: "someone", 268 TargetPassword: "secret", 269 TargetMacaroons: []macaroon.Slice{{mac}}, 270 } 271 } 272 273 func randomUUID() string { 274 return utils.MustNewUUID().String() 275 } 276 277 func (s *Suite) TestModelStatusEmpty(c *gc.C) { 278 apiCaller := apitesting.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error { 279 c.Check(objType, gc.Equals, "Controller") 280 c.Check(id, gc.Equals, "") 281 c.Check(request, gc.Equals, "ModelStatus") 282 c.Check(result, gc.FitsTypeOf, ¶ms.ModelStatusResults{}) 283 284 return nil 285 }) 286 287 client := controller.NewClient(apiCaller) 288 results, err := client.ModelStatus() 289 c.Assert(err, jc.ErrorIsNil) 290 c.Assert(results, jc.DeepEquals, []base.ModelStatus{}) 291 } 292 293 func (s *Suite) TestModelStatus(c *gc.C) { 294 apiCaller := apitesting.BestVersionCaller{ 295 BestVersion: 4, 296 APICallerFunc: func(objType string, version int, id, request string, arg, result interface{}) error { 297 c.Check(objType, gc.Equals, "Controller") 298 c.Check(id, gc.Equals, "") 299 c.Check(request, gc.Equals, "ModelStatus") 300 c.Check(arg, jc.DeepEquals, params.Entities{ 301 []params.Entity{ 302 {Tag: coretesting.ModelTag.String()}, 303 {Tag: coretesting.ModelTag.String()}, 304 }, 305 }) 306 c.Check(result, gc.FitsTypeOf, ¶ms.ModelStatusResults{}) 307 308 out := result.(*params.ModelStatusResults) 309 out.Results = []params.ModelStatus{ 310 { 311 ModelTag: coretesting.ModelTag.String(), 312 OwnerTag: "user-glenda", 313 ApplicationCount: 3, 314 HostedMachineCount: 2, 315 Life: "alive", 316 Machines: []params.ModelMachineInfo{{ 317 Id: "0", 318 InstanceId: "inst-ance", 319 Status: "pending", 320 }}, 321 }, 322 {Error: common.ServerError(errors.New("model error"))}, 323 } 324 return nil 325 }, 326 } 327 328 client := controller.NewClient(apiCaller) 329 results, err := client.ModelStatus(coretesting.ModelTag, coretesting.ModelTag) 330 c.Assert(err, jc.ErrorIsNil) 331 c.Assert(results[0], jc.DeepEquals, base.ModelStatus{ 332 UUID: coretesting.ModelTag.Id(), 333 TotalMachineCount: 1, 334 HostedMachineCount: 2, 335 ApplicationCount: 3, 336 Owner: "glenda", 337 Life: string(params.Alive), 338 Machines: []base.Machine{{Id: "0", InstanceId: "inst-ance", Status: "pending"}}, 339 }) 340 c.Assert(results[1].Error, gc.ErrorMatches, "model error") 341 } 342 343 func (s *Suite) TestModelStatusError(c *gc.C) { 344 apiCaller := apitesting.APICallerFunc( 345 func(objType string, version int, id, request string, args, result interface{}) error { 346 return errors.New("model error") 347 }) 348 client := controller.NewClient(apiCaller) 349 out, err := client.ModelStatus(coretesting.ModelTag, coretesting.ModelTag) 350 c.Assert(err, gc.ErrorMatches, "model error") 351 c.Assert(out, gc.IsNil) 352 } 353 354 func (s *Suite) TestConfigSet(c *gc.C) { 355 apiCaller := apitesting.BestVersionCaller{ 356 BestVersion: 5, 357 APICallerFunc: func(objType string, version int, id, request string, args, result interface{}) error { 358 c.Assert(objType, gc.Equals, "Controller") 359 c.Assert(version, gc.Equals, 5) 360 c.Assert(request, gc.Equals, "ConfigSet") 361 c.Assert(result, gc.IsNil) 362 c.Assert(args, gc.DeepEquals, params.ControllerConfigSet{Config: map[string]interface{}{ 363 "some-setting": 345, 364 }}) 365 return errors.New("ruth mundy") 366 }, 367 } 368 client := controller.NewClient(apiCaller) 369 err := client.ConfigSet(map[string]interface{}{ 370 "some-setting": 345, 371 }) 372 c.Assert(err, gc.ErrorMatches, "ruth mundy") 373 } 374 375 func (s *Suite) TestConfigSetAgainstOlderAPIVersion(c *gc.C) { 376 apiCaller := apitesting.BestVersionCaller{BestVersion: 4} 377 client := controller.NewClient(apiCaller) 378 err := client.ConfigSet(map[string]interface{}{ 379 "some-setting": 345, 380 }) 381 c.Assert(err, gc.ErrorMatches, "this controller version doesn't support updating controller config") 382 }