github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/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 "github.com/juju/mgo/v3/txn" 9 ) 10 11 var errCharmInUse = errors.New("charm in use") 12 13 // appCharmIncRefOps returns the operations necessary to record a reference 14 // to a charm and its per-application settings and storage constraints 15 // documents. It will fail if the charm is not Alive. 16 func appCharmIncRefOps(mb modelBackend, appName string, cURL *string, canCreate bool) ([]txn.Op, error) { 17 charms, cCloser := mb.db().GetCollection(charmsC) 18 defer cCloser() 19 20 // If we're migrating. charm document will not be present. But 21 // if we're not migrating, we need to check the charm is alive. 22 var checkOps []txn.Op 23 count, err := charms.FindId(*cURL).Count() 24 if err != nil { 25 return nil, errors.Annotate(err, "charm") 26 } else if count != 0 { 27 checkOp, err := nsLife.aliveOp(charms, *cURL) 28 if err != nil { 29 return nil, errors.Annotate(err, "charm") 30 } 31 checkOps = []txn.Op{checkOp} 32 } 33 34 refcounts, rCloser := mb.db().GetCollection(refcountsC) 35 defer rCloser() 36 37 getIncRefOp := nsRefcounts.CreateOrIncRefOp 38 if !canCreate { 39 getIncRefOp = nsRefcounts.StrictIncRefOp 40 } 41 settingsKey := applicationCharmConfigKey(appName, cURL) 42 settingsOp, err := getIncRefOp(refcounts, settingsKey, 1) 43 if err != nil { 44 return nil, errors.Annotate(err, "settings reference") 45 } 46 storageConstraintsKey := applicationStorageConstraintsKey(appName, cURL) 47 storageConstraintsOp, err := getIncRefOp(refcounts, storageConstraintsKey, 1) 48 if err != nil { 49 return nil, errors.Annotate(err, "storage constraints reference") 50 } 51 charmKey := charmGlobalKey(cURL) 52 charmOp, err := getIncRefOp(refcounts, charmKey, 1) 53 if err != nil { 54 return nil, errors.Annotate(err, "charm reference") 55 } 56 57 return append(checkOps, settingsOp, storageConstraintsOp, charmOp), nil 58 } 59 60 // appCharmDecRefOps returns the operations necessary to delete a 61 // reference to a charm and its per-application settings and storage 62 // constraints document. 63 // If maybeDoFinal is true, and no references to a given (app, charm) pair 64 // remain, the operations returned will also remove the settings and 65 // storage constraints documents for that pair, and schedule a cleanup 66 // to see if the charm itself is now unreferenced and can be tidied 67 // away itself. 68 // When 'force' is set, this call will return some, if not all, needed operations 69 // and will accumulate operational errors encountered in the operation. 70 // If the 'force' is not set, any error will be fatal and no operations will be returned. 71 func appCharmDecRefOps(st modelBackend, appName string, cURL *string, maybeDoFinal bool, op *ForcedOperation) ([]txn.Op, error) { 72 refcounts, closer := st.db().GetCollection(refcountsC) 73 defer closer() 74 75 fail := func(e error) ([]txn.Op, error) { 76 return nil, errors.Trace(e) 77 } 78 ops := []txn.Op{} 79 charmKey := charmGlobalKey(cURL) 80 charmOp, err := nsRefcounts.AliveDecRefOp(refcounts, charmKey) 81 if op.FatalError(err) { 82 return fail(errors.Annotate(err, "charm reference")) 83 } 84 if err == nil { 85 ops = append(ops, charmOp) 86 } 87 88 settingsKey := applicationCharmConfigKey(appName, cURL) 89 settingsOp, isFinal, err := nsRefcounts.DyingDecRefOp(refcounts, settingsKey) 90 if op.FatalError(err) { 91 return fail(errors.Annotatef(err, "settings reference %s", settingsKey)) 92 } 93 if err == nil { 94 ops = append(ops, settingsOp) 95 } 96 97 storageConstraintsKey := applicationStorageConstraintsKey(appName, cURL) 98 storageConstraintsOp, _, err := nsRefcounts.DyingDecRefOp(refcounts, storageConstraintsKey) 99 if op.FatalError(err) { 100 return fail(errors.Annotatef(err, "storage constraints reference %s", storageConstraintsKey)) 101 } 102 if err == nil { 103 ops = append(ops, storageConstraintsOp) 104 } 105 106 if isFinal && maybeDoFinal { 107 // XXX(fwereade): this construction, in common with ~all 108 // our refcount logic, is safe in parallel but not in 109 // serial. If this logic is used twice while composing a 110 // single transaction, the removal won't be triggered. 111 // see `Application.removeOps` for the workaround. 112 ops = append(ops, finalAppCharmRemoveOps(appName, cURL)...) 113 } 114 return ops, nil 115 } 116 117 // finalAppCharmRemoveOps returns operations to delete the settings 118 // and storage, device constraints documents and queue a charm cleanup. 119 func finalAppCharmRemoveOps(appName string, curl *string) []txn.Op { 120 settingsKey := applicationCharmConfigKey(appName, curl) 121 removeSettingsOp := txn.Op{ 122 C: settingsC, 123 Id: settingsKey, 124 Remove: true, 125 } 126 // ensure removing storage constraints doc 127 storageConstraintsKey := applicationStorageConstraintsKey(appName, curl) 128 removeStorageConstraintsOp := removeStorageConstraintsOp(storageConstraintsKey) 129 // ensure removing device constraints doc 130 deviceConstraintsKey := applicationDeviceConstraintsKey(appName, curl) 131 removeDeviceConstraintsOp := removeDeviceConstraintsOp(deviceConstraintsKey) 132 133 cleanupOp := newCleanupOp(cleanupCharm, *curl) 134 return []txn.Op{removeSettingsOp, removeStorageConstraintsOp, removeDeviceConstraintsOp, cleanupOp} 135 } 136 137 // charmDestroyOps implements the logic of charm.Destroy. 138 func charmDestroyOps(st modelBackend, curl string) ([]txn.Op, error) { 139 if curl == "" { 140 return nil, errors.BadRequestf("curl is empty") 141 } 142 db := st.db() 143 charms, cCloser := db.GetCollection(charmsC) 144 defer cCloser() 145 146 charmOp, err := nsLife.destroyOp(charms, curl, nil) 147 if err != nil { 148 return nil, errors.Annotate(err, "charm") 149 } 150 151 refcounts, rCloser := db.GetCollection(refcountsC) 152 defer rCloser() 153 154 refcountKey := charmGlobalKey(&curl) 155 refcountOp, err := nsRefcounts.RemoveOp(refcounts, refcountKey, 0) 156 switch errors.Cause(err) { 157 case nil: 158 case errRefcountChanged: 159 return nil, errCharmInUse 160 default: 161 return nil, errors.Annotate(err, "charm reference") 162 } 163 164 return []txn.Op{charmOp, refcountOp}, nil 165 }