-
Notifications
You must be signed in to change notification settings - Fork 0
/
charmref.go
169 lines (143 loc) · 5.59 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
// 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, 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 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" {
// 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()
charmKey := curl.String()
// Remove the charm document as long as the charm is dying.
charmOp, err := nsLife.dyingOp(charms, charmKey)
if err != nil {
return nil, errors.Annotate(err, "charm")
}
charmOp.Remove = true
return []txn.Op{charmOp}, nil
}