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