@toa.io/operations 1.0.0-alpha.26 → 1.0.0-alpha.262
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 +66 -0
- package/image/runtime.Dockerfile +4 -0
- package/package.json +8 -8
- package/readme.md +16 -0
- package/src/deployment/.deployment/.describe/compositions.js +7 -2
- package/src/deployment/.deployment/.describe/mounts.js +24 -0
- package/src/deployment/.deployment/.describe/services.js +28 -1
- package/src/deployment/.deployment/.describe/services.test.js +61 -0
- package/src/deployment/.deployment/.describe/variables.js +6 -5
- package/src/deployment/.deployment/describe.js +74 -5
- package/src/deployment/.deployment/merge.js +41 -1
- package/src/deployment/.deployment/merge.test.js +74 -0
- package/src/deployment/chart/templates/compositions.yaml +70 -0
- package/src/deployment/chart/templates/mono.yaml +177 -0
- package/src/deployment/chart/templates/services.yaml +71 -4
- package/src/deployment/chart/values.yaml +11 -1
- package/src/deployment/composition.js +1 -0
- package/src/deployment/deployment.js +6 -2
- package/src/deployment/factory.js +24 -8
- package/src/deployment/images/composition.Dockerfile +2 -2
- package/src/deployment/images/composition.js +16 -6
- package/src/deployment/images/factory.js +12 -3
- package/src/deployment/images/image.js +16 -7
- package/src/deployment/images/mono.Dockerfile +18 -0
- package/src/deployment/images/mono.js +75 -0
- package/src/deployment/images/runtime-base.test.js +90 -0
- package/src/deployment/images/service.Dockerfile +3 -3
- package/src/deployment/operator.js +4 -0
- package/src/deployment/registry.js +64 -16
- package/src/deployment/registry.test.js +175 -0
- package/types/_deployment/images/image.d.ts +4 -2
- package/types/_deployment/registry.d.ts +9 -7
- package/types/dependency.ts +69 -0
- package/types/dependency.d.ts +0 -39
- /package/types/{index.d.ts → index.ts} +0 -0
|
@@ -0,0 +1,177 @@
|
|
|
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: 50%
|
|
15
|
+
selector:
|
|
16
|
+
matchLabels:
|
|
17
|
+
toa.io/composition: mono
|
|
18
|
+
template:
|
|
19
|
+
metadata:
|
|
20
|
+
labels:
|
|
21
|
+
toa.io/composition: mono
|
|
22
|
+
{{- range .components }}
|
|
23
|
+
{{ . }}: "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
|
+
periodSeconds: 10
|
|
79
|
+
timeoutSeconds: 3
|
|
80
|
+
failureThreshold: 3
|
|
81
|
+
{{- end }}
|
|
82
|
+
{{- if .mounts }}
|
|
83
|
+
volumeMounts:
|
|
84
|
+
{{- range .mounts }}
|
|
85
|
+
- name: {{ .name }}
|
|
86
|
+
mountPath: {{ .path }}
|
|
87
|
+
{{- end }}
|
|
88
|
+
{{- end }}
|
|
89
|
+
lifecycle:
|
|
90
|
+
preStop:
|
|
91
|
+
exec:
|
|
92
|
+
command: [sleep, "5"]
|
|
93
|
+
terminationGracePeriodSeconds: 45
|
|
94
|
+
{{- if $.Values.credentials }}
|
|
95
|
+
imagePullSecrets:
|
|
96
|
+
- name: {{ $.Values.credentials }}
|
|
97
|
+
{{- end }}
|
|
98
|
+
{{- if .mounts }}
|
|
99
|
+
volumes:
|
|
100
|
+
{{- range .mounts }}
|
|
101
|
+
- name: {{ .name }}
|
|
102
|
+
persistentVolumeClaim:
|
|
103
|
+
claimName: {{ .claim }}
|
|
104
|
+
{{- end }}
|
|
105
|
+
{{- end }}
|
|
106
|
+
topologySpreadConstraints:
|
|
107
|
+
- maxSkew: 1
|
|
108
|
+
topologyKey: kubernetes.io/hostname
|
|
109
|
+
whenUnsatisfiable: ScheduleAnyway
|
|
110
|
+
labelSelector:
|
|
111
|
+
matchLabels:
|
|
112
|
+
toa.io/composition: mono
|
|
113
|
+
{{- if .backends }}
|
|
114
|
+
---
|
|
115
|
+
apiVersion: v1
|
|
116
|
+
kind: Service
|
|
117
|
+
metadata:
|
|
118
|
+
name: mono
|
|
119
|
+
spec:
|
|
120
|
+
type: ClusterIP
|
|
121
|
+
selector:
|
|
122
|
+
toa.io/composition: mono
|
|
123
|
+
ports:
|
|
124
|
+
{{- range .backends }}
|
|
125
|
+
- name: port-{{ .port }}
|
|
126
|
+
protocol: TCP
|
|
127
|
+
port: {{ .port }}
|
|
128
|
+
targetPort: {{ .port }}
|
|
129
|
+
{{- end }}
|
|
130
|
+
{{- end }}
|
|
131
|
+
{{- if .ingress }}
|
|
132
|
+
---
|
|
133
|
+
apiVersion: networking.k8s.io/v1
|
|
134
|
+
kind: Ingress
|
|
135
|
+
metadata:
|
|
136
|
+
name: mono
|
|
137
|
+
{{- if .ingress.annotations }}
|
|
138
|
+
annotations:
|
|
139
|
+
{{ toYaml .ingress.annotations | indent 4 }}
|
|
140
|
+
{{- end }}
|
|
141
|
+
spec:
|
|
142
|
+
{{- if .ingress.class }}
|
|
143
|
+
ingressClassName: {{ .ingress.class }}
|
|
144
|
+
{{- end }}
|
|
145
|
+
{{- $backends := .backends }}
|
|
146
|
+
rules:
|
|
147
|
+
{{- range .ingress.hosts }}
|
|
148
|
+
- host: {{ . }}
|
|
149
|
+
http:
|
|
150
|
+
paths:
|
|
151
|
+
{{- range $backends }}
|
|
152
|
+
- path: {{ .path }}
|
|
153
|
+
pathType: Prefix
|
|
154
|
+
backend:
|
|
155
|
+
service:
|
|
156
|
+
name: mono
|
|
157
|
+
port:
|
|
158
|
+
number: {{ .port }}
|
|
159
|
+
{{- end }}
|
|
160
|
+
{{- end }}
|
|
161
|
+
{{- if .ingress.default }}
|
|
162
|
+
- http:
|
|
163
|
+
paths:
|
|
164
|
+
{{- range $backends }}
|
|
165
|
+
- path: {{ .path }}
|
|
166
|
+
pathType: Prefix
|
|
167
|
+
backend:
|
|
168
|
+
service:
|
|
169
|
+
name: mono
|
|
170
|
+
port:
|
|
171
|
+
number: {{ .port }}
|
|
172
|
+
{{- end }}
|
|
173
|
+
{{- end }}
|
|
174
|
+
{{- end }}
|
|
175
|
+
---
|
|
176
|
+
{{- end }}
|
|
177
|
+
{{- end }}
|
|
@@ -5,6 +5,16 @@ metadata:
|
|
|
5
5
|
name: extension-{{ required "deployment name is required" .name }}
|
|
6
6
|
spec:
|
|
7
7
|
replicas: {{ .replicas | default 2 }}
|
|
8
|
+
# A backstop for stuck rollouts only. Must stay above the `helm upgrade --timeout`
|
|
9
|
+
# the deploy runs with, so a slow but progressing rollout is not marked failed.
|
|
10
|
+
progressDeadlineSeconds: 900
|
|
11
|
+
strategy:
|
|
12
|
+
type: RollingUpdate
|
|
13
|
+
rollingUpdate:
|
|
14
|
+
# a replacement must pass readiness before a running replica is taken out,
|
|
15
|
+
# otherwise a single pod serves all traffic while the others start up
|
|
16
|
+
maxUnavailable: 0
|
|
17
|
+
maxSurge: 50%
|
|
8
18
|
selector:
|
|
9
19
|
matchLabels:
|
|
10
20
|
toa.io/service: extension-{{ .name }}
|
|
@@ -16,6 +26,25 @@ spec:
|
|
|
16
26
|
containers:
|
|
17
27
|
- name: extension-{{ .name }}
|
|
18
28
|
image: {{ .image }}
|
|
29
|
+
{{- if .resources }}
|
|
30
|
+
resources:
|
|
31
|
+
{{- if or .resources.cpu .resources.memory }}
|
|
32
|
+
requests:
|
|
33
|
+
{{- if .resources.cpu }}
|
|
34
|
+
cpu: {{ index .resources.cpu 0 }}
|
|
35
|
+
{{- end }}
|
|
36
|
+
{{- if .resources.memory }}
|
|
37
|
+
memory: {{ index .resources.memory 0 }}
|
|
38
|
+
{{- end }}
|
|
39
|
+
limits:
|
|
40
|
+
{{- if .resources.cpu }}
|
|
41
|
+
cpu: {{ index .resources.cpu 1 }}
|
|
42
|
+
{{- end }}
|
|
43
|
+
{{- if .resources.memory }}
|
|
44
|
+
memory: {{ index .resources.memory 1 }}
|
|
45
|
+
{{- end }}
|
|
46
|
+
{{- end }}
|
|
47
|
+
{{- end }}
|
|
19
48
|
{{- if .variables }}
|
|
20
49
|
env:
|
|
21
50
|
{{- range .variables }}
|
|
@@ -23,14 +52,37 @@ spec:
|
|
|
23
52
|
{{- end }}
|
|
24
53
|
{{- end }}
|
|
25
54
|
{{- if .probe }}
|
|
26
|
-
|
|
55
|
+
startupProbe:
|
|
27
56
|
httpGet:
|
|
28
57
|
path: {{ .probe.path }}
|
|
29
58
|
port: {{ .probe.port }}
|
|
30
59
|
{{- if .probe.delay }}
|
|
31
60
|
initialDelaySeconds: {{ .probe.delay }}
|
|
32
61
|
{{- end }}
|
|
62
|
+
periodSeconds: 2
|
|
63
|
+
timeoutSeconds: 3
|
|
64
|
+
failureThreshold: 150
|
|
65
|
+
readinessProbe:
|
|
66
|
+
httpGet:
|
|
67
|
+
path: {{ .probe.path }}
|
|
68
|
+
port: {{ .probe.port }}
|
|
69
|
+
periodSeconds: 10
|
|
70
|
+
timeoutSeconds: 3
|
|
71
|
+
failureThreshold: 3
|
|
33
72
|
{{- end }}
|
|
73
|
+
lifecycle:
|
|
74
|
+
preStop:
|
|
75
|
+
exec:
|
|
76
|
+
command: [sleep, "5"]
|
|
77
|
+
terminationGracePeriodSeconds: 45
|
|
78
|
+
topologySpreadConstraints:
|
|
79
|
+
- maxSkew: 1
|
|
80
|
+
topologyKey: kubernetes.io/hostname
|
|
81
|
+
whenUnsatisfiable: ScheduleAnyway
|
|
82
|
+
labelSelector:
|
|
83
|
+
matchLabels:
|
|
84
|
+
toa.io/service: extension-{{ .name }}
|
|
85
|
+
{{- if .port }}
|
|
34
86
|
---
|
|
35
87
|
apiVersion: v1
|
|
36
88
|
kind: Service
|
|
@@ -45,9 +97,12 @@ spec:
|
|
|
45
97
|
protocol: TCP
|
|
46
98
|
port: {{ .port }}
|
|
47
99
|
targetPort: {{ .port }}
|
|
48
|
-
|
|
100
|
+
{{- end }}
|
|
49
101
|
{{- if .ingress }}
|
|
50
102
|
{{- $service := .name }}
|
|
103
|
+
{{- $port := .port }}
|
|
104
|
+
{{- $path := .ingress.path | default "/" }}
|
|
105
|
+
---
|
|
51
106
|
apiVersion: networking.k8s.io/v1
|
|
52
107
|
kind: Ingress
|
|
53
108
|
metadata:
|
|
@@ -65,13 +120,25 @@ spec:
|
|
|
65
120
|
- host: {{ . }}
|
|
66
121
|
http:
|
|
67
122
|
paths:
|
|
68
|
-
- path:
|
|
123
|
+
- path: {{ $path }}
|
|
69
124
|
pathType: Prefix
|
|
70
125
|
backend:
|
|
71
126
|
service:
|
|
72
127
|
name: extension-{{ $service }}
|
|
73
128
|
port:
|
|
74
|
-
number:
|
|
129
|
+
number: {{ $port }}
|
|
130
|
+
{{- end }}
|
|
131
|
+
{{- if .ingress.default }}
|
|
132
|
+
- http:
|
|
133
|
+
paths:
|
|
134
|
+
- path: {{ $path }}
|
|
135
|
+
pathType: Prefix
|
|
136
|
+
backend:
|
|
137
|
+
service:
|
|
138
|
+
name: extension-{{ $service }}
|
|
139
|
+
port:
|
|
140
|
+
number: {{ $port }}
|
|
75
141
|
{{- end }}
|
|
76
142
|
{{- end }}
|
|
143
|
+
---
|
|
77
144
|
{{- 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,7 +25,6 @@ compositions:
|
|
|
18
25
|
replicas: 3
|
|
19
26
|
components:
|
|
20
27
|
- users-users
|
|
21
|
-
# TODO: create component services only if sync binding is being used
|
|
22
28
|
components:
|
|
23
29
|
- todos-tasks
|
|
24
30
|
- todos-stats
|
|
@@ -28,7 +34,11 @@ services:
|
|
|
28
34
|
image: localhost:5000/resources-gateway:0.0.0
|
|
29
35
|
port: 8000
|
|
30
36
|
replicas: 2
|
|
37
|
+
resources:
|
|
38
|
+
cpu: [100m, 1]
|
|
39
|
+
memory: [100Mi, 1Gi]
|
|
31
40
|
ingress:
|
|
41
|
+
default: true
|
|
32
42
|
hosts: [dummies.toa.io]
|
|
33
43
|
class: alb
|
|
34
44
|
annotations:
|
|
@@ -13,11 +13,11 @@ class Deployment {
|
|
|
13
13
|
#process
|
|
14
14
|
#target
|
|
15
15
|
|
|
16
|
-
constructor (context, compositions, dependencies, process) {
|
|
16
|
+
constructor (context, compositions, dependencies, process, image) {
|
|
17
17
|
const dependency = merge(dependencies)
|
|
18
18
|
|
|
19
19
|
this.#chart = declare(context, dependency)
|
|
20
|
-
this.#values = describe(context, compositions, dependency)
|
|
20
|
+
this.#values = describe(context, compositions, dependency, image)
|
|
21
21
|
this.#process = process
|
|
22
22
|
}
|
|
23
23
|
|
|
@@ -42,6 +42,7 @@ class Deployment {
|
|
|
42
42
|
|
|
43
43
|
if (options.namespace !== undefined) args.push('-n', options.namespace)
|
|
44
44
|
if (options.wait === true) args.push('--wait')
|
|
45
|
+
if (options.timeout !== undefined) args.push('--timeout', options.timeout)
|
|
45
46
|
|
|
46
47
|
await this.#process.execute('helm', ['dependency', 'update', this.#target])
|
|
47
48
|
await this.#process.execute('helm', ['upgrade', this.#chart.name, '-i', ...args, this.#target])
|
|
@@ -68,6 +69,9 @@ class Deployment {
|
|
|
68
69
|
addVariables(this.#values.compositions, variables, used)
|
|
69
70
|
addVariables(this.#values.services, variables, used)
|
|
70
71
|
|
|
72
|
+
if (this.#values.mono !== undefined)
|
|
73
|
+
addVariables([this.#values.mono], variables, used)
|
|
74
|
+
|
|
71
75
|
return variables
|
|
72
76
|
}
|
|
73
77
|
}
|
|
@@ -11,25 +11,40 @@ const { Service } = require('./service')
|
|
|
11
11
|
|
|
12
12
|
class Factory {
|
|
13
13
|
#context
|
|
14
|
+
#mono
|
|
14
15
|
#compositions
|
|
15
16
|
#dependencies
|
|
16
17
|
#registry
|
|
17
18
|
#process
|
|
18
|
-
#
|
|
19
|
+
#image
|
|
19
20
|
|
|
20
|
-
constructor (context) {
|
|
21
|
+
constructor (context, options = {}) {
|
|
21
22
|
this.#context = context
|
|
23
|
+
this.#mono = options.mono === true
|
|
22
24
|
this.#process = new Process()
|
|
23
25
|
|
|
24
26
|
const imagesFactory = new ImagesFactory(context.name, context.runtime, context.registry)
|
|
25
27
|
|
|
26
|
-
this.#registry = new Registry(context.registry, imagesFactory, this.#process)
|
|
27
|
-
this.#compositions = context.compositions.map((composition) => this.#composition(composition))
|
|
28
|
+
this.#registry = new Registry(context.name, context.registry, imagesFactory, this.#process)
|
|
28
29
|
this.#dependencies = this.#getDependencies()
|
|
30
|
+
this.#compositions = []
|
|
31
|
+
|
|
32
|
+
if (this.#mono)
|
|
33
|
+
this.#image = this.#registry.mono({
|
|
34
|
+
components: context.components
|
|
35
|
+
})
|
|
36
|
+
else
|
|
37
|
+
this.#compositions = context.compositions.map((composition) => this.#composition(composition))
|
|
29
38
|
}
|
|
30
39
|
|
|
31
40
|
operator () {
|
|
32
|
-
const deployment = new Deployment(
|
|
41
|
+
const deployment = new Deployment(
|
|
42
|
+
this.#context,
|
|
43
|
+
this.#compositions,
|
|
44
|
+
this.#dependencies,
|
|
45
|
+
this.#process,
|
|
46
|
+
this.#image
|
|
47
|
+
)
|
|
33
48
|
|
|
34
49
|
return new Operator(deployment, this.#registry)
|
|
35
50
|
}
|
|
@@ -71,7 +86,8 @@ class Factory {
|
|
|
71
86
|
const dependency = module.deployment(instances, annotation)
|
|
72
87
|
|
|
73
88
|
/** @type {toa.deployment.Service[]} */
|
|
74
|
-
const services = dependency.services?.map((service) =>
|
|
89
|
+
const services = dependency.services?.map((service) =>
|
|
90
|
+
this.#mono ? service : this.#service(path, service))
|
|
75
91
|
|
|
76
92
|
return { ...dependency, services }
|
|
77
93
|
}
|
|
@@ -87,10 +103,10 @@ class Factory {
|
|
|
87
103
|
return new Service(service, image)
|
|
88
104
|
}
|
|
89
105
|
|
|
90
|
-
static async create (path, environment) {
|
|
106
|
+
static async create (path, environment, options = {}) {
|
|
91
107
|
const context = await load(path, environment)
|
|
92
108
|
|
|
93
|
-
return new Factory(context)
|
|
109
|
+
return new Factory(context, options)
|
|
94
110
|
}
|
|
95
111
|
}
|
|
96
112
|
|
|
@@ -5,7 +5,6 @@ FROM {{build.image}}
|
|
|
5
5
|
ENV NODE_ENV=production
|
|
6
6
|
RUN if [ "{{runtime.registry}}" != "" ]; then npm set registry {{runtime.registry}}; fi
|
|
7
7
|
RUN if [ "{{runtime.proxy}}" != "" ]; then npm set proxy {{runtime.proxy}}; fi
|
|
8
|
-
RUN npm i -g @toa.io/runtime@{{runtime.version}} --omit=dev
|
|
9
8
|
|
|
10
9
|
WORKDIR /composition
|
|
11
10
|
COPY --chown=node:node . /composition
|
|
@@ -13,7 +12,8 @@ COPY --chown=node:node . /composition
|
|
|
13
12
|
{{build.run}}
|
|
14
13
|
|
|
15
14
|
# run 'npm i' in each component
|
|
16
|
-
RUN
|
|
15
|
+
RUN --mount=type=cache,target=/root/.npm \
|
|
16
|
+
for entry in *; do if [ -f "$entry/package.json" ]; then (cd $entry && npm i --omit=dev); fi; done
|
|
17
17
|
|
|
18
18
|
USER node
|
|
19
19
|
CMD toa compose *
|
|
@@ -5,6 +5,7 @@ const fs = require('fs-extra')
|
|
|
5
5
|
const { createHash } = require('node:crypto')
|
|
6
6
|
|
|
7
7
|
const { Image } = require('./image')
|
|
8
|
+
const { undef } = require('@toa.io/concise/source/expressions/undefined')
|
|
8
9
|
|
|
9
10
|
class Composition extends Image {
|
|
10
11
|
dockerfile = join(__dirname, 'composition.Dockerfile')
|
|
@@ -37,11 +38,7 @@ class Composition extends Image {
|
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
get base () {
|
|
40
|
-
|
|
41
|
-
return this.#image
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
let image = null
|
|
41
|
+
let image = this.#image
|
|
45
42
|
|
|
46
43
|
for (const component of this.#components) {
|
|
47
44
|
const value = component.build?.image
|
|
@@ -53,7 +50,20 @@ class Composition extends Image {
|
|
|
53
50
|
image = value
|
|
54
51
|
}
|
|
55
52
|
|
|
56
|
-
return image
|
|
53
|
+
return image
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
get run () {
|
|
57
|
+
const commands = []
|
|
58
|
+
|
|
59
|
+
for (const component of this.#components) {
|
|
60
|
+
const run = component.build?.run
|
|
61
|
+
|
|
62
|
+
if (run !== undefined)
|
|
63
|
+
commands.push(run)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return commands.join('\n')
|
|
57
67
|
}
|
|
58
68
|
|
|
59
69
|
async prepare (root) {
|
|
@@ -2,10 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
const { Composition } = require('./composition')
|
|
4
4
|
const { Service } = require('./service')
|
|
5
|
+
const { Mono } = require('./mono')
|
|
5
6
|
|
|
6
|
-
/**
|
|
7
|
-
* @implements {toa.deployment.images.Factory}
|
|
8
|
-
*/
|
|
9
7
|
class Factory {
|
|
10
8
|
/** @type {string} */
|
|
11
9
|
#scope
|
|
@@ -48,6 +46,17 @@ class Factory {
|
|
|
48
46
|
|
|
49
47
|
return instance
|
|
50
48
|
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @returns {Mono}
|
|
52
|
+
*/
|
|
53
|
+
mono (composition) {
|
|
54
|
+
const instance = new Mono(this.#scope, this.#runtime, this.#registry, composition)
|
|
55
|
+
|
|
56
|
+
instance.tag()
|
|
57
|
+
|
|
58
|
+
return instance
|
|
59
|
+
}
|
|
51
60
|
}
|
|
52
61
|
|
|
53
62
|
exports.Factory = Factory
|
|
@@ -26,9 +26,7 @@ class Image {
|
|
|
26
26
|
#registry
|
|
27
27
|
#runtime
|
|
28
28
|
#values = {
|
|
29
|
-
build: {
|
|
30
|
-
image: 'node:20.9.0-alpine3.18'
|
|
31
|
-
}
|
|
29
|
+
build: {}
|
|
32
30
|
}
|
|
33
31
|
|
|
34
32
|
constructor (scope, runtime, registry) {
|
|
@@ -54,6 +52,8 @@ class Image {
|
|
|
54
52
|
|
|
55
53
|
get base () {}
|
|
56
54
|
|
|
55
|
+
get run () {}
|
|
56
|
+
|
|
57
57
|
async prepare (root) {
|
|
58
58
|
if (this.dockerfile === undefined) throw new Error('Dockerfile isn\'t specified')
|
|
59
59
|
|
|
@@ -65,7 +65,7 @@ class Image {
|
|
|
65
65
|
|
|
66
66
|
const template = await read(this.dockerfile, 'utf-8')
|
|
67
67
|
const contents = template.replace(/{{(\S{1,32})}}/g, (_, key) => this.#value(key))
|
|
68
|
-
const ignore = 'Dockerfile'
|
|
68
|
+
const ignore = ['Dockerfile', '**/node_modules'].join('\r\n')
|
|
69
69
|
|
|
70
70
|
await write(join(path, 'Dockerfile'), contents)
|
|
71
71
|
await write(join(path, '.dockerignore'), ignore)
|
|
@@ -77,13 +77,19 @@ class Image {
|
|
|
77
77
|
|
|
78
78
|
#setValues () {
|
|
79
79
|
this.#values.runtime = this.#runtime
|
|
80
|
-
this.#values.build = overwrite(
|
|
80
|
+
this.#values.build = overwrite({
|
|
81
|
+
image: `${RUNTIME_IMAGE}:${this.#runtime.version}`
|
|
82
|
+
}, this.#registry.build)
|
|
81
83
|
|
|
82
84
|
const image = this.base
|
|
83
85
|
|
|
84
|
-
if (image !== undefined)
|
|
86
|
+
if (image !== undefined)
|
|
85
87
|
this.#values.build.image = image
|
|
86
|
-
|
|
88
|
+
|
|
89
|
+
const run = this.run
|
|
90
|
+
|
|
91
|
+
if (run !== undefined)
|
|
92
|
+
this.#values.build.run = (this.#values.build.run === undefined ? '' : this.#values.build.run + '\n') + run
|
|
87
93
|
|
|
88
94
|
if (this.#values.build.arguments !== undefined) this.#values.build.arguments = createArguments(this.#values.build.arguments)
|
|
89
95
|
if (this.#values.build.run !== undefined) this.#values.build.run = createRunCommands(this.#values.build.run)
|
|
@@ -121,4 +127,7 @@ function createArguments (variables) {
|
|
|
121
127
|
return args.join('\n')
|
|
122
128
|
}
|
|
123
129
|
|
|
130
|
+
const RUNTIME_IMAGE = 'ghcr.io/toa-io/runtime'
|
|
131
|
+
|
|
124
132
|
exports.Image = Image
|
|
133
|
+
exports.RUNTIME_IMAGE = RUNTIME_IMAGE
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
FROM {{build.image}}
|
|
2
|
+
|
|
3
|
+
{{build.arguments}}
|
|
4
|
+
|
|
5
|
+
ENV NODE_ENV=production
|
|
6
|
+
RUN if [ "{{runtime.registry}}" != "" ]; then npm set registry {{runtime.registry}}; fi
|
|
7
|
+
RUN if [ "{{runtime.proxy}}" != "" ]; then npm set proxy {{runtime.proxy}}; fi
|
|
8
|
+
|
|
9
|
+
WORKDIR /composition
|
|
10
|
+
COPY --chown=node:node . /composition
|
|
11
|
+
|
|
12
|
+
{{build.run}}
|
|
13
|
+
|
|
14
|
+
RUN --mount=type=cache,target=/root/.npm \
|
|
15
|
+
for entry in *; do if [ -f "$entry/package.json" ]; then (cd $entry && npm i --omit=dev); fi; done
|
|
16
|
+
|
|
17
|
+
USER node
|
|
18
|
+
CMD toa mono *
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { join } = require('node:path')
|
|
4
|
+
const fs = require('fs-extra')
|
|
5
|
+
const { createHash } = require('node:crypto')
|
|
6
|
+
|
|
7
|
+
const { Image } = require('./image')
|
|
8
|
+
|
|
9
|
+
class Mono extends Image {
|
|
10
|
+
dockerfile = join(__dirname, 'mono.Dockerfile')
|
|
11
|
+
|
|
12
|
+
#image
|
|
13
|
+
#components
|
|
14
|
+
|
|
15
|
+
constructor (scope, runtime, registry, composition) {
|
|
16
|
+
super(scope, runtime, registry)
|
|
17
|
+
|
|
18
|
+
this.#image = composition.image
|
|
19
|
+
this.#components = composition.components
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
get name () {
|
|
23
|
+
return 'mono'
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
get version () {
|
|
27
|
+
const hash = createHash('sha256')
|
|
28
|
+
|
|
29
|
+
for (const component of this.#components) {
|
|
30
|
+
hash.update(component.locator.id)
|
|
31
|
+
hash.update(component.version)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return hash.digest('hex').slice(0, 8)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
get base () {
|
|
38
|
+
let image = this.#image
|
|
39
|
+
|
|
40
|
+
for (const component of this.#components) {
|
|
41
|
+
const value = component.build?.image
|
|
42
|
+
|
|
43
|
+
if (image !== null && image !== value)
|
|
44
|
+
throw new Error('Mono deployment requires different base images for its components. Specify base image for the composition in the context.')
|
|
45
|
+
|
|
46
|
+
image = value
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return image
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
get run () {
|
|
53
|
+
const commands = []
|
|
54
|
+
|
|
55
|
+
for (const component of this.#components) {
|
|
56
|
+
const run = component.build?.run
|
|
57
|
+
|
|
58
|
+
if (run !== undefined)
|
|
59
|
+
commands.push(run)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return commands.join('\n')
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async prepare (root) {
|
|
66
|
+
const context = await super.prepare(root)
|
|
67
|
+
|
|
68
|
+
for (const component of this.#components)
|
|
69
|
+
await fs.copy(component.path, join(context, component.locator.label))
|
|
70
|
+
|
|
71
|
+
return context
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
exports.Mono = Mono
|