1
// Copyright 2015 Canonical Ltd.
2
// Licensed under the LGPLv3, see LICENCE file for details.
9
"gopkg.in/juju/charm.v6-unstable"
12
// FromData generates and returns the list of changes required to deploy the
13
// given bundle data. The changes are sorted by requirements, so that they can
14
// be applied in order. The bundle data is assumed to be already verified.
15
func FromData(data *charm.BundleData) []Change {
17
addedApplications := handleApplications(cs.add, data.Applications, data.Series)
18
addedMachines := handleMachines(cs.add, data.Machines, data.Series)
19
handleRelations(cs.add, data.Relations, addedApplications)
20
handleUnits(cs.add, data.Applications, addedApplications, addedMachines, data.Series)
24
// Change holds a single change required to deploy a bundle.
25
type Change interface {
26
// Id returns the unique identifier for this change.
28
// Requires returns the ids of all the changes that must
29
// be applied before this one.
31
// Method returns the action to be performed to apply this change.
33
// GUIArgs returns positional arguments to pass to the method, suitable for
34
// being JSON-serialized and sent to the Juju GUI.
35
GUIArgs() []interface{}
36
// setId is used to set the identifier for the change.
40
// changeInfo holds information on a change, suitable for embedding into a more
41
// specific change type.
42
type changeInfo struct {
48
// Id implements Change.Id.
49
func (ch *changeInfo) Id() string {
53
// Requires implements Change.Requires.
54
func (ch *changeInfo) Requires() []string {
55
// Avoid returning a nil interface because so that avoid returning a slice
56
// that will serialize to JSON null.
57
if ch.requires == nil {
63
// Method implements Change.Method.
64
func (ch *changeInfo) Method() string {
68
// setId implements Change.setId.
69
func (ch *changeInfo) setId(id string) {
73
// newAddCharmChange creates a new change for adding a charm.
74
func newAddCharmChange(params AddCharmParams, requires ...string) *AddCharmChange {
75
return &AddCharmChange{
76
changeInfo: changeInfo{
84
// AddCharmChange holds a change for adding a charm to the environment.
85
type AddCharmChange struct {
87
// Params holds parameters for adding a charm.
91
// GUIArgs implements Change.GUIArgs.
92
func (ch *AddCharmChange) GUIArgs() []interface{} {
93
return []interface{}{ch.Params.Charm, ch.Params.Series}
96
// AddCharmParams holds parameters for adding a charm to the environment.
97
type AddCharmParams struct {
98
// Charm holds the URL of the charm to be added.
100
// Series holds the series of the charm to be added
101
// if the charm default is not sufficient.
105
// newAddMachineChange creates a new change for adding a machine or container.
106
func newAddMachineChange(params AddMachineParams, requires ...string) *AddMachineChange {
107
return &AddMachineChange{
108
changeInfo: changeInfo{
110
method: "addMachines",
116
// AddMachineChange holds a change for adding a machine or container.
117
type AddMachineChange struct {
119
// Params holds parameters for adding a machine.
120
Params AddMachineParams
123
// GUIArgs implements Change.GUIArgs.
124
func (ch *AddMachineChange) GUIArgs() []interface{} {
125
options := AddMachineOptions{
126
Series: ch.Params.Series,
127
Constraints: ch.Params.Constraints,
128
ContainerType: ch.Params.ContainerType,
129
ParentId: ch.Params.ParentId,
131
return []interface{}{options}
134
// AddMachineOptions holds GUI options for adding a machine or container.
135
type AddMachineOptions struct {
136
// Series holds the machine OS series.
137
Series string `json:"series,omitempty"`
138
// Constraints holds the machine constraints.
139
Constraints string `json:"constraints,omitempty"`
140
// ContainerType holds the machine container type (like "lxc" or "kvm").
141
ContainerType string `json:"containerType,omitempty"`
142
// ParentId holds the id of the parent machine.
143
ParentId string `json:"parentId,omitempty"`
146
// AddMachineParams holds parameters for adding a machine or container.
147
type AddMachineParams struct {
148
// Series holds the optional machine OS series.
150
// Constraints holds the optional machine constraints.
152
// ContainerType optionally holds the type of the container (for instance
153
// ""lxc" or kvm"). It is not specified for top level machines.
155
// ParentId optionally holds a placeholder pointing to another machine
156
// change or to a unit change. This value is only specified in the case
157
// this machine is a container, in which case also ContainerType is set.
161
// newAddRelationChange creates a new change for adding a relation.
162
func newAddRelationChange(params AddRelationParams, requires ...string) *AddRelationChange {
163
return &AddRelationChange{
164
changeInfo: changeInfo{
166
method: "addRelation",
172
// AddRelationChange holds a change for adding a relation between two applications.
173
type AddRelationChange struct {
175
// Params holds parameters for adding a relation.
176
Params AddRelationParams
179
// GUIArgs implements Change.GUIArgs.
180
func (ch *AddRelationChange) GUIArgs() []interface{} {
181
return []interface{}{ch.Params.Endpoint1, ch.Params.Endpoint2}
184
// AddRelationParams holds parameters for adding a relation between two applications.
185
type AddRelationParams struct {
186
// Endpoint1 and Endpoint2 hold relation endpoints in the
187
// "application:interface" form, where the application is always a placeholder
188
// pointing to an application change, and the interface is optional. Examples
189
// are "$deploy-42:web" or just "$deploy-42".
194
// newAddApplicationChange creates a new change for adding an application.
195
func newAddApplicationChange(params AddApplicationParams, requires ...string) *AddApplicationChange {
196
return &AddApplicationChange{
197
changeInfo: changeInfo{
205
// AddApplicationChange holds a change for deploying a Juju application.
206
type AddApplicationChange struct {
208
// Params holds parameters for adding an application.
209
Params AddApplicationParams
212
// GUIArgs implements Change.GUIArgs.
213
func (ch *AddApplicationChange) GUIArgs() []interface{} {
214
options := ch.Params.Options
216
options = make(map[string]interface{}, 0)
218
storage := ch.Params.Storage
220
storage = make(map[string]string, 0)
222
endpointBindings := ch.Params.EndpointBindings
223
if endpointBindings == nil {
224
endpointBindings = make(map[string]string, 0)
226
resources := ch.Params.Resources
227
if resources == nil {
228
resources = make(map[string]int, 0)
230
return []interface{}{
233
ch.Params.Application,
235
ch.Params.Constraints,
242
// AddApplicationParams holds parameters for deploying a Juju application.
243
type AddApplicationParams struct {
244
// Charm holds the URL of the charm to be used to deploy this application.
246
// Series holds the series of the application to be deployed
247
// if the charm default is not sufficient.
249
// Application holds the application name.
251
// Options holds application options.
252
Options map[string]interface{}
253
// Constraints holds the optional application constraints.
255
// Storage holds the optional storage constraints.
256
Storage map[string]string
257
// EndpointBindings holds the optional endpoint bindings
258
EndpointBindings map[string]string
259
// Resources identifies the revision to use for each resource
260
// of the application's charm.
261
Resources map[string]int
264
// newAddUnitChange creates a new change for adding an application unit.
265
func newAddUnitChange(params AddUnitParams, requires ...string) *AddUnitChange {
266
return &AddUnitChange{
267
changeInfo: changeInfo{
275
// AddUnitChange holds a change for adding an application unit.
276
type AddUnitChange struct {
278
// Params holds parameters for adding a unit.
282
// GUIArgs implements Change.GUIArgs.
283
func (ch *AddUnitChange) GUIArgs() []interface{} {
284
args := []interface{}{ch.Params.Application, nil}
285
if ch.Params.To != "" {
286
args[1] = ch.Params.To
291
// AddUnitParams holds parameters for adding an application unit.
292
type AddUnitParams struct {
293
// Application holds the application placeholder name for which a unit is added.
295
// To holds the optional location where to add the unit, as a placeholder
296
// pointing to another unit change or to a machine change.
300
// newExposeChange creates a new change for exposing an application.
301
func newExposeChange(params ExposeParams, requires ...string) *ExposeChange {
302
return &ExposeChange{
303
changeInfo: changeInfo{
311
// ExposeChange holds a change for exposing an application.
312
type ExposeChange struct {
314
// Params holds parameters for exposing an application.
318
// GUIArgs implements Change.GUIArgs.
319
func (ch *ExposeChange) GUIArgs() []interface{} {
320
return []interface{}{ch.Params.Application}
323
// ExposeParams holds parameters for exposing an application.
324
type ExposeParams struct {
325
// Application holds the placeholder name of the application that must be exposed.
329
// newSetAnnotationsChange creates a new change for setting annotations.
330
func newSetAnnotationsChange(params SetAnnotationsParams, requires ...string) *SetAnnotationsChange {
331
return &SetAnnotationsChange{
332
changeInfo: changeInfo{
334
method: "setAnnotations",
340
// SetAnnotationsChange holds a change for setting application and machine
342
type SetAnnotationsChange struct {
344
// Params holds parameters for setting annotations.
345
Params SetAnnotationsParams
348
// GUIArgs implements Change.GUIArgs.
349
func (ch *SetAnnotationsChange) GUIArgs() []interface{} {
350
return []interface{}{ch.Params.Id, string(ch.Params.EntityType), ch.Params.Annotations}
353
// EntityType holds entity types ("application" or "machine").
354
type EntityType string
357
ApplicationType EntityType = "application"
358
MachineType EntityType = "machine"
361
// SetAnnotationsParams holds parameters for setting annotations.
362
type SetAnnotationsParams struct {
363
// Id is the placeholder for the application or machine change corresponding to
364
// the entity to be annotated.
366
// EntityType holds the type of the entity, "application" or "machine".
367
EntityType EntityType
368
// Annotations holds the annotations as key/value pairs.
369
Annotations map[string]string
372
// changeset holds the list of changes returned by FromData.
373
type changeset struct {
377
// add adds the given change to this change set.
378
func (cs *changeset) add(change Change) {
379
change.setId(fmt.Sprintf("%s-%d", change.Method(), len(cs.changes)))
380
cs.changes = append(cs.changes, change)
383
// sorted returns the changes sorted by requirements, required first.
384
func (cs *changeset) sorted() []Change {
385
numChanges := len(cs.changes)
386
records := make(map[string]bool, numChanges)
387
sorted := make([]Change, 0, numChanges)
388
changes := make([]Change, numChanges, numChanges*2)
389
copy(changes, cs.changes)
391
for len(changes) != 0 {
392
// Note that all valid bundles have at least two changes
393
// (add one charm and deploy one application).
395
changes = changes[1:]
396
for _, r := range change.Requires() {
398
// This change requires a change which is not yet listed.
399
// Push this change at the end of the list and retry later.
400
changes = append(changes, change)
404
records[change.Id()] = true
405
sorted = append(sorted, change)