github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/resource/state/resource_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 "bytes" 8 "io" 9 "io/ioutil" 10 "strings" 11 "time" 12 13 "github.com/juju/errors" 14 "github.com/juju/names" 15 "github.com/juju/testing" 16 jc "github.com/juju/testing/checkers" 17 gc "gopkg.in/check.v1" 18 charmresource "gopkg.in/juju/charm.v6-unstable/resource" 19 "gopkg.in/mgo.v2/txn" 20 21 "github.com/juju/juju/resource" 22 "github.com/juju/juju/resource/resourcetesting" 23 ) 24 25 var _ = gc.Suite(&ResourceSuite{}) 26 27 type ResourceSuite struct { 28 testing.IsolationSuite 29 30 stub *testing.Stub 31 raw *stubRawState 32 persist *stubPersistence 33 storage *stubStorage 34 timestamp time.Time 35 pendingID string 36 } 37 38 func (s *ResourceSuite) SetUpTest(c *gc.C) { 39 s.IsolationSuite.SetUpTest(c) 40 41 s.stub = &testing.Stub{} 42 s.raw = &stubRawState{stub: s.stub} 43 s.persist = &stubPersistence{stub: s.stub} 44 s.persist.ReturnStageResource = &stubStagedResource{stub: s.stub} 45 s.storage = &stubStorage{stub: s.stub} 46 s.raw.ReturnPersistence = s.persist 47 s.raw.ReturnStorage = s.storage 48 s.timestamp = time.Now().UTC() 49 s.pendingID = "" 50 } 51 52 func (s *ResourceSuite) now() time.Time { 53 s.stub.AddCall("currentTimestamp") 54 s.stub.NextErr() // Pop one off. 55 56 return s.timestamp 57 } 58 59 func (s *ResourceSuite) newPendingID() (string, error) { 60 s.stub.AddCall("newPendingID") 61 if err := s.stub.NextErr(); err != nil { 62 return "", errors.Trace(err) 63 } 64 65 return s.pendingID, nil 66 } 67 68 func (s *ResourceSuite) TestListResourcesOkay(c *gc.C) { 69 expected := newUploadResources(c, "spam", "eggs") 70 s.persist.ReturnListResources = resource.ServiceResources{Resources: expected} 71 tag := names.NewUnitTag("a-service/0") 72 s.raw.ReturnUnits = []names.UnitTag{tag} 73 st := NewState(s.raw) 74 s.stub.ResetCalls() 75 76 resources, err := st.ListResources("a-service") 77 c.Assert(err, jc.ErrorIsNil) 78 79 c.Check(resources.Resources, jc.DeepEquals, expected) 80 c.Check(resources.UnitResources, jc.DeepEquals, []resource.UnitResources{{ 81 Tag: tag, 82 }}) 83 s.stub.CheckCallNames(c, "ListResources", "Units") 84 s.stub.CheckCall(c, 0, "ListResources", "a-service") 85 } 86 87 func (s *ResourceSuite) TestListResourcesNoUnits(c *gc.C) { 88 expected := newUploadResources(c, "spam", "eggs") 89 s.persist.ReturnListResources = resource.ServiceResources{Resources: expected} 90 st := NewState(s.raw) 91 s.stub.ResetCalls() 92 93 resources, err := st.ListResources("a-service") 94 c.Assert(err, jc.ErrorIsNil) 95 96 c.Check(resources.Resources, jc.DeepEquals, expected) 97 c.Check(resources.UnitResources, gc.HasLen, 0) 98 s.stub.CheckCallNames(c, "ListResources", "Units") 99 s.stub.CheckCall(c, 0, "ListResources", "a-service") 100 } 101 102 func (s *ResourceSuite) TestListResourcesEmpty(c *gc.C) { 103 s.raw.ReturnUnits = []names.UnitTag{ 104 names.NewUnitTag("a-service/0"), 105 } 106 st := NewState(s.raw) 107 s.stub.ResetCalls() 108 109 resources, err := st.ListResources("a-service") 110 c.Assert(err, jc.ErrorIsNil) 111 112 c.Check(resources.Resources, gc.HasLen, 0) 113 c.Check(resources.UnitResources, gc.HasLen, 1) 114 s.stub.CheckCallNames(c, "ListResources", "Units") 115 } 116 117 func (s *ResourceSuite) TestListResourcesError(c *gc.C) { 118 expected := newUploadResources(c, "spam", "eggs") 119 s.persist.ReturnListResources = resource.ServiceResources{Resources: expected} 120 st := NewState(s.raw) 121 s.stub.ResetCalls() 122 failure := errors.New("<failure>") 123 s.stub.SetErrors(failure) 124 125 _, err := st.ListResources("a-service") 126 127 c.Check(errors.Cause(err), gc.Equals, failure) 128 s.stub.CheckCallNames(c, "ListResources", "VerifyService") 129 } 130 131 func (s *ResourceSuite) TestGetPendingResource(c *gc.C) { 132 resources := newUploadResources(c, "spam", "eggs") 133 resources[0].PendingID = "some-unique-id" 134 resources[1].PendingID = "other-unique-id" 135 s.persist.ReturnListPendingResources = resources 136 st := NewState(s.raw) 137 s.stub.ResetCalls() 138 139 res, err := st.GetPendingResource("a-service", "eggs", "other-unique-id") 140 c.Assert(err, jc.ErrorIsNil) 141 142 s.stub.CheckCallNames(c, "ListPendingResources") 143 s.stub.CheckCall(c, 0, "ListPendingResources", "a-service") 144 c.Check(res, jc.DeepEquals, resources[1]) 145 } 146 147 func (s *ResourceSuite) TestSetResourceOkay(c *gc.C) { 148 expected := newUploadResource(c, "spam", "spamspamspam") 149 expected.Timestamp = s.timestamp 150 chRes := expected.Resource 151 hash := chRes.Fingerprint.String() 152 path := "service-a-service/resources/spam" 153 file := &stubReader{stub: s.stub} 154 st := NewState(s.raw) 155 st.currentTimestamp = s.now 156 s.stub.ResetCalls() 157 158 res, err := st.SetResource("a-service", "a-user", chRes, file) 159 c.Assert(err, jc.ErrorIsNil) 160 161 s.stub.CheckCallNames(c, 162 "currentTimestamp", 163 "StageResource", 164 "PutAndCheckHash", 165 "Activate", 166 ) 167 s.stub.CheckCall(c, 1, "StageResource", expected, path) 168 s.stub.CheckCall(c, 2, "PutAndCheckHash", path, file, res.Size, hash) 169 c.Check(res, jc.DeepEquals, resource.Resource{ 170 Resource: chRes, 171 ID: "a-service/" + res.Name, 172 ServiceID: "a-service", 173 Username: "a-user", 174 Timestamp: s.timestamp, 175 }) 176 } 177 178 func (s *ResourceSuite) TestSetResourceInfoOnly(c *gc.C) { 179 expected := newUploadResource(c, "spam", "spamspamspam") 180 expected.Timestamp = time.Time{} 181 expected.Username = "" 182 chRes := expected.Resource 183 st := NewState(s.raw) 184 st.currentTimestamp = s.now 185 s.stub.ResetCalls() 186 187 res, err := st.SetResource("a-service", "a-user", chRes, nil) 188 c.Assert(err, jc.ErrorIsNil) 189 190 s.stub.CheckCallNames(c, 191 "SetResource", 192 ) 193 s.stub.CheckCall(c, 0, "SetResource", expected) 194 c.Check(res, jc.DeepEquals, resource.Resource{ 195 Resource: chRes, 196 ID: "a-service/" + res.Name, 197 ServiceID: "a-service", 198 }) 199 } 200 201 func (s *ResourceSuite) TestSetResourceBadResource(c *gc.C) { 202 res := newUploadResource(c, "spam", "spamspamspam") 203 res.Fingerprint = charmresource.Fingerprint{} 204 file := &stubReader{stub: s.stub} 205 st := NewState(s.raw) 206 st.currentTimestamp = s.now 207 s.stub.ResetCalls() 208 209 _, err := st.SetResource("a-service", "a-user", res.Resource, file) 210 211 c.Check(err, jc.Satisfies, errors.IsNotValid) 212 c.Check(err, gc.ErrorMatches, `bad resource metadata.*`) 213 s.stub.CheckCallNames(c, "currentTimestamp") 214 } 215 216 func (s *ResourceSuite) TestSetResourceStagingFailure(c *gc.C) { 217 expected := newUploadResource(c, "spam", "spamspamspam") 218 expected.Timestamp = s.timestamp 219 path := "service-a-service/resources/spam" 220 file := &stubReader{stub: s.stub} 221 st := NewState(s.raw) 222 st.currentTimestamp = s.now 223 s.stub.ResetCalls() 224 failure := errors.New("<failure>") 225 ignoredErr := errors.New("<never reached>") 226 s.stub.SetErrors(nil, failure, ignoredErr) 227 228 _, err := st.SetResource("a-service", "a-user", expected.Resource, file) 229 230 c.Check(errors.Cause(err), gc.Equals, failure) 231 s.stub.CheckCallNames(c, "currentTimestamp", "StageResource") 232 s.stub.CheckCall(c, 1, "StageResource", expected, path) 233 } 234 235 func (s *ResourceSuite) TestSetResourcePutFailureBasic(c *gc.C) { 236 expected := newUploadResource(c, "spam", "spamspamspam") 237 expected.Timestamp = s.timestamp 238 hash := expected.Fingerprint.String() 239 path := "service-a-service/resources/spam" 240 file := &stubReader{stub: s.stub} 241 st := NewState(s.raw) 242 st.currentTimestamp = s.now 243 s.stub.ResetCalls() 244 failure := errors.New("<failure>") 245 ignoredErr := errors.New("<never reached>") 246 s.stub.SetErrors(nil, nil, failure, nil, ignoredErr) 247 248 _, err := st.SetResource("a-service", "a-user", expected.Resource, file) 249 250 c.Check(errors.Cause(err), gc.Equals, failure) 251 s.stub.CheckCallNames(c, 252 "currentTimestamp", 253 "StageResource", 254 "PutAndCheckHash", 255 "Unstage", 256 ) 257 s.stub.CheckCall(c, 1, "StageResource", expected, path) 258 s.stub.CheckCall(c, 2, "PutAndCheckHash", path, file, expected.Size, hash) 259 } 260 261 func (s *ResourceSuite) TestSetResourcePutFailureExtra(c *gc.C) { 262 expected := newUploadResource(c, "spam", "spamspamspam") 263 expected.Timestamp = s.timestamp 264 hash := expected.Fingerprint.String() 265 path := "service-a-service/resources/spam" 266 file := &stubReader{stub: s.stub} 267 st := NewState(s.raw) 268 st.currentTimestamp = s.now 269 s.stub.ResetCalls() 270 failure := errors.New("<failure>") 271 extraErr := errors.New("<just not your day>") 272 ignoredErr := errors.New("<never reached>") 273 s.stub.SetErrors(nil, nil, failure, extraErr, ignoredErr) 274 275 _, err := st.SetResource("a-service", "a-user", expected.Resource, file) 276 277 c.Check(errors.Cause(err), gc.Equals, failure) 278 s.stub.CheckCallNames(c, 279 "currentTimestamp", 280 "StageResource", 281 "PutAndCheckHash", 282 "Unstage", 283 ) 284 s.stub.CheckCall(c, 1, "StageResource", expected, path) 285 s.stub.CheckCall(c, 2, "PutAndCheckHash", path, file, expected.Size, hash) 286 } 287 288 func (s *ResourceSuite) TestSetResourceSetFailureBasic(c *gc.C) { 289 expected := newUploadResource(c, "spam", "spamspamspam") 290 expected.Timestamp = s.timestamp 291 hash := expected.Fingerprint.String() 292 path := "service-a-service/resources/spam" 293 file := &stubReader{stub: s.stub} 294 st := NewState(s.raw) 295 st.currentTimestamp = s.now 296 s.stub.ResetCalls() 297 failure := errors.New("<failure>") 298 ignoredErr := errors.New("<never reached>") 299 s.stub.SetErrors(nil, nil, nil, failure, nil, nil, ignoredErr) 300 301 _, err := st.SetResource("a-service", "a-user", expected.Resource, file) 302 303 c.Check(errors.Cause(err), gc.Equals, failure) 304 s.stub.CheckCallNames(c, 305 "currentTimestamp", 306 "StageResource", 307 "PutAndCheckHash", 308 "Activate", 309 "Remove", 310 "Unstage", 311 ) 312 s.stub.CheckCall(c, 1, "StageResource", expected, path) 313 s.stub.CheckCall(c, 2, "PutAndCheckHash", path, file, expected.Size, hash) 314 s.stub.CheckCall(c, 4, "Remove", path) 315 } 316 317 func (s *ResourceSuite) TestSetResourceSetFailureExtra(c *gc.C) { 318 expected := newUploadResource(c, "spam", "spamspamspam") 319 expected.Timestamp = s.timestamp 320 hash := expected.Fingerprint.String() 321 path := "service-a-service/resources/spam" 322 file := &stubReader{stub: s.stub} 323 st := NewState(s.raw) 324 st.currentTimestamp = s.now 325 s.stub.ResetCalls() 326 failure := errors.New("<failure>") 327 extraErr1 := errors.New("<just not your day>") 328 extraErr2 := errors.New("<wow...just wow>") 329 ignoredErr := errors.New("<never reached>") 330 s.stub.SetErrors(nil, nil, nil, failure, extraErr1, extraErr2, ignoredErr) 331 332 _, err := st.SetResource("a-service", "a-user", expected.Resource, file) 333 334 c.Check(errors.Cause(err), gc.Equals, failure) 335 s.stub.CheckCallNames(c, 336 "currentTimestamp", 337 "StageResource", 338 "PutAndCheckHash", 339 "Activate", 340 "Remove", 341 "Unstage", 342 ) 343 s.stub.CheckCall(c, 1, "StageResource", expected, path) 344 s.stub.CheckCall(c, 2, "PutAndCheckHash", path, file, expected.Size, hash) 345 s.stub.CheckCall(c, 4, "Remove", path) 346 } 347 348 func (s *ResourceSuite) TestUpdatePendingResourceOkay(c *gc.C) { 349 expected := newUploadResource(c, "spam", "spamspamspam") 350 expected.PendingID = "some-unique-id" 351 expected.Timestamp = s.timestamp 352 chRes := expected.Resource 353 hash := chRes.Fingerprint.String() 354 path := "service-a-service/resources/spam-some-unique-id" 355 file := &stubReader{stub: s.stub} 356 st := NewState(s.raw) 357 st.currentTimestamp = s.now 358 s.stub.ResetCalls() 359 360 res, err := st.UpdatePendingResource("a-service", "some-unique-id", "a-user", chRes, file) 361 c.Assert(err, jc.ErrorIsNil) 362 363 s.stub.CheckCallNames(c, 364 "currentTimestamp", 365 "StageResource", 366 "PutAndCheckHash", 367 "Activate", 368 ) 369 s.stub.CheckCall(c, 1, "StageResource", expected, path) 370 s.stub.CheckCall(c, 2, "PutAndCheckHash", path, file, res.Size, hash) 371 c.Check(res, jc.DeepEquals, resource.Resource{ 372 Resource: chRes, 373 ID: "a-service/" + res.Name, 374 ServiceID: "a-service", 375 PendingID: "some-unique-id", 376 Username: "a-user", 377 Timestamp: s.timestamp, 378 }) 379 } 380 381 func (s *ResourceSuite) TestAddPendingResourceOkay(c *gc.C) { 382 s.pendingID = "some-unique-ID-001" 383 expected := newUploadResource(c, "spam", "spamspamspam") 384 expected.PendingID = s.pendingID 385 expected.Timestamp = s.timestamp 386 chRes := expected.Resource 387 hash := chRes.Fingerprint.String() 388 path := "service-a-service/resources/spam-some-unique-ID-001" 389 file := &stubReader{stub: s.stub} 390 st := NewState(s.raw) 391 st.currentTimestamp = s.now 392 st.newPendingID = s.newPendingID 393 s.stub.ResetCalls() 394 395 pendingID, err := st.AddPendingResource("a-service", "a-user", chRes, file) 396 c.Assert(err, jc.ErrorIsNil) 397 398 s.stub.CheckCallNames(c, 399 "newPendingID", 400 "currentTimestamp", 401 "StageResource", 402 "PutAndCheckHash", 403 "Activate", 404 ) 405 s.stub.CheckCall(c, 2, "StageResource", expected, path) 406 s.stub.CheckCall(c, 3, "PutAndCheckHash", path, file, expected.Size, hash) 407 c.Check(pendingID, gc.Equals, s.pendingID) 408 } 409 410 func (s *ResourceSuite) TestOpenResourceOkay(c *gc.C) { 411 data := "some data" 412 opened := resourcetesting.NewResource(c, s.stub, "spam", "a-service", data) 413 s.persist.ReturnGetResource = opened.Resource 414 s.persist.ReturnGetResourcePath = "service-a-service/resources/spam" 415 s.storage.ReturnGet = opened.Content() 416 st := NewState(s.raw) 417 s.stub.ResetCalls() 418 419 info, reader, err := st.OpenResource("a-service", "spam") 420 c.Assert(err, jc.ErrorIsNil) 421 422 s.stub.CheckCallNames(c, "GetResource", "Get") 423 s.stub.CheckCall(c, 1, "Get", "service-a-service/resources/spam") 424 c.Check(info, jc.DeepEquals, opened.Resource) 425 c.Check(reader, gc.Equals, opened.ReadCloser) 426 } 427 428 func (s *ResourceSuite) TestOpenResourceNotFound(c *gc.C) { 429 st := NewState(s.raw) 430 s.stub.ResetCalls() 431 errNotFound := errors.NotFoundf("resource") 432 s.stub.SetErrors(errNotFound) 433 434 _, _, err := st.OpenResource("a-service", "spam") 435 436 s.stub.CheckCallNames(c, "GetResource", "VerifyService") 437 c.Check(err, jc.Satisfies, errors.IsNotFound) 438 } 439 440 func (s *ResourceSuite) TestOpenResourcePlaceholder(c *gc.C) { 441 res := resourcetesting.NewPlaceholderResource(c, "spam", "a-service") 442 s.persist.ReturnGetResource = res 443 s.persist.ReturnGetResourcePath = "service-a-service/resources/spam" 444 st := NewState(s.raw) 445 s.stub.ResetCalls() 446 447 _, _, err := st.OpenResource("a-service", "spam") 448 449 s.stub.CheckCallNames(c, "GetResource") 450 c.Check(err, jc.Satisfies, errors.IsNotFound) 451 } 452 453 func (s *ResourceSuite) TestOpenResourceSizeMismatch(c *gc.C) { 454 opened := resourcetesting.NewResource(c, s.stub, "spam", "a-service", "some data") 455 s.persist.ReturnGetResource = opened.Resource 456 s.persist.ReturnGetResourcePath = "service-a-service/resources/spam" 457 content := opened.Content() 458 content.Size += 1 459 s.storage.ReturnGet = content 460 st := NewState(s.raw) 461 s.stub.ResetCalls() 462 463 _, _, err := st.OpenResource("a-service", "spam") 464 465 s.stub.CheckCallNames(c, "GetResource", "Get") 466 c.Check(err, gc.ErrorMatches, `storage returned a size \(10\) which doesn't match resource metadata \(9\)`) 467 } 468 469 func (s *ResourceSuite) TestOpenResourceForUniterOkay(c *gc.C) { 470 data := "some data" 471 opened := resourcetesting.NewResource(c, s.stub, "spam", "a-service", data) 472 s.persist.ReturnGetResource = opened.Resource 473 s.persist.ReturnGetResourcePath = "service-a-service/resources/spam" 474 s.storage.ReturnGet = opened.Content() 475 unit := newUnit(s.stub, "a-service/0") 476 st := NewState(s.raw) 477 s.stub.ResetCalls() 478 479 info, reader, err := st.OpenResourceForUniter(unit, "spam") 480 c.Assert(err, jc.ErrorIsNil) 481 482 s.stub.CheckCallNames(c, "ServiceName", "GetResource", "Get", "Name", "SetUnitResourceProgress") 483 s.stub.CheckCall(c, 2, "Get", "service-a-service/resources/spam") 484 c.Check(info, jc.DeepEquals, opened.Resource) 485 486 b, err := ioutil.ReadAll(reader) 487 // note ioutil.ReadAll converts EOF to nil 488 c.Check(err, jc.ErrorIsNil) 489 c.Check(b, gc.DeepEquals, []byte(data)) 490 } 491 492 func (s *ResourceSuite) TestOpenResourceForUniterNotFound(c *gc.C) { 493 unit := newUnit(s.stub, "a-service/0") 494 st := NewState(s.raw) 495 s.stub.ResetCalls() 496 errNotFound := errors.NotFoundf("resource") 497 s.stub.SetErrors(nil, errNotFound) 498 499 _, _, err := st.OpenResourceForUniter(unit, "spam") 500 501 s.stub.CheckCallNames(c, "ServiceName", "GetResource", "VerifyService") 502 c.Check(err, jc.Satisfies, errors.IsNotFound) 503 } 504 505 func (s *ResourceSuite) TestOpenResourceForUniterPlaceholder(c *gc.C) { 506 res := resourcetesting.NewPlaceholderResource(c, "spam", "a-service") 507 s.persist.ReturnGetResource = res 508 s.persist.ReturnGetResourcePath = "service-a-service/resources/spam" 509 unit := newUnit(s.stub, "a-service/0") 510 st := NewState(s.raw) 511 s.stub.ResetCalls() 512 513 _, _, err := st.OpenResourceForUniter(unit, "spam") 514 515 s.stub.CheckCallNames(c, "ServiceName", "GetResource") 516 c.Check(err, jc.Satisfies, errors.IsNotFound) 517 } 518 519 func (s *ResourceSuite) TestOpenResourceForUniterSizeMismatch(c *gc.C) { 520 opened := resourcetesting.NewResource(c, s.stub, "spam", "a-service", "some data") 521 s.persist.ReturnGetResource = opened.Resource 522 s.persist.ReturnGetResourcePath = "service-a-service/resources/spam" 523 content := opened.Content() 524 content.Size += 1 525 s.storage.ReturnGet = content 526 unit := newUnit(s.stub, "a-service/0") 527 st := NewState(s.raw) 528 s.stub.ResetCalls() 529 530 _, _, err := st.OpenResourceForUniter(unit, "spam") 531 532 s.stub.CheckCallNames(c, "ServiceName", "GetResource", "Get") 533 c.Check(err, gc.ErrorMatches, `storage returned a size \(10\) which doesn't match resource metadata \(9\)`) 534 } 535 536 func (s *ResourceSuite) TestSetCharmStoreResources(c *gc.C) { 537 lastPolled := time.Now().UTC() 538 resources := newStoreResources(c, "spam", "eggs") 539 var info []charmresource.Resource 540 for _, res := range resources { 541 chRes := res.Resource 542 info = append(info, chRes) 543 } 544 st := NewState(s.raw) 545 s.stub.ResetCalls() 546 547 err := st.SetCharmStoreResources("a-service", info, lastPolled) 548 c.Assert(err, jc.ErrorIsNil) 549 550 s.stub.CheckCallNames(c, 551 "SetCharmStoreResource", 552 "SetCharmStoreResource", 553 ) 554 s.stub.CheckCall(c, 0, "SetCharmStoreResource", "a-service/spam", "a-service", info[0], lastPolled) 555 s.stub.CheckCall(c, 1, "SetCharmStoreResource", "a-service/eggs", "a-service", info[1], lastPolled) 556 } 557 558 func (s *ResourceSuite) TestNewResourcePendingResourcesOps(c *gc.C) { 559 doc1 := map[string]string{"a": "1"} 560 doc2 := map[string]string{"b": "2"} 561 expected := []txn.Op{{ 562 C: "resources", 563 Id: "resource#a-service/spam#pending-some-unique-ID-001", 564 Assert: txn.DocExists, 565 Remove: true, 566 }, { 567 C: "resources", 568 Id: "resource#a-service/spam", 569 Assert: txn.DocMissing, 570 Insert: &doc1, 571 }, { 572 C: "resources", 573 Id: "resource#a-service/spam#pending-some-unique-ID-001", 574 Assert: txn.DocExists, 575 Remove: true, 576 }, { 577 C: "resources", 578 Id: "resource#a-service/spam", 579 Assert: txn.DocMissing, 580 Insert: &doc2, 581 }} 582 s.persist.ReturnNewResolvePendingResourceOps = [][]txn.Op{ 583 expected[:2], 584 expected[2:], 585 } 586 serviceID := "a-service" 587 st := NewState(s.raw) 588 s.stub.ResetCalls() 589 pendingIDs := map[string]string{ 590 "spam": "some-unique-id", 591 "eggs": "other-unique-id", 592 } 593 594 ops, err := st.NewResolvePendingResourcesOps(serviceID, pendingIDs) 595 c.Assert(err, jc.ErrorIsNil) 596 597 s.stub.CheckCallNames(c, 598 "NewResolvePendingResourceOps", 599 "NewResolvePendingResourceOps", 600 ) 601 c.Check(s.persist.CallsForNewResolvePendingResourceOps, jc.DeepEquals, map[string]string{ 602 "a-service/spam": "some-unique-id", 603 "a-service/eggs": "other-unique-id", 604 }) 605 c.Check(ops, jc.DeepEquals, expected) 606 } 607 608 func (s *ResourceSuite) TestUnitSetterEOF(c *gc.C) { 609 r := unitSetter{ 610 ReadCloser: ioutil.NopCloser(&bytes.Buffer{}), 611 persist: &stubPersistence{stub: s.stub}, 612 unit: newUnit(s.stub, "some-service/0"), 613 resource: newUploadResource(c, "res", "res"), 614 } 615 // have to try to read non-zero data, or bytes.buffer will happily return 616 // nil. 617 p := make([]byte, 5) 618 n, err := r.Read(p) 619 c.Assert(n, gc.Equals, 0) 620 c.Assert(err, gc.Equals, io.EOF) 621 622 s.stub.CheckCallNames(c, "Name", "SetUnitResource") 623 s.stub.CheckCall(c, 1, "SetUnitResource", "some-service/0", r.resource) 624 } 625 626 func (s *ResourceSuite) TestUnitSetterNoEOF(c *gc.C) { 627 r := unitSetter{ 628 ReadCloser: ioutil.NopCloser(bytes.NewBufferString("foobar")), 629 persist: &stubPersistence{stub: s.stub}, 630 unit: newUnit(s.stub, "some-service/0"), 631 resource: newUploadResource(c, "res", "res"), 632 } 633 // read less than the full buffer 634 p := make([]byte, 3) 635 n, err := r.Read(p) 636 c.Assert(n, gc.Equals, 3) 637 c.Assert(err, gc.Equals, nil) 638 639 // Assert that we don't call SetUnitResource if we read but don't reach the 640 // end of the buffer. 641 s.stub.CheckCallNames(c, "Name", "SetUnitResourceProgress") 642 } 643 644 func (s *ResourceSuite) TestUnitSetterSetUnitErr(c *gc.C) { 645 r := unitSetter{ 646 ReadCloser: ioutil.NopCloser(&bytes.Buffer{}), 647 persist: &stubPersistence{stub: s.stub}, 648 unit: newUnit(s.stub, "some-service/0"), 649 resource: newUploadResource(c, "res", "res"), 650 } 651 652 s.stub.SetErrors(errors.Errorf("oops!")) 653 // have to try to read non-zero data, or bytes.buffer will happily return 654 // nil. 655 p := make([]byte, 5) 656 n, err := r.Read(p) 657 c.Assert(n, gc.Equals, 0) 658 659 // ensure that we return the EOF from bytes.buffer and not the error from SetUnitResource. 660 c.Assert(err, gc.Equals, io.EOF) 661 662 s.stub.CheckCallNames(c, "Name", "SetUnitResource") 663 s.stub.CheckCall(c, 1, "SetUnitResource", "some-service/0", r.resource) 664 } 665 666 func (s *ResourceSuite) TestUnitSetterErr(c *gc.C) { 667 r := unitSetter{ 668 ReadCloser: ioutil.NopCloser(&stubReader{stub: s.stub}), 669 persist: &stubPersistence{stub: s.stub}, 670 unit: newUnit(s.stub, "some-service/0"), 671 resource: newUploadResource(c, "res", "res"), 672 } 673 expected := errors.Errorf("some-err") 674 s.stub.SetErrors(expected) 675 // have to try to read non-zero data, or bytes.buffer will happily return 676 // nil. 677 p := make([]byte, 5) 678 n, err := r.Read(p) 679 c.Assert(n, gc.Equals, 0) 680 c.Assert(err, gc.Equals, expected) 681 682 s.stub.CheckCall(c, 0, "Read", p) 683 } 684 685 func newUploadResources(c *gc.C, names ...string) []resource.Resource { 686 var resources []resource.Resource 687 for _, name := range names { 688 res := newUploadResource(c, name, name) 689 resources = append(resources, res) 690 } 691 return resources 692 } 693 694 func newUploadResource(c *gc.C, name, data string) resource.Resource { 695 opened := resourcetesting.NewResource(c, nil, name, "a-service", data) 696 return opened.Resource 697 } 698 699 func newStoreResources(c *gc.C, names ...string) []resource.Resource { 700 var resources []resource.Resource 701 for _, name := range names { 702 res := newStoreResource(c, name, name) 703 resources = append(resources, res) 704 } 705 return resources 706 } 707 708 func newStoreResource(c *gc.C, name, data string) resource.Resource { 709 opened := resourcetesting.NewResource(c, nil, name, "a-service", data) 710 res := opened.Resource 711 res.Origin = charmresource.OriginStore 712 res.Revision = 1 713 res.Username = "" 714 res.Timestamp = time.Time{} 715 return res 716 } 717 718 func newCharmResource(c *gc.C, name, data string, rev int) charmresource.Resource { 719 opened := resourcetesting.NewResource(c, nil, name, "a-service", data) 720 chRes := opened.Resource.Resource 721 chRes.Origin = charmresource.OriginStore 722 chRes.Revision = rev 723 return chRes 724 } 725 726 func newUnit(stub *testing.Stub, name string) *resourcetesting.StubUnit { 727 return &resourcetesting.StubUnit{ 728 Stub: stub, 729 ReturnName: name, 730 ReturnServiceName: strings.Split(name, "/")[0], 731 } 732 }