@toa.io/operations 1.0.0-alpha.31 → 1.0.0-alpha.310
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +213 -0
- package/image/runtime.Dockerfile +14 -0
- package/package.json +10 -10
- package/readme.md +201 -0
- package/src/deployment/.deployment/.describe/components.js +1 -5
- package/src/deployment/.deployment/.describe/compositions.js +26 -7
- package/src/deployment/.deployment/.describe/dependencies.js +1 -6
- package/src/deployment/.deployment/.describe/events.js +63 -0
- package/src/deployment/.deployment/.describe/fold.js +42 -0
- package/src/deployment/.deployment/.describe/fold.test.js +108 -0
- package/src/deployment/.deployment/.describe/index.js +4 -13
- package/src/deployment/.deployment/.describe/mounts.js +17 -0
- package/src/deployment/.deployment/.describe/resources.js +129 -0
- package/src/deployment/.deployment/.describe/resources.test.js +129 -0
- package/src/deployment/.deployment/.describe/services.js +32 -6
- package/src/deployment/.deployment/.describe/services.test.js +78 -0
- package/src/deployment/.deployment/.describe/variables.js +5 -9
- package/src/deployment/.deployment/declare.js +1 -5
- package/src/deployment/.deployment/describe.js +153 -10
- package/src/deployment/.deployment/index.js +3 -9
- package/src/deployment/.deployment/merge.js +66 -6
- package/src/deployment/.deployment/merge.test.js +133 -0
- package/src/deployment/chart/templates/components.configmap.yaml +14 -0
- package/src/deployment/chart/templates/components.yaml +1 -1
- package/src/deployment/chart/templates/compositions.yaml +109 -4
- package/src/deployment/chart/templates/mono.yaml +195 -0
- package/src/deployment/chart/templates/services.yaml +97 -7
- package/src/deployment/chart/values.yaml +23 -1
- package/src/deployment/composition.js +8 -6
- package/src/deployment/deployment.js +147 -34
- package/src/deployment/drain.js +70 -0
- package/src/deployment/drain.test.js +64 -0
- package/src/deployment/factory.js +169 -37
- package/src/deployment/images/bundle.js +88 -0
- package/src/deployment/images/composition.Dockerfile +9 -17
- package/src/deployment/images/composition.js +9 -56
- package/src/deployment/images/dependencies.Dockerfile +23 -0
- package/src/deployment/images/dependencies.js +171 -0
- package/src/deployment/images/dependencies.test.js +268 -0
- package/src/deployment/images/factory.js +31 -15
- package/src/deployment/images/format.js +66 -0
- package/src/deployment/images/format.test.js +149 -0
- package/src/deployment/images/image.fixtures.js +17 -19
- package/src/deployment/images/image.js +79 -38
- package/src/deployment/images/image.test.js +11 -5
- package/src/deployment/images/index.js +1 -5
- package/src/deployment/images/mono.Dockerfile +11 -0
- package/src/deployment/images/mono.js +15 -0
- package/src/deployment/images/publish.js +91 -0
- package/src/deployment/images/runtime-base.test.js +97 -0
- package/src/deployment/images/service.Dockerfile +17 -5
- package/src/deployment/images/service.js +63 -14
- package/src/deployment/index.js +1 -5
- package/src/deployment/operator.js +22 -16
- package/src/deployment/operator.test.js +57 -7
- package/src/deployment/registry.js +210 -46
- package/src/deployment/registry.test.js +539 -0
- package/src/deployment/service.js +4 -6
- package/src/deployment/workspace.js +13 -8
- package/src/index.js +3 -2
- package/src/process.js +51 -13
- package/src/process.test.js +33 -0
- package/types/_deployment/composition.d.ts +2 -3
- package/types/_deployment/dependency.d.ts +13 -7
- package/types/_deployment/deployment.d.ts +7 -8
- package/types/_deployment/factory.d.ts +2 -4
- package/types/_deployment/images/factory.d.ts +6 -8
- package/types/_deployment/images/image.d.ts +16 -1
- package/types/_deployment/images/registry.d.ts +8 -11
- package/types/_deployment/operator.d.ts +10 -9
- package/types/_deployment/registry.d.ts +7 -4
- package/types/_deployment/service.d.ts +4 -3
- package/types/dependency.ts +79 -0
- package/types/index.ts +1 -0
- package/types/dependency.d.ts +0 -39
- package/types/index.d.ts +0 -1
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
{{- if .Values.mono }}
|
|
2
|
+
{{- with .Values.mono }}
|
|
3
|
+
apiVersion: apps/v1
|
|
4
|
+
kind: Deployment
|
|
5
|
+
metadata:
|
|
6
|
+
name: mono
|
|
7
|
+
spec:
|
|
8
|
+
replicas: {{ .replicas | default 2 }}
|
|
9
|
+
progressDeadlineSeconds: 900
|
|
10
|
+
strategy:
|
|
11
|
+
type: RollingUpdate
|
|
12
|
+
rollingUpdate:
|
|
13
|
+
maxUnavailable: 0
|
|
14
|
+
maxSurge: 100%
|
|
15
|
+
selector:
|
|
16
|
+
matchLabels:
|
|
17
|
+
toa/composition: mono
|
|
18
|
+
template:
|
|
19
|
+
metadata:
|
|
20
|
+
labels:
|
|
21
|
+
toa/composition: mono
|
|
22
|
+
{{- range .components }}
|
|
23
|
+
toa/component-{{ . }}: "1"
|
|
24
|
+
{{- end }}
|
|
25
|
+
spec:
|
|
26
|
+
containers:
|
|
27
|
+
- name: mono
|
|
28
|
+
image: {{ .image }}
|
|
29
|
+
{{- if or .backends .probe }}
|
|
30
|
+
ports:
|
|
31
|
+
{{- range .backends }}
|
|
32
|
+
- containerPort: {{ .port }}
|
|
33
|
+
{{- end }}
|
|
34
|
+
{{- if .probe }}
|
|
35
|
+
- containerPort: {{ .probe.port }}
|
|
36
|
+
{{- end }}
|
|
37
|
+
{{- end }}
|
|
38
|
+
{{- if .resources }}
|
|
39
|
+
resources:
|
|
40
|
+
{{- if or .resources.cpu .resources.memory }}
|
|
41
|
+
requests:
|
|
42
|
+
{{- if .resources.cpu }}
|
|
43
|
+
cpu: {{ index .resources.cpu 0 }}
|
|
44
|
+
{{- end }}
|
|
45
|
+
{{- if .resources.memory }}
|
|
46
|
+
memory: {{ index .resources.memory 0 }}
|
|
47
|
+
{{- end }}
|
|
48
|
+
limits:
|
|
49
|
+
{{- if .resources.cpu }}
|
|
50
|
+
cpu: {{ index .resources.cpu 1 }}
|
|
51
|
+
{{- end }}
|
|
52
|
+
{{- if .resources.memory }}
|
|
53
|
+
memory: {{ index .resources.memory 1 }}
|
|
54
|
+
{{- end }}
|
|
55
|
+
{{- end }}
|
|
56
|
+
{{- end }}
|
|
57
|
+
{{- if .variables }}
|
|
58
|
+
env:
|
|
59
|
+
{{- range .variables }}
|
|
60
|
+
{{- include "env.var" . | indent 12 }}
|
|
61
|
+
{{- end }}
|
|
62
|
+
{{- end }}
|
|
63
|
+
{{- if .probe }}
|
|
64
|
+
startupProbe:
|
|
65
|
+
httpGet:
|
|
66
|
+
path: {{ .probe.path }}
|
|
67
|
+
port: {{ .probe.port }}
|
|
68
|
+
{{- if .probe.delay }}
|
|
69
|
+
initialDelaySeconds: {{ .probe.delay }}
|
|
70
|
+
{{- end }}
|
|
71
|
+
periodSeconds: 2
|
|
72
|
+
timeoutSeconds: 3
|
|
73
|
+
failureThreshold: 150
|
|
74
|
+
readinessProbe:
|
|
75
|
+
httpGet:
|
|
76
|
+
path: {{ .probe.path }}
|
|
77
|
+
port: {{ .probe.port }}
|
|
78
|
+
# a replica is ready when it says so, not at the next tick of a slow clock
|
|
79
|
+
periodSeconds: 2
|
|
80
|
+
timeoutSeconds: 3
|
|
81
|
+
failureThreshold: 5
|
|
82
|
+
{{- end }}
|
|
83
|
+
{{- if or .mounts $.Values.map }}
|
|
84
|
+
volumeMounts:
|
|
85
|
+
{{- range .mounts }}
|
|
86
|
+
- name: {{ .name }}
|
|
87
|
+
mountPath: {{ .path }}
|
|
88
|
+
{{- end }}
|
|
89
|
+
{{- if $.Values.map }}
|
|
90
|
+
- name: components
|
|
91
|
+
mountPath: {{ $.Values.map.directory }}
|
|
92
|
+
readOnly: true
|
|
93
|
+
{{- end }}
|
|
94
|
+
{{- end }}
|
|
95
|
+
lifecycle:
|
|
96
|
+
preStop:
|
|
97
|
+
exec:
|
|
98
|
+
command: [sleep, "5"]
|
|
99
|
+
terminationGracePeriodSeconds: 45
|
|
100
|
+
{{- if $.Values.credentials }}
|
|
101
|
+
imagePullSecrets:
|
|
102
|
+
- name: {{ $.Values.credentials }}
|
|
103
|
+
{{- end }}
|
|
104
|
+
{{- if or .mounts $.Values.map }}
|
|
105
|
+
volumes:
|
|
106
|
+
{{- range .mounts }}
|
|
107
|
+
- name: {{ .name }}
|
|
108
|
+
persistentVolumeClaim:
|
|
109
|
+
claimName: {{ .claim }}
|
|
110
|
+
{{- end }}
|
|
111
|
+
{{- if $.Values.map }}
|
|
112
|
+
- name: components
|
|
113
|
+
configMap:
|
|
114
|
+
name: components
|
|
115
|
+
{{- end }}
|
|
116
|
+
{{- end }}
|
|
117
|
+
topologySpreadConstraints:
|
|
118
|
+
- maxSkew: 1
|
|
119
|
+
topologyKey: kubernetes.io/hostname
|
|
120
|
+
whenUnsatisfiable: ScheduleAnyway
|
|
121
|
+
labelSelector:
|
|
122
|
+
matchLabels:
|
|
123
|
+
toa/composition: mono
|
|
124
|
+
# only this revision's pods: a rollout spreads the new ones, whatever the old ones occupy
|
|
125
|
+
matchLabelKeys:
|
|
126
|
+
- pod-template-hash
|
|
127
|
+
{{- if .backends }}
|
|
128
|
+
---
|
|
129
|
+
apiVersion: v1
|
|
130
|
+
kind: Service
|
|
131
|
+
metadata:
|
|
132
|
+
name: mono
|
|
133
|
+
{{- if .annotations }}
|
|
134
|
+
annotations:
|
|
135
|
+
{{ toYaml .annotations | indent 4 }}
|
|
136
|
+
{{- end }}
|
|
137
|
+
spec:
|
|
138
|
+
type: ClusterIP
|
|
139
|
+
selector:
|
|
140
|
+
toa/composition: mono
|
|
141
|
+
ports:
|
|
142
|
+
{{- range .backends }}
|
|
143
|
+
- name: port-{{ .port }}
|
|
144
|
+
protocol: TCP
|
|
145
|
+
port: {{ .port }}
|
|
146
|
+
targetPort: {{ .port }}
|
|
147
|
+
{{- end }}
|
|
148
|
+
{{- end }}
|
|
149
|
+
{{- if .ingress }}
|
|
150
|
+
---
|
|
151
|
+
apiVersion: networking.k8s.io/v1
|
|
152
|
+
kind: Ingress
|
|
153
|
+
metadata:
|
|
154
|
+
name: mono
|
|
155
|
+
{{- if .ingress.annotations }}
|
|
156
|
+
annotations:
|
|
157
|
+
{{ toYaml .ingress.annotations | indent 4 }}
|
|
158
|
+
{{- end }}
|
|
159
|
+
spec:
|
|
160
|
+
{{- if .ingress.class }}
|
|
161
|
+
ingressClassName: {{ .ingress.class }}
|
|
162
|
+
{{- end }}
|
|
163
|
+
{{- $backends := .backends }}
|
|
164
|
+
rules:
|
|
165
|
+
{{- range .ingress.hosts }}
|
|
166
|
+
- host: {{ . }}
|
|
167
|
+
http:
|
|
168
|
+
paths:
|
|
169
|
+
{{- range $backends }}
|
|
170
|
+
- path: {{ .path }}
|
|
171
|
+
pathType: Prefix
|
|
172
|
+
backend:
|
|
173
|
+
service:
|
|
174
|
+
name: mono
|
|
175
|
+
port:
|
|
176
|
+
number: {{ .port }}
|
|
177
|
+
{{- end }}
|
|
178
|
+
{{- end }}
|
|
179
|
+
{{- if .ingress.default }}
|
|
180
|
+
- http:
|
|
181
|
+
paths:
|
|
182
|
+
{{- range $backends }}
|
|
183
|
+
- path: {{ .path }}
|
|
184
|
+
pathType: Prefix
|
|
185
|
+
backend:
|
|
186
|
+
service:
|
|
187
|
+
name: mono
|
|
188
|
+
port:
|
|
189
|
+
number: {{ .port }}
|
|
190
|
+
{{- end }}
|
|
191
|
+
{{- end }}
|
|
192
|
+
{{- end }}
|
|
193
|
+
---
|
|
194
|
+
{{- end }}
|
|
195
|
+
{{- end }}
|
|
@@ -1,21 +1,52 @@
|
|
|
1
1
|
{{- range .Values.services }}
|
|
2
|
+
{{- if not .workload }}
|
|
2
3
|
apiVersion: apps/v1
|
|
3
4
|
kind: Deployment
|
|
4
5
|
metadata:
|
|
5
6
|
name: extension-{{ required "deployment name is required" .name }}
|
|
6
7
|
spec:
|
|
7
8
|
replicas: {{ .replicas | default 2 }}
|
|
9
|
+
# A backstop for stuck rollouts only. Must stay above the `helm upgrade --timeout`
|
|
10
|
+
# the deploy runs with, so a slow but progressing rollout is not marked failed.
|
|
11
|
+
progressDeadlineSeconds: 900
|
|
12
|
+
strategy:
|
|
13
|
+
type: RollingUpdate
|
|
14
|
+
rollingUpdate:
|
|
15
|
+
# a replacement must pass readiness before a running replica is taken out, and every
|
|
16
|
+
# replacement starts at once: the rollout takes one start-up, not one per replica
|
|
17
|
+
maxUnavailable: 0
|
|
18
|
+
maxSurge: 100%
|
|
8
19
|
selector:
|
|
9
20
|
matchLabels:
|
|
10
|
-
toa
|
|
21
|
+
toa/service: extension-{{ .name }}
|
|
11
22
|
template:
|
|
12
23
|
metadata:
|
|
13
24
|
labels:
|
|
14
|
-
toa
|
|
25
|
+
toa/service: extension-{{ .name }}
|
|
26
|
+
toa/service-{{ .name }}: "1"
|
|
15
27
|
spec:
|
|
16
28
|
containers:
|
|
17
29
|
- name: extension-{{ .name }}
|
|
18
30
|
image: {{ .image }}
|
|
31
|
+
{{- if .resources }}
|
|
32
|
+
resources:
|
|
33
|
+
{{- if or .resources.cpu .resources.memory }}
|
|
34
|
+
requests:
|
|
35
|
+
{{- if .resources.cpu }}
|
|
36
|
+
cpu: {{ index .resources.cpu 0 }}
|
|
37
|
+
{{- end }}
|
|
38
|
+
{{- if .resources.memory }}
|
|
39
|
+
memory: {{ index .resources.memory 0 }}
|
|
40
|
+
{{- end }}
|
|
41
|
+
limits:
|
|
42
|
+
{{- if .resources.cpu }}
|
|
43
|
+
cpu: {{ index .resources.cpu 1 }}
|
|
44
|
+
{{- end }}
|
|
45
|
+
{{- if .resources.memory }}
|
|
46
|
+
memory: {{ index .resources.memory 1 }}
|
|
47
|
+
{{- end }}
|
|
48
|
+
{{- end }}
|
|
49
|
+
{{- end }}
|
|
19
50
|
{{- if .variables }}
|
|
20
51
|
env:
|
|
21
52
|
{{- range .variables }}
|
|
@@ -23,31 +54,78 @@ spec:
|
|
|
23
54
|
{{- end }}
|
|
24
55
|
{{- end }}
|
|
25
56
|
{{- if .probe }}
|
|
26
|
-
|
|
57
|
+
startupProbe:
|
|
27
58
|
httpGet:
|
|
28
59
|
path: {{ .probe.path }}
|
|
29
60
|
port: {{ .probe.port }}
|
|
30
61
|
{{- if .probe.delay }}
|
|
31
62
|
initialDelaySeconds: {{ .probe.delay }}
|
|
32
63
|
{{- end }}
|
|
64
|
+
periodSeconds: 2
|
|
65
|
+
timeoutSeconds: 3
|
|
66
|
+
failureThreshold: 150
|
|
67
|
+
readinessProbe:
|
|
68
|
+
httpGet:
|
|
69
|
+
path: {{ .probe.path }}
|
|
70
|
+
port: {{ .probe.port }}
|
|
71
|
+
# a replica is ready when it says so, not at the next tick of a slow clock
|
|
72
|
+
periodSeconds: 2
|
|
73
|
+
timeoutSeconds: 3
|
|
74
|
+
failureThreshold: 5
|
|
75
|
+
{{- end }}
|
|
76
|
+
{{- if $.Values.map }}
|
|
77
|
+
volumeMounts:
|
|
78
|
+
- name: components
|
|
79
|
+
mountPath: {{ $.Values.map.directory }}
|
|
80
|
+
readOnly: true
|
|
33
81
|
{{- end }}
|
|
82
|
+
lifecycle:
|
|
83
|
+
preStop:
|
|
84
|
+
exec:
|
|
85
|
+
command: [sleep, "5"]
|
|
86
|
+
{{- if $.Values.map }}
|
|
87
|
+
volumes:
|
|
88
|
+
- name: components
|
|
89
|
+
configMap:
|
|
90
|
+
name: components
|
|
91
|
+
{{- end }}
|
|
92
|
+
terminationGracePeriodSeconds: 45
|
|
93
|
+
topologySpreadConstraints:
|
|
94
|
+
- maxSkew: 1
|
|
95
|
+
topologyKey: kubernetes.io/hostname
|
|
96
|
+
whenUnsatisfiable: ScheduleAnyway
|
|
97
|
+
labelSelector:
|
|
98
|
+
matchLabels:
|
|
99
|
+
toa/service: extension-{{ .name }}
|
|
100
|
+
# only this revision's pods: a rollout spreads the new ones, whatever the old ones occupy
|
|
101
|
+
matchLabelKeys:
|
|
102
|
+
- pod-template-hash
|
|
103
|
+
{{- end }}
|
|
104
|
+
{{- if .port }}
|
|
34
105
|
---
|
|
35
106
|
apiVersion: v1
|
|
36
107
|
kind: Service
|
|
37
108
|
metadata:
|
|
38
109
|
name: extension-{{ .name }}
|
|
110
|
+
{{- if .annotations }}
|
|
111
|
+
annotations:
|
|
112
|
+
{{ toYaml .annotations | indent 4 }}
|
|
113
|
+
{{- end }}
|
|
39
114
|
spec:
|
|
40
115
|
type: ClusterIP
|
|
41
116
|
selector:
|
|
42
|
-
toa
|
|
117
|
+
toa/service-{{ .name }}: "1"
|
|
43
118
|
ports:
|
|
44
119
|
- name: port-{{ .port }}
|
|
45
120
|
protocol: TCP
|
|
46
121
|
port: {{ .port }}
|
|
47
122
|
targetPort: {{ .port }}
|
|
48
|
-
|
|
123
|
+
{{- end }}
|
|
49
124
|
{{- if .ingress }}
|
|
50
125
|
{{- $service := .name }}
|
|
126
|
+
{{- $port := .port }}
|
|
127
|
+
{{- $path := .ingress.path | default "/" }}
|
|
128
|
+
---
|
|
51
129
|
apiVersion: networking.k8s.io/v1
|
|
52
130
|
kind: Ingress
|
|
53
131
|
metadata:
|
|
@@ -65,13 +143,25 @@ spec:
|
|
|
65
143
|
- host: {{ . }}
|
|
66
144
|
http:
|
|
67
145
|
paths:
|
|
68
|
-
- path:
|
|
146
|
+
- path: {{ $path }}
|
|
69
147
|
pathType: Prefix
|
|
70
148
|
backend:
|
|
71
149
|
service:
|
|
72
150
|
name: extension-{{ $service }}
|
|
73
151
|
port:
|
|
74
|
-
number:
|
|
152
|
+
number: {{ $port }}
|
|
153
|
+
{{- end }}
|
|
154
|
+
{{- if .ingress.default }}
|
|
155
|
+
- http:
|
|
156
|
+
paths:
|
|
157
|
+
- path: {{ $path }}
|
|
158
|
+
pathType: Prefix
|
|
159
|
+
backend:
|
|
160
|
+
service:
|
|
161
|
+
name: extension-{{ $service }}
|
|
162
|
+
port:
|
|
163
|
+
number: {{ $port }}
|
|
75
164
|
{{- end }}
|
|
76
165
|
{{- end }}
|
|
166
|
+
---
|
|
77
167
|
{{- end }}
|
|
@@ -3,9 +3,16 @@ compositions:
|
|
|
3
3
|
- name: todos
|
|
4
4
|
image: localhost:5000/composition-todos:0.0.0
|
|
5
5
|
replicas: 2
|
|
6
|
+
mounts:
|
|
7
|
+
- name: mount-name
|
|
8
|
+
path: /storage
|
|
9
|
+
claim: storage-pvc
|
|
6
10
|
components:
|
|
7
11
|
- todos-tasks
|
|
8
12
|
- todos-stats
|
|
13
|
+
resources:
|
|
14
|
+
cpu: [100m, 1]
|
|
15
|
+
memory: [100Mi, 1Gi]
|
|
9
16
|
variables:
|
|
10
17
|
- name: TOA_CONFIGURATION_TODOS_TASKS
|
|
11
18
|
value: foo
|
|
@@ -18,17 +25,32 @@ compositions:
|
|
|
18
25
|
replicas: 3
|
|
19
26
|
components:
|
|
20
27
|
- users-users
|
|
21
|
-
#
|
|
28
|
+
# the extensions whose services this composition runs in its own pod
|
|
29
|
+
services:
|
|
30
|
+
- '@toa.io/extensions.exposition'
|
|
31
|
+
# each of those, as its own Service selects it
|
|
32
|
+
hosted:
|
|
33
|
+
- exposition-gateway
|
|
34
|
+
# the ports they bind
|
|
35
|
+
backends:
|
|
36
|
+
- port: 8000
|
|
37
|
+
path: /
|
|
22
38
|
components:
|
|
23
39
|
- todos-tasks
|
|
24
40
|
- todos-stats
|
|
25
41
|
- users-users
|
|
26
42
|
services:
|
|
43
|
+
# a service a composition runs carries `workload`, and gets no deployment of its own —
|
|
44
|
+
# its Service and Ingress stay, and select that composition's pods
|
|
27
45
|
- name: resources-gateway
|
|
28
46
|
image: localhost:5000/resources-gateway:0.0.0
|
|
29
47
|
port: 8000
|
|
30
48
|
replicas: 2
|
|
49
|
+
resources:
|
|
50
|
+
cpu: [100m, 1]
|
|
51
|
+
memory: [100Mi, 1Gi]
|
|
31
52
|
ingress:
|
|
53
|
+
default: true
|
|
32
54
|
hosts: [dummies.toa.io]
|
|
33
55
|
class: alb
|
|
34
56
|
annotations:
|
|
@@ -1,18 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
class Composition {
|
|
1
|
+
export class Composition {
|
|
4
2
|
name
|
|
5
3
|
image
|
|
6
4
|
/** @type {string[]} */
|
|
7
5
|
components
|
|
8
6
|
|
|
9
|
-
constructor
|
|
7
|
+
constructor(composition, image) {
|
|
10
8
|
this.name = composition.name
|
|
11
9
|
this.image = image.reference
|
|
12
10
|
this.components = composition.components.map(component)
|
|
11
|
+
this.resources = composition.resources
|
|
12
|
+
|
|
13
|
+
if (composition.replicas !== undefined) this.replicas = composition.replicas
|
|
14
|
+
|
|
15
|
+
// the extensions whose services this composition runs, as `TOA_SERVICES` for the process
|
|
16
|
+
if (composition.services !== undefined) this.services = composition.services
|
|
13
17
|
}
|
|
14
18
|
}
|
|
15
19
|
|
|
16
20
|
const component = (component) => component.locator.label
|
|
17
|
-
|
|
18
|
-
exports.Composition = Composition
|
|
@@ -1,90 +1,203 @@
|
|
|
1
|
-
|
|
1
|
+
import { join } from 'node:path'
|
|
2
|
+
import { writeFile as write } from 'node:fs/promises'
|
|
3
|
+
import { yaml as jsyaml } from '@toa.io/generic'
|
|
4
|
+
import { cp } from 'node:fs/promises'
|
|
5
|
+
import { shortcuts } from '@toa.io/norm'
|
|
2
6
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const { dump } = require('@toa.io/yaml')
|
|
6
|
-
const fs = require('fs-extra')
|
|
7
|
+
import { merge, declare, describe } from './.deployment/index.js'
|
|
8
|
+
import { drain } from './drain.js'
|
|
7
9
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class Deployment {
|
|
10
|
+
export class Deployment {
|
|
11
11
|
#chart
|
|
12
12
|
#values
|
|
13
|
+
#keyed
|
|
13
14
|
#process
|
|
14
15
|
#target
|
|
15
16
|
|
|
16
|
-
constructor
|
|
17
|
+
constructor(context, compositions, dependencies, process, image) {
|
|
17
18
|
const dependency = merge(dependencies)
|
|
18
19
|
|
|
19
20
|
this.#chart = declare(context, dependency)
|
|
20
|
-
this.#values = describe(context, compositions, dependency)
|
|
21
|
+
this.#values = describe(context, compositions, dependency, image)
|
|
22
|
+
this.#keyed = dependency.variables
|
|
21
23
|
this.#process = process
|
|
22
24
|
}
|
|
23
25
|
|
|
24
|
-
async export
|
|
26
|
+
async export(target) {
|
|
25
27
|
const chart = dump(this.#chart)
|
|
26
28
|
const values = dump(this.#values)
|
|
27
29
|
|
|
28
30
|
await Promise.all([
|
|
29
31
|
write(join(target, 'Chart.yaml'), chart),
|
|
30
32
|
write(join(target, 'values.yaml'), values),
|
|
31
|
-
|
|
33
|
+
cp(TEMPLATES, join(target, 'templates'), { recursive: true })
|
|
32
34
|
])
|
|
33
35
|
|
|
34
36
|
this.#target = target
|
|
35
37
|
}
|
|
36
38
|
|
|
37
|
-
async install
|
|
39
|
+
async install(options) {
|
|
38
40
|
if (options.target) this.#target = options.target
|
|
39
|
-
if (this.#target === undefined) throw new Error(
|
|
41
|
+
if (this.#target === undefined) throw new Error("Deployment hasn't been exported")
|
|
40
42
|
|
|
41
43
|
const args = []
|
|
42
44
|
|
|
43
45
|
if (options.namespace !== undefined) args.push('-n', options.namespace)
|
|
44
46
|
if (options.wait === true) args.push('--wait')
|
|
47
|
+
if (options.timeout !== undefined) args.push('--timeout', options.timeout)
|
|
48
|
+
|
|
49
|
+
await this.#update()
|
|
50
|
+
await this.#process.execute('helm', [
|
|
51
|
+
'upgrade',
|
|
52
|
+
this.#chart.name,
|
|
53
|
+
'-i',
|
|
54
|
+
...args,
|
|
55
|
+
this.#target
|
|
56
|
+
])
|
|
45
57
|
|
|
46
|
-
|
|
47
|
-
|
|
58
|
+
// ready is not done: the replicas replaced are still draining when helm answers
|
|
59
|
+
if (options.wait === true) await drain(this.#process, options)
|
|
48
60
|
}
|
|
49
61
|
|
|
50
|
-
async template
|
|
51
|
-
if (this.#target === undefined) throw new Error(
|
|
62
|
+
async template(options) {
|
|
63
|
+
if (this.#target === undefined) throw new Error("Deployment hasn't been exported")
|
|
52
64
|
|
|
53
|
-
await this.#
|
|
65
|
+
await this.#update({ silently: true })
|
|
54
66
|
|
|
55
67
|
const args = []
|
|
56
68
|
|
|
57
69
|
if (options.namespace !== undefined) args.push('-n', options.namespace)
|
|
58
70
|
|
|
59
|
-
return await this.#process.execute(
|
|
71
|
+
return await this.#process.execute(
|
|
72
|
+
'helm',
|
|
60
73
|
['template', this.#chart.name, ...args, this.#target],
|
|
61
|
-
{ silently: true }
|
|
74
|
+
{ silently: true }
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* A chart with no subcharts has nothing to resolve, and the call still reaches out for
|
|
80
|
+
* the repositories it would have read.
|
|
81
|
+
*
|
|
82
|
+
* @param {object} [options]
|
|
83
|
+
* @returns {Promise<void>}
|
|
84
|
+
*/
|
|
85
|
+
async #update(options = {}) {
|
|
86
|
+
if (this.#chart.dependencies.length === 0) return
|
|
87
|
+
|
|
88
|
+
await this.#process.execute('helm', ['dependency', 'update', this.#target], options)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* @param {{ components?: string[], services?: string[] }} [options]
|
|
93
|
+
* @returns {toa.deployment.dependency.Variable[]}
|
|
94
|
+
*/
|
|
95
|
+
variables(options = {}) {
|
|
96
|
+
if (options.components === undefined && options.services === undefined) {
|
|
97
|
+
const variables = []
|
|
98
|
+
const used = new Set()
|
|
99
|
+
|
|
100
|
+
addVariables(this.#values.compositions, variables, used)
|
|
101
|
+
addVariables(this.#values.services, variables, used)
|
|
102
|
+
|
|
103
|
+
if (this.#values.mono !== undefined)
|
|
104
|
+
addVariables([this.#values.mono], variables, used)
|
|
105
|
+
|
|
106
|
+
return variables
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return this.#select(options.components, options.services)
|
|
62
110
|
}
|
|
63
111
|
|
|
64
|
-
|
|
112
|
+
/**
|
|
113
|
+
* @param {string[] | undefined} components
|
|
114
|
+
* @param {string[] | undefined} services
|
|
115
|
+
* @returns {toa.deployment.dependency.Variable[]}
|
|
116
|
+
*/
|
|
117
|
+
#select(components, services) {
|
|
118
|
+
const labels = labelsOf(this.#values, this.#keyed)
|
|
119
|
+
const keys = new Set(['global'])
|
|
120
|
+
const selected = []
|
|
121
|
+
|
|
122
|
+
for (const token of components ?? []) keys.add(matchComponent(token, labels))
|
|
123
|
+
|
|
124
|
+
for (const token of services ?? []) {
|
|
125
|
+
const service = matchService(token, this.#values.services)
|
|
126
|
+
|
|
127
|
+
selected.push(service)
|
|
128
|
+
|
|
129
|
+
for (const label of service.components ?? []) keys.add(label)
|
|
130
|
+
}
|
|
131
|
+
|
|
65
132
|
const variables = []
|
|
66
133
|
const used = new Set()
|
|
67
134
|
|
|
68
|
-
|
|
69
|
-
|
|
135
|
+
for (const key of keys) append(this.#keyed[key], variables, used)
|
|
136
|
+
|
|
137
|
+
for (const service of selected) append(service.variables, variables, used)
|
|
70
138
|
|
|
71
139
|
return variables
|
|
72
140
|
}
|
|
73
141
|
}
|
|
74
142
|
|
|
75
|
-
function addVariables
|
|
76
|
-
|
|
77
|
-
if (item.variables === undefined) continue
|
|
143
|
+
function addVariables(list, variables, used = new Set()) {
|
|
144
|
+
if (list === undefined) return
|
|
78
145
|
|
|
79
|
-
|
|
80
|
-
|
|
146
|
+
for (const item of list) append(item.variables, variables, used)
|
|
147
|
+
}
|
|
81
148
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
149
|
+
function append(set, variables, used) {
|
|
150
|
+
if (set === undefined) return
|
|
151
|
+
|
|
152
|
+
for (const variable of set) {
|
|
153
|
+
if (used.has(variable.name)) continue
|
|
154
|
+
|
|
155
|
+
variables.push(variable)
|
|
156
|
+
used.add(variable.name)
|
|
85
157
|
}
|
|
86
158
|
}
|
|
87
159
|
|
|
88
|
-
|
|
160
|
+
function labelsOf(values, keyed) {
|
|
161
|
+
const labels = new Set(values.components ?? [])
|
|
162
|
+
|
|
163
|
+
for (const service of values.services ?? [])
|
|
164
|
+
for (const label of service.components ?? []) labels.add(label)
|
|
165
|
+
|
|
166
|
+
for (const key of Object.keys(keyed ?? {})) if (key !== 'global') labels.add(key)
|
|
167
|
+
|
|
168
|
+
return labels
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function matchComponent(token, labels) {
|
|
172
|
+
const candidates = [token, token.replaceAll('.', '-').toLowerCase()]
|
|
173
|
+
|
|
174
|
+
if (!token.includes('.')) candidates.push('default-' + token.toLowerCase())
|
|
175
|
+
|
|
176
|
+
for (const candidate of candidates) if (labels.has(candidate)) return candidate
|
|
177
|
+
|
|
178
|
+
throw new Error(`Component '${token}' is not in the context`)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function matchService(token, services) {
|
|
182
|
+
const resolved = shortcuts.resolve(token)
|
|
183
|
+
|
|
184
|
+
const service = (services ?? []).find((service) => {
|
|
185
|
+
if (service.name === token || service.group === token) return true
|
|
186
|
+
|
|
187
|
+
for (const [alias, pkg] of Object.entries(shortcuts.SHORTCUTS))
|
|
188
|
+
if (resolved === pkg && service.group === alias) return true
|
|
189
|
+
|
|
190
|
+
return false
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
if (service === undefined) throw new Error(`Service '${token}' is not in the context`)
|
|
194
|
+
|
|
195
|
+
return service
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const TEMPLATES = join(import.meta.dirname, 'chart/templates')
|
|
89
199
|
|
|
90
|
-
|
|
200
|
+
function dump(object) {
|
|
201
|
+
// js-yaml writes plain objects only, and the values carry locators and images
|
|
202
|
+
return jsyaml.dump(JSON.parse(JSON.stringify(object)), { noRefs: true, lineWidth: -1 })
|
|
203
|
+
}
|