-
Notifications
You must be signed in to change notification settings - Fork 20
/
Copy pathdeployment.go
273 lines (245 loc) · 8.44 KB
/
deployment.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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
package expr
import (
"fmt"
)
type (
// DeploymentEnvironment provides context to the other deployment expressions.
DeploymentEnvironment struct {
// Name of environment.
Name string
}
// DeploymentNode describes a single deployment node.
DeploymentNode struct {
*Element
Parent *DeploymentNode
Children []*DeploymentNode
InfrastructureNodes []*InfrastructureNode
ContainerInstances []*ContainerInstance
Instances *string
Environment string
}
// InfrastructureNode describes an infrastructure node.
InfrastructureNode struct {
*Element
Parent *DeploymentNode
Environment string
}
// ContainerInstance describes an instance of a container.
ContainerInstance struct {
// cheating a bit: a ContainerInstance does not have a name,
// description, technology or URL.
*Element
Parent *DeploymentNode
Container *Container
HealthChecks []*HealthCheck
ContainerID string
InstanceID int
Environment string
}
// InfrastructureNodes is a slice of infrastructure nodes that can be
// converted into a slice of ElementHolder.
InfrastructureNodes []*InfrastructureNode
// ContainerInstances is a slice of container instances that can be
// converted into a slice of ElementHolder.
ContainerInstances []*ContainerInstance
// HealthCheck is a HTTP-based health check.
HealthCheck struct {
Name string
URL string
Interval int
Timeout int
Headers map[string]string
}
)
// DeploymentNodeTags list the tags that are automatically added to all
// deployment nodes.
var DeploymentNodeTags = []string{"Element", "Deployment Node"}
// InfrastructureNodeTags list the tags that are automatically added to all
// infrastructure nodes.
var InfrastructureNodeTags = []string{"Element", "Infrastructure Node"}
// ContainerInstanceTags list the tags that are automatically added to all
// container instances.
var ContainerInstanceTags = []string{"Container Instance"}
// EvalName returns the generic expression name used in error messages.
func (d *DeploymentEnvironment) EvalName() string {
return fmt.Sprintf("deployment environment %q", d.Name)
}
// EvalName returns the generic expression name used in error messages.
func (d *DeploymentNode) EvalName() string { return fmt.Sprintf("deployment node %q", d.Name) }
// Finalize adds the 'Deployment Node' tag ands finalizes relationships.
func (d *DeploymentNode) Finalize() {
d.PrefixTags(DeploymentNodeTags...)
d.Element.Finalize()
}
// Child returns the child deployment node with the given name if any,
// nil otherwise.
func (d *DeploymentNode) Child(name string) *DeploymentNode {
for _, dd := range d.Children {
if dd.Name == name {
return dd
}
}
return nil
}
// InfrastructureNode returns the infrastructure node with the given name if
// any, nil otherwise.
func (d *DeploymentNode) InfrastructureNode(name string) *InfrastructureNode {
for _, i := range d.InfrastructureNodes {
if i.Name == name {
return i
}
}
return nil
}
// ContainerInstanceByID returns the container instance for the given container
// with the given instance ID if any, nil otherwise.
func (d *DeploymentNode) ContainerInstanceByID(containerID string, instanceID int) *ContainerInstance {
for _, ci := range d.ContainerInstances {
if ci.ContainerID == containerID && ci.InstanceID == instanceID {
return ci
}
}
return nil
}
// ContainerInstanceByName returns the container instance for the given
// container with the given name if any, nil otherwise.
//
// Note that in theory there could be be multiple containers with the given name
// coming from different software systems in a single deployment node. In
// practice the likelyhood of this happening seems pretty slim so we'll keep it
// simple for now...
func (d *DeploymentNode) ContainerInstanceByName(name string, instanceID int) *ContainerInstance {
for _, ci := range d.ContainerInstances {
if ci.Name == name && ci.InstanceID == instanceID {
return ci
}
}
return nil
}
// AddChild adds the given child deployment node to the parent. If
// there is already a deployment node with the given name then AddChild
// merges both definitions. The merge algorithm:
//
// - overrides the description, technology and URL if provided,
// - merges any new tag or propery into the existing tags and properties,
// - merges any new child deployment node into the existing children,
// - merges any new container instance or infrastructure nodes into existing
// ones.
//
// AddChild returns the new or merged deployment node.
func (d *DeploymentNode) AddChild(n *DeploymentNode) *DeploymentNode {
existing := d.Child(n.Name)
if existing == nil {
Identify(n)
d.Children = append(d.Children, n)
return n
}
if n.Description != "" {
existing.Description = n.Description
}
if n.Technology != "" {
existing.Technology = n.Technology
}
if olddsl := existing.DSLFunc; olddsl != nil {
existing.DSLFunc = func() { olddsl(); n.DSLFunc() }
}
return existing
}
// AddInfrastructureNode adds the given infrastructure node to the deployment
// node. If there is already an infrastructure node with the given name then
// AddInfrastructureNode merges both definitions. The merge algorithm:
//
// - overrides the description, technology and URL if provided,
// - merges any new tag or propery into the existing tags and properties.
//
// AddInfrastructureNode returns the new or merged infrastructure node.
func (d *DeploymentNode) AddInfrastructureNode(n *InfrastructureNode) *InfrastructureNode {
existing := d.InfrastructureNode(n.Name)
if existing == nil {
Identify(n)
d.InfrastructureNodes = append(d.InfrastructureNodes, n)
return n
}
if n.Description != "" {
existing.Description = n.Description
}
if n.Technology != "" {
existing.Technology = n.Technology
}
if olddsl := existing.DSLFunc; olddsl != nil {
existing.DSLFunc = func() { olddsl(); n.DSLFunc() }
}
return existing
}
// AddContainerInstance adds the given container instance to the deployment
// node. If there is already a container instance with the given container and
// instance ID then AddContainerInstance merges both definitions. The merge
// algorithm:
//
// - overrides the description, technology and URL if provided,
// - merges any new tag or propery into the existing tags and properties,
// - merges any new health check into the existing health checks.
//
// AddContainerInstance returns the new or merged container instance.
func (d *DeploymentNode) AddContainerInstance(ci *ContainerInstance) *ContainerInstance {
c := Registry[ci.ContainerID].(*Container)
existing := d.ContainerInstanceByID(c.ID, ci.InstanceID)
if existing == nil {
Identify(ci)
d.ContainerInstances = append(d.ContainerInstances, ci)
return ci
}
if ci.Description != "" {
existing.Description = ci.Description
}
if ci.Technology != "" {
existing.Technology = ci.Technology
}
existing.HealthChecks = append(existing.HealthChecks, ci.HealthChecks...)
if olddsl := existing.DSLFunc; olddsl != nil {
existing.DSLFunc = func() { olddsl(); ci.DSLFunc() }
}
return existing
}
// EvalName returns the generic expression name used in error messages.
func (i *InfrastructureNode) EvalName() string {
return fmt.Sprintf("infrastructure node %q", i.Name)
}
// Finalize adds the 'Infrastructure Node' tag ands finalizes relationships.
func (i *InfrastructureNode) Finalize() {
i.PrefixTags(InfrastructureNodeTags...)
i.Element.Finalize()
}
// EvalName returns the generic expression name used in error messages.
func (ci *ContainerInstance) EvalName() string {
n := "unknown container"
if cn, ok := Registry[ci.ContainerID]; ok {
n = fmt.Sprintf("container %q", cn.(*Container).Name)
}
return fmt.Sprintf("instance %d of %s", ci.InstanceID, n)
}
// Finalize adds the "Container Instance" tag if not present.
func (ci *ContainerInstance) Finalize() {
ci.PrefixTags(ContainerInstanceTags...)
ci.Element.Finalize()
}
// EvalName returns the generic expression name used in error messages.
func (hc *HealthCheck) EvalName() string {
return fmt.Sprintf("health check %q", hc.Name)
}
// Elements returns a slice of ElementHolder that contains the elements of inf.
func (inf InfrastructureNodes) Elements() []ElementHolder {
res := make([]ElementHolder, len(inf))
for i, cc := range inf {
res[i] = cc
}
return res
}
// Elements returns a slice of ElementHolder that contains the elements of ci.
func (ci ContainerInstances) Elements() []ElementHolder {
res := make([]ElementHolder, len(ci))
for i, cc := range ci {
res[i] = cc
}
return res
}