@toa.io/operations 1.0.0-alpha.31 → 1.0.0-alpha.311
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 +221 -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
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# Change Log
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
|
+
|
|
6
|
+
# [1.0.0-alpha.311](https://github.com/toa-io/toa/compare/v1.0.0-alpha.310...v1.0.0-alpha.311) (2026-09-17)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @toa.io/operations
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# [1.0.0-alpha.310](https://github.com/toa-io/toa/compare/v1.0.0-alpha.309...v1.0.0-alpha.310) (2026-09-16)
|
|
15
|
+
|
|
16
|
+
**Note:** Version bump only for package @toa.io/operations
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# [1.0.0-alpha.309](https://github.com/toa-io/toa/compare/v1.0.0-alpha.308...v1.0.0-alpha.309) (2026-09-16)
|
|
23
|
+
|
|
24
|
+
**Note:** Version bump only for package @toa.io/operations
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# [1.0.0-alpha.308](https://github.com/toa-io/toa/compare/v1.0.0-alpha.307...v1.0.0-alpha.308) (2026-09-15)
|
|
31
|
+
|
|
32
|
+
**Note:** Version bump only for package @toa.io/operations
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# [1.0.0-alpha.307](https://github.com/toa-io/toa/compare/v1.0.0-alpha.306...v1.0.0-alpha.307) (2026-09-15)
|
|
39
|
+
|
|
40
|
+
**Note:** Version bump only for package @toa.io/operations
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
# [1.0.0-alpha.306](https://github.com/toa-io/toa/compare/v1.0.0-alpha.305...v1.0.0-alpha.306) (2026-09-14)
|
|
47
|
+
|
|
48
|
+
**Note:** Version bump only for package @toa.io/operations
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
# [1.0.0-alpha.305](https://github.com/toa-io/toa/compare/v1.0.0-alpha.304...v1.0.0-alpha.305) (2026-09-13)
|
|
55
|
+
|
|
56
|
+
**Note:** Version bump only for package @toa.io/operations
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
# [1.0.0-alpha.304](https://github.com/toa-io/toa/compare/v1.0.0-alpha.303...v1.0.0-alpha.304) (2026-09-13)
|
|
63
|
+
|
|
64
|
+
**Note:** Version bump only for package @toa.io/operations
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
# [1.0.0-alpha.303](https://github.com/toa-io/toa/compare/v1.0.0-alpha.302...v1.0.0-alpha.303) (2026-09-12)
|
|
71
|
+
|
|
72
|
+
### Features
|
|
73
|
+
|
|
74
|
+
* **operations:** a composition states how many replicas it runs ([4466f5a](https://github.com/toa-io/toa/commit/4466f5abcc22c6dceb2994a0143b6bf9d5201d9d))
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
# [1.0.0-alpha.302](https://github.com/toa-io/toa/compare/v1.0.0-alpha.301...v1.0.0-alpha.302) (2026-09-11)
|
|
78
|
+
|
|
79
|
+
**Note:** Version bump only for package @toa.io/operations
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# [1.0.0-alpha.301](https://github.com/toa-io/toa/compare/v1.0.0-alpha.300...v1.0.0-alpha.301) (2026-09-11)
|
|
86
|
+
|
|
87
|
+
### Bug Fixes
|
|
88
|
+
|
|
89
|
+
* **operations:** a composition of evicted components asks for no resources ([a6a3ec7](https://github.com/toa-io/toa/commit/a6a3ec76781812f5a9bdd46c6de0fe7769174d57))
|
|
90
|
+
|
|
91
|
+
### Features
|
|
92
|
+
|
|
93
|
+
* **operations:** an evicted component is given its environment and configuration ([6cf7890](https://github.com/toa-io/toa/commit/6cf7890a632a2185d2b7d9790890a04187e5a897))
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
# [1.0.0-alpha.300](https://github.com/toa-io/toa/compare/v1.0.0-alpha.299...v1.0.0-alpha.300) (2026-09-11)
|
|
97
|
+
|
|
98
|
+
* feat(core)!: an operation may ask to run once ([dca616c](https://github.com/toa-io/toa/commit/dca616cb380a7a389e277875d3bd5ca55a6e4d45))
|
|
99
|
+
|
|
100
|
+
### Features
|
|
101
|
+
|
|
102
|
+
* **cli:** generate env for listed components and services ([b830613](https://github.com/toa-io/toa/commit/b830613ae2a0443bf8e72a353ffb6f2efaa72fb5))
|
|
103
|
+
* **core:** a call may name the process it goes to, and wait for a set time ([7d8adb2](https://github.com/toa-io/toa/commit/7d8adb2772a95ff45ae5374efdedfb5a857fbbe9))
|
|
104
|
+
* **norm:** a context says what it does not deploy ([135e6fd](https://github.com/toa-io/toa/commit/135e6fdfbc4270762028e9e5a0516766960ce59c))
|
|
105
|
+
* **operations:** a rollout spreads the pods of its revision across nodes ([63be649](https://github.com/toa-io/toa/commit/63be649a73978a415f0c1e7ffb3837ffa7802da7))
|
|
106
|
+
|
|
107
|
+
### BREAKING CHANGES
|
|
108
|
+
|
|
109
|
+
* `Storage.store`, `.upsert` and `.ensure` take one more
|
|
110
|
+
argument, and `Storage` has two more members. A connector that ignores them
|
|
111
|
+
works exactly as it did; a component that asks it for `once` is refused at
|
|
112
|
+
boot. See migrations/299.md.
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
# [1.0.0-alpha.299](https://github.com/toa-io/toa/compare/v1.0.0-alpha.298...v1.0.0-alpha.299) (2026-09-10)
|
|
116
|
+
|
|
117
|
+
**Note:** Version bump only for package @toa.io/operations
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
# [1.0.0-alpha.298](https://github.com/toa-io/toa/compare/v1.0.0-alpha.297...v1.0.0-alpha.298) (2026-09-08)
|
|
124
|
+
|
|
125
|
+
### Features
|
|
126
|
+
|
|
127
|
+
* **operations:** tag a deploy with the environment so registries can prune ([7247c35](https://github.com/toa-io/toa/commit/7247c35f33fb34dcfb3ffa04b196c14bfb3d5620))
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
# [1.0.0-alpha.297](https://github.com/toa-io/toa/compare/v1.0.0-alpha.296...v1.0.0-alpha.297) (2026-09-08)
|
|
131
|
+
|
|
132
|
+
**Note:** Version bump only for package @toa.io/operations
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
# [1.0.0-alpha.296](https://github.com/toa-io/toa/compare/v1.0.0-alpha.295...v1.0.0-alpha.296) (2026-09-08)
|
|
139
|
+
|
|
140
|
+
### Bug Fixes
|
|
141
|
+
|
|
142
|
+
* **exposition:** the gateway's image carries every storage provider ([ab19d25](https://github.com/toa-io/toa/commit/ab19d2567764d5a3148b067f186819d7e646908f))
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
# [1.0.0-alpha.295](https://github.com/toa-io/toa/compare/v1.0.0-alpha.294...v1.0.0-alpha.295) (2026-09-07)
|
|
146
|
+
|
|
147
|
+
**Note:** Version bump only for package @toa.io/operations
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
# [1.0.0-alpha.294](https://github.com/toa-io/toa/compare/v1.0.0-alpha.293...v1.0.0-alpha.294) (2026-09-07)
|
|
154
|
+
|
|
155
|
+
**Note:** Version bump only for package @toa.io/operations
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
# [1.0.0-alpha.293](https://github.com/toa-io/toa/compare/v1.0.0-alpha.292...v1.0.0-alpha.293) (2026-09-07)
|
|
162
|
+
|
|
163
|
+
**Note:** Version bump only for package @toa.io/operations
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
# [1.0.0-alpha.292](https://github.com/toa-io/toa/compare/v1.0.0-alpha.291...v1.0.0-alpha.292) (2026-09-07)
|
|
170
|
+
|
|
171
|
+
### Features
|
|
172
|
+
|
|
173
|
+
* **cli:** the deployment library is a peer, and the binary is the CLI's ([a923f81](https://github.com/toa-io/toa/commit/a923f8173a55ed1c091213a7c7db1863ca1d0ba6))
|
|
174
|
+
* **norm:** a package's definition is read by one resolver ([bffd41c](https://github.com/toa-io/toa/commit/bffd41c8d18c42786b0bf321623088b9dabc4724))
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
# [1.0.0-alpha.291](https://github.com/toa-io/toa/compare/v1.0.0-alpha.290...v1.0.0-alpha.291) (2026-09-07)
|
|
178
|
+
|
|
179
|
+
**Note:** Version bump only for package @toa.io/operations
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
# [1.0.0-alpha.290](https://github.com/toa-io/toa/compare/v1.0.0-alpha.289...v1.0.0-alpha.290) (2026-09-07)
|
|
186
|
+
|
|
187
|
+
* fix(operations)!: an image starts as root again ([9eb0db7](https://github.com/toa-io/toa/commit/9eb0db75e3d02098fe75710280e8e15e63b73d57))
|
|
188
|
+
* The runtime's environment is not a component's (#1070), closes [#1070](https://github.com/toa-io/toa/issues/1070)
|
|
189
|
+
|
|
190
|
+
### BREAKING CHANGES
|
|
191
|
+
|
|
192
|
+
* a component that read `process.env.TOA_*` reads `context` instead;
|
|
193
|
+
`echo(input)` no longer substitutes from the environment; a bash operation sees no
|
|
194
|
+
`TOA_*`; images no longer set `USER node` — see migrations/289.md.
|
|
195
|
+
* an image built by alpha.289 runs its composition as `node` and
|
|
196
|
+
leaves the deployed environment readable under /proc; rebuild on 290. Nothing in
|
|
197
|
+
a context or a manifest changes.
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
# [1.0.0-alpha.289](https://github.com/toa-io/toa/compare/v1.0.0-alpha.288...v1.0.0-alpha.289) (2026-09-07)
|
|
201
|
+
|
|
202
|
+
* A deploy moves only what changed, and a component sees none of the runtime's environment (#1073) ([f38e3db](https://github.com/toa-io/toa/commit/f38e3db533f24db866c57bfa1ef294eff1eaf499)), closes [#1073](https://github.com/toa-io/toa/issues/1073) [#1064](https://github.com/toa-io/toa/issues/1064) [#1066](https://github.com/toa-io/toa/issues/1066) [#1067](https://github.com/toa-io/toa/issues/1067) [#1068](https://github.com/toa-io/toa/issues/1068) [#1069](https://github.com/toa-io/toa/issues/1069) [#1071](https://github.com/toa-io/toa/issues/1071) [#1070](https://github.com/toa-io/toa/issues/1070) [#1072](https://github.com/toa-io/toa/issues/1072)
|
|
203
|
+
|
|
204
|
+
### BREAKING CHANGES
|
|
205
|
+
|
|
206
|
+
* a component that read `process.env.TOA_*` reads `context` instead;
|
|
207
|
+
`echo(input)` no longer substitutes from the environment; a bash operation sees no
|
|
208
|
+
`TOA_*`; images no longer set `USER node` — see migrations/289.md.
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
# [1.0.0-alpha.288](https://github.com/toa-io/toa/compare/v1.0.0-alpha.287...v1.0.0-alpha.288) (2026-09-06)
|
|
212
|
+
|
|
213
|
+
**Note:** Version bump only for package @toa.io/operations
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
# [1.0.0-alpha.287](https://github.com/toa-io/toa/compare/v1.0.0-alpha.286...v1.0.0-alpha.287) (2026-09-06)
|
|
220
|
+
|
|
221
|
+
**Note:** Version bump only for package @toa.io/operations
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
FROM node:24.14.0-alpine3.22
|
|
2
|
+
|
|
3
|
+
ARG VERSION
|
|
4
|
+
|
|
5
|
+
# installed locally rather than globally: the binary is @toa.io/cli's, which the runtime depends
|
|
6
|
+
# on, and npm links a dependency's binaries only into a local `node_modules/.bin`
|
|
7
|
+
WORKDIR /toa
|
|
8
|
+
|
|
9
|
+
# mounted rather than written: npm's cache is a hundred megabytes, and a layer keeps whatever
|
|
10
|
+
# the command left behind
|
|
11
|
+
RUN --mount=type=cache,target=/root/.npm,sharing=locked \
|
|
12
|
+
npm i @toa.io/runtime@${VERSION} --omit=dev
|
|
13
|
+
|
|
14
|
+
ENV PATH=/toa/node_modules/.bin:$PATH
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@toa.io/operations",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.311",
|
|
4
|
+
"type": "module",
|
|
4
5
|
"description": "Toa Deployment",
|
|
5
6
|
"homepage": "https://toa.io",
|
|
6
7
|
"author": {
|
|
@@ -16,23 +17,22 @@
|
|
|
16
17
|
"url": "https://github.com/toa-io/toa/issues"
|
|
17
18
|
},
|
|
18
19
|
"engines": {
|
|
19
|
-
"node": ">=
|
|
20
|
+
"node": ">= 24.0.0"
|
|
20
21
|
},
|
|
21
22
|
"publishConfig": {
|
|
22
23
|
"access": "public"
|
|
23
24
|
},
|
|
24
25
|
"main": "src/index.js",
|
|
25
|
-
"types": "types/index.
|
|
26
|
+
"types": "types/index.ts",
|
|
26
27
|
"scripts": {
|
|
27
28
|
"test": "echo \"Error: run tests from root\" && exit 1"
|
|
28
29
|
},
|
|
29
30
|
"dependencies": {
|
|
30
|
-
"@toa.io/
|
|
31
|
-
"@toa.io/
|
|
32
|
-
"@toa.io/norm": "1.0.0-alpha.
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"fs-extra": "11.1.1"
|
|
31
|
+
"@toa.io/generic": "1.0.0-alpha.311",
|
|
32
|
+
"@toa.io/kubernetes": "1.0.0-alpha.301",
|
|
33
|
+
"@toa.io/norm": "1.0.0-alpha.311",
|
|
34
|
+
"oxc-parser": "0.149.0",
|
|
35
|
+
"paseto": "4.0.1"
|
|
36
36
|
},
|
|
37
|
-
"gitHead": "
|
|
37
|
+
"gitHead": "4f45061964e275296a27ff8ff39f757ce27d4008"
|
|
38
38
|
}
|
package/readme.md
CHANGED
|
@@ -1,9 +1,167 @@
|
|
|
1
1
|
# Toa Operations
|
|
2
2
|
|
|
3
|
+
## Installing
|
|
4
|
+
|
|
5
|
+
`toa deploy`, `toa build`, `toa push`, `toa env`, `toa export` and `toa conceal` need this package
|
|
6
|
+
beside the CLI; the runtime does not carry it, and a container that runs a composition cannot
|
|
7
|
+
deploy one. An application that deploys lists it with the runtime:
|
|
8
|
+
|
|
9
|
+
```shell
|
|
10
|
+
$ npm i -D @toa.io/runtime @toa.io/operations
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
A machine that only deploys needs no runtime and no extension:
|
|
14
|
+
|
|
15
|
+
```shell
|
|
16
|
+
$ npm i @toa.io/cli @toa.io/operations
|
|
17
|
+
$ npx toa deploy production -p application
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
What the extensions declare is read from `@toa.io/definitions`, which the CLI brings; what they run
|
|
21
|
+
is not needed to render a chart. `registry.services: build` is the exception: it builds an
|
|
22
|
+
extension's service image from the installed package, so it needs the runtime installed beside
|
|
23
|
+
this one. `published` does not.
|
|
24
|
+
|
|
25
|
+
## Compositions
|
|
26
|
+
|
|
27
|
+
A composition is deployed as one pod. Beside its components it may run extension services,
|
|
28
|
+
which are otherwise each deployed on their own:
|
|
29
|
+
|
|
30
|
+
```yaml
|
|
31
|
+
# context.toa.yaml
|
|
32
|
+
|
|
33
|
+
compositions:
|
|
34
|
+
- name: edge
|
|
35
|
+
components:
|
|
36
|
+
- todos.tasks
|
|
37
|
+
services:
|
|
38
|
+
- exposition
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
See [compositions](../documentation/compositions.md).
|
|
42
|
+
|
|
43
|
+
## Evicted
|
|
44
|
+
|
|
45
|
+
What Toa does not deploy for a context, whatever a composition lists. It stays part of the
|
|
46
|
+
context, and is deployed by other means:
|
|
47
|
+
|
|
48
|
+
```yaml
|
|
49
|
+
# context.toa.yaml
|
|
50
|
+
|
|
51
|
+
evicted:
|
|
52
|
+
components:
|
|
53
|
+
- todos.stats
|
|
54
|
+
services:
|
|
55
|
+
- exposition
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Nothing an evicted component alone required is deployed either — its storage, the extensions it
|
|
59
|
+
declared. `evicted@production` states it for one environment.
|
|
60
|
+
|
|
61
|
+
See [compositions](../documentation/compositions.md#evicted).
|
|
62
|
+
|
|
3
63
|
## Context
|
|
4
64
|
|
|
5
65
|
### Container Registry
|
|
6
66
|
|
|
67
|
+
Deploy images default to `FROM ghcr.io/toa-io/runtime:<runtime.version>`
|
|
68
|
+
(published with each Toa release). The base image already has `@toa.io/runtime`
|
|
69
|
+
installed; composition/service Dockerfiles only install component dependencies.
|
|
70
|
+
|
|
71
|
+
To use a custom base image, set `registry.build.image` (or `composition.image`).
|
|
72
|
+
That image must provide the `toa` CLI, or install it via `registry.build.run`. The binary is
|
|
73
|
+
`@toa.io/cli`'s, which the runtime depends on, so a local install puts it on the `PATH`:
|
|
74
|
+
|
|
75
|
+
```yaml
|
|
76
|
+
# context.toa.yaml
|
|
77
|
+
|
|
78
|
+
registry:
|
|
79
|
+
build:
|
|
80
|
+
image: node:24.14.0-alpine3.22
|
|
81
|
+
run: |
|
|
82
|
+
npm i --prefix /toa @toa.io/runtime --omit=dev
|
|
83
|
+
ln -s /toa/node_modules/.bin/toa /usr/local/bin/toa
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
`/toa` is where it goes: what an extension needs for what a component declares is installed
|
|
87
|
+
there, beside the extension that reads the declaration.
|
|
88
|
+
|
|
89
|
+
#### Composition Images
|
|
90
|
+
|
|
91
|
+
A composition is two images in one repository. `composition-<name>:deps-<hash>` is what its
|
|
92
|
+
components depend on, installed on the base image: it is tagged by everything the install
|
|
93
|
+
reads — the runtime version, the base image, the build options, each component's
|
|
94
|
+
`package.json` (and `package-lock.json`, where there is one), and what the extensions install
|
|
95
|
+
for what the components declare — and is built only when one of those changes. `composition-<name>:<hash>` is the sources laid over it in a single linked
|
|
96
|
+
layer, so a deploy that changes code alone builds and pushes that layer, and neither
|
|
97
|
+
downloads nor uploads the dependencies again. The same holds for `mono`.
|
|
98
|
+
|
|
99
|
+
Beside each component's sources the layer carries `manifest.toa.json`, the manifest as this
|
|
100
|
+
build normalised it. A composition reads it instead of normalising again, so it loads no
|
|
101
|
+
bridge to read what a module declares; a workspace has no such file and is read as it always
|
|
102
|
+
was. A component an extension ships has none either — its package is published long before any
|
|
103
|
+
image is built — and is read from the digest `@toa.io/definitions` carries, which a deploy has
|
|
104
|
+
always read. So nothing that runs reads a module's source, and `oxc-parser` is installed only
|
|
105
|
+
where a manifest is normalised: a workspace and a deploy, both through `@toa.io/operations`. What a build cannot express there — a prototype in a directory of the application's own
|
|
106
|
+
rather than in a package — fails the build, where before it failed the container.
|
|
107
|
+
|
|
108
|
+
A pushed image builds on the `toa` container builder, whose registry exporter is what lays
|
|
109
|
+
an image over a base it never pulled; `toa build` loads on the daemon's own.
|
|
110
|
+
|
|
111
|
+
A dependency named by a moving git ref is installed when the dependencies image is built and
|
|
112
|
+
not again until its manifest changes, so pin such a dependency to a commit and bump it there.
|
|
113
|
+
|
|
114
|
+
An extension's heavy dependency is not installed with the extension: `@toa.io/extensions.storages`
|
|
115
|
+
declares the AWS and Cloudinary SDKs as optional peers, so the base image carries neither. What a
|
|
116
|
+
component declares is what a deploy installs — the storages named in its `manifest.toa.yaml`,
|
|
117
|
+
resolved through the context's annotation to their providers' packages, installed into `/toa`
|
|
118
|
+
beside the extension that reads them. A composition whose components declare no `s3` storage
|
|
119
|
+
carries no AWS SDK, whatever the context declares for another composition. A workspace installs
|
|
120
|
+
the same set with `toa npm`.
|
|
121
|
+
|
|
122
|
+
#### Extension Service Images
|
|
123
|
+
|
|
124
|
+
`registry.services` says where an extension service's image comes from.
|
|
125
|
+
|
|
126
|
+
`build`, the default, builds one per service into `registry.base`. Its tag carries the
|
|
127
|
+
runtime version, so every Toa release rebuilds them all.
|
|
128
|
+
|
|
129
|
+
`published` deploys the image the extension ships and builds nothing:
|
|
130
|
+
|
|
131
|
+
```yaml
|
|
132
|
+
# context.toa.yaml
|
|
133
|
+
|
|
134
|
+
registry:
|
|
135
|
+
services: published
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
The images are tagged with the runtime version:
|
|
139
|
+
|
|
140
|
+
```
|
|
141
|
+
ghcr.io/toa-io/extension-exposition-gateway:1.0.0-alpha.285
|
|
142
|
+
ghcr.io/toa-io/extension-realtime-streams:1.0.0-alpha.285
|
|
143
|
+
ghcr.io/toa-io/extension-introspection-explorer:1.0.0-alpha.285
|
|
144
|
+
ghcr.io/toa-io/extension-configuration-values:1.0.0-alpha.285
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
The cluster pulls them from `ghcr.io` rather than from `registry.base`. They are public and
|
|
148
|
+
`registry.credentials` does not apply; a cluster that cannot reach `ghcr.io` needs `build`.
|
|
149
|
+
|
|
150
|
+
The field is environment-scoped like the rest:
|
|
151
|
+
|
|
152
|
+
```yaml
|
|
153
|
+
# context.toa.yaml
|
|
154
|
+
|
|
155
|
+
registry:
|
|
156
|
+
base@local: localhost:5000
|
|
157
|
+
base@production: registry.digitalocean.com/acme
|
|
158
|
+
services: published
|
|
159
|
+
services@local: build
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
An extension that states no image is built whatever this says, as is a service a
|
|
163
|
+
composition runs in its own pod.
|
|
164
|
+
|
|
7
165
|
#### Build Options
|
|
8
166
|
|
|
9
167
|
```yaml
|
|
@@ -40,3 +198,46 @@ a secret containing required credentials can be specified using `registry.creden
|
|
|
40
198
|
registry:
|
|
41
199
|
credentials: docker-credentials-secret-name
|
|
42
200
|
```
|
|
201
|
+
|
|
202
|
+
#### Toa images
|
|
203
|
+
|
|
204
|
+
Each release publishes `ghcr.io/toa-io/runtime:<version>` and, beside it, `:alpha` or `:latest`.
|
|
205
|
+
The extension services share that version:
|
|
206
|
+
|
|
207
|
+
```
|
|
208
|
+
ghcr.io/toa-io/extension-exposition-gateway:1.0.0-alpha.285
|
|
209
|
+
ghcr.io/toa-io/extension-realtime-streams:1.0.0-alpha.285
|
|
210
|
+
ghcr.io/toa-io/extension-introspection-explorer:1.0.0-alpha.285
|
|
211
|
+
ghcr.io/toa-io/extension-configuration-values:1.0.0-alpha.285
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Untagged manifests are deleted. A prerelease older than 90 days, and not among the last 30, is
|
|
215
|
+
deleted with the four service images of that version. `:alpha`, `:latest`, and a version without
|
|
216
|
+
a pre-release suffix stay.
|
|
217
|
+
|
|
218
|
+
An application still on a deleted prerelease cannot pull `registry.services: published` and cannot
|
|
219
|
+
rebuild `FROM` that runtime. Images already in `registry.base` keep running. Stay inside the
|
|
220
|
+
window, or pin a graduated release.
|
|
221
|
+
|
|
222
|
+
#### Userspace images
|
|
223
|
+
|
|
224
|
+
`toa deploy production` writes two tags on the same composition image (`mono` and a service
|
|
225
|
+
`registry.services: build` built are the same):
|
|
226
|
+
|
|
227
|
+
- `<name>:<hash>` — what the chart pins. A hash of the runtime and the sources. `toa push` writes
|
|
228
|
+
this tag and nothing else.
|
|
229
|
+
- `<name>:production` — moved onto that image. The next production deploy moves it again.
|
|
230
|
+
|
|
231
|
+
`deps-<hash>` is not tagged with the environment. Do not name an environment eight hex digits, or
|
|
232
|
+
`deps-` plus eight: a retention job would treat it as a content tag.
|
|
233
|
+
|
|
234
|
+
A job the application runs deletes every `<hash>` and `deps-<hash>` older than 7 days, except the
|
|
235
|
+
last 5, except the image `:production` (or `:staging`, or `:latest`) currently names. Then:
|
|
236
|
+
|
|
237
|
+
```shell
|
|
238
|
+
$ doctl registries garbage-collection start --include-untagged-manifests <registry>
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Rollback is only those 7 days, those last 5, and whatever the environment tag still names. Turn
|
|
242
|
+
the job on after the first deploy that writes that tag. The first garbage collection returns the
|
|
243
|
+
space the old tags held, and blocks pushes while it runs.
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
2
|
* @param {toa.deployment.Composition[]} compositions
|
|
5
3
|
* @returns {string[]}
|
|
6
4
|
*/
|
|
7
|
-
const components = (compositions) => {
|
|
5
|
+
export const components = (compositions) => {
|
|
8
6
|
/** @type {Set<string>} */
|
|
9
7
|
const components = new Set()
|
|
10
8
|
|
|
@@ -16,5 +14,3 @@ const components = (compositions) => {
|
|
|
16
14
|
|
|
17
15
|
return Array.from(components)
|
|
18
16
|
}
|
|
19
|
-
|
|
20
|
-
exports.components = components
|
|
@@ -1,11 +1,30 @@
|
|
|
1
|
-
|
|
1
|
+
import { addVariables } from './variables.js'
|
|
2
|
+
import { addMounts } from './mounts.js'
|
|
3
|
+
import { fold } from './fold.js'
|
|
2
4
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
function compositions (compositions, variables) {
|
|
5
|
+
export function compositions(compositions, dependency) {
|
|
6
6
|
for (const composition of compositions) {
|
|
7
|
-
|
|
7
|
+
const claimed = (dependency.services ?? []).filter(
|
|
8
|
+
(service) => service.workload?.includes(composition.name) === true
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
// a service brings components of its own — the identity components inside the gateway —
|
|
12
|
+
// and their variables are keyed by their own labels, not by the composition's
|
|
13
|
+
const keys = composition.components.concat(
|
|
14
|
+
...claimed.map((service) => service.components ?? [])
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
addVariables(composition, dependency.variables, keys)
|
|
18
|
+
addMounts(composition, dependency.mounts, keys)
|
|
19
|
+
|
|
20
|
+
if (claimed.length > 0) {
|
|
21
|
+
// the label each hosted service's own Service selects on
|
|
22
|
+
composition.hosted = claimed.map((service) => service.name)
|
|
23
|
+
|
|
24
|
+
fold(composition, claimed, dependency)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (dependency.probe !== undefined && dependency.probe !== false)
|
|
28
|
+
composition.probe ??= dependency.probe
|
|
8
29
|
}
|
|
9
30
|
}
|
|
10
|
-
|
|
11
|
-
exports.compositions = compositions
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
2
|
* @param {toa.deployment.dependency.Reference[]} references
|
|
5
3
|
* @returns {*}
|
|
6
4
|
*/
|
|
7
|
-
const dependencies = (references) => {
|
|
5
|
+
export const dependencies = (references) => {
|
|
8
6
|
return references?.reduce((map, reference) => {
|
|
9
7
|
const { name, alias, values } = reference
|
|
10
8
|
|
|
@@ -13,6 +11,3 @@ const dependencies = (references) => {
|
|
|
13
11
|
return map
|
|
14
12
|
}, {})
|
|
15
13
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
exports.dependencies = dependencies
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tells each component which of its events something consumes. An event nobody consumes gets
|
|
3
|
+
* no emitter, no exchange and no outbox row, and a component none of whose events are consumed
|
|
4
|
+
* gets no outbox at all.
|
|
5
|
+
*
|
|
6
|
+
* A component that declares events always gets the variable, empty when nothing is consumed —
|
|
7
|
+
* an absent variable means every event, which is what a run without a deployment gets.
|
|
8
|
+
*
|
|
9
|
+
* @param {toa.norm.Context} context
|
|
10
|
+
* @param {toa.deployment.Dependency} dependency
|
|
11
|
+
*/
|
|
12
|
+
export function events(context, dependency) {
|
|
13
|
+
const components = deployed(context)
|
|
14
|
+
const consumed = collect(components, context.events, dependency.events)
|
|
15
|
+
|
|
16
|
+
for (const { locator, events } of components) {
|
|
17
|
+
if (events === undefined) continue
|
|
18
|
+
|
|
19
|
+
const labels = Object.keys(events).filter((label) =>
|
|
20
|
+
consumed.has(locator.id + '.' + label)
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
dependency.variables[locator.label] ??= []
|
|
24
|
+
|
|
25
|
+
dependency.variables[locator.label].push({
|
|
26
|
+
name: VARIABLE + locator.uppercase,
|
|
27
|
+
value: labels.join(' ')
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Every component this context deploys, its own and those its extensions bring. `components`
|
|
34
|
+
* holds only the former; the latter reach a deployment as instances of what they depend on,
|
|
35
|
+
* which is also how they are given the rest of their variables.
|
|
36
|
+
*/
|
|
37
|
+
function deployed(context) {
|
|
38
|
+
const components = new Map()
|
|
39
|
+
|
|
40
|
+
for (const instances of Object.values(context.dependencies ?? {}))
|
|
41
|
+
for (const { component } of instances) components.set(component.locator.id, component)
|
|
42
|
+
|
|
43
|
+
return [...components.values()]
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Everything that consumes an event of this context: the receivers of its own components, what
|
|
48
|
+
* a dependency declares it consumes, and what the context says is consumed outside it.
|
|
49
|
+
*/
|
|
50
|
+
function collect(components, declared, contributed) {
|
|
51
|
+
const consumed = new Set(declared ?? [])
|
|
52
|
+
|
|
53
|
+
for (const label of contributed ?? []) consumed.add(label)
|
|
54
|
+
|
|
55
|
+
for (const { receivers } of components)
|
|
56
|
+
for (const [label, receiver] of Object.entries(receivers ?? {}))
|
|
57
|
+
// a receiver with a source consumes from a foreign broker, not from this context
|
|
58
|
+
if (receiver.source === undefined) consumed.add(label)
|
|
59
|
+
|
|
60
|
+
return consumed
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const VARIABLE = 'TOA_EVENTS_'
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a workload takes on by running services in its own pod: their variables, the ports
|
|
3
|
+
* they bind, and a readiness probe answered by one of them rather than by the workload.
|
|
4
|
+
*
|
|
5
|
+
* What it does *not* take on is how they are reached. A composition-hosted service keeps
|
|
6
|
+
* its own Service and Ingress — only the pods behind them change — so the ingress union
|
|
7
|
+
* belongs to `mono`, the one workload that fronts every service under a single name.
|
|
8
|
+
*
|
|
9
|
+
* @param {object} workload
|
|
10
|
+
* @param {toa.deployment.dependency.Service[]} services
|
|
11
|
+
* @param {toa.deployment.Dependency} dependency
|
|
12
|
+
*/
|
|
13
|
+
export function fold(workload, services, dependency) {
|
|
14
|
+
workload.variables ??= []
|
|
15
|
+
|
|
16
|
+
for (const service of services) {
|
|
17
|
+
if (service.variables !== undefined)
|
|
18
|
+
for (const variable of service.variables)
|
|
19
|
+
if (!workload.variables.some((item) => item.name === variable.name))
|
|
20
|
+
workload.variables.push(variable)
|
|
21
|
+
|
|
22
|
+
// every declared port is bound by the single process, none is primary
|
|
23
|
+
if (service.port !== undefined)
|
|
24
|
+
(workload.backends ??= []).push({
|
|
25
|
+
port: service.port,
|
|
26
|
+
path: service.ingress?.path ?? '/'
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
if (service.probe !== undefined && service.probe !== false)
|
|
30
|
+
workload.probe = service.probe
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (
|
|
34
|
+
workload.probe === undefined &&
|
|
35
|
+
dependency.probe !== undefined &&
|
|
36
|
+
dependency.probe !== false
|
|
37
|
+
)
|
|
38
|
+
workload.probe = dependency.probe
|
|
39
|
+
|
|
40
|
+
// the more specific prefix must come first, whatever the controller's tie-break
|
|
41
|
+
workload.backends?.sort((a, b) => b.path.length - a.path.length)
|
|
42
|
+
}
|