~arosales/juju-core/update-azure-boilerplate

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
// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package state

import (
	"strings"

	"labix.org/v2/mgo/txn"

	"launchpad.net/juju-core/instance"
)

// machineContainers holds the machine ids of all the containers belonging to a parent machine.
// All machines have an associated container ref doc, regardless of whether they host any containers.
type machineContainers struct {
	Id       string   `bson:"_id"`
	Children []string `bson:",omitempty"`
}

// createContainerRefOp the txn.Op's that update a parent machine's container references to add
// a new container with the specified container id. If no container id is specified, an empty
// machineContainers doc is created.
func createContainerRefOp(st *State, params *containerRefParams) []txn.Op {
	// See if we are creating a parent machine to subsequently host a new container. In this case,
	// the container ref doc will be created later once the container id is known.
	if !params.hostOnly && params.containerId == "" {
		return []txn.Op{}
	}
	if params.hostOnly {
		// Create an empty containers reference for a new host machine which will have no containers.
		return []txn.Op{
			{
				C:      st.containerRefs.Name,
				Id:     params.hostId,
				Assert: txn.DocMissing,
				Insert: &machineContainers{
					Id: params.hostId,
				},
			},
		}
	}
	var ops []txn.Op
	if params.newHost {
		// If the host machine doesn't exist yet, create a new containers record.
		mc := machineContainers{
			Id:       params.hostId,
			Children: []string{params.containerId},
		}
		ops = []txn.Op{
			{
				C:      st.containerRefs.Name,
				Id:     mc.Id,
				Assert: txn.DocMissing,
				Insert: &mc,
			},
		}
	} else {
		// The host machine exists so update its containers record.
		ops = []txn.Op{
			{
				C:      st.containerRefs.Name,
				Id:     params.hostId,
				Assert: txn.DocExists,
				Update: D{{"$addToSet", D{{"children", params.containerId}}}},
			},
		}
	}
	// Create a containers reference document for the container itself.
	mc := machineContainers{
		Id: params.containerId,
	}
	ops = append(ops, txn.Op{
		C:      st.containerRefs.Name,
		Id:     mc.Id,
		Assert: txn.DocMissing,
		Insert: &mc,
	})
	return ops
}

// ParentId returns the id of the host machine if machineId a container id, or ""
// if machineId is not for a container.
func ParentId(machineId string) string {
	idParts := strings.Split(machineId, "/")
	if len(idParts) < 3 {
		return ""
	}
	return strings.Join(idParts[:len(idParts)-2], "/")
}

// ContainerTypeFromId returns the container type if machineId is a container id, or ""
// if machineId is not for a container.
func ContainerTypeFromId(machineId string) instance.ContainerType {
	idParts := strings.Split(machineId, "/")
	if len(idParts) < 3 {
		return instance.ContainerType("")
	}
	return instance.ContainerType(idParts[len(idParts)-2])
}

// NestingLevel returns how many levels of nesting exist for a machine id.
func NestingLevel(machineId string) int {
	idParts := strings.Split(machineId, "/")
	return (len(idParts) - 1) / 2
}

// TopParentId returns the id of the top level host machine for a container id.
func TopParentId(machineId string) string {
	idParts := strings.Split(machineId, "/")
	return idParts[0]
}

// removeContainerRefOps returns the txn.Op's necessary to remove a machine container record.
// These include removing the record itself and updating the host machine's children property.
func removeContainerRefOps(st *State, machineId string) []txn.Op {
	removeRefOp := txn.Op{
		C:      st.containerRefs.Name,
		Id:     machineId,
		Assert: txn.DocExists,
		Remove: true,
	}
	// If the machine is a container, figure out its parent host.
	parentId := ParentId(machineId)
	if parentId == "" {
		return []txn.Op{removeRefOp}
	}
	removeParentRefOp := txn.Op{
		C:      st.containerRefs.Name,
		Id:     parentId,
		Assert: txn.DocExists,
		Update: D{{"$pull", D{{"children", machineId}}}},
	}
	return []txn.Op{removeRefOp, removeParentRefOp}
}