github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/state/resources_persistence_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package state 5 6 import ( 7 "time" 8 9 "github.com/juju/errors" 10 "github.com/juju/testing" 11 jc "github.com/juju/testing/checkers" 12 gc "gopkg.in/check.v1" 13 "gopkg.in/juju/names.v2" 14 "gopkg.in/mgo.v2/bson" 15 "gopkg.in/mgo.v2/txn" 16 17 "github.com/juju/juju/resource" 18 "github.com/juju/juju/resource/resourcetesting" 19 "github.com/juju/juju/state/statetest" 20 ) 21 22 var _ = gc.Suite(&ResourcePersistenceSuite{}) 23 24 type ResourcePersistenceSuite struct { 25 testing.IsolationSuite 26 27 stub *testing.Stub 28 base *statetest.StubPersistence 29 } 30 31 func (s *ResourcePersistenceSuite) SetUpTest(c *gc.C) { 32 s.IsolationSuite.SetUpTest(c) 33 34 s.stub = &testing.Stub{} 35 s.base = statetest.NewStubPersistence(s.stub) 36 s.base.ReturnApplicationExistsOps = []txn.Op{{ 37 C: "application", 38 Id: "a-application", 39 Assert: txn.DocExists, 40 }} 41 } 42 43 func (s *ResourcePersistenceSuite) TestListResourcesOkay(c *gc.C) { 44 expected, docs := newPersistenceResources(c, "a-application", "spam", "eggs") 45 expected.CharmStoreResources[1].Revision += 1 46 docs[3].Revision += 1 47 unitRes, unitDocs := newPersistenceUnitResources(c, "a-application", "a-application/0", expected.Resources) 48 var progress int64 = 3 49 unitDocs[1].DownloadProgress = &progress // the "eggs" doc 50 expected.UnitResources = []resource.UnitResources{{ 51 Tag: names.NewUnitTag("a-application/0"), 52 Resources: unitRes, 53 DownloadProgress: map[string]int64{ 54 "eggs": progress, 55 }, 56 }} 57 docs = append(docs, unitDocs...) 58 s.base.ReturnAll = docs 59 p := NewResourcePersistence(s.base) 60 61 resources, err := p.ListResources("a-application") 62 c.Assert(err, jc.ErrorIsNil) 63 64 s.stub.CheckCallNames(c, "All") 65 s.stub.CheckCall(c, 0, "All", 66 "resources", 67 bson.D{{"application-id", "a-application"}}, 68 &docs, 69 ) 70 c.Check(resources, jc.DeepEquals, expected) 71 } 72 73 func (s *ResourcePersistenceSuite) TestListResourcesNoResources(c *gc.C) { 74 p := NewResourcePersistence(s.base) 75 resources, err := p.ListResources("a-application") 76 c.Assert(err, jc.ErrorIsNil) 77 78 c.Check(resources.Resources, gc.HasLen, 0) 79 s.stub.CheckCallNames(c, "All") 80 s.stub.CheckCall(c, 0, "All", 81 "resources", 82 bson.D{{"application-id", "a-application"}}, 83 &[]resourceDoc{}, 84 ) 85 } 86 87 func (s *ResourcePersistenceSuite) TestListResourcesIgnorePending(c *gc.C) { 88 expected, docs := newPersistenceResources(c, "a-application", "spam", "eggs") 89 expected.Resources = expected.Resources[:1] 90 docs[2].PendingID = "some-unique-ID-001" 91 s.base.ReturnAll = docs 92 p := NewResourcePersistence(s.base) 93 94 resources, err := p.ListResources("a-application") 95 c.Assert(err, jc.ErrorIsNil) 96 97 s.stub.CheckCallNames(c, "All") 98 s.stub.CheckCall(c, 0, "All", 99 "resources", 100 bson.D{{"application-id", "a-application"}}, 101 &docs, 102 ) 103 checkResources(c, resources, expected) 104 } 105 106 func (s *ResourcePersistenceSuite) TestListResourcesBaseError(c *gc.C) { 107 failure := errors.New("<failure>") 108 s.stub.SetErrors(failure) 109 110 p := NewResourcePersistence(s.base) 111 _, err := p.ListResources("a-application") 112 113 c.Check(errors.Cause(err), gc.Equals, failure) 114 s.stub.CheckCallNames(c, "All") 115 s.stub.CheckCall(c, 0, "All", 116 "resources", 117 bson.D{{"application-id", "a-application"}}, 118 &[]resourceDoc{}, 119 ) 120 } 121 122 func (s *ResourcePersistenceSuite) TestListResourcesBadDoc(c *gc.C) { 123 _, docs := newPersistenceResources(c, "a-application", "spam", "eggs") 124 docs[0].Timestamp = time.Time{} 125 s.base.ReturnAll = docs 126 127 p := NewResourcePersistence(s.base) 128 _, err := p.ListResources("a-application") 129 130 c.Check(err, gc.ErrorMatches, `got invalid data from DB.*`) 131 s.stub.CheckCallNames(c, "All") 132 s.stub.CheckCall(c, 0, "All", 133 "resources", 134 bson.D{{"application-id", "a-application"}}, 135 &docs, 136 ) 137 } 138 139 func (s *ResourcePersistenceSuite) TestListPendingResourcesOkay(c *gc.C) { 140 var expected []resource.Resource 141 var docs []resourceDoc 142 for _, name := range []string{"spam", "ham"} { 143 res, doc := newPersistenceResource(c, "a-application", name) 144 expected = append(expected, res.Resource) 145 docs = append(docs, doc) 146 } 147 expected = expected[1:] 148 expected[0].PendingID = "some-unique-ID-001" 149 docs[1].PendingID = "some-unique-ID-001" 150 s.base.ReturnAll = docs 151 p := NewResourcePersistence(s.base) 152 153 resources, err := p.ListPendingResources("a-application") 154 c.Assert(err, jc.ErrorIsNil) 155 156 s.stub.CheckCallNames(c, "All") 157 s.stub.CheckCall(c, 0, "All", 158 "resources", 159 bson.D{{"application-id", "a-application"}}, 160 &docs, 161 ) 162 checkBasicResources(c, resources, expected) 163 } 164 165 func (s *ResourcePersistenceSuite) TestGetResourceOkay(c *gc.C) { 166 expected, doc := newPersistenceResource(c, "a-application", "spam") 167 unitDoc := doc // a copy 168 unitDoc.ID = doc.ID + "#unit-a-application/0" 169 unitDoc.UnitID = "a-application/0" 170 pendingDoc := doc // a copy 171 pendingDoc.ID = doc.ID + "#pending-some-unique-ID" 172 pendingDoc.PendingID = "some-unique-ID" 173 s.base.ReturnAll = []resourceDoc{ 174 doc, 175 unitDoc, 176 pendingDoc, 177 } 178 s.base.ReturnOne = doc 179 p := NewResourcePersistence(s.base) 180 181 res, storagePath, err := p.GetResource("a-application/spam") 182 c.Assert(err, jc.ErrorIsNil) 183 184 s.stub.CheckCallNames(c, "One") 185 s.stub.CheckCall(c, 0, "One", "resources", "resource#a-application/spam", &doc) 186 c.Check(res, jc.DeepEquals, expected.Resource) 187 c.Check(storagePath, gc.Equals, expected.storagePath) 188 } 189 190 func (s *ResourcePersistenceSuite) TestStageResourceOkay(c *gc.C) { 191 res, doc := newPersistenceResource(c, "a-application", "spam") 192 doc.DocID += "#staged" 193 p := NewResourcePersistence(s.base) 194 ignoredErr := errors.New("<never reached>") 195 s.stub.SetErrors(nil, nil, nil, ignoredErr) 196 197 staged, err := p.StageResource(res.Resource, res.storagePath) 198 c.Assert(err, jc.ErrorIsNil) 199 200 s.stub.CheckCallNames(c, "Run", "ApplicationExistsOps", "RunTransaction") 201 s.stub.CheckCall(c, 2, "RunTransaction", []txn.Op{{ 202 C: "resources", 203 Id: "resource#a-application/spam#staged", 204 Assert: txn.DocMissing, 205 Insert: &doc, 206 }, { 207 C: "application", 208 Id: "a-application", 209 Assert: txn.DocExists, 210 }}) 211 c.Check(staged, jc.DeepEquals, &StagedResource{ 212 base: s.base, 213 id: res.ID, 214 stored: res, 215 }) 216 } 217 218 func (s *ResourcePersistenceSuite) TestStageResourceMissingStoragePath(c *gc.C) { 219 res, _ := newPersistenceResource(c, "a-application", "spam") 220 p := NewResourcePersistence(s.base) 221 222 _, err := p.StageResource(res.Resource, "") 223 224 s.stub.CheckNoCalls(c) 225 c.Check(err, gc.ErrorMatches, `missing storage path`) 226 } 227 228 func (s *ResourcePersistenceSuite) TestStageResourceBadResource(c *gc.C) { 229 res, _ := newPersistenceResource(c, "a-application", "spam") 230 res.Resource.Timestamp = time.Time{} 231 p := NewResourcePersistence(s.base) 232 233 _, err := p.StageResource(res.Resource, res.storagePath) 234 235 c.Check(err, jc.Satisfies, errors.IsNotValid) 236 c.Check(err, gc.ErrorMatches, `bad resource.*`) 237 238 s.stub.CheckNoCalls(c) 239 } 240 241 func (s *ResourcePersistenceSuite) TestSetResourceOkay(c *gc.C) { 242 applicationname := "a-application" 243 res, doc := newPersistenceResource(c, applicationname, "spam") 244 s.base.ReturnOne = doc 245 p := NewResourcePersistence(s.base) 246 ignoredErr := errors.New("<never reached>") 247 s.stub.SetErrors(nil, nil, nil, nil, ignoredErr) 248 249 err := p.SetResource(res.Resource) 250 c.Assert(err, jc.ErrorIsNil) 251 252 s.stub.CheckCallNames(c, 253 "One", 254 "Run", 255 "ApplicationExistsOps", 256 "RunTransaction", 257 ) 258 s.stub.CheckCall(c, 3, "RunTransaction", []txn.Op{{ 259 C: "resources", 260 Id: "resource#a-application/spam", 261 Assert: txn.DocMissing, 262 Insert: &doc, 263 }, { 264 C: "application", 265 Id: "a-application", 266 Assert: txn.DocExists, 267 }}) 268 } 269 270 func (s *ResourcePersistenceSuite) TestSetResourceNotFound(c *gc.C) { 271 applicationname := "a-application" 272 res, doc := newPersistenceResource(c, applicationname, "spam") 273 s.base.ReturnOne = doc 274 expected := doc // a copy 275 expected.StoragePath = "" 276 p := NewResourcePersistence(s.base) 277 notFound := errors.NewNotFound(nil, "") 278 ignoredErr := errors.New("<never reached>") 279 s.stub.SetErrors(notFound, nil, nil, nil, ignoredErr) 280 281 err := p.SetResource(res.Resource) 282 c.Assert(err, jc.ErrorIsNil) 283 284 s.stub.CheckCallNames(c, 285 "One", 286 "Run", 287 "ApplicationExistsOps", 288 "RunTransaction", 289 ) 290 s.stub.CheckCall(c, 3, "RunTransaction", []txn.Op{{ 291 C: "resources", 292 Id: "resource#a-application/spam", 293 Assert: txn.DocMissing, 294 Insert: &expected, 295 }, { 296 C: "application", 297 Id: "a-application", 298 Assert: txn.DocExists, 299 }}) 300 } 301 302 func (s *ResourcePersistenceSuite) TestSetCharmStoreResourceOkay(c *gc.C) { 303 lastPolled := time.Now().UTC() 304 applicationname := "a-application" 305 res, doc := newPersistenceResource(c, applicationname, "spam") 306 expected := doc // a copy 307 expected.DocID += "#charmstore" 308 expected.Username = "" 309 expected.Timestamp = time.Time{} 310 expected.StoragePath = "" 311 expected.LastPolled = lastPolled 312 p := NewResourcePersistence(s.base) 313 ignoredErr := errors.New("<never reached>") 314 s.stub.SetErrors(nil, nil, nil, ignoredErr) 315 316 err := p.SetCharmStoreResource(res.ID, res.ApplicationID, res.Resource.Resource, lastPolled) 317 c.Assert(err, jc.ErrorIsNil) 318 319 s.stub.CheckCallNames(c, 320 "Run", 321 "ApplicationExistsOps", 322 "RunTransaction", 323 ) 324 s.stub.CheckCall(c, 2, "RunTransaction", []txn.Op{{ 325 C: "resources", 326 Id: "resource#a-application/spam#charmstore", 327 Assert: txn.DocMissing, 328 Insert: &expected, 329 }, { 330 C: "application", 331 Id: "a-application", 332 Assert: txn.DocExists, 333 }}) 334 } 335 336 func (s *ResourcePersistenceSuite) TestSetUnitResourceOkay(c *gc.C) { 337 applicationname := "a-application" 338 unitname := "a-application/0" 339 res, doc := newPersistenceUnitResource(c, applicationname, unitname, "eggs") 340 s.base.ReturnOne = doc 341 p := NewResourcePersistence(s.base) 342 ignoredErr := errors.New("<never reached>") 343 s.stub.SetErrors(nil, nil, nil, nil, ignoredErr) 344 345 err := p.SetUnitResource("a-application/0", res) 346 c.Assert(err, jc.ErrorIsNil) 347 348 s.stub.CheckCallNames(c, "One", "Run", "ApplicationExistsOps", "RunTransaction") 349 s.stub.CheckCall(c, 3, "RunTransaction", []txn.Op{{ 350 C: "resources", 351 Id: "resource#a-application/eggs#unit-a-application/0", 352 Assert: txn.DocMissing, 353 Insert: &doc, 354 }, { 355 C: "application", 356 Id: "a-application", 357 Assert: txn.DocExists, 358 }}) 359 } 360 361 func (s *ResourcePersistenceSuite) TestSetUnitResourceNotFound(c *gc.C) { 362 applicationname := "a-application" 363 unitname := "a-application/0" 364 res, _ := newPersistenceUnitResource(c, applicationname, unitname, "eggs") 365 p := NewResourcePersistence(s.base) 366 notFound := errors.NewNotFound(nil, "") 367 s.stub.SetErrors(notFound) 368 369 err := p.SetUnitResource("a-application/0", res) 370 371 s.stub.CheckCallNames(c, "One") 372 c.Check(err, jc.Satisfies, errors.IsNotFound) 373 c.Check(err, gc.ErrorMatches, `resource "eggs" not found`) 374 } 375 376 func (s *ResourcePersistenceSuite) TestSetUnitResourceExists(c *gc.C) { 377 res, doc := newPersistenceUnitResource(c, "a-application", "a-application/0", "spam") 378 s.base.ReturnOne = doc 379 p := NewResourcePersistence(s.base) 380 ignoredErr := errors.New("<never reached>") 381 s.stub.SetErrors(nil, nil, nil, txn.ErrAborted, nil, nil, ignoredErr) 382 383 err := p.SetUnitResource("a-application/0", res) 384 c.Assert(err, jc.ErrorIsNil) 385 386 s.stub.CheckCallNames(c, "One", "Run", "ApplicationExistsOps", "RunTransaction", "ApplicationExistsOps", "RunTransaction") 387 s.stub.CheckCall(c, 3, "RunTransaction", []txn.Op{{ 388 C: "resources", 389 Id: "resource#a-application/spam#unit-a-application/0", 390 Assert: txn.DocMissing, 391 Insert: &doc, 392 }, { 393 C: "application", 394 Id: "a-application", 395 Assert: txn.DocExists, 396 }}) 397 s.stub.CheckCall(c, 5, "RunTransaction", []txn.Op{{ 398 C: "resources", 399 Id: "resource#a-application/spam#unit-a-application/0", 400 Assert: txn.DocExists, 401 Remove: true, 402 }, { 403 C: "resources", 404 Id: "resource#a-application/spam#unit-a-application/0", 405 Assert: txn.DocMissing, 406 Insert: &doc, 407 }, { 408 C: "application", 409 Id: "a-application", 410 Assert: txn.DocExists, 411 }}) 412 } 413 414 func (s *ResourcePersistenceSuite) TestSetUnitResourceBadResource(c *gc.C) { 415 res, doc := newPersistenceUnitResource(c, "a-application", "a-application/0", "spam") 416 s.base.ReturnOne = doc 417 res.Timestamp = time.Time{} 418 p := NewResourcePersistence(s.base) 419 420 err := p.SetUnitResource("a-application/0", res) 421 422 c.Check(err, jc.Satisfies, errors.IsNotValid) 423 c.Check(err, gc.ErrorMatches, `bad resource.*`) 424 425 s.stub.CheckCallNames(c, "One") 426 } 427 428 func (s *ResourcePersistenceSuite) TestSetUnitResourceProgress(c *gc.C) { 429 applicationname := "a-application" 430 unitname := "a-application/0" 431 res, doc := newPersistenceUnitResource(c, applicationname, unitname, "eggs") 432 s.base.ReturnOne = doc 433 pendingID := "<a pending ID>" 434 res.PendingID = pendingID 435 expected := doc // a copy 436 expected.PendingID = pendingID 437 var progress int64 = 2 438 expected.DownloadProgress = &progress 439 p := NewResourcePersistence(s.base) 440 ignoredErr := errors.New("<never reached>") 441 s.stub.SetErrors(nil, nil, nil, nil, ignoredErr) 442 443 err := p.SetUnitResourceProgress("a-application/0", res, progress) 444 c.Assert(err, jc.ErrorIsNil) 445 446 s.stub.CheckCallNames(c, "One", "Run", "ApplicationExistsOps", "RunTransaction") 447 s.stub.CheckCall(c, 3, "RunTransaction", []txn.Op{{ 448 C: "resources", 449 Id: "resource#a-application/eggs#unit-a-application/0", 450 Assert: txn.DocMissing, 451 Insert: &expected, 452 }, { 453 C: "application", 454 Id: "a-application", 455 Assert: txn.DocExists, 456 }}) 457 } 458 459 func (s *ResourcePersistenceSuite) TestNewResourcePendingResourceOpsExists(c *gc.C) { 460 pendingID := "some-unique-ID-001" 461 stored, expected := newPersistenceResource(c, "a-application", "spam") 462 stored.PendingID = pendingID 463 doc := expected // a copy 464 doc.DocID = pendingResourceID(stored.ID, pendingID) 465 doc.PendingID = pendingID 466 s.base.ReturnOne = doc 467 p := NewResourcePersistence(s.base) 468 469 lastPolled := time.Now().UTC().Round(time.Second) 470 471 ops, err := p.NewResolvePendingResourceOps(stored.ID, stored.PendingID) 472 c.Assert(err, jc.ErrorIsNil) 473 474 csresourceDoc := expected 475 csresourceDoc.DocID = "resource#a-application/spam#charmstore" 476 csresourceDoc.Username = "" 477 csresourceDoc.Timestamp = time.Time{} 478 csresourceDoc.StoragePath = "" 479 csresourceDoc.LastPolled = lastPolled 480 481 res := ops[4].Insert.(*resourceDoc) 482 res.LastPolled = res.LastPolled.Round(time.Second) 483 484 s.stub.CheckCallNames(c, "One", "One") 485 s.stub.CheckCall(c, 0, "One", "resources", "resource#a-application/spam#pending-some-unique-ID-001", &doc) 486 c.Check(ops, jc.DeepEquals, []txn.Op{ 487 { 488 C: "resources", 489 Id: doc.DocID, 490 Assert: txn.DocExists, 491 Remove: true, 492 }, { 493 C: "resources", 494 Id: expected.DocID, 495 Assert: txn.DocExists, 496 Remove: true, 497 }, { 498 C: "resources", 499 Id: expected.DocID, 500 Assert: txn.DocMissing, 501 Insert: &expected, 502 }, 503 { 504 C: "resources", 505 Id: csresourceDoc.DocID, 506 Assert: txn.DocExists, 507 Remove: true, 508 }, 509 { 510 C: "resources", 511 Id: csresourceDoc.DocID, 512 Assert: txn.DocMissing, 513 Insert: &csresourceDoc, 514 }, 515 }) 516 } 517 518 func (s *ResourcePersistenceSuite) TestNewResourcePendingResourceOpsNotFound(c *gc.C) { 519 pendingID := "some-unique-ID-001" 520 stored, expected := newPersistenceResource(c, "a-application", "spam") 521 stored.PendingID = pendingID 522 doc := expected // a copy 523 doc.DocID = pendingResourceID(stored.ID, pendingID) 524 doc.PendingID = pendingID 525 s.base.ReturnOne = doc 526 notFound := errors.NewNotFound(nil, "") 527 s.stub.SetErrors(nil, notFound) 528 p := NewResourcePersistence(s.base) 529 530 lastPolled := time.Now().UTC().Round(time.Second) 531 ops, err := p.NewResolvePendingResourceOps(stored.ID, stored.PendingID) 532 c.Assert(err, jc.ErrorIsNil) 533 534 s.stub.CheckCallNames(c, "One", "One") 535 s.stub.CheckCall(c, 0, "One", "resources", "resource#a-application/spam#pending-some-unique-ID-001", &doc) 536 537 csresourceDoc := expected 538 csresourceDoc.DocID = "resource#a-application/spam#charmstore" 539 csresourceDoc.Username = "" 540 csresourceDoc.Timestamp = time.Time{} 541 csresourceDoc.StoragePath = "" 542 csresourceDoc.LastPolled = lastPolled 543 544 res := ops[2].Insert.(*resourceDoc) 545 res.LastPolled = res.LastPolled.Round(time.Second) 546 547 c.Check(ops, jc.DeepEquals, []txn.Op{ 548 { 549 C: "resources", 550 Id: doc.DocID, 551 Assert: txn.DocExists, 552 Remove: true, 553 }, { 554 C: "resources", 555 Id: expected.DocID, 556 Assert: txn.DocMissing, 557 Insert: &expected, 558 }, 559 { 560 C: "resources", 561 Id: csresourceDoc.DocID, 562 Assert: txn.DocMissing, 563 Insert: &csresourceDoc, 564 }, 565 }) 566 } 567 568 func newPersistenceUnitResources(c *gc.C, serviceID, unitID string, resources []resource.Resource) ([]resource.Resource, []resourceDoc) { 569 var unitResources []resource.Resource 570 var docs []resourceDoc 571 for _, res := range resources { 572 res, doc := newPersistenceUnitResource(c, serviceID, unitID, res.Name) 573 unitResources = append(unitResources, res) 574 docs = append(docs, doc) 575 } 576 return unitResources, docs 577 } 578 579 func newPersistenceUnitResource(c *gc.C, serviceID, unitID, name string) (resource.Resource, resourceDoc) { 580 res, doc := newPersistenceResource(c, serviceID, name) 581 doc.DocID += "#unit-" + unitID 582 doc.UnitID = unitID 583 return res.Resource, doc 584 } 585 586 func newPersistenceResources(c *gc.C, serviceID string, names ...string) (resource.ServiceResources, []resourceDoc) { 587 var svcResources resource.ServiceResources 588 var docs []resourceDoc 589 for _, name := range names { 590 res, doc := newPersistenceResource(c, serviceID, name) 591 svcResources.Resources = append(svcResources.Resources, res.Resource) 592 svcResources.CharmStoreResources = append(svcResources.CharmStoreResources, res.Resource.Resource) 593 docs = append(docs, doc) 594 csDoc := doc // a copy 595 csDoc.DocID += "#charmstore" 596 csDoc.Username = "" 597 csDoc.Timestamp = time.Time{} 598 csDoc.StoragePath = "" 599 csDoc.LastPolled = time.Now().UTC() 600 docs = append(docs, csDoc) 601 } 602 return svcResources, docs 603 } 604 605 func newPersistenceResource(c *gc.C, serviceID, name string) (storedResource, resourceDoc) { 606 content := name 607 opened := resourcetesting.NewResource(c, nil, name, serviceID, content) 608 res := opened.Resource 609 610 stored := storedResource{ 611 Resource: res, 612 storagePath: "application-" + serviceID + "/resources/" + name, 613 } 614 615 doc := resourceDoc{ 616 DocID: "resource#" + res.ID, 617 ID: res.ID, 618 ApplicationID: res.ApplicationID, 619 620 Name: res.Name, 621 Type: res.Type.String(), 622 Path: res.Path, 623 Description: res.Description, 624 625 Origin: res.Origin.String(), 626 Revision: res.Revision, 627 Fingerprint: res.Fingerprint.Bytes(), 628 Size: res.Size, 629 630 Username: res.Username, 631 Timestamp: res.Timestamp, 632 633 StoragePath: stored.storagePath, 634 } 635 636 return stored, doc 637 } 638 639 func checkResources(c *gc.C, resources, expected resource.ServiceResources) { 640 resMap := make(map[string]resource.Resource) 641 for _, res := range resources.Resources { 642 resMap[res.Name] = res 643 } 644 expMap := make(map[string]resource.Resource) 645 for _, res := range expected.Resources { 646 expMap[res.Name] = res 647 } 648 c.Check(resMap, jc.DeepEquals, expMap) 649 } 650 651 func checkBasicResources(c *gc.C, resources, expected []resource.Resource) { 652 resMap := make(map[string]resource.Resource) 653 for _, res := range resources { 654 resMap[res.ID] = res 655 } 656 expMap := make(map[string]resource.Resource) 657 for _, res := range expected { 658 expMap[res.ID] = res 659 } 660 c.Check(resMap, jc.DeepEquals, expMap) 661 }