github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/apiserver/instancepoller/instancepoller_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package instancepoller_test 5 6 import ( 7 "time" 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/instancepoller" 15 "github.com/juju/juju/apiserver/params" 16 apiservertesting "github.com/juju/juju/apiserver/testing" 17 "github.com/juju/juju/network" 18 "github.com/juju/juju/state" 19 statetesting "github.com/juju/juju/state/testing" 20 "github.com/juju/juju/status" 21 coretesting "github.com/juju/juju/testing" 22 ) 23 24 type InstancePollerSuite struct { 25 coretesting.BaseSuite 26 27 st *mockState 28 api *instancepoller.InstancePollerAPI 29 authoriser apiservertesting.FakeAuthorizer 30 resources *common.Resources 31 32 machineEntities params.Entities 33 machineErrorResults params.ErrorResults 34 35 mixedEntities params.Entities 36 mixedErrorResults params.ErrorResults 37 } 38 39 var _ = gc.Suite(&InstancePollerSuite{}) 40 41 func (s *InstancePollerSuite) SetUpTest(c *gc.C) { 42 s.BaseSuite.SetUpTest(c) 43 44 s.authoriser = apiservertesting.FakeAuthorizer{ 45 EnvironManager: true, 46 } 47 s.resources = common.NewResources() 48 s.AddCleanup(func(*gc.C) { s.resources.StopAll() }) 49 50 s.st = NewMockState() 51 instancepoller.PatchState(s, s.st) 52 53 var err error 54 s.api, err = instancepoller.NewInstancePollerAPI(nil, s.resources, s.authoriser) 55 c.Assert(err, jc.ErrorIsNil) 56 57 s.machineEntities = params.Entities{ 58 Entities: []params.Entity{ 59 {Tag: "machine-1"}, 60 {Tag: "machine-2"}, 61 {Tag: "machine-3"}, 62 }} 63 s.machineErrorResults = params.ErrorResults{ 64 Results: []params.ErrorResult{ 65 {Error: apiservertesting.ServerError("pow!")}, 66 {Error: apiservertesting.ServerError("FAIL")}, 67 {Error: apiservertesting.NotProvisionedError("42")}, 68 }} 69 70 s.mixedEntities = params.Entities{ 71 Entities: []params.Entity{ 72 {Tag: "machine-1"}, 73 {Tag: "machine-2"}, 74 {Tag: "machine-42"}, 75 {Tag: "service-unknown"}, 76 {Tag: "invalid-tag"}, 77 {Tag: "unit-missing-1"}, 78 {Tag: ""}, 79 {Tag: "42"}, 80 }} 81 s.mixedErrorResults = params.ErrorResults{ 82 Results: []params.ErrorResult{ 83 {Error: nil}, 84 {Error: nil}, 85 {Error: apiservertesting.NotFoundError("machine 42")}, 86 {Error: apiservertesting.ServerError(`"service-unknown" is not a valid machine tag`)}, 87 {Error: apiservertesting.ServerError(`"invalid-tag" is not a valid tag`)}, 88 {Error: apiservertesting.ServerError(`"unit-missing-1" is not a valid machine tag`)}, 89 {Error: apiservertesting.ServerError(`"" is not a valid tag`)}, 90 {Error: apiservertesting.ServerError(`"42" is not a valid tag`)}, 91 }} 92 } 93 94 func (s *InstancePollerSuite) TestNewInstancePollerAPIRequiresEnvironManager(c *gc.C) { 95 anAuthoriser := s.authoriser 96 anAuthoriser.EnvironManager = false 97 api, err := instancepoller.NewInstancePollerAPI(nil, s.resources, anAuthoriser) 98 c.Assert(api, gc.IsNil) 99 c.Assert(err, gc.ErrorMatches, "permission denied") 100 } 101 102 func (s *InstancePollerSuite) TestModelConfigFailure(c *gc.C) { 103 s.st.SetErrors(errors.New("boom")) 104 105 result, err := s.api.ModelConfig() 106 c.Assert(err, gc.ErrorMatches, "boom") 107 c.Assert(result, jc.DeepEquals, params.ModelConfigResult{}) 108 109 s.st.CheckCallNames(c, "ModelConfig") 110 } 111 112 func (s *InstancePollerSuite) TestModelConfigSuccess(c *gc.C) { 113 envConfig := coretesting.ModelConfig(c) 114 s.st.SetConfig(c, envConfig) 115 116 result, err := s.api.ModelConfig() 117 c.Assert(err, jc.ErrorIsNil) 118 c.Assert(result, jc.DeepEquals, params.ModelConfigResult{ 119 Config: envConfig.AllAttrs(), 120 }) 121 122 s.st.CheckCallNames(c, "ModelConfig") 123 } 124 125 func (s *InstancePollerSuite) TestWatchForModelConfigChangesFailure(c *gc.C) { 126 // Force the Changes() method of the mock watcher to return a 127 // closed channel by setting an error. 128 s.st.SetErrors(errors.New("boom")) 129 130 result, err := s.api.WatchForModelConfigChanges() 131 c.Assert(err, gc.ErrorMatches, "boom") 132 c.Assert(result, jc.DeepEquals, params.NotifyWatchResult{}) 133 134 c.Assert(s.resources.Count(), gc.Equals, 0) // no watcher registered 135 s.st.CheckCallNames(c, "WatchForModelConfigChanges") 136 } 137 138 func (s *InstancePollerSuite) TestWatchForModelConfigChangesSuccess(c *gc.C) { 139 result, err := s.api.WatchForModelConfigChanges() 140 c.Assert(err, jc.ErrorIsNil) 141 c.Assert(result, jc.DeepEquals, params.NotifyWatchResult{ 142 Error: nil, NotifyWatcherId: "1", 143 }) 144 145 // Verify the watcher resource was registered. 146 c.Assert(s.resources.Count(), gc.Equals, 1) 147 resource := s.resources.Get("1") 148 defer statetesting.AssertStop(c, resource) 149 150 // Check that the watcher has consumed the initial event 151 wc := statetesting.NewNotifyWatcherC(c, s.st, resource.(state.NotifyWatcher)) 152 wc.AssertNoChange() 153 154 s.st.CheckCallNames(c, "WatchForModelConfigChanges") 155 156 // Try changing the config to verify an event is reported. 157 envConfig := coretesting.ModelConfig(c) 158 s.st.SetConfig(c, envConfig) 159 wc.AssertOneChange() 160 } 161 162 func (s *InstancePollerSuite) TestWatchModelMachinesFailure(c *gc.C) { 163 // Force the Changes() method of the mock watcher to return a 164 // closed channel by setting an error. 165 s.st.SetErrors(errors.Errorf("boom")) 166 167 result, err := s.api.WatchModelMachines() 168 c.Assert(err, gc.ErrorMatches, "cannot obtain initial model machines: boom") 169 c.Assert(result, jc.DeepEquals, params.StringsWatchResult{}) 170 171 c.Assert(s.resources.Count(), gc.Equals, 0) // no watcher registered 172 s.st.CheckCallNames(c, "WatchModelMachines") 173 } 174 175 func (s *InstancePollerSuite) TestWatchModelMachinesSuccess(c *gc.C) { 176 // Add a couple of machines. 177 s.st.SetMachineInfo(c, machineInfo{id: "2"}) 178 s.st.SetMachineInfo(c, machineInfo{id: "1"}) 179 180 expectedResult := params.StringsWatchResult{ 181 Error: nil, 182 StringsWatcherId: "1", 183 Changes: []string{"1", "2"}, // initial event (sorted ids) 184 } 185 result, err := s.api.WatchModelMachines() 186 c.Assert(err, jc.ErrorIsNil) 187 c.Assert(result, jc.DeepEquals, expectedResult) 188 189 // Verify the watcher resource was registered. 190 c.Assert(s.resources.Count(), gc.Equals, 1) 191 resource1 := s.resources.Get("1") 192 defer func() { 193 if resource1 != nil { 194 statetesting.AssertStop(c, resource1) 195 } 196 }() 197 198 // Check that the watcher has consumed the initial event 199 wc1 := statetesting.NewStringsWatcherC(c, s.st, resource1.(state.StringsWatcher)) 200 wc1.AssertNoChange() 201 202 s.st.CheckCallNames(c, "WatchModelMachines") 203 204 // Add another watcher to verify events coalescence. 205 result, err = s.api.WatchModelMachines() 206 c.Assert(err, jc.ErrorIsNil) 207 expectedResult.StringsWatcherId = "2" 208 c.Assert(result, jc.DeepEquals, expectedResult) 209 s.st.CheckCallNames(c, "WatchModelMachines", "WatchModelMachines") 210 c.Assert(s.resources.Count(), gc.Equals, 2) 211 resource2 := s.resources.Get("2") 212 defer statetesting.AssertStop(c, resource2) 213 wc2 := statetesting.NewStringsWatcherC(c, s.st, resource2.(state.StringsWatcher)) 214 wc2.AssertNoChange() 215 216 // Remove machine 1, check it's reported. 217 s.st.RemoveMachine(c, "1") 218 wc1.AssertChangeInSingleEvent("1") 219 220 // Make separate changes, check they're combined. 221 s.st.SetMachineInfo(c, machineInfo{id: "2", life: state.Dying}) 222 s.st.SetMachineInfo(c, machineInfo{id: "3"}) 223 s.st.RemoveMachine(c, "42") // ignored 224 wc1.AssertChangeInSingleEvent("2", "3") 225 wc2.AssertChangeInSingleEvent("1", "2", "3") 226 227 // Stop the first watcher and assert its changes chan is closed. 228 c.Assert(resource1.Stop(), jc.ErrorIsNil) 229 wc1.AssertClosed() 230 resource1 = nil 231 } 232 233 func (s *InstancePollerSuite) TestLifeSuccess(c *gc.C) { 234 s.st.SetMachineInfo(c, machineInfo{id: "1", life: state.Alive}) 235 s.st.SetMachineInfo(c, machineInfo{id: "2", life: state.Dying}) 236 237 result, err := s.api.Life(s.mixedEntities) 238 c.Assert(err, jc.ErrorIsNil) 239 c.Assert(result, jc.DeepEquals, params.LifeResults{ 240 Results: []params.LifeResult{ 241 {Life: params.Alive}, 242 {Life: params.Dying}, 243 {Error: apiservertesting.NotFoundError("machine 42")}, 244 {Error: apiservertesting.ErrUnauthorized}, 245 {Error: apiservertesting.ErrUnauthorized}, 246 {Error: apiservertesting.ErrUnauthorized}, 247 {Error: apiservertesting.ErrUnauthorized}, 248 {Error: apiservertesting.ErrUnauthorized}, 249 }}, 250 ) 251 252 s.st.CheckFindEntityCall(c, 0, "1") 253 s.st.CheckCall(c, 1, "Life") 254 s.st.CheckFindEntityCall(c, 2, "2") 255 s.st.CheckCall(c, 3, "Life") 256 s.st.CheckFindEntityCall(c, 4, "42") 257 } 258 259 func (s *InstancePollerSuite) TestLifeFailure(c *gc.C) { 260 s.st.SetErrors( 261 errors.New("pow!"), // m1 := FindEntity("1"); Life not called 262 nil, // m2 := FindEntity("2") 263 errors.New("FAIL"), // m2.Life() - unused 264 errors.NotProvisionedf("machine 42"), // FindEntity("3") (ensure wrapping is preserved) 265 ) 266 s.st.SetMachineInfo(c, machineInfo{id: "1", life: state.Alive}) 267 s.st.SetMachineInfo(c, machineInfo{id: "2", life: state.Dead}) 268 s.st.SetMachineInfo(c, machineInfo{id: "3", life: state.Dying}) 269 270 result, err := s.api.Life(s.machineEntities) 271 c.Assert(err, jc.ErrorIsNil) 272 c.Assert(result, jc.DeepEquals, params.LifeResults{ 273 Results: []params.LifeResult{ 274 {Error: apiservertesting.ServerError("pow!")}, 275 {Life: params.Dead}, 276 {Error: apiservertesting.NotProvisionedError("42")}, 277 }}, 278 ) 279 280 s.st.CheckFindEntityCall(c, 0, "1") 281 s.st.CheckFindEntityCall(c, 1, "2") 282 s.st.CheckCall(c, 2, "Life") 283 s.st.CheckFindEntityCall(c, 3, "3") 284 } 285 286 func (s *InstancePollerSuite) TestInstanceIdSuccess(c *gc.C) { 287 s.st.SetMachineInfo(c, machineInfo{id: "1", instanceId: "i-foo"}) 288 s.st.SetMachineInfo(c, machineInfo{id: "2", instanceId: ""}) 289 290 result, err := s.api.InstanceId(s.mixedEntities) 291 c.Assert(err, jc.ErrorIsNil) 292 c.Assert(result, jc.DeepEquals, params.StringResults{ 293 Results: []params.StringResult{ 294 {Result: "i-foo"}, 295 {Result: ""}, 296 {Error: apiservertesting.NotFoundError("machine 42")}, 297 {Error: apiservertesting.ErrUnauthorized}, 298 {Error: apiservertesting.ErrUnauthorized}, 299 {Error: apiservertesting.ErrUnauthorized}, 300 {Error: apiservertesting.ErrUnauthorized}, 301 {Error: apiservertesting.ErrUnauthorized}, 302 }}, 303 ) 304 305 s.st.CheckFindEntityCall(c, 0, "1") 306 s.st.CheckCall(c, 1, "InstanceId") 307 s.st.CheckFindEntityCall(c, 2, "2") 308 s.st.CheckCall(c, 3, "InstanceId") 309 s.st.CheckFindEntityCall(c, 4, "42") 310 } 311 312 func (s *InstancePollerSuite) TestInstanceIdFailure(c *gc.C) { 313 s.st.SetErrors( 314 errors.New("pow!"), // m1 := FindEntity("1"); InstanceId not called 315 nil, // m2 := FindEntity("2") 316 errors.New("FAIL"), // m2.InstanceId() 317 errors.NotProvisionedf("machine 42"), // FindEntity("3") (ensure wrapping is preserved) 318 ) 319 s.st.SetMachineInfo(c, machineInfo{id: "1", instanceId: ""}) 320 s.st.SetMachineInfo(c, machineInfo{id: "2", instanceId: "i-bar"}) 321 322 result, err := s.api.InstanceId(s.machineEntities) 323 c.Assert(err, jc.ErrorIsNil) 324 c.Assert(result, jc.DeepEquals, params.StringResults{ 325 Results: []params.StringResult{ 326 {Error: apiservertesting.ServerError("pow!")}, 327 {Error: apiservertesting.ServerError("FAIL")}, 328 {Error: apiservertesting.NotProvisionedError("42")}, 329 }}, 330 ) 331 332 s.st.CheckFindEntityCall(c, 0, "1") 333 s.st.CheckFindEntityCall(c, 1, "2") 334 s.st.CheckCall(c, 2, "InstanceId") 335 s.st.CheckFindEntityCall(c, 3, "3") 336 } 337 338 func (s *InstancePollerSuite) TestStatusSuccess(c *gc.C) { 339 now := time.Now() 340 s1 := status.StatusInfo{ 341 Status: status.StatusError, 342 Message: "not really", 343 Data: map[string]interface{}{ 344 "price": 4.2, 345 "bool": false, 346 "bar": []string{"a", "b"}, 347 }, 348 Since: &now, 349 } 350 s2 := status.StatusInfo{} 351 s.st.SetMachineInfo(c, machineInfo{id: "1", status: s1}) 352 s.st.SetMachineInfo(c, machineInfo{id: "2", status: s2}) 353 354 result, err := s.api.Status(s.mixedEntities) 355 c.Assert(err, jc.ErrorIsNil) 356 c.Assert(result, jc.DeepEquals, params.StatusResults{ 357 Results: []params.StatusResult{ 358 { 359 Status: status.StatusError, 360 Info: s1.Message, 361 Data: s1.Data, 362 Since: s1.Since, 363 }, 364 {Status: "", Info: "", Data: nil, Since: nil}, 365 {Error: apiservertesting.NotFoundError("machine 42")}, 366 {Error: apiservertesting.ErrUnauthorized}, 367 {Error: apiservertesting.ServerError(`"invalid-tag" is not a valid tag`)}, 368 {Error: apiservertesting.ErrUnauthorized}, 369 {Error: apiservertesting.ServerError(`"" is not a valid tag`)}, 370 {Error: apiservertesting.ServerError(`"42" is not a valid tag`)}, 371 }}, 372 ) 373 374 s.st.CheckFindEntityCall(c, 0, "1") 375 s.st.CheckCall(c, 1, "Status") 376 s.st.CheckFindEntityCall(c, 2, "2") 377 s.st.CheckCall(c, 3, "Status") 378 s.st.CheckFindEntityCall(c, 4, "42") 379 } 380 381 func (s *InstancePollerSuite) TestStatusFailure(c *gc.C) { 382 s.st.SetErrors( 383 errors.New("pow!"), // m1 := FindEntity("1"); Status not called 384 nil, // m2 := FindEntity("2") 385 errors.New("FAIL"), // m2.Status() 386 errors.NotProvisionedf("machine 42"), // FindEntity("3") (ensure wrapping is preserved) 387 ) 388 s.st.SetMachineInfo(c, machineInfo{id: "1"}) 389 s.st.SetMachineInfo(c, machineInfo{id: "2"}) 390 391 result, err := s.api.Status(s.machineEntities) 392 c.Assert(err, jc.ErrorIsNil) 393 c.Assert(result, jc.DeepEquals, params.StatusResults{ 394 Results: []params.StatusResult{ 395 {Error: apiservertesting.ServerError("pow!")}, 396 {Error: apiservertesting.ServerError("FAIL")}, 397 {Error: apiservertesting.NotProvisionedError("42")}, 398 }}, 399 ) 400 401 s.st.CheckFindEntityCall(c, 0, "1") 402 s.st.CheckFindEntityCall(c, 1, "2") 403 s.st.CheckCall(c, 2, "Status") 404 s.st.CheckFindEntityCall(c, 3, "3") 405 } 406 407 func (s *InstancePollerSuite) TestProviderAddressesSuccess(c *gc.C) { 408 addrs := network.NewAddresses("0.1.2.3", "127.0.0.1", "8.8.8.8") 409 expectedAddresses := params.FromNetworkAddresses(addrs) 410 s.st.SetMachineInfo(c, machineInfo{id: "1", providerAddresses: addrs}) 411 s.st.SetMachineInfo(c, machineInfo{id: "2", providerAddresses: nil}) 412 413 result, err := s.api.ProviderAddresses(s.mixedEntities) 414 c.Assert(err, jc.ErrorIsNil) 415 c.Assert(result, jc.DeepEquals, params.MachineAddressesResults{ 416 Results: []params.MachineAddressesResult{ 417 {Addresses: expectedAddresses}, 418 {Addresses: nil}, 419 {Error: apiservertesting.NotFoundError("machine 42")}, 420 {Error: apiservertesting.ServerError(`"service-unknown" is not a valid machine tag`)}, 421 {Error: apiservertesting.ServerError(`"invalid-tag" is not a valid tag`)}, 422 {Error: apiservertesting.ServerError(`"unit-missing-1" is not a valid machine tag`)}, 423 {Error: apiservertesting.ServerError(`"" is not a valid tag`)}, 424 {Error: apiservertesting.ServerError(`"42" is not a valid tag`)}, 425 }}, 426 ) 427 428 s.st.CheckFindEntityCall(c, 0, "1") 429 s.st.CheckCall(c, 1, "ProviderAddresses") 430 s.st.CheckFindEntityCall(c, 2, "2") 431 s.st.CheckCall(c, 3, "ProviderAddresses") 432 s.st.CheckFindEntityCall(c, 4, "42") 433 } 434 435 func (s *InstancePollerSuite) TestProviderAddressesFailure(c *gc.C) { 436 s.st.SetErrors( 437 errors.New("pow!"), // m1 := FindEntity("1") 438 nil, // m2 := FindEntity("2") 439 errors.New("FAIL"), // m2.ProviderAddresses()- unused 440 errors.NotProvisionedf("machine 42"), // FindEntity("3") (ensure wrapping is preserved) 441 ) 442 s.st.SetMachineInfo(c, machineInfo{id: "1"}) 443 s.st.SetMachineInfo(c, machineInfo{id: "2"}) 444 445 result, err := s.api.ProviderAddresses(s.machineEntities) 446 c.Assert(err, jc.ErrorIsNil) 447 c.Assert(result, jc.DeepEquals, params.MachineAddressesResults{ 448 Results: []params.MachineAddressesResult{ 449 {Error: apiservertesting.ServerError("pow!")}, 450 {Addresses: nil}, 451 {Error: apiservertesting.NotProvisionedError("42")}, 452 }}, 453 ) 454 455 s.st.CheckFindEntityCall(c, 0, "1") 456 s.st.CheckFindEntityCall(c, 1, "2") 457 s.st.CheckCall(c, 2, "ProviderAddresses") 458 s.st.CheckFindEntityCall(c, 3, "3") 459 } 460 461 func (s *InstancePollerSuite) TestSetProviderAddressesSuccess(c *gc.C) { 462 oldAddrs := network.NewAddresses("0.1.2.3", "127.0.0.1", "8.8.8.8") 463 newAddrs := network.NewAddresses("1.2.3.4", "8.4.4.8", "2001:db8::") 464 s.st.SetMachineInfo(c, machineInfo{id: "1", providerAddresses: oldAddrs}) 465 s.st.SetMachineInfo(c, machineInfo{id: "2", providerAddresses: nil}) 466 467 result, err := s.api.SetProviderAddresses(params.SetMachinesAddresses{ 468 MachineAddresses: []params.MachineAddresses{ 469 {Tag: "machine-1", Addresses: nil}, 470 {Tag: "machine-2", Addresses: params.FromNetworkAddresses(newAddrs)}, 471 {Tag: "machine-42"}, 472 {Tag: "service-unknown"}, 473 {Tag: "invalid-tag"}, 474 {Tag: "unit-missing-1"}, 475 {Tag: ""}, 476 {Tag: "42"}, 477 }}, 478 ) 479 c.Assert(err, jc.ErrorIsNil) 480 c.Assert(result, jc.DeepEquals, s.mixedErrorResults) 481 482 s.st.CheckFindEntityCall(c, 0, "1") 483 s.st.CheckSetProviderAddressesCall(c, 1, []network.Address{}) 484 s.st.CheckFindEntityCall(c, 2, "2") 485 s.st.CheckSetProviderAddressesCall(c, 3, newAddrs) 486 s.st.CheckFindEntityCall(c, 4, "42") 487 488 // Ensure machines were updated. 489 machine, err := s.st.Machine("1") 490 c.Assert(err, jc.ErrorIsNil) 491 c.Assert(machine.ProviderAddresses(), gc.HasLen, 0) 492 493 machine, err = s.st.Machine("2") 494 c.Assert(err, jc.ErrorIsNil) 495 c.Assert(machine.ProviderAddresses(), jc.DeepEquals, newAddrs) 496 } 497 498 func (s *InstancePollerSuite) TestSetProviderAddressesFailure(c *gc.C) { 499 s.st.SetErrors( 500 errors.New("pow!"), // m1 := FindEntity("1") 501 nil, // m2 := FindEntity("2") 502 errors.New("FAIL"), // m2.SetProviderAddresses() 503 errors.NotProvisionedf("machine 42"), // FindEntity("3") (ensure wrapping is preserved) 504 ) 505 oldAddrs := network.NewAddresses("0.1.2.3", "127.0.0.1", "8.8.8.8") 506 newAddrs := network.NewAddresses("1.2.3.4", "8.4.4.8", "2001:db8::") 507 s.st.SetMachineInfo(c, machineInfo{id: "1", providerAddresses: oldAddrs}) 508 s.st.SetMachineInfo(c, machineInfo{id: "2", providerAddresses: nil}) 509 510 result, err := s.api.SetProviderAddresses(params.SetMachinesAddresses{ 511 MachineAddresses: []params.MachineAddresses{ 512 {Tag: "machine-1", Addresses: nil}, 513 {Tag: "machine-2", Addresses: params.FromNetworkAddresses(newAddrs)}, 514 {Tag: "machine-3"}, 515 }}, 516 ) 517 c.Assert(err, jc.ErrorIsNil) 518 c.Assert(result, jc.DeepEquals, s.machineErrorResults) 519 520 s.st.CheckFindEntityCall(c, 0, "1") 521 s.st.CheckFindEntityCall(c, 1, "2") 522 s.st.CheckSetProviderAddressesCall(c, 2, newAddrs) 523 s.st.CheckFindEntityCall(c, 3, "3") 524 525 // Ensure machine 2 wasn't updated. 526 machine, err := s.st.Machine("2") 527 c.Assert(err, jc.ErrorIsNil) 528 c.Assert(machine.ProviderAddresses(), gc.HasLen, 0) 529 } 530 531 func (s *InstancePollerSuite) TestInstanceStatusSuccess(c *gc.C) { 532 s.st.SetMachineInfo(c, machineInfo{id: "1", instanceStatus: statusInfo("foo")}) 533 s.st.SetMachineInfo(c, machineInfo{id: "2", instanceStatus: statusInfo("")}) 534 535 result, err := s.api.InstanceStatus(s.mixedEntities) 536 c.Assert(err, jc.ErrorIsNil) 537 c.Assert(result, jc.DeepEquals, params.StatusResults{ 538 Results: []params.StatusResult{ 539 {Status: status.Status("foo")}, 540 {Status: status.Status("")}, 541 {Error: apiservertesting.NotFoundError("machine 42")}, 542 {Error: apiservertesting.ServerError(`"service-unknown" is not a valid machine tag`)}, 543 {Error: apiservertesting.ServerError(`"invalid-tag" is not a valid tag`)}, 544 {Error: apiservertesting.ServerError(`"unit-missing-1" is not a valid machine tag`)}, 545 {Error: apiservertesting.ServerError(`"" is not a valid tag`)}, 546 {Error: apiservertesting.ServerError(`"42" is not a valid tag`)}, 547 }, 548 }, 549 ) 550 551 s.st.CheckFindEntityCall(c, 0, "1") 552 s.st.CheckCall(c, 1, "InstanceStatus") 553 s.st.CheckFindEntityCall(c, 2, "2") 554 s.st.CheckCall(c, 3, "InstanceStatus") 555 s.st.CheckFindEntityCall(c, 4, "42") 556 } 557 558 func (s *InstancePollerSuite) TestInstanceStatusFailure(c *gc.C) { 559 s.st.SetErrors( 560 errors.New("pow!"), // m1 := FindEntity("1") 561 nil, // m2 := FindEntity("2") 562 errors.New("FAIL"), // m2.InstanceStatus() 563 errors.NotProvisionedf("machine 42"), // FindEntity("3") (ensure wrapping is preserved) 564 ) 565 s.st.SetMachineInfo(c, machineInfo{id: "1", instanceStatus: statusInfo("foo")}) 566 s.st.SetMachineInfo(c, machineInfo{id: "2", instanceStatus: statusInfo("")}) 567 568 result, err := s.api.InstanceStatus(s.machineEntities) 569 c.Assert(err, jc.ErrorIsNil) 570 c.Assert(result, jc.DeepEquals, params.StatusResults{ 571 Results: []params.StatusResult{ 572 {Error: apiservertesting.ServerError("pow!")}, 573 {Error: apiservertesting.ServerError("FAIL")}, 574 {Error: apiservertesting.NotProvisionedError("42")}, 575 }}, 576 ) 577 578 s.st.CheckFindEntityCall(c, 0, "1") 579 s.st.CheckFindEntityCall(c, 1, "2") 580 s.st.CheckCall(c, 2, "InstanceStatus") 581 s.st.CheckFindEntityCall(c, 3, "3") 582 } 583 584 func (s *InstancePollerSuite) TestSetInstanceStatusSuccess(c *gc.C) { 585 s.st.SetMachineInfo(c, machineInfo{id: "1", instanceStatus: statusInfo("foo")}) 586 s.st.SetMachineInfo(c, machineInfo{id: "2", instanceStatus: statusInfo("")}) 587 588 result, err := s.api.SetInstanceStatus(params.SetStatus{ 589 Entities: []params.EntityStatusArgs{ 590 {Tag: "machine-1", Status: status.Status("")}, 591 {Tag: "machine-2", Status: status.Status("new status")}, 592 {Tag: "machine-42", Status: status.Status("")}, 593 {Tag: "service-unknown", Status: status.Status("")}, 594 {Tag: "invalid-tag", Status: status.Status("")}, 595 {Tag: "unit-missing-1", Status: status.Status("")}, 596 {Tag: "", Status: status.Status("")}, 597 {Tag: "42", Status: status.Status("")}, 598 }}, 599 ) 600 c.Assert(err, jc.ErrorIsNil) 601 c.Assert(result, jc.DeepEquals, s.mixedErrorResults) 602 603 s.st.CheckFindEntityCall(c, 0, "1") 604 s.st.CheckCall(c, 1, "SetInstanceStatus", status.Status("")) 605 s.st.CheckFindEntityCall(c, 2, "2") 606 s.st.CheckCall(c, 3, "SetInstanceStatus", status.Status("new status")) 607 s.st.CheckFindEntityCall(c, 4, "42") 608 609 // Ensure machines were updated. 610 machine, err := s.st.Machine("1") 611 c.Assert(err, jc.ErrorIsNil) 612 setStatus, err := machine.InstanceStatus() 613 c.Assert(err, jc.ErrorIsNil) 614 c.Assert(setStatus, gc.DeepEquals, status.StatusInfo{}) 615 616 machine, err = s.st.Machine("2") 617 c.Assert(err, jc.ErrorIsNil) 618 setStatus, err = machine.InstanceStatus() 619 c.Assert(err, jc.ErrorIsNil) 620 c.Assert(setStatus, gc.DeepEquals, status.StatusInfo{Status: "new status"}) 621 } 622 623 func (s *InstancePollerSuite) TestSetInstanceStatusFailure(c *gc.C) { 624 s.st.SetErrors( 625 errors.New("pow!"), // m1 := FindEntity("1") 626 nil, // m2 := FindEntity("2") 627 errors.New("FAIL"), // m2.SetInstanceStatus() 628 errors.NotProvisionedf("machine 42"), // FindEntity("3") (ensure wrapping is preserved) 629 ) 630 s.st.SetMachineInfo(c, machineInfo{id: "1", instanceStatus: statusInfo("foo")}) 631 s.st.SetMachineInfo(c, machineInfo{id: "2", instanceStatus: statusInfo("")}) 632 633 result, err := s.api.SetInstanceStatus(params.SetStatus{ 634 Entities: []params.EntityStatusArgs{ 635 {Tag: "machine-1", Status: status.Status("new")}, 636 {Tag: "machine-2", Status: status.Status("invalid")}, 637 {Tag: "machine-3", Status: status.Status("")}, 638 }}, 639 ) 640 c.Assert(err, jc.ErrorIsNil) 641 c.Assert(result, jc.DeepEquals, s.machineErrorResults) 642 643 s.st.CheckFindEntityCall(c, 0, "1") 644 s.st.CheckFindEntityCall(c, 1, "2") 645 s.st.CheckCall(c, 2, "SetInstanceStatus", status.Status("invalid")) 646 s.st.CheckFindEntityCall(c, 3, "3") 647 } 648 649 func (s *InstancePollerSuite) TestAreManuallyProvisionedSuccess(c *gc.C) { 650 s.st.SetMachineInfo(c, machineInfo{id: "1", isManual: true}) 651 s.st.SetMachineInfo(c, machineInfo{id: "2", isManual: false}) 652 653 result, err := s.api.AreManuallyProvisioned(s.mixedEntities) 654 c.Assert(err, jc.ErrorIsNil) 655 c.Assert(result, jc.DeepEquals, params.BoolResults{ 656 Results: []params.BoolResult{ 657 {Result: true}, 658 {Result: false}, 659 {Error: apiservertesting.NotFoundError("machine 42")}, 660 {Error: apiservertesting.ServerError(`"service-unknown" is not a valid machine tag`)}, 661 {Error: apiservertesting.ServerError(`"invalid-tag" is not a valid tag`)}, 662 {Error: apiservertesting.ServerError(`"unit-missing-1" is not a valid machine tag`)}, 663 {Error: apiservertesting.ServerError(`"" is not a valid tag`)}, 664 {Error: apiservertesting.ServerError(`"42" is not a valid tag`)}, 665 }}, 666 ) 667 668 s.st.CheckFindEntityCall(c, 0, "1") 669 s.st.CheckCall(c, 1, "IsManual") 670 s.st.CheckFindEntityCall(c, 2, "2") 671 s.st.CheckCall(c, 3, "IsManual") 672 s.st.CheckFindEntityCall(c, 4, "42") 673 } 674 675 func (s *InstancePollerSuite) TestAreManuallyProvisionedFailure(c *gc.C) { 676 s.st.SetErrors( 677 errors.New("pow!"), // m1 := FindEntity("1") 678 nil, // m2 := FindEntity("2") 679 errors.New("FAIL"), // m2.IsManual() 680 errors.NotProvisionedf("machine 42"), // FindEntity("3") (ensure wrapping is preserved) 681 ) 682 s.st.SetMachineInfo(c, machineInfo{id: "1", isManual: true}) 683 s.st.SetMachineInfo(c, machineInfo{id: "2", isManual: false}) 684 685 result, err := s.api.AreManuallyProvisioned(s.machineEntities) 686 c.Assert(err, jc.ErrorIsNil) 687 c.Assert(result, jc.DeepEquals, params.BoolResults{ 688 Results: []params.BoolResult{ 689 {Error: apiservertesting.ServerError("pow!")}, 690 {Error: apiservertesting.ServerError("FAIL")}, 691 {Error: apiservertesting.NotProvisionedError("42")}, 692 }}, 693 ) 694 695 s.st.CheckFindEntityCall(c, 0, "1") 696 s.st.CheckFindEntityCall(c, 1, "2") 697 s.st.CheckCall(c, 2, "IsManual") 698 s.st.CheckFindEntityCall(c, 3, "3") 699 } 700 701 func statusInfo(st string) status.StatusInfo { 702 return status.StatusInfo{Status: status.Status(st)} 703 }