github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/state/charmref.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package state 5 6 import ( 7 "github.com/juju/errors" 8 "gopkg.in/juju/charm.v6-unstable" 9 "gopkg.in/mgo.v2/txn" 10 ) 11 12 var errCharmInUse = errors.New("charm in use") 13 14 // appCharmIncRefOps returns the operations necessary to record a reference 15 // to a charm and its per-application settings and storage constraints 16 // documents. It will fail if the charm is not Alive. 17 func appCharmIncRefOps(st modelBackend, appName string, curl *charm.URL, canCreate bool) ([]txn.Op, error) { 18 19 charms, closer := st.getCollection(charmsC) 20 defer closer() 21 22 // If we're migrating. charm document will not be present. But 23 // if we're not migrating, we need to check the charm is alive. 24 var checkOps []txn.Op 25 count, err := charms.FindId(curl.String()).Count() 26 if err != nil { 27 return nil, errors.Annotate(err, "charm") 28 } else if count != 0 { 29 checkOp, err := nsLife.aliveOp(charms, curl.String()) 30 if err != nil { 31 return nil, errors.Annotate(err, "charm") 32 } 33 checkOps = []txn.Op{checkOp} 34 } 35 36 refcounts, closer := st.getCollection(refcountsC) 37 defer closer() 38 39 getIncRefOp := nsRefcounts.CreateOrIncRefOp 40 if !canCreate { 41 getIncRefOp = nsRefcounts.StrictIncRefOp 42 } 43 settingsKey := applicationSettingsKey(appName, curl) 44 settingsOp, err := getIncRefOp(refcounts, settingsKey, 1) 45 if err != nil { 46 return nil, errors.Annotate(err, "settings reference") 47 } 48 storageConstraintsKey := applicationStorageConstraintsKey(appName, curl) 49 storageConstraintsOp, err := getIncRefOp(refcounts, storageConstraintsKey, 1) 50 if err != nil { 51 return nil, errors.Annotate(err, "storage constraints reference") 52 } 53 charmKey := charmGlobalKey(curl) 54 charmOp, err := getIncRefOp(refcounts, charmKey, 1) 55 if err != nil { 56 return nil, errors.Annotate(err, "charm reference") 57 } 58 59 return append(checkOps, settingsOp, storageConstraintsOp, charmOp), nil 60 } 61 62 // appCharmDecRefOps returns the operations necessary to delete a 63 // reference to a charm and its per-application settings and storage 64 // constraints document. If no references to a given (app, charm) pair 65 // remain, the operations returned will also remove the settings and 66 // storage constraints documents for that pair, and schedule a cleanup 67 // to see if the charm itself is now unreferenced and can be tidied 68 // away itself. 69 func appCharmDecRefOps(st modelBackend, appName string, curl *charm.URL) ([]txn.Op, error) { 70 71 refcounts, closer := st.getCollection(refcountsC) 72 defer closer() 73 74 charmKey := charmGlobalKey(curl) 75 charmOp, err := nsRefcounts.AliveDecRefOp(refcounts, charmKey) 76 if err != nil { 77 return nil, errors.Annotate(err, "charm reference") 78 } 79 80 settingsKey := applicationSettingsKey(appName, curl) 81 settingsOp, isFinal, err := nsRefcounts.DyingDecRefOp(refcounts, settingsKey) 82 if err != nil { 83 return nil, errors.Annotatef(err, "settings reference %s", settingsKey) 84 } 85 86 storageConstraintsKey := applicationStorageConstraintsKey(appName, curl) 87 storageConstraintsOp, _, err := nsRefcounts.DyingDecRefOp(refcounts, storageConstraintsKey) 88 if err != nil { 89 return nil, errors.Annotatef(err, "storage constraints reference %s", storageConstraintsKey) 90 } 91 92 ops := []txn.Op{settingsOp, storageConstraintsOp, charmOp} 93 if isFinal { 94 // XXX(fwereade): this construction, in common with ~all 95 // our refcount logic, is safe in parallel but not in 96 // serial. If this logic is used twice while composing a 97 // single transaction, the removal won't be triggered. 98 // see `Application.removeOps` for the workaround. 99 ops = append(ops, finalAppCharmRemoveOps(appName, curl)...) 100 } 101 return ops, nil 102 } 103 104 // finalAppCharmRemoveOps returns operations to delete the settings 105 // and storage constraints documents and queue a charm cleanup. 106 func finalAppCharmRemoveOps(appName string, curl *charm.URL) []txn.Op { 107 settingsKey := applicationSettingsKey(appName, curl) 108 removeSettingsOp := txn.Op{ 109 C: settingsC, 110 Id: settingsKey, 111 Remove: true, 112 } 113 storageConstraintsKey := applicationStorageConstraintsKey(appName, curl) 114 removeStorageConstraintsOp := removeStorageConstraintsOp(storageConstraintsKey) 115 cleanupOp := newCleanupOp(cleanupCharm, curl.String()) 116 return []txn.Op{removeSettingsOp, removeStorageConstraintsOp, cleanupOp} 117 } 118 119 // charmDestroyOps implements the logic of charm.Destroy. 120 func charmDestroyOps(st modelBackend, curl *charm.URL) ([]txn.Op, error) { 121 122 if curl.Schema != "local" { 123 // local charms keep a document around to prevent reuse 124 // of charm URLs, which several components believe to be 125 // unique keys (this is always true within a model). 126 // 127 // it's not so much that it's bad to delete store 128 // charms; but we don't have a way to reinstate them 129 // once purged, so we don't allow removal in the first 130 // place. 131 return nil, errors.New("cannot destroy non-local charms") 132 } 133 134 charms, closer := st.getCollection(charmsC) 135 defer closer() 136 137 charmKey := curl.String() 138 charmOp, err := nsLife.destroyOp(charms, charmKey, nil) 139 if err != nil { 140 return nil, errors.Annotate(err, "charm") 141 } 142 143 refcounts, closer := st.getCollection(refcountsC) 144 defer closer() 145 146 refcountKey := charmGlobalKey(curl) 147 refcountOp, err := nsRefcounts.RemoveOp(refcounts, refcountKey, 0) 148 switch errors.Cause(err) { 149 case nil: 150 case errRefcountChanged: 151 return nil, errCharmInUse 152 default: 153 return nil, errors.Annotate(err, "charm reference") 154 } 155 156 return []txn.Op{charmOp, refcountOp}, nil 157 } 158 159 // charmRemoveOps implements the logic of charm.Remove. 160 func charmRemoveOps(st modelBackend, curl *charm.URL) ([]txn.Op, error) { 161 162 charms, closer := st.getCollection(charmsC) 163 defer closer() 164 165 // NOTE: we do *not* actually remove the charm document, to 166 // prevent its URL from being recycled, and breaking caches. 167 // The "remove" terminology refers to the client's view of the 168 // change (after which the charm really will be inaccessible). 169 charmKey := curl.String() 170 charmOp, err := nsLife.dieOp(charms, charmKey, nil) 171 if err != nil { 172 return nil, errors.Annotate(err, "charm") 173 } 174 return []txn.Op{charmOp}, nil 175 }