github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/api/uniter/unit_test.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package uniter_test 5 6 import ( 7 "fmt" 8 "time" 9 10 "github.com/juju/errors" 11 "github.com/juju/schema" 12 coretesting "github.com/juju/testing" 13 jc "github.com/juju/testing/checkers" 14 "github.com/juju/utils" 15 gc "gopkg.in/check.v1" 16 "gopkg.in/juju/charm.v6" 17 "gopkg.in/juju/environschema.v1" 18 "gopkg.in/juju/names.v2" 19 20 "github.com/juju/juju/api" 21 "github.com/juju/juju/api/base/testing" 22 "github.com/juju/juju/api/uniter" 23 "github.com/juju/juju/apiserver/common" 24 "github.com/juju/juju/apiserver/params" 25 "github.com/juju/juju/core/application" 26 "github.com/juju/juju/core/model" 27 corenetwork "github.com/juju/juju/core/network" 28 "github.com/juju/juju/core/status" 29 "github.com/juju/juju/core/watcher/watchertest" 30 jujutesting "github.com/juju/juju/juju/testing" 31 "github.com/juju/juju/network" 32 "github.com/juju/juju/state" 33 jujufactory "github.com/juju/juju/testing/factory" 34 ) 35 36 type unitSuite struct { 37 uniterSuite 38 39 apiUnit *uniter.Unit 40 } 41 42 var _ = gc.Suite(&unitSuite{}) 43 44 func (s *unitSuite) SetUpTest(c *gc.C) { 45 s.uniterSuite.SetUpTest(c) 46 47 var err error 48 s.apiUnit, err = s.uniter.Unit(s.wordpressUnit.Tag().(names.UnitTag)) 49 c.Assert(err, jc.ErrorIsNil) 50 } 51 52 func (s *unitSuite) TestRequestReboot(c *gc.C) { 53 err := s.apiUnit.RequestReboot() 54 c.Assert(err, jc.ErrorIsNil) 55 rFlag, err := s.wordpressMachine.GetRebootFlag() 56 c.Assert(err, jc.ErrorIsNil) 57 c.Assert(rFlag, jc.IsTrue) 58 } 59 60 func (s *unitSuite) TestUnitAndUnitTag(c *gc.C) { 61 apiUnitFoo, err := s.uniter.Unit(names.NewUnitTag("foo/42")) 62 c.Assert(err, gc.ErrorMatches, "permission denied") 63 c.Assert(err, jc.Satisfies, params.IsCodeUnauthorized) 64 c.Assert(apiUnitFoo, gc.IsNil) 65 66 c.Assert(s.apiUnit.Tag(), gc.Equals, s.wordpressUnit.Tag().(names.UnitTag)) 67 } 68 69 func (s *unitSuite) TestSetAgentStatus(c *gc.C) { 70 statusInfo, err := s.wordpressUnit.AgentStatus() 71 c.Assert(err, jc.ErrorIsNil) 72 c.Assert(statusInfo.Status, gc.Equals, status.Allocating) 73 c.Assert(statusInfo.Message, gc.Equals, "") 74 c.Assert(statusInfo.Data, gc.HasLen, 0) 75 76 unitStatusInfo, err := s.wordpressUnit.Status() 77 c.Assert(err, jc.ErrorIsNil) 78 c.Assert(unitStatusInfo.Status, gc.Equals, status.Waiting) 79 c.Assert(unitStatusInfo.Message, gc.Equals, "waiting for machine") 80 c.Assert(unitStatusInfo.Data, gc.HasLen, 0) 81 82 err = s.apiUnit.SetAgentStatus(status.Idle, "blah", nil) 83 c.Assert(err, jc.ErrorIsNil) 84 85 statusInfo, err = s.wordpressUnit.AgentStatus() 86 c.Assert(err, jc.ErrorIsNil) 87 c.Assert(statusInfo.Status, gc.Equals, status.Idle) 88 c.Assert(statusInfo.Message, gc.Equals, "blah") 89 c.Assert(statusInfo.Data, gc.HasLen, 0) 90 c.Assert(statusInfo.Since, gc.NotNil) 91 92 // Ensure that unit has not changed. 93 unitStatusInfo, err = s.wordpressUnit.Status() 94 c.Assert(err, jc.ErrorIsNil) 95 c.Assert(unitStatusInfo.Status, gc.Equals, status.Waiting) 96 c.Assert(unitStatusInfo.Message, gc.Equals, "waiting for machine") 97 c.Assert(unitStatusInfo.Data, gc.HasLen, 0) 98 } 99 100 func (s *unitSuite) TestSetUnitStatus(c *gc.C) { 101 statusInfo, err := s.wordpressUnit.Status() 102 c.Assert(err, jc.ErrorIsNil) 103 c.Assert(statusInfo.Status, gc.Equals, status.Waiting) 104 c.Assert(statusInfo.Message, gc.Equals, "waiting for machine") 105 c.Assert(statusInfo.Data, gc.HasLen, 0) 106 107 agentStatusInfo, err := s.wordpressUnit.AgentStatus() 108 c.Assert(err, jc.ErrorIsNil) 109 c.Assert(agentStatusInfo.Status, gc.Equals, status.Allocating) 110 c.Assert(agentStatusInfo.Message, gc.Equals, "") 111 c.Assert(agentStatusInfo.Data, gc.HasLen, 0) 112 113 err = s.apiUnit.SetUnitStatus(status.Active, "blah", nil) 114 c.Assert(err, jc.ErrorIsNil) 115 116 statusInfo, err = s.wordpressUnit.Status() 117 c.Assert(err, jc.ErrorIsNil) 118 c.Assert(statusInfo.Status, gc.Equals, status.Active) 119 c.Assert(statusInfo.Message, gc.Equals, "blah") 120 c.Assert(statusInfo.Data, gc.HasLen, 0) 121 c.Assert(statusInfo.Since, gc.NotNil) 122 123 // Ensure unit's agent has not changed. 124 agentStatusInfo, err = s.wordpressUnit.AgentStatus() 125 c.Assert(err, jc.ErrorIsNil) 126 c.Assert(agentStatusInfo.Status, gc.Equals, status.Allocating) 127 c.Assert(agentStatusInfo.Message, gc.Equals, "") 128 c.Assert(agentStatusInfo.Data, gc.HasLen, 0) 129 } 130 131 func (s *unitSuite) TestUnitStatus(c *gc.C) { 132 now := time.Now() 133 sInfo := status.StatusInfo{ 134 Status: status.Maintenance, 135 Message: "blah", 136 Since: &now, 137 } 138 err := s.wordpressUnit.SetStatus(sInfo) 139 c.Assert(err, jc.ErrorIsNil) 140 141 result, err := s.apiUnit.UnitStatus() 142 c.Assert(err, jc.ErrorIsNil) 143 c.Assert(result.Since, gc.NotNil) 144 result.Since = nil 145 c.Assert(result, gc.DeepEquals, params.StatusResult{ 146 Status: status.Maintenance.String(), 147 Info: "blah", 148 Data: map[string]interface{}{}, 149 }) 150 } 151 152 func (s *unitSuite) TestEnsureDead(c *gc.C) { 153 c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Alive) 154 155 err := s.apiUnit.EnsureDead() 156 c.Assert(err, jc.ErrorIsNil) 157 158 err = s.wordpressUnit.Refresh() 159 c.Assert(err, jc.ErrorIsNil) 160 c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Dead) 161 162 err = s.apiUnit.EnsureDead() 163 c.Assert(err, jc.ErrorIsNil) 164 err = s.wordpressUnit.Refresh() 165 c.Assert(err, jc.ErrorIsNil) 166 c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Dead) 167 168 err = s.wordpressUnit.Remove() 169 c.Assert(err, jc.ErrorIsNil) 170 err = s.wordpressUnit.Refresh() 171 c.Assert(err, jc.Satisfies, errors.IsNotFound) 172 173 err = s.apiUnit.EnsureDead() 174 c.Assert(err, gc.ErrorMatches, `unit "wordpress/0" not found`) 175 c.Assert(err, jc.Satisfies, params.IsCodeNotFound) 176 } 177 178 func (s *unitSuite) TestDestroy(c *gc.C) { 179 c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Alive) 180 181 err := s.apiUnit.Destroy() 182 c.Assert(err, jc.ErrorIsNil) 183 184 err = s.wordpressUnit.Refresh() 185 c.Assert(err, gc.ErrorMatches, `unit "wordpress/0" not found`) 186 } 187 188 func (s *unitSuite) TestDestroyAllSubordinates(c *gc.C) { 189 c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Alive) 190 191 // Call without subordinates - no change. 192 err := s.apiUnit.DestroyAllSubordinates() 193 c.Assert(err, jc.ErrorIsNil) 194 195 // Add a couple of subordinates and try again. 196 _, _, loggingSub := s.addRelatedApplication(c, "wordpress", "logging", s.wordpressUnit) 197 _, _, monitoringSub := s.addRelatedApplication(c, "wordpress", "monitoring", s.wordpressUnit) 198 c.Assert(loggingSub.Life(), gc.Equals, state.Alive) 199 c.Assert(monitoringSub.Life(), gc.Equals, state.Alive) 200 201 err = s.apiUnit.DestroyAllSubordinates() 202 c.Assert(err, jc.ErrorIsNil) 203 204 // Verify they got destroyed. 205 err = loggingSub.Refresh() 206 c.Assert(err, jc.ErrorIsNil) 207 c.Assert(loggingSub.Life(), gc.Equals, state.Dying) 208 err = monitoringSub.Refresh() 209 c.Assert(err, jc.ErrorIsNil) 210 c.Assert(monitoringSub.Life(), gc.Equals, state.Dying) 211 } 212 213 func (s *unitSuite) TestRefreshLife(c *gc.C) { 214 c.Assert(s.apiUnit.Life(), gc.Equals, params.Alive) 215 216 err := s.apiUnit.EnsureDead() 217 c.Assert(err, jc.ErrorIsNil) 218 c.Assert(s.apiUnit.Life(), gc.Equals, params.Alive) 219 220 err = s.apiUnit.Refresh() 221 c.Assert(err, jc.ErrorIsNil) 222 c.Assert(s.apiUnit.Life(), gc.Equals, params.Dead) 223 } 224 225 func (s *unitSuite) TestRefreshResolve(c *gc.C) { 226 err := s.wordpressUnit.SetResolved(state.ResolvedRetryHooks) 227 c.Assert(err, jc.ErrorIsNil) 228 229 err = s.apiUnit.Refresh() 230 c.Assert(err, jc.ErrorIsNil) 231 mode := s.apiUnit.Resolved() 232 c.Assert(mode, gc.Equals, params.ResolvedRetryHooks) 233 234 err = s.apiUnit.ClearResolved() 235 c.Assert(err, jc.ErrorIsNil) 236 mode = s.apiUnit.Resolved() 237 c.Assert(mode, gc.Equals, params.ResolvedRetryHooks) 238 239 err = s.apiUnit.Refresh() 240 c.Assert(err, jc.ErrorIsNil) 241 mode = s.apiUnit.Resolved() 242 c.Assert(mode, gc.Equals, params.ResolvedNone) 243 } 244 245 func (s *unitSuite) TestWatch(c *gc.C) { 246 c.Assert(s.apiUnit.Life(), gc.Equals, params.Alive) 247 248 w, err := s.apiUnit.Watch() 249 c.Assert(err, jc.ErrorIsNil) 250 wc := watchertest.NewNotifyWatcherC(c, w, s.BackingState.StartSync) 251 defer wc.AssertStops() 252 253 // Initial event. 254 wc.AssertOneChange() 255 256 // Change something other than the lifecycle and make sure it's 257 // not detected. 258 err = s.apiUnit.SetAgentStatus(status.Idle, "not really", nil) 259 c.Assert(err, jc.ErrorIsNil) 260 wc.AssertNoChange() 261 262 // Make the unit dead and check it's detected. 263 err = s.apiUnit.EnsureDead() 264 c.Assert(err, jc.ErrorIsNil) 265 wc.AssertOneChange() 266 } 267 268 func (s *unitSuite) TestWatchRelations(c *gc.C) { 269 w, err := s.apiUnit.WatchRelations() 270 c.Assert(err, jc.ErrorIsNil) 271 wc := watchertest.NewStringsWatcherC(c, w, s.BackingState.StartSync) 272 defer wc.AssertStops() 273 274 // Initial event. 275 wc.AssertChange() 276 wc.AssertNoChange() 277 278 // Change something other than the lifecycle and make sure it's 279 // not detected. 280 err = s.wordpressApplication.SetExposed() 281 c.Assert(err, jc.ErrorIsNil) 282 wc.AssertNoChange() 283 284 // Add another application and relate it to wordpress, 285 // check it's detected. 286 s.addMachineAppCharmAndUnit(c, "mysql") 287 rel := s.addRelation(c, "wordpress", "mysql") 288 wc.AssertChange(rel.String()) 289 290 // Destroy the relation and check it's detected. 291 err = rel.Destroy() 292 c.Assert(err, jc.ErrorIsNil) 293 wc.AssertChange(rel.String()) 294 wc.AssertNoChange() 295 } 296 297 func (s *unitSuite) TestSubordinateWatchRelations(c *gc.C) { 298 // A subordinate unit deployed with this wordpress unit shouldn't 299 // be notified about changes to logging mysql. 300 loggingRel, _, loggingUnit := s.addRelatedApplication(c, "wordpress", "logging", s.wordpressUnit) 301 password, err := utils.RandomPassword() 302 c.Assert(err, jc.ErrorIsNil) 303 err = loggingUnit.SetPassword(password) 304 c.Assert(err, jc.ErrorIsNil) 305 306 // Add another principal app that we can relate logging to. 307 s.addMachineAppCharmAndUnit(c, "mysql") 308 309 api := s.OpenAPIAs(c, loggingUnit.Tag(), password) 310 uniter, err := api.Uniter() 311 c.Assert(err, jc.ErrorIsNil) 312 apiUnit, err := uniter.Unit(loggingUnit.Tag().(names.UnitTag)) 313 c.Assert(err, jc.ErrorIsNil) 314 315 w, err := apiUnit.WatchRelations() 316 c.Assert(err, jc.ErrorIsNil) 317 318 wc := watchertest.NewStringsWatcherC(c, w, s.BackingState.StartSync) 319 defer wc.AssertStops() 320 321 wc.AssertChange(loggingRel.Tag().Id()) 322 wc.AssertNoChange() 323 324 // Adding a subordinate relation to another application doesn't notify this unit. 325 s.addRelation(c, "mysql", "logging") 326 wc.AssertNoChange() 327 328 // Destroying a relevant relation does notify it. 329 err = loggingRel.Destroy() 330 c.Assert(err, jc.ErrorIsNil) 331 332 wc.AssertChange(loggingRel.Tag().Id()) 333 wc.AssertNoChange() 334 } 335 336 func (s *unitSuite) TestAssignedMachine(c *gc.C) { 337 machineTag, err := s.apiUnit.AssignedMachine() 338 c.Assert(err, jc.ErrorIsNil) 339 c.Assert(machineTag, gc.Equals, s.wordpressMachine.Tag()) 340 } 341 342 func (s *unitSuite) TestPrincipalName(c *gc.C) { 343 unitName, ok, err := s.apiUnit.PrincipalName() 344 c.Assert(err, jc.ErrorIsNil) 345 c.Assert(ok, jc.IsFalse) 346 c.Assert(unitName, gc.Equals, "") 347 } 348 349 func (s *unitSuite) TestHasSubordinates(c *gc.C) { 350 found, err := s.apiUnit.HasSubordinates() 351 c.Assert(err, jc.ErrorIsNil) 352 c.Assert(found, jc.IsFalse) 353 354 // Add a couple of subordinates and try again. 355 s.addRelatedApplication(c, "wordpress", "logging", s.wordpressUnit) 356 s.addRelatedApplication(c, "wordpress", "monitoring", s.wordpressUnit) 357 358 found, err = s.apiUnit.HasSubordinates() 359 c.Assert(err, jc.ErrorIsNil) 360 c.Assert(found, jc.IsTrue) 361 } 362 363 func (s *unitSuite) TestPublicAddress(c *gc.C) { 364 address, err := s.apiUnit.PublicAddress() 365 c.Assert(err, gc.ErrorMatches, `"unit-wordpress-0" has no public address set`) 366 367 err = s.wordpressMachine.SetProviderAddresses( 368 network.NewScopedAddress("1.2.3.4", network.ScopePublic), 369 ) 370 c.Assert(err, jc.ErrorIsNil) 371 372 address, err = s.apiUnit.PublicAddress() 373 c.Assert(err, jc.ErrorIsNil) 374 c.Assert(address, gc.Equals, "1.2.3.4") 375 } 376 377 func (s *unitSuite) TestPrivateAddress(c *gc.C) { 378 address, err := s.apiUnit.PrivateAddress() 379 c.Assert(err, gc.ErrorMatches, `"unit-wordpress-0" has no private address set`) 380 381 err = s.wordpressMachine.SetProviderAddresses( 382 network.NewScopedAddress("1.2.3.4", network.ScopeCloudLocal), 383 ) 384 c.Assert(err, jc.ErrorIsNil) 385 386 address, err = s.apiUnit.PrivateAddress() 387 c.Assert(err, jc.ErrorIsNil) 388 c.Assert(address, gc.Equals, "1.2.3.4") 389 } 390 391 func (s *unitSuite) TestAvailabilityZone(c *gc.C) { 392 uniter.PatchUnitResponse(s, s.apiUnit, "AvailabilityZone", 393 func(result interface{}) error { 394 if results, ok := result.(*params.StringResults); ok { 395 results.Results = []params.StringResult{{ 396 Result: "a-zone", 397 }} 398 } 399 return nil 400 }, 401 ) 402 403 zone, err := s.apiUnit.AvailabilityZone() 404 c.Assert(err, jc.ErrorIsNil) 405 406 c.Check(zone, gc.Equals, "a-zone") 407 } 408 409 func (s *unitSuite) TestOpenClosePortRanges(c *gc.C) { 410 ports, err := s.wordpressUnit.OpenedPorts() 411 c.Assert(err, jc.ErrorIsNil) 412 c.Assert(ports, gc.HasLen, 0) 413 414 err = s.apiUnit.OpenPorts("tcp", 1234, 1400) 415 c.Assert(err, jc.ErrorIsNil) 416 err = s.apiUnit.OpenPorts("udp", 4321, 5000) 417 c.Assert(err, jc.ErrorIsNil) 418 419 ports, err = s.wordpressUnit.OpenedPorts() 420 c.Assert(err, jc.ErrorIsNil) 421 // OpenedPorts returns a sorted slice. 422 c.Assert(ports, gc.DeepEquals, []corenetwork.PortRange{ 423 {Protocol: "tcp", FromPort: 1234, ToPort: 1400}, 424 {Protocol: "udp", FromPort: 4321, ToPort: 5000}, 425 }) 426 427 err = s.apiUnit.ClosePorts("udp", 4321, 5000) 428 c.Assert(err, jc.ErrorIsNil) 429 430 ports, err = s.wordpressUnit.OpenedPorts() 431 c.Assert(err, jc.ErrorIsNil) 432 // OpenedPorts returns a sorted slice. 433 c.Assert(ports, gc.DeepEquals, []corenetwork.PortRange{ 434 {Protocol: "tcp", FromPort: 1234, ToPort: 1400}, 435 }) 436 437 err = s.apiUnit.ClosePorts("tcp", 1234, 1400) 438 c.Assert(err, jc.ErrorIsNil) 439 440 ports, err = s.wordpressUnit.OpenedPorts() 441 c.Assert(err, jc.ErrorIsNil) 442 c.Assert(ports, gc.HasLen, 0) 443 } 444 445 func (s *unitSuite) TestGetSetCharmURL(c *gc.C) { 446 // No charm URL set yet. 447 curl, ok := s.wordpressUnit.CharmURL() 448 c.Assert(curl, gc.IsNil) 449 c.Assert(ok, jc.IsFalse) 450 451 // Now check the same through the API. 452 _, err := s.apiUnit.CharmURL() 453 c.Assert(err, gc.Equals, uniter.ErrNoCharmURLSet) 454 455 err = s.apiUnit.SetCharmURL(s.wordpressCharm.URL()) 456 c.Assert(err, jc.ErrorIsNil) 457 458 curl, err = s.apiUnit.CharmURL() 459 c.Assert(err, jc.ErrorIsNil) 460 c.Assert(curl, gc.NotNil) 461 c.Assert(curl.String(), gc.Equals, s.wordpressCharm.String()) 462 } 463 464 func (s *unitSuite) TestNetworkInfo(c *gc.C) { 465 var called int 466 relId := 2 467 apiCaller := testing.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error { 468 called++ 469 if called == 1 { 470 *(result.(*params.UnitRefreshResults)) = params.UnitRefreshResults{ 471 Results: []params.UnitRefreshResult{{Life: params.Alive, Resolved: params.ResolvedNone}}} 472 return nil 473 } 474 c.Check(objType, gc.Equals, "Uniter") 475 c.Check(version, gc.Equals, expectedVersion) 476 c.Check(id, gc.Equals, "") 477 c.Check(request, gc.Equals, "NetworkInfo") 478 c.Check(arg, gc.DeepEquals, params.NetworkInfoParams{ 479 Unit: "unit-mysql-0", 480 Bindings: []string{"server"}, 481 RelationId: &relId, 482 }) 483 c.Assert(result, gc.FitsTypeOf, ¶ms.NetworkInfoResults{}) 484 *(result.(*params.NetworkInfoResults)) = params.NetworkInfoResults{ 485 Results: map[string]params.NetworkInfoResult{ 486 "db": { 487 Error: ¶ms.Error{Message: "FAIL"}, 488 }}, 489 } 490 return nil 491 }) 492 493 ut := names.NewUnitTag("mysql/0") 494 st := uniter.NewState(apiCaller, ut) 495 unit, err := st.Unit(ut) 496 c.Assert(err, jc.ErrorIsNil) 497 result, err := unit.NetworkInfo([]string{"server"}, &relId) 498 c.Assert(err, jc.ErrorIsNil) 499 c.Assert(result["db"].Error, gc.ErrorMatches, "FAIL") 500 c.Assert(called, gc.Equals, 2) 501 } 502 503 func (s *unitSuite) TestConfigSettings(c *gc.C) { 504 // Make sure ConfigSettings returns an error when 505 // no charm URL is set, as its state counterpart does. 506 settings, err := s.apiUnit.ConfigSettings() 507 c.Assert(err, gc.ErrorMatches, "unit charm not set") 508 509 // Now set the charm and try again. 510 err = s.apiUnit.SetCharmURL(s.wordpressCharm.URL()) 511 c.Assert(err, jc.ErrorIsNil) 512 513 settings, err = s.apiUnit.ConfigSettings() 514 c.Assert(err, jc.ErrorIsNil) 515 c.Assert(settings, gc.DeepEquals, charm.Settings{ 516 "blog-title": "My Title", 517 }) 518 519 // Update the config and check we get the changes on the next call. 520 err = s.wordpressApplication.UpdateCharmConfig(charm.Settings{ 521 "blog-title": "superhero paparazzi", 522 }) 523 c.Assert(err, jc.ErrorIsNil) 524 525 settings, err = s.apiUnit.ConfigSettings() 526 c.Assert(err, jc.ErrorIsNil) 527 c.Assert(settings, gc.DeepEquals, charm.Settings{ 528 "blog-title": "superhero paparazzi", 529 }) 530 } 531 func (s *unitSuite) TestWatchConfigSettingsHash(c *gc.C) { 532 // Make sure WatchConfigSettingsHash returns an error when 533 // no charm URL is set, as its state counterpart does. 534 w, err := s.apiUnit.WatchConfigSettingsHash() 535 c.Assert(err, gc.ErrorMatches, "unit charm not set") 536 537 // Now set the charm and try again. 538 err = s.apiUnit.SetCharmURL(s.wordpressCharm.URL()) 539 c.Assert(err, jc.ErrorIsNil) 540 541 w, err = s.apiUnit.WatchConfigSettingsHash() 542 wc := watchertest.NewStringsWatcherC(c, w, s.BackingState.StartSync) 543 defer wc.AssertStops() 544 545 // Initial event - this is the sha-256 hash of an empty bson.D. 546 wc.AssertChange("e8d7e8dfff0eed1e77b15638581672f7b25ecc1163cc5fd5a52d29d51d096c00") 547 548 err = s.wordpressApplication.UpdateCharmConfig(charm.Settings{ 549 "blog-title": "sauceror central", 550 }) 551 c.Assert(err, jc.ErrorIsNil) 552 wc.AssertChange("7ed6151e9c3d5144faf0946d20c283c466b4885dded6a6122ff3fdac7ee2334f") 553 554 // Non-change is not reported. 555 err = s.wordpressApplication.UpdateCharmConfig(charm.Settings{ 556 "blog-title": "sauceror central", 557 }) 558 c.Assert(err, jc.ErrorIsNil) 559 wc.AssertNoChange() 560 } 561 562 func (s *unitSuite) TestWatchTrustConfigSettingsHash(c *gc.C) { 563 watcher, err := s.apiUnit.WatchTrustConfigSettingsHash() 564 c.Assert(err, jc.ErrorIsNil) 565 566 stringsWatcher := watchertest.NewStringsWatcherC(c, watcher, s.BackingState.StartSync) 567 defer stringsWatcher.AssertStops() 568 569 // Initial event - this is the hash of the settings key 570 // a#wordpress#application + an empty bson.D. 571 stringsWatcher.AssertChange("92652ce7679e295c6567a3891c562dcab727c71543f8c1c3a38c3626ce064019") 572 573 // Update application config and see if it is reported 574 trustFieldKey := "trust" 575 s.wordpressApplication.UpdateApplicationConfig(application.ConfigAttributes{ 576 trustFieldKey: true, 577 }, 578 []string{}, 579 environschema.Fields{trustFieldKey: { 580 Description: "Does this application have access to trusted credentials", 581 Type: environschema.Tbool, 582 Group: environschema.JujuGroup, 583 }}, 584 schema.Defaults{ 585 trustFieldKey: false, 586 }, 587 ) 588 stringsWatcher.AssertChange("2f1368bde39be8106dcdca15e35cc3b5f7db5b8e429806369f621a47fb938519") 589 } 590 591 func (s *unitSuite) TestWatchActionNotifications(c *gc.C) { 592 w, err := s.apiUnit.WatchActionNotifications() 593 c.Assert(err, jc.ErrorIsNil) 594 wc := watchertest.NewStringsWatcherC(c, w, s.BackingState.StartSync) 595 defer wc.AssertStops() 596 597 // Initial event. 598 wc.AssertChange() 599 600 // Add a couple of actions and make sure the changes are detected. 601 action, err := s.wordpressUnit.AddAction("fakeaction", map[string]interface{}{ 602 "outfile": "foo.txt", 603 }) 604 c.Assert(err, jc.ErrorIsNil) 605 wc.AssertChange(action.Id()) 606 607 action, err = s.wordpressUnit.AddAction("fakeaction", map[string]interface{}{ 608 "outfile": "foo.bz2", 609 "compression": map[string]interface{}{ 610 "kind": "bzip", 611 "quality": float64(5.0), 612 }, 613 }) 614 c.Assert(err, jc.ErrorIsNil) 615 wc.AssertChange(action.Id()) 616 } 617 618 func (s *unitSuite) TestWatchActionNotificationsError(c *gc.C) { 619 uniter.PatchUnitResponse(s, s.apiUnit, "WatchActionNotifications", 620 func(result interface{}) error { 621 return fmt.Errorf("Test error") 622 }, 623 ) 624 625 _, err := s.apiUnit.WatchActionNotifications() 626 c.Assert(err.Error(), gc.Equals, "Test error") 627 } 628 629 func (s *unitSuite) TestWatchActionNotificationsErrorResults(c *gc.C) { 630 uniter.PatchUnitResponse(s, s.apiUnit, "WatchActionNotifications", 631 func(results interface{}) error { 632 if results, ok := results.(*params.StringsWatchResults); ok { 633 results.Results = make([]params.StringsWatchResult, 1) 634 results.Results[0] = params.StringsWatchResult{ 635 Error: ¶ms.Error{ 636 Message: "An error in the watch result.", 637 Code: params.CodeNotAssigned, 638 }, 639 } 640 } 641 return nil 642 }, 643 ) 644 645 _, err := s.apiUnit.WatchActionNotifications() 646 c.Assert(err.Error(), gc.Equals, "An error in the watch result.") 647 } 648 649 func (s *unitSuite) TestWatchActionNotificationsNoResults(c *gc.C) { 650 uniter.PatchUnitResponse(s, s.apiUnit, "WatchActionNotifications", 651 func(results interface{}) error { 652 return nil 653 }, 654 ) 655 656 _, err := s.apiUnit.WatchActionNotifications() 657 c.Assert(err.Error(), gc.Equals, "expected 1 result, got 0") 658 } 659 660 func (s *unitSuite) TestWatchActionNotificationsMoreResults(c *gc.C) { 661 uniter.PatchUnitResponse(s, s.apiUnit, "WatchActionNotifications", 662 func(results interface{}) error { 663 if results, ok := results.(*params.StringsWatchResults); ok { 664 results.Results = make([]params.StringsWatchResult, 2) 665 } 666 return nil 667 }, 668 ) 669 670 _, err := s.apiUnit.WatchActionNotifications() 671 c.Assert(err.Error(), gc.Equals, "expected 1 result, got 2") 672 } 673 674 func (s *unitSuite) TestWatchUpgradeSeriesNotifications(c *gc.C) { 675 watcher, err := s.apiUnit.WatchUpgradeSeriesNotifications() 676 c.Assert(err, jc.ErrorIsNil) 677 678 notifyWatcher := watchertest.NewNotifyWatcherC(c, watcher, s.BackingState.StartSync) 679 defer notifyWatcher.AssertStops() 680 681 notifyWatcher.AssertOneChange() 682 683 s.CreateUpgradeSeriesLock(c) 684 685 // Expect a notification that the document was created (i.e. a lock was placed) 686 notifyWatcher.AssertOneChange() 687 688 err = s.wordpressMachine.RemoveUpgradeSeriesLock() 689 c.Assert(err, jc.ErrorIsNil) 690 691 // A notification that the document was removed (i.e. the lock was released) 692 notifyWatcher.AssertOneChange() 693 } 694 695 func (s *unitSuite) TestUpgradeSeriesStatusIsInitializedToUnitStarted(c *gc.C) { 696 // First we create the prepare lock 697 s.CreateUpgradeSeriesLock(c) 698 699 // Then we check to see the status of our upgrade. We note that creating 700 // the lock essentially kicks off an upgrade from the perspective of 701 // assigned units. 702 status, err := s.apiUnit.UpgradeSeriesStatus() 703 c.Assert(err, jc.ErrorIsNil) 704 c.Assert(status, gc.Equals, model.UpgradeSeriesPrepareStarted) 705 } 706 707 func (s *unitSuite) TestSetUpgradeSeriesStatusFailsIfNoLockExists(c *gc.C) { 708 arbitraryStatus := model.UpgradeSeriesNotStarted 709 arbitraryReason := "" 710 711 err := s.apiUnit.SetUpgradeSeriesStatus(arbitraryStatus, arbitraryReason) 712 c.Assert(err, gc.ErrorMatches, "upgrade lock for machine \"[0-9]*\" not found") 713 } 714 715 func (s *unitSuite) TestSetUpgradeSeriesStatusUpdatesStatus(c *gc.C) { 716 arbitraryNonDefaultStatus := model.UpgradeSeriesPrepareRunning 717 arbitraryReason := "" 718 719 // First we create the prepare lock or the required state will not exists 720 s.CreateUpgradeSeriesLock(c) 721 722 // Change the state to something other than the default remote state of UpgradeSeriesPrepareStarted 723 err := s.apiUnit.SetUpgradeSeriesStatus(arbitraryNonDefaultStatus, arbitraryReason) 724 c.Assert(err, jc.ErrorIsNil) 725 726 // Check to see that the upgrade status has been set appropriately 727 status, err := s.apiUnit.UpgradeSeriesStatus() 728 c.Assert(err, jc.ErrorIsNil) 729 c.Assert(status, gc.Equals, arbitraryNonDefaultStatus) 730 } 731 732 func (s *unitSuite) TestSetUpgradeSeriesStatusShouldOnlySetSpecifiedUnit(c *gc.C) { 733 // add another unit 734 unit2, err := s.wordpressApplication.AddUnit(state.AddUnitParams{}) 735 c.Assert(err, jc.ErrorIsNil) 736 737 err = unit2.AssignToMachine(s.wordpressMachine) 738 c.Assert(err, jc.ErrorIsNil) 739 740 // Creating a lock for the machine transitions all units to started state 741 s.CreateUpgradeSeriesLock(c, unit2.Name()) 742 743 // Complete one unit 744 err = unit2.SetUpgradeSeriesStatus(model.UpgradeSeriesPrepareCompleted, "") 745 c.Assert(err, jc.ErrorIsNil) 746 747 // The other unit should still be in the started state 748 status, err := s.wordpressUnit.UpgradeSeriesStatus() 749 c.Assert(err, jc.ErrorIsNil) 750 c.Assert(status, gc.Equals, model.UpgradeSeriesPrepareStarted) 751 } 752 753 func (s *unitSuite) CreateUpgradeSeriesLock(c *gc.C, additionalUnits ...string) { 754 unitNames := additionalUnits 755 unitNames = append(unitNames, s.wordpressUnit.Name()) 756 series := "trust" 757 758 err := s.wordpressMachine.CreateUpgradeSeriesLock(unitNames, series) 759 c.Assert(err, jc.ErrorIsNil) 760 } 761 762 func (s *unitSuite) TestApplicationNameAndTag(c *gc.C) { 763 c.Assert(s.apiUnit.ApplicationName(), gc.Equals, s.wordpressApplication.Name()) 764 c.Assert(s.apiUnit.ApplicationTag(), gc.Equals, s.wordpressApplication.Tag()) 765 } 766 767 func (s *unitSuite) TestRelationSuspended(c *gc.C) { 768 relationStatus, err := s.apiUnit.RelationsStatus() 769 c.Assert(err, jc.ErrorIsNil) 770 c.Assert(relationStatus, gc.HasLen, 0) 771 772 rel1, _, _ := s.addRelatedApplication(c, "wordpress", "monitoring", s.wordpressUnit) 773 relationStatus, err = s.apiUnit.RelationsStatus() 774 c.Assert(err, jc.ErrorIsNil) 775 c.Assert(relationStatus, gc.DeepEquals, []uniter.RelationStatus{{ 776 Tag: rel1.Tag().(names.RelationTag), 777 InScope: true, 778 Suspended: false, 779 }}) 780 781 rel2 := s.addRelationSuspended(c, "wordpress", "logging", s.wordpressUnit) 782 relationStatus, err = s.apiUnit.RelationsStatus() 783 c.Assert(err, jc.ErrorIsNil) 784 c.Assert(relationStatus, jc.SameContents, []uniter.RelationStatus{{ 785 Tag: rel1.Tag().(names.RelationTag), 786 InScope: true, 787 Suspended: false, 788 }, { 789 Tag: rel2.Tag().(names.RelationTag), 790 InScope: false, 791 Suspended: true, 792 }}) 793 } 794 795 func (s *unitSuite) TestWatchAddressesHash(c *gc.C) { 796 w, err := s.apiUnit.WatchAddressesHash() 797 c.Assert(err, jc.ErrorIsNil) 798 wc := watchertest.NewStringsWatcherC(c, w, s.BackingState.StartSync) 799 defer wc.AssertStops() 800 801 // Initial event. 802 wc.AssertChange("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855") 803 804 // Update config get an event. 805 err = s.wordpressMachine.SetProviderAddresses(network.NewAddress("0.1.2.4")) 806 c.Assert(err, jc.ErrorIsNil) 807 wc.AssertChange("e8686213014563c18d8b3838ac3ac247dc2c7ceb0000cb01c19aa401ffc76e80") 808 809 // Non-change is not reported. 810 err = s.wordpressMachine.SetProviderAddresses(network.NewAddress("0.1.2.4")) 811 c.Assert(err, jc.ErrorIsNil) 812 wc.AssertNoChange() 813 814 // Change is reported for machine addresses. 815 err = s.wordpressMachine.SetMachineAddresses(network.NewAddress("0.1.2.5")) 816 c.Assert(err, jc.ErrorIsNil) 817 wc.AssertChange("ad269642567ef00c2c9c6ff84e9c04ecf3aa3342c1b4d98d76142471781c4495") 818 819 // Set machine addresses to empty is reported. 820 err = s.wordpressMachine.SetMachineAddresses() 821 c.Assert(err, jc.ErrorIsNil) 822 wc.AssertChange("e8686213014563c18d8b3838ac3ac247dc2c7ceb0000cb01c19aa401ffc76e80") 823 } 824 825 func (s *unitSuite) TestWatchAddressesHashErrors(c *gc.C) { 826 err := s.wordpressUnit.UnassignFromMachine() 827 c.Assert(err, jc.ErrorIsNil) 828 _, err = s.apiUnit.WatchAddressesHash() 829 c.Assert(err, jc.Satisfies, params.IsCodeNotAssigned) 830 } 831 832 func (s *unitSuite) TestAddMetrics(c *gc.C) { 833 uniter.PatchUnitResponse(s, s.apiUnit, "AddMetrics", 834 func(results interface{}) error { 835 result := results.(*params.ErrorResults) 836 result.Results = make([]params.ErrorResult, 1) 837 return nil 838 }, 839 ) 840 metrics := []params.Metric{{ 841 Key: "A", Value: "23", Time: time.Now(), 842 }, { 843 Key: "B", Value: "27.0", Time: time.Now(), Labels: map[string]string{"foo": "bar"}, 844 }} 845 err := s.apiUnit.AddMetrics(metrics) 846 c.Assert(err, jc.ErrorIsNil) 847 } 848 849 func (s *unitSuite) TestAddMetricsError(c *gc.C) { 850 uniter.PatchUnitResponse(s, s.apiUnit, "AddMetrics", 851 func(results interface{}) error { 852 result := results.(*params.ErrorResults) 853 result.Results = make([]params.ErrorResult, 1) 854 return fmt.Errorf("test error") 855 }, 856 ) 857 metrics := []params.Metric{{ 858 Key: "A", Value: "23", Time: time.Now(), 859 }, { 860 Key: "B", Value: "27.0", Time: time.Now(), Labels: map[string]string{"foo": "bar"}, 861 }} 862 err := s.apiUnit.AddMetrics(metrics) 863 c.Assert(err, gc.ErrorMatches, "unable to add metric: test error") 864 } 865 866 func (s *unitSuite) TestAddMetricsResultError(c *gc.C) { 867 uniter.PatchUnitResponse(s, s.apiUnit, "AddMetrics", 868 func(results interface{}) error { 869 result := results.(*params.ErrorResults) 870 result.Results = make([]params.ErrorResult, 1) 871 result.Results[0].Error = ¶ms.Error{ 872 Message: "error adding metrics", 873 Code: params.CodeNotAssigned, 874 } 875 return nil 876 }, 877 ) 878 metrics := []params.Metric{{ 879 Key: "A", Value: "23", Time: time.Now(), 880 }, { 881 Key: "B", Value: "27.0", Time: time.Now(), Labels: map[string]string{"foo": "bar"}, 882 }} 883 err := s.apiUnit.AddMetrics(metrics) 884 c.Assert(err, gc.ErrorMatches, "error adding metrics") 885 } 886 887 func (s *unitSuite) TestMeterStatus(c *gc.C) { 888 uniter.PatchUnitResponse(s, s.apiUnit, "GetMeterStatus", 889 func(results interface{}) error { 890 result := results.(*params.MeterStatusResults) 891 result.Results = make([]params.MeterStatusResult, 1) 892 result.Results[0].Code = "GREEN" 893 result.Results[0].Info = "All ok." 894 return nil 895 }, 896 ) 897 statusCode, statusInfo, err := s.apiUnit.MeterStatus() 898 c.Assert(err, jc.ErrorIsNil) 899 c.Assert(statusCode, gc.Equals, "GREEN") 900 c.Assert(statusInfo, gc.Equals, "All ok.") 901 } 902 903 func (s *unitSuite) TestMeterStatusError(c *gc.C) { 904 uniter.PatchUnitResponse(s, s.apiUnit, "GetMeterStatus", 905 func(results interface{}) error { 906 result := results.(*params.MeterStatusResults) 907 result.Results = make([]params.MeterStatusResult, 1) 908 return fmt.Errorf("boo") 909 }, 910 ) 911 statusCode, statusInfo, err := s.apiUnit.MeterStatus() 912 c.Assert(err, gc.ErrorMatches, "boo") 913 c.Assert(statusCode, gc.Equals, "") 914 c.Assert(statusInfo, gc.Equals, "") 915 } 916 917 func (s *unitSuite) TestMeterStatusResultError(c *gc.C) { 918 uniter.PatchUnitResponse(s, s.apiUnit, "GetMeterStatus", 919 func(results interface{}) error { 920 result := results.(*params.MeterStatusResults) 921 result.Results = make([]params.MeterStatusResult, 1) 922 result.Results[0].Error = ¶ms.Error{ 923 Message: "error getting meter status", 924 Code: params.CodeNotAssigned, 925 } 926 return nil 927 }, 928 ) 929 statusCode, statusInfo, err := s.apiUnit.MeterStatus() 930 c.Assert(err, gc.ErrorMatches, "error getting meter status") 931 c.Assert(statusCode, gc.Equals, "") 932 c.Assert(statusInfo, gc.Equals, "") 933 } 934 935 func (s *unitSuite) TestUpgradeSeriesStatusMultipleReturnsError(c *gc.C) { 936 facadeCaller := testing.StubFacadeCaller{Stub: &coretesting.Stub{}} 937 facadeCaller.FacadeCallFn = func(name string, args, response interface{}) error { 938 *(response.(*params.UpgradeSeriesStatusResults)) = params.UpgradeSeriesStatusResults{ 939 Results: []params.UpgradeSeriesStatusResult{ 940 {Status: "prepare started"}, 941 {Status: "completed"}, 942 }, 943 } 944 return nil 945 } 946 uniter.PatchUnitUpgradeSeriesFacade(s.apiUnit, &facadeCaller) 947 948 _, err := s.apiUnit.UpgradeSeriesStatus() 949 c.Assert(err, gc.ErrorMatches, "expected 1 result, got 2") 950 } 951 952 func (s *unitSuite) TestUpgradeSeriesStatusSingleResult(c *gc.C) { 953 facadeCaller := testing.StubFacadeCaller{Stub: &coretesting.Stub{}} 954 facadeCaller.FacadeCallFn = func(name string, args, response interface{}) error { 955 *(response.(*params.UpgradeSeriesStatusResults)) = params.UpgradeSeriesStatusResults{ 956 Results: []params.UpgradeSeriesStatusResult{{Status: "completed"}}, 957 } 958 return nil 959 } 960 uniter.PatchUnitUpgradeSeriesFacade(s.apiUnit, &facadeCaller) 961 962 sts, err := s.apiUnit.UpgradeSeriesStatus() 963 c.Assert(err, jc.ErrorIsNil) 964 c.Check(sts, gc.Equals, model.UpgradeSeriesCompleted) 965 } 966 967 type unitMetricBatchesSuite struct { 968 jujutesting.JujuConnSuite 969 970 st api.Connection 971 uniter *uniter.State 972 apiUnit *uniter.Unit 973 charm *state.Charm 974 } 975 976 var _ = gc.Suite(&unitMetricBatchesSuite{}) 977 978 func (s *unitMetricBatchesSuite) SetUpTest(c *gc.C) { 979 s.JujuConnSuite.SetUpTest(c) 980 981 s.charm = s.Factory.MakeCharm(c, &jujufactory.CharmParams{ 982 Name: "metered", 983 URL: "cs:quantal/metered", 984 }) 985 application := s.Factory.MakeApplication(c, &jujufactory.ApplicationParams{ 986 Charm: s.charm, 987 }) 988 unit := s.Factory.MakeUnit(c, &jujufactory.UnitParams{ 989 Application: application, 990 SetCharmURL: true, 991 }) 992 993 password, err := utils.RandomPassword() 994 c.Assert(err, jc.ErrorIsNil) 995 err = unit.SetPassword(password) 996 c.Assert(err, jc.ErrorIsNil) 997 s.st = s.OpenAPIAs(c, unit.Tag(), password) 998 999 // Create the uniter API facade. 1000 s.uniter, err = s.st.Uniter() 1001 c.Assert(err, jc.ErrorIsNil) 1002 c.Assert(s.uniter, gc.NotNil) 1003 1004 s.apiUnit, err = s.uniter.Unit(unit.Tag().(names.UnitTag)) 1005 c.Assert(err, jc.ErrorIsNil) 1006 } 1007 1008 func (s *unitMetricBatchesSuite) TestSendMetricBatchPatch(c *gc.C) { 1009 metrics := []params.Metric{{ 1010 Key: "pings", Value: "5", Time: time.Now().UTC(), 1011 }, { 1012 Key: "pongs", Value: "6", Time: time.Now().UTC(), Labels: map[string]string{"foo": "bar"}, 1013 }} 1014 uuid := utils.MustNewUUID().String() 1015 batch := params.MetricBatch{ 1016 UUID: uuid, 1017 CharmURL: s.charm.URL().String(), 1018 Created: time.Now(), 1019 Metrics: metrics, 1020 } 1021 1022 var called bool 1023 uniter.PatchUnitResponse(s, s.apiUnit, "AddMetricBatches", 1024 func(response interface{}) error { 1025 called = true 1026 result := response.(*params.ErrorResults) 1027 result.Results = make([]params.ErrorResult, 1) 1028 return nil 1029 }) 1030 1031 results, err := s.apiUnit.AddMetricBatches([]params.MetricBatch{batch}) 1032 c.Assert(err, jc.ErrorIsNil) 1033 c.Assert(results, gc.HasLen, 1) 1034 c.Assert(results[batch.UUID], gc.IsNil) 1035 c.Assert(called, jc.IsTrue) 1036 } 1037 1038 func (s *unitMetricBatchesSuite) TestSendMetricBatchFail(c *gc.C) { 1039 var called bool 1040 uniter.PatchUnitResponse(s, s.apiUnit, "AddMetricBatches", 1041 func(response interface{}) error { 1042 called = true 1043 result := response.(*params.ErrorResults) 1044 result.Results = make([]params.ErrorResult, 1) 1045 result.Results[0].Error = common.ServerError(common.ErrPerm) 1046 return nil 1047 }) 1048 metrics := []params.Metric{{ 1049 Key: "pings", Value: "5", Time: time.Now().UTC(), 1050 }, { 1051 Key: "pongs", Value: "6", Time: time.Now().UTC(), Labels: map[string]string{"foo": "bar"}, 1052 }} 1053 uuid := utils.MustNewUUID().String() 1054 batch := params.MetricBatch{ 1055 UUID: uuid, 1056 CharmURL: s.charm.URL().String(), 1057 Created: time.Now(), 1058 Metrics: metrics, 1059 } 1060 1061 results, err := s.apiUnit.AddMetricBatches([]params.MetricBatch{batch}) 1062 c.Assert(err, jc.ErrorIsNil) 1063 c.Assert(results, gc.HasLen, 1) 1064 c.Assert(results[batch.UUID], gc.ErrorMatches, "permission denied") 1065 c.Assert(called, jc.IsTrue) 1066 } 1067 1068 func (s *unitMetricBatchesSuite) TestSendMetricBatch(c *gc.C) { 1069 uuid := utils.MustNewUUID().String() 1070 now := time.Now().Round(time.Second).UTC() 1071 metrics := []params.Metric{{ 1072 Key: "pings", Value: "5", Time: now, 1073 }, { 1074 Key: "pongs", Value: "6", Time: time.Now().UTC(), Labels: map[string]string{"foo": "bar"}, 1075 }} 1076 batch := params.MetricBatch{ 1077 UUID: uuid, 1078 CharmURL: s.charm.URL().String(), 1079 Created: now, 1080 Metrics: metrics, 1081 } 1082 1083 results, err := s.apiUnit.AddMetricBatches([]params.MetricBatch{batch}) 1084 c.Assert(err, jc.ErrorIsNil) 1085 c.Assert(results, gc.HasLen, 1) 1086 c.Assert(results[batch.UUID], gc.IsNil) 1087 1088 batches, err := s.State.AllMetricBatches() 1089 c.Assert(err, gc.IsNil) 1090 c.Assert(batches, gc.HasLen, 1) 1091 c.Assert(batches[0].UUID(), gc.Equals, uuid) 1092 c.Assert(batches[0].Sent(), jc.IsFalse) 1093 c.Assert(batches[0].CharmURL(), gc.Equals, s.charm.URL().String()) 1094 c.Assert(batches[0].Metrics(), gc.HasLen, 2) 1095 c.Assert(batches[0].Metrics()[0].Key, gc.Equals, "pings") 1096 c.Assert(batches[0].Metrics()[0].Value, gc.Equals, "5") 1097 c.Assert(batches[0].Metrics()[0].Labels, gc.HasLen, 0) 1098 c.Assert(batches[0].Metrics()[1].Key, gc.Equals, "pongs") 1099 c.Assert(batches[0].Metrics()[1].Value, gc.Equals, "6") 1100 c.Assert(batches[0].Metrics()[1].Labels, gc.DeepEquals, map[string]string{"foo": "bar"}) 1101 }