-
Notifications
You must be signed in to change notification settings - Fork 0
/
charmref.go
175 lines (149 loc) · 5.92 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
165
166
167
168
169
170
171
172
173
174
175
// Copyright 2016 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.
package state
import (
"github.com/juju/errors"
"gopkg.in/juju/charm.v6-unstable"
"gopkg.in/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(st modelBackend, appName string, curl *charm.URL, canCreate bool) ([]txn.Op, error) {
charms, closer := st.getCollection(charmsC)
defer closer()
// 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, closer := st.getCollection(refcountsC)
defer closer()
getIncRefOp := nsRefcounts.CreateOrIncRefOp
if !canCreate {
getIncRefOp = nsRefcounts.StrictIncRefOp
}
settingsKey := applicationSettingsKey(appName, curl)
settingsOp, err := getIncRefOp(refcounts, settingsKey)
if err != nil {
return nil, errors.Annotate(err, "settings reference")
}
storageConstraintsKey := applicationStorageConstraintsKey(appName, curl)
storageConstraintsOp, err := getIncRefOp(refcounts, storageConstraintsKey)
if err != nil {
return nil, errors.Annotate(err, "storage constraints reference")
}
charmKey := charmGlobalKey(curl)
charmOp, err := getIncRefOp(refcounts, charmKey)
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 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.
func appCharmDecRefOps(st modelBackend, appName string, curl *charm.URL) ([]txn.Op, error) {
refcounts, closer := st.getCollection(refcountsC)
defer closer()
charmKey := charmGlobalKey(curl)
charmOp, err := nsRefcounts.AliveDecRefOp(refcounts, charmKey)
if err != nil {
return nil, errors.Annotate(err, "charm reference")
}
settingsKey := applicationSettingsKey(appName, curl)
settingsOp, isFinal, err := nsRefcounts.DyingDecRefOp(refcounts, settingsKey)
if err != nil {
return nil, errors.Annotatef(err, "settings reference %s", settingsKey)
}
storageConstraintsKey := applicationStorageConstraintsKey(appName, curl)
storageConstraintsOp, _, err := nsRefcounts.DyingDecRefOp(refcounts, storageConstraintsKey)
if err != nil {
return nil, errors.Annotatef(err, "storage constraints reference %s", storageConstraintsKey)
}
ops := []txn.Op{settingsOp, storageConstraintsOp, charmOp}
if isFinal {
// 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 constraints documents and queue a charm cleanup.
func finalAppCharmRemoveOps(appName string, curl *charm.URL) []txn.Op {
settingsKey := applicationSettingsKey(appName, curl)
removeSettingsOp := txn.Op{
C: settingsC,
Id: settingsKey,
Remove: true,
}
storageConstraintsKey := applicationStorageConstraintsKey(appName, curl)
removeStorageConstraintsOp := removeStorageConstraintsOp(storageConstraintsKey)
cleanupOp := newCleanupOp(cleanupCharm, curl.String())
return []txn.Op{removeSettingsOp, removeStorageConstraintsOp, cleanupOp}
}
// charmDestroyOps implements the logic of charm.Destroy.
func charmDestroyOps(st modelBackend, curl *charm.URL) ([]txn.Op, error) {
if curl.Schema != "local" {
// local charms keep a document around to prevent reuse
// of charm URLs, which several components believe to be
// unique keys (this is always true within a model).
//
// it's not so much that it's bad to delete store
// charms; but we don't have a way to reinstate them
// once purged, so we don't allow removal in the first
// place.
return nil, errors.New("cannot destroy non-local charms")
}
charms, closer := st.getCollection(charmsC)
defer closer()
charmKey := curl.String()
charmOp, err := nsLife.destroyOp(charms, charmKey, nil)
if err != nil {
return nil, errors.Annotate(err, "charm")
}
refcounts, closer := st.getCollection(refcountsC)
defer closer()
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
}
// charmRemoveOps implements the logic of charm.Remove.
func charmRemoveOps(st modelBackend, curl *charm.URL) ([]txn.Op, error) {
charms, closer := st.getCollection(charmsC)
defer closer()
// NOTE: we do *not* actually remove the charm document, to
// prevent its URL from being recycled, and breaking caches.
// The "remove" terminology refers to the client's view of the
// change (after which the charm really will be inaccessible).
charmKey := curl.String()
charmOp, err := nsLife.dieOp(charms, charmKey, nil)
if err != nil {
return nil, errors.Annotate(err, "charm")
}
return []txn.Op{charmOp}, nil
}