~juju-qa/ubuntu/xenial/juju/2.0-rc2

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/state/charmref.go

  • Committer: Nicholas Skaggs
  • Date: 2016-09-30 14:39:30 UTC
  • mfrom: (1.8.1)
  • Revision ID: nicholas.skaggs@canonical.com-20160930143930-vwwhrefh6ftckccy
import upstream

Show diffs side-by-side

added added

removed removed

Lines of Context:
9
9
        "gopkg.in/mgo.v2/txn"
10
10
)
11
11
 
12
 
func charmIncRefOps(st modelBackend, appName string, curl *charm.URL, canCreate bool) ([]txn.Op, error) {
 
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
 
13
36
        refcounts, closer := st.getCollection(refcountsC)
14
37
        defer closer()
15
38
 
17
40
        if !canCreate {
18
41
                getIncRefOp = nsRefcounts.StrictIncRefOp
19
42
        }
20
 
 
21
43
        settingsKey := applicationSettingsKey(appName, curl)
22
 
        settingsOp, err := getIncRefOp(refcounts, settingsKey)
23
 
        if err != nil {
24
 
                return nil, errors.Trace(err)
 
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")
25
52
        }
26
53
        charmKey := charmGlobalKey(curl)
27
 
        charmOp, err := getIncRefOp(refcounts, charmKey)
 
54
        charmOp, err := getIncRefOp(refcounts, charmKey, 1)
28
55
        if err != nil {
29
 
                return nil, errors.Trace(err)
 
56
                return nil, errors.Annotate(err, "charm reference")
30
57
        }
31
58
 
32
 
        return []txn.Op{
33
 
                settingsOp,
34
 
                charmOp,
35
 
        }, nil
 
59
        return append(checkOps, settingsOp, storageConstraintsOp, charmOp), nil
36
60
}
37
61
 
38
 
func charmDecRefOps(st modelBackend, appName string, curl *charm.URL) ([]txn.Op, error) {
 
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
 
39
71
        refcounts, closer := st.getCollection(refcountsC)
40
72
        defer closer()
41
73
 
42
74
        charmKey := charmGlobalKey(curl)
43
75
        charmOp, err := nsRefcounts.AliveDecRefOp(refcounts, charmKey)
 
76
        if err != nil {
 
77
                return nil, errors.Annotate(err, "charm reference")
 
78
        }
44
79
 
45
80
        settingsKey := applicationSettingsKey(appName, curl)
46
81
        settingsOp, isFinal, err := nsRefcounts.DyingDecRefOp(refcounts, settingsKey)
47
82
        if err != nil {
48
 
                return nil, errors.Trace(err)
49
 
        }
50
 
 
51
 
        ops := []txn.Op{settingsOp, charmOp}
 
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}
52
93
        if isFinal {
53
 
                ops = append(ops, txn.Op{
54
 
                        C:      settingsC,
55
 
                        Id:     settingsKey,
56
 
                        Remove: true,
57
 
                })
 
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)...)
58
100
        }
59
101
        return ops, nil
60
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
}