445
445
return coll, id, nil
448
// AddCharm adds the ch charm with curl to the state. bundleUrl must be
449
// set to a URL where the bundle for ch may be downloaded from.
450
// On success the newly added charm state is returned.
448
// AddCharm adds the ch charm with curl to the state. bundleURL must
449
// be set to a URL where the bundle for ch may be downloaded from. On
450
// success the newly added charm state is returned.
451
451
func (st *State) AddCharm(ch charm.Charm, curl *charm.URL, bundleURL *url.URL, bundleSha256 string) (stch *Charm, err error) {
452
// The charm may already exist in state as a placeholder, so we check for that situation and update the
453
// existing charm record if necessary, otherwise add a new record.
452
// The charm may already exist in state as a placeholder, so we
453
// check for that situation and update the existing charm record
454
// if necessary, otherwise add a new record.
454
455
var existing charmDoc
455
456
err = st.charms.Find(D{{"_id", curl.String()}, {"placeholder", true}}).One(&existing)
456
457
if err == mgo.ErrNotFound {
469
470
} else if err != nil {
472
return st.updateCharmDoc(ch, curl, bundleURL, bundleSha256, StillPlaceholder)
473
return st.updateCharmDoc(ch, curl, bundleURL, bundleSha256, stillPlaceholder)
475
// Charm returns the charm with the given URL.
476
// Charm returns the charm with the given URL. Charms pending upload
477
// to storage and placeholders are never returned.
476
478
func (st *State) Charm(curl *charm.URL) (*Charm, error) {
477
479
cdoc := &charmDoc{}
478
err := st.charms.Find(D{{"_id", curl}, {"pendingupload", false}, {"placeholder", false}}).One(cdoc)
482
{"placeholder", D{{"$ne", true}}},
483
{"pendingupload", D{{"$ne", true}}},
485
err := st.charms.Find(what).One(&cdoc)
479
486
if err == mgo.ErrNotFound {
480
487
return nil, errors.NotFoundf("charm %q", curl)
488
495
return newCharm(st, cdoc)
491
// LatestPlaceholderCharm returns the latest charm described by the given URL but which is not yet deployed.
498
// LatestPlaceholderCharm returns the latest charm described by the
499
// given URL but which is not yet deployed.
492
500
func (st *State) LatestPlaceholderCharm(curl *charm.URL) (*Charm, error) {
493
501
noRevURL := curl.WithRevision(-1)
494
502
curlRegex := "^" + regexp.QuoteMeta(noRevURL.String())
510
518
return newCharm(st, &latest)
513
// PrepareLocalCharmUpload must be called before a charm is uploaded
514
// to the provider storage in order to create a charm document in
515
// state. It returns a new Charm with no metadata and a unique
521
// PrepareLocalCharmUpload must be called before a local charm is
522
// uploaded to the provider storage in order to create a charm
523
// document in state. It returns the chosen unique charm URL reserved
524
// in state for the charm.
518
526
// The url's schema must be "local" and it must include a revision.
519
527
func (st *State) PrepareLocalCharmUpload(curl *charm.URL) (chosenUrl *charm.URL, err error) {
575
583
return chosenUrl, nil
578
var StillPending = D{{"pendingupload", true}}
579
var StillPlaceholder = D{{"placeholder", true}}
586
// PrepareStoreCharmUpload must be called before a charm store charm
587
// is uploaded to the provider storage in order to create a charm
588
// document in state. If a charm with the same URL is already in
589
// state, it will be returned as a *state.Charm (is can be still
590
// pending or already uploaded). Otherwise, a new charm document is
591
// added in state with just the given charm URL and
592
// PendingUpload=true, which is then returned as a *state.Charm.
594
// The url's schema must be "cs" and it must include a revision.
595
func (st *State) PrepareStoreCharmUpload(curl *charm.URL) (*Charm, error) {
596
// Perform a few sanity checks first.
597
if curl.Schema != "cs" {
598
return nil, fmt.Errorf("expected charm URL with cs schema, got %q", curl)
600
if curl.Revision < 0 {
601
return nil, fmt.Errorf("expected charm URL with revision, got %q", curl)
605
uploadedCharm charmDoc
608
for attempt := 0; attempt < 3; attempt++ {
609
// Find an uploaded or pending charm with the given exact curl.
610
err = st.charms.FindId(curl).One(&uploadedCharm)
611
if err != nil && err != mgo.ErrNotFound {
613
} else if err == nil && !uploadedCharm.Placeholder {
614
// The charm exists and it's either uploaded or still
615
// pending, but it's not a placeholder. In any case, we
616
// just return what we got.
617
return newCharm(st, &uploadedCharm)
618
} else if err == mgo.ErrNotFound {
619
// Prepare the pending charm document for insertion.
620
uploadedCharm = charmDoc{
628
if uploadedCharm.Placeholder {
629
// Convert the placeholder to a pending charm, while
630
// asserting the fields updated after an upload have not
636
{"bundlesha256", ""},
637
{"pendingupload", false},
638
{"placeholder", true},
640
Update: D{{"$set", D{
641
{"pendingupload", true},
642
{"placeholder", false},
645
// Update the fields of the document we're returning.
646
uploadedCharm.PendingUpload = true
647
uploadedCharm.Placeholder = false
649
// No charm document with this curl yet, insert it.
653
Assert: txn.DocMissing,
654
Insert: uploadedCharm,
658
// Run the transaction, and retry on abort.
659
err = st.runTransaction(ops)
660
if err == txn.ErrAborted {
662
} else if err != nil {
664
} else if err == nil {
665
return newCharm(st, &uploadedCharm)
668
return nil, ErrExcessiveContention
672
stillPending = D{{"pendingupload", true}}
673
stillPlaceholder = D{{"placeholder", true}}
581
676
// AddStoreCharmPlaceholder creates a charm document in state for the given charm URL which
582
677
// must reference a charm from the store. The charm document is marked as a placeholder which
653
748
ops = append(ops, txn.Op{
654
749
C: st.charms.Name,
655
750
Id: doc.URL.String(),
656
Assert: StillPlaceholder,
751
Assert: stillPlaceholder,
758
// ErrCharmAlreadyUploaded is returned by UpdateUploadedCharm() when
759
// the given charm is already uploaded and marked as not pending in
761
type ErrCharmAlreadyUploaded struct {
765
func (e *ErrCharmAlreadyUploaded) Error() string {
766
return fmt.Sprintf("charm %q already uploaded", e.curl)
769
// IsCharmAlreadyUploadedError returns if the given error is
770
// ErrCharmAlreadyUploaded.
771
func IsCharmAlreadyUploadedError(err interface{}) bool {
775
_, ok := err.(*ErrCharmAlreadyUploaded)
779
// ErrCharmRevisionAlreadyModified is returned when a pending or
780
// placeholder charm is no longer pending or a placeholder, signaling
781
// the charm is available in state with its full information.
782
var ErrCharmRevisionAlreadyModified = fmt.Errorf("charm revision already modified")
663
784
// UpdateUploadedCharm marks the given charm URL as uploaded and
664
785
// updates the rest of its data, returning it as *state.Charm.
665
786
func (st *State) UpdateUploadedCharm(ch charm.Charm, curl *charm.URL, bundleURL *url.URL, bundleSha256 string) (*Charm, error) {
674
795
if !doc.PendingUpload {
675
return nil, fmt.Errorf("charm %q already uploaded", curl)
796
return nil, &ErrCharmAlreadyUploaded{curl}
678
return st.updateCharmDoc(ch, curl, bundleURL, bundleSha256, StillPending)
799
return st.updateCharmDoc(ch, curl, bundleURL, bundleSha256, stillPending)
681
// updateCharmDoc updates the charm with specified URL with the given data, and resets the placeholder
682
// and pendingupdate flags.
802
// updateCharmDoc updates the charm with specified URL with the given
803
// data, and resets the placeholder and pendingupdate flags. If the
804
// charm is no longer a placeholder or pending (depending on preReq),
805
// it returns ErrCharmRevisionAlreadyModified.
683
806
func (st *State) updateCharmDoc(
684
807
ch charm.Charm, curl *charm.URL, bundleURL *url.URL, bundleSha256 string, preReq interface{}) (*Charm, error) {