Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(786)

Side by Side Diff: state/addmachine.go

Issue 28980043: state: factor out addmachine logic
Patch Set: Created 11 years, 5 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « [revision details] ('k') | state/container.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2012, 2013 Canonical Ltd.
2 // Licensed under the AGPLv3, see LICENCE file for details.
3
4 package state
5
6 import (
7 "fmt"
8 "strconv"
9
10 "labix.org/v2/mgo/txn"
11
12 "launchpad.net/juju-core/constraints"
13 "launchpad.net/juju-core/instance"
14 "launchpad.net/juju-core/state/api/params"
15 "launchpad.net/juju-core/utils"
16 )
17
18 // AddMachineParams encapsulates the parameters used to create a new machine.
19 type AddMachineParams struct {
20 Series string
21 Constraints constraints.Value
22 ParentId string
23 ContainerType instance.ContainerType
24 InstanceId instance.Id
25 HardwareCharacteristics instance.HardwareCharacteristics
26 Nonce string
27 Jobs []MachineJob
28 }
29
30 // AddMachine adds a new machine configured to run the supplied jobs on the
31 // supplied series. The machine's constraints will be taken from the
32 // environment constraints.
33 func (st *State) AddMachine(series string, jobs ...MachineJob) (m *Machine, err error) {
34 return st.addMachine(&AddMachineParams{Series: series, Jobs: jobs})
35 }
36
37 // AddMachineWithConstraints adds a new machine configured to run the supplied j obs on the
38 // supplied series. The machine's constraints and other configuration will be ta ken from
39 // the supplied params struct.
40 func (st *State) AddMachineWithConstraints(params *AddMachineParams) (m *Machine , err error) {
41 if params.InstanceId != "" {
42 return nil, fmt.Errorf("cannot specify an instance id when addin g a new machine")
43 }
44 if params.Nonce != "" {
45 return nil, fmt.Errorf("cannot specify a nonce when adding a new machine")
46 }
47
48 // TODO(wallyworld) - if a container is required, and when the actual ma chine characteristics
49 // are made available, we need to check the machine constraints to ensur e the container can be
50 // created on the specifed machine.
51 // ie it makes no sense asking for a 16G container on a machine with 8G.
52
53 return st.addMachine(params)
54 }
55
56 // InjectMachine adds a new machine, corresponding to an existing provider
57 // instance, configured according to the supplied params struct.
58 func (st *State) InjectMachine(params *AddMachineParams) (m *Machine, err error) {
59 if params.InstanceId == "" {
60 return nil, fmt.Errorf("cannot inject a machine without an insta nce id")
61 }
62 if params.Nonce == "" {
63 return nil, fmt.Errorf("cannot inject a machine without a nonce" )
64 }
65 return st.addMachine(params)
66 }
67
68 // addMachine implements AddMachine and InjectMachine.
69 func (st *State) addMachine(params *AddMachineParams) (m *Machine, err error) {
70 msg := "cannot add a new machine"
71 if params.ParentId != "" || params.ContainerType != "" {
72 msg = "cannot add a new container"
73 }
74 defer utils.ErrorContextf(&err, msg)
75
76 cons, err := st.EnvironConstraints()
77 if err != nil {
78 return nil, err
79 }
80 cons = params.Constraints.WithFallbacks(cons)
81
82 ops, instData, containerParams, err := st.addMachineContainerOps(params, cons)
83 if err != nil {
84 return nil, err
85 }
86 mdoc := &machineDoc{
87 Series: params.Series,
88 ContainerType: string(params.ContainerType),
89 Jobs: params.Jobs,
90 Clean: true,
91 }
92 if mdoc.ContainerType == "" {
93 mdoc.InstanceId = params.InstanceId
94 mdoc.Nonce = params.Nonce
95 }
96 mdoc, machineOps, err := st.addMachineOps(mdoc, instData, cons, containe rParams)
97 if err != nil {
98 return nil, err
99 }
100 ops = append(ops, machineOps...)
101
102 err = st.runTransaction(ops)
103 if err != nil {
104 return nil, err
105 }
106 // Refresh to pick the txn-revno.
107 m = newMachine(st, mdoc)
108 if err = m.Refresh(); err != nil {
109 return nil, err
110 }
111 return m, nil
112 }
113
114 func (st *State) addMachineOps(mdoc *machineDoc, metadata *instanceData, cons co nstraints.Value, containerParams *containerRefParams) (*machineDoc, []txn.Op, er ror) {
115 if mdoc.Series == "" {
116 return nil, nil, fmt.Errorf("no series specified")
117 }
118 if len(mdoc.Jobs) == 0 {
119 return nil, nil, fmt.Errorf("no jobs specified")
120 }
121 if containerParams.hostId != "" && mdoc.ContainerType == "" {
122 return nil, nil, fmt.Errorf("no container type specified")
123 }
124 jset := make(map[MachineJob]bool)
125 for _, j := range mdoc.Jobs {
126 if jset[j] {
127 return nil, nil, fmt.Errorf("duplicate job: %s", j)
128 }
129 jset[j] = true
130 }
131 if containerParams.hostId == "" {
132 // we are creating a new machine instance (not a container).
133 seq, err := st.sequence("machine")
134 if err != nil {
135 return nil, nil, err
136 }
137 mdoc.Id = strconv.Itoa(seq)
138 containerParams.hostId = mdoc.Id
139 containerParams.newHost = true
140 }
141 if mdoc.ContainerType != "" {
142 // we are creating a container so set up a namespaced id.
143 seq, err := st.sequence(fmt.Sprintf("machine%s%sContainer", cont ainerParams.hostId, mdoc.ContainerType))
144 if err != nil {
145 return nil, nil, err
146 }
147 mdoc.Id = fmt.Sprintf("%s/%s/%d", containerParams.hostId, mdoc.C ontainerType, seq)
148 containerParams.containerId = mdoc.Id
149 }
150 mdoc.Life = Alive
151 sdoc := statusDoc{
152 Status: params.StatusPending,
153 }
154 // Machine constraints do not use a container constraint value.
155 // Both provisioning and deployment constraints use the same constraints .Value struct
156 // so here we clear the container value. Provisioning ignores the contai ner value but
157 // clearing it avoids potential confusion.
158 cons.Container = nil
159 ops := []txn.Op{
160 {
161 C: st.machines.Name,
162 Id: mdoc.Id,
163 Assert: txn.DocMissing,
164 Insert: *mdoc,
165 },
166 createConstraintsOp(st, machineGlobalKey(mdoc.Id), cons),
167 createStatusOp(st, machineGlobalKey(mdoc.Id), sdoc),
168 }
169 if metadata != nil {
170 ops = append(ops, txn.Op{
171 C: st.instanceData.Name,
172 Id: mdoc.Id,
173 Assert: txn.DocMissing,
174 Insert: *metadata,
175 })
176 }
177 ops = append(ops, createContainerRefOp(st, containerParams)...)
178 return mdoc, ops, nil
179 }
180
181 // addMachineContainerOps returns txn operations and associated Mongo records us ed to create a new machine,
182 // accounting for the fact that a machine may require a container and may requir e instance data.
183 // This method exists to cater for:
184 // 1. InjectMachine, which is used to record in state an instantiated bootstrap node. When adding
185 // a machine to state so that it is provisioned normally, the instance id is not known at this point.
186 // 2. AssignToNewMachine, which is used to create a new machine on which to depl oy a unit.
187 func (st *State) addMachineContainerOps(params *AddMachineParams, cons constrain ts.Value) ([]txn.Op, *instanceData, *containerRefParams, error) {
188 var instData *instanceData
189 if params.InstanceId != "" {
190 instData = &instanceData{
191 InstanceId: params.InstanceId,
192 Arch: params.HardwareCharacteristics.Arch,
193 Mem: params.HardwareCharacteristics.Mem,
194 RootDisk: params.HardwareCharacteristics.RootDisk,
195 CpuCores: params.HardwareCharacteristics.CpuCores,
196 CpuPower: params.HardwareCharacteristics.CpuPower,
197 Tags: params.HardwareCharacteristics.Tags,
198 }
199 }
200 var ops []txn.Op
201 var containerParams = &containerRefParams{hostId: params.ParentId, hostO nly: true}
202 // If we are creating a container, first create the host (parent) machin e if necessary.
203 if params.ContainerType != "" {
204 containerParams.hostOnly = false
205 if params.ParentId == "" {
206 // No parent machine is specified so create one.
207 mdoc := &machineDoc{
208 Series: params.Series,
209 Jobs: params.Jobs,
210 Clean: true,
211 }
212 mdoc, parentOps, err := st.addMachineOps(mdoc, instData, cons, &containerRefParams{})
213 if err != nil {
214 return nil, nil, nil, err
215 }
216 ops = parentOps
217 containerParams.hostId = mdoc.Id
218 containerParams.newHost = true
219 } else {
220 // If a parent machine is specified, make sure it exists .
221 host, err := st.Machine(containerParams.hostId)
222 if err != nil {
223 return nil, nil, nil, err
224 }
225 // We will try and check if the specified parent machine can run a container of the specified type.
226 // If the machine's supportedContainers attribute is set , this decision can be made right here.
227 // If it is not yet known what containers a machine supp orts, we will assume that everything will
228 // be ok and later on put the container into an error st ate if necessary.
229 if supportedContainers, ok := host.SupportedContainers() ; ok {
230 supported := false
231 for _, containerType := range supportedContainer s {
232 if containerType == params.ContainerType {
233 supported = true
234 break
235 }
236 }
237 if !supported {
238 return nil, nil, nil, fmt.Errorf("machin e %s cannot host %s containers", host, params.ContainerType)
239 }
240 }
241 }
242 }
243 return ops, instData, containerParams, nil
244 }
OLDNEW
« no previous file with comments | « [revision details] ('k') | state/container.go » ('j') | no next file with comments »

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b