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