@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.
Files changed (35) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/image/runtime.Dockerfile +4 -0
  3. package/package.json +8 -8
  4. package/readme.md +16 -0
  5. package/src/deployment/.deployment/.describe/compositions.js +7 -2
  6. package/src/deployment/.deployment/.describe/mounts.js +24 -0
  7. package/src/deployment/.deployment/.describe/services.js +28 -1
  8. package/src/deployment/.deployment/.describe/services.test.js +61 -0
  9. package/src/deployment/.deployment/.describe/variables.js +6 -5
  10. package/src/deployment/.deployment/describe.js +74 -5
  11. package/src/deployment/.deployment/merge.js +41 -1
  12. package/src/deployment/.deployment/merge.test.js +74 -0
  13. package/src/deployment/chart/templates/compositions.yaml +70 -0
  14. package/src/deployment/chart/templates/mono.yaml +177 -0
  15. package/src/deployment/chart/templates/services.yaml +71 -4
  16. package/src/deployment/chart/values.yaml +11 -1
  17. package/src/deployment/composition.js +1 -0
  18. package/src/deployment/deployment.js +6 -2
  19. package/src/deployment/factory.js +24 -8
  20. package/src/deployment/images/composition.Dockerfile +2 -2
  21. package/src/deployment/images/composition.js +16 -6
  22. package/src/deployment/images/factory.js +12 -3
  23. package/src/deployment/images/image.js +16 -7
  24. package/src/deployment/images/mono.Dockerfile +18 -0
  25. package/src/deployment/images/mono.js +75 -0
  26. package/src/deployment/images/runtime-base.test.js +90 -0
  27. package/src/deployment/images/service.Dockerfile +3 -3
  28. package/src/deployment/operator.js +4 -0
  29. package/src/deployment/registry.js +64 -16
  30. package/src/deployment/registry.test.js +175 -0
  31. package/types/_deployment/images/image.d.ts +4 -2
  32. package/types/_deployment/registry.d.ts +9 -7
  33. package/types/dependency.ts +69 -0
  34. package/types/dependency.d.ts +0 -39
  35. /package/types/{index.d.ts → index.ts} +0 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,66 @@
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.262](https://github.com/toa-io/toa/compare/v1.0.0-alpha.261...v1.0.0-alpha.262) (2026-08-28)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * **cli:** boot toa mono from components and env, not context ([1518f72](https://github.com/toa-io/toa/commit/1518f729d7611ae88e0538f286231d283a7098ef))
12
+
13
+
14
+ * feat(operations)!: require service ports to be unique ([b22cf87](https://github.com/toa-io/toa/commit/b22cf8770d24c555b5f355a5f117a9043a57799b))
15
+
16
+
17
+ ### Features
18
+
19
+ * **operations:** let a service claim a path prefix on the host ([02677d1](https://github.com/toa-io/toa/commit/02677d17fcd4222b07c3be458d93bcc496a016a8))
20
+
21
+
22
+ ### BREAKING CHANGES
23
+
24
+ * an application whose own extension runs a service on a port
25
+ already taken by another — 8000 by the exposition gateway, 8001 by the telemetry
26
+ readiness probe — must move it.
27
+
28
+ Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
29
+
30
+
31
+
32
+
33
+
34
+ # [1.0.0-alpha.261](https://github.com/toa-io/toa/compare/v1.0.0-alpha.260...v1.0.0-alpha.261) (2026-08-26)
35
+
36
+
37
+ ### Bug Fixes
38
+
39
+ * **operations:** include extension pointer variables in --mono ([f8be23d](https://github.com/toa-io/toa/commit/f8be23d2aaf7bbe1b0580101c8bf4d32c3cab99f))
40
+
41
+
42
+
43
+
44
+
45
+ # [1.0.0-alpha.259](https://github.com/toa-io/toa/compare/v1.0.0-alpha.258...v1.0.0-alpha.259) (2026-08-24)
46
+
47
+ **Note:** Version bump only for package @toa.io/operations
48
+
49
+
50
+
51
+
52
+
53
+ # [1.0.0-alpha.257](https://github.com/toa-io/toa/compare/v1.0.0-alpha.256...v1.0.0-alpha.257) (2026-08-24)
54
+
55
+ **Note:** Version bump only for package @toa.io/operations
56
+
57
+
58
+
59
+
60
+
61
+ # [1.0.0-alpha.256](https://github.com/toa-io/toa/compare/v1.0.0-alpha.255...v1.0.0-alpha.256) (2026-08-23)
62
+
63
+
64
+ ### Features
65
+
66
+ * deploy a single mono image with toa deploy --mono ([a7f14f9](https://github.com/toa-io/toa/commit/a7f14f9877a280e9c74d16c195028b35d74fb15a))
@@ -0,0 +1,4 @@
1
+ FROM node:24.14.0-alpine3.22
2
+
3
+ ARG VERSION
4
+ RUN npm i -g @toa.io/runtime@${VERSION} --omit=dev
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toa.io/operations",
3
- "version": "1.0.0-alpha.26",
3
+ "version": "1.0.0-alpha.262",
4
4
  "description": "Toa Deployment",
5
5
  "homepage": "https://toa.io",
6
6
  "author": {
@@ -16,23 +16,23 @@
16
16
  "url": "https://github.com/toa-io/toa/issues"
17
17
  },
18
18
  "engines": {
19
- "node": ">= 18.0.0"
19
+ "node": ">= 24.0.0"
20
20
  },
21
21
  "publishConfig": {
22
22
  "access": "public"
23
23
  },
24
24
  "main": "src/index.js",
25
- "types": "types/index.d.ts",
25
+ "types": "types/index.ts",
26
26
  "scripts": {
27
27
  "test": "echo \"Error: run tests from root\" && exit 1"
28
28
  },
29
29
  "dependencies": {
30
- "@toa.io/filesystem": "1.0.0-alpha.26",
31
- "@toa.io/generic": "1.0.0-alpha.26",
32
- "@toa.io/norm": "1.0.0-alpha.26",
33
- "@toa.io/yaml": "1.0.0-alpha.26",
30
+ "@toa.io/filesystem": "1.0.0-alpha.254",
31
+ "@toa.io/generic": "1.0.0-alpha.254",
32
+ "@toa.io/norm": "1.0.0-alpha.262",
33
+ "@toa.io/yaml": "1.0.0-alpha.254",
34
34
  "execa": "5.1.1",
35
35
  "fs-extra": "11.1.1"
36
36
  },
37
- "gitHead": "0ddd9260762d1fe2b8be88eb7b9725e3f7838299"
37
+ "gitHead": "e14059cf5cc364d933f2a4b3d7114f3b5ec0764f"
38
38
  }
package/readme.md CHANGED
@@ -4,6 +4,22 @@
4
4
 
5
5
  ### Container Registry
6
6
 
7
+ Deploy images default to `FROM ghcr.io/toa-io/runtime:<runtime.version>`
8
+ (published with each Toa release). The base image already has `@toa.io/runtime`
9
+ installed; composition/service Dockerfiles only install component dependencies.
10
+
11
+ To use a custom base image, set `registry.build.image` (or `composition.image`).
12
+ That image must provide the `toa` CLI, or install it via `registry.build.run`:
13
+
14
+ ```yaml
15
+ # context.toa.yaml
16
+
17
+ registry:
18
+ build:
19
+ image: node:24.14.0-alpine3.22
20
+ run: npm i -g @toa.io/runtime --omit=dev
21
+ ```
22
+
7
23
  #### Build Options
8
24
 
9
25
  ```yaml
@@ -1,10 +1,15 @@
1
1
  'use strict'
2
2
 
3
3
  const { addVariables } = require('./variables')
4
+ const { addMounts } = require('./mounts')
4
5
 
5
- function compositions (compositions, variables) {
6
+ function compositions (compositions, dependency) {
6
7
  for (const composition of compositions) {
7
- addVariables(composition, variables)
8
+ addVariables(composition, dependency.variables)
9
+ addMounts(composition, dependency.mounts)
10
+
11
+ if (dependency.probe !== undefined && dependency.probe !== false)
12
+ composition.probe ??= dependency.probe
8
13
  }
9
14
  }
10
15
 
@@ -0,0 +1,24 @@
1
+ 'use strict'
2
+
3
+ function addMounts (composition, mounts, keys = composition.components) {
4
+ if (mounts === undefined)
5
+ return
6
+
7
+ const used = new Set()
8
+
9
+ for (const [key, mount] of Object.entries(mounts)) {
10
+ if (key !== 'global' && !keys?.includes(key))
11
+ continue
12
+
13
+ for (const { name, path, claim } of mount) {
14
+ if (used.has(name))
15
+ continue
16
+
17
+ composition.mounts ??= []
18
+ composition.mounts.push({ name, path, claim })
19
+ used.add(name)
20
+ }
21
+ }
22
+ }
23
+
24
+ exports.addMounts = addMounts
@@ -2,10 +2,37 @@
2
2
 
3
3
  const { addVariables } = require('./variables')
4
4
 
5
- function services (services, variables) {
5
+ function services (services, variables, probe, ingress) {
6
6
  for (const service of services) {
7
7
  addVariables(service, variables)
8
+
9
+ if (service.probe === false)
10
+ delete service.probe
11
+ else if (service.probe === undefined && probe !== undefined && probe !== false)
12
+ service.probe = probe
13
+
14
+ if (service.ingress !== undefined)
15
+ expose(service, ingress)
8
16
  }
9
17
  }
10
18
 
19
+ /**
20
+ * A service declares only its own intent — a path, a port. Where that lands is
21
+ * the context's business, so everything else comes from its `ingress` section.
22
+ * What the service did declare wins.
23
+ */
24
+ function expose (service, ingress = {}) {
25
+ const declared = Object.fromEntries(
26
+ Object.entries(service.ingress).filter(([, value]) => value !== undefined))
27
+
28
+ service.ingress = Object.assign({}, ingress, declared)
29
+
30
+ if (service.ingress.hosts === undefined)
31
+ throw new Error(`Service '${service.name}' declares an ingress, but no hosts are defined. ` +
32
+ "Declare them in the context's 'ingress' section.")
33
+
34
+ if (service.port === undefined)
35
+ throw new Error(`Service '${service.name}' declares an ingress, but no port.`)
36
+ }
37
+
11
38
  exports.services = services
@@ -0,0 +1,61 @@
1
+ 'use strict'
2
+
3
+ const { services } = require('./services')
4
+
5
+ const service = (extra = {}) => ({ group: 'group', name: 'group-one', version: '0', ...extra })
6
+
7
+ it('should leave a service without an ingress alone', () => {
8
+ const list = [service()]
9
+
10
+ services(list, {}, undefined, { hosts: ['api.dev'] })
11
+
12
+ expect(list[0].ingress).toBeUndefined()
13
+ })
14
+
15
+ /*
16
+ * A service declares only where it wants to land; the cluster-level plumbing
17
+ * belongs to the context.
18
+ */
19
+ it('should supply hosts, class and annotations from the context', () => {
20
+ const list = [service({ port: 8002, ingress: { path: '/.introspection' } })]
21
+
22
+ services(list, {}, undefined, { hosts: ['api.dev'], class: 'alb', annotations: { a: 'b' } })
23
+
24
+ expect(list[0].ingress).toStrictEqual({
25
+ path: '/.introspection',
26
+ hosts: ['api.dev'],
27
+ class: 'alb',
28
+ annotations: { a: 'b' }
29
+ })
30
+ })
31
+
32
+ it('should keep what the service declared itself', () => {
33
+ const list = [service({ port: 8000, ingress: { path: '/', hosts: ['own.dev'] } })]
34
+
35
+ services(list, {}, undefined, { hosts: ['api.dev'], class: 'alb' })
36
+
37
+ expect(list[0].ingress.hosts).toStrictEqual(['own.dev'])
38
+ expect(list[0].ingress.class).toStrictEqual('alb')
39
+ })
40
+
41
+ it('should ignore properties the service left undefined', () => {
42
+ const list = [service({ port: 8000, ingress: { path: '/', class: undefined } })]
43
+
44
+ services(list, {}, undefined, { hosts: ['api.dev'], class: 'alb' })
45
+
46
+ expect(list[0].ingress.class).toStrictEqual('alb')
47
+ })
48
+
49
+ it('should reject an ingress with nowhere to land', () => {
50
+ const list = [service({ port: 8002, ingress: { path: '/.introspection' } })]
51
+
52
+ expect(() => services(list, {}, undefined, undefined))
53
+ .toThrow("Service 'group-one' declares an ingress, but no hosts are defined")
54
+ })
55
+
56
+ it('should reject an ingress without a port', () => {
57
+ const list = [service({ ingress: { path: '/.introspection' } })]
58
+
59
+ expect(() => services(list, {}, undefined, { hosts: ['api.dev'] }))
60
+ .toThrow("Service 'group-one' declares an ingress, but no port")
61
+ })
@@ -1,17 +1,18 @@
1
1
  'use strict'
2
2
 
3
- function addVariables (deployment, variables) {
4
- const used = new Set()
3
+ function addVariables (composition, variables, keys = composition.components) {
4
+ composition.variables ??= []
5
5
 
6
- deployment.variables ??= []
6
+ const used = new Set(composition.variables.map((variable) => variable.name))
7
7
 
8
8
  for (const [key, set] of Object.entries(variables)) {
9
- if (key !== 'global' && !deployment.components?.includes(key)) continue
9
+ if (key !== 'global' && !keys?.includes(key))
10
+ continue
10
11
 
11
12
  for (const variable of set) {
12
13
  if (used.has(variable.name)) continue
13
14
 
14
- deployment.variables.push(variable)
15
+ composition.variables.push(variable)
15
16
  used.add(variable.name)
16
17
  }
17
18
  }
@@ -1,8 +1,10 @@
1
1
  'use strict'
2
2
 
3
- const get = require('./.describe')
3
+ const desc = require('./.describe')
4
+ const { addVariables } = require('./.describe/variables')
5
+ const { addMounts } = require('./.describe/mounts')
4
6
 
5
- const describe = (context, compositions, dependency) => {
7
+ const describe = (context, compositions, dependency, image) => {
6
8
  const { services } = dependency
7
9
 
8
10
  dependency.variables.global ??= []
@@ -17,11 +19,26 @@ const describe = (context, compositions, dependency) => {
17
19
  }
18
20
  )
19
21
 
20
- const components = get.components(compositions)
21
22
  const credentials = context.registry?.credentials
22
23
 
23
- get.compositions(compositions, dependency.variables, context.environment)
24
- get.services(services, dependency.variables)
24
+ if (image !== undefined) {
25
+ const mono = unit(context, dependency)
26
+
27
+ mono.image = image.reference
28
+
29
+ return {
30
+ compositions: [],
31
+ components: mono.components,
32
+ services: [],
33
+ credentials,
34
+ mono
35
+ }
36
+ }
37
+
38
+ const components = desc.components(compositions)
39
+
40
+ desc.compositions(compositions, dependency)
41
+ desc.services(services, dependency.variables, dependency.probe, context.ingress)
25
42
 
26
43
  return {
27
44
  compositions,
@@ -31,4 +48,56 @@ const describe = (context, compositions, dependency) => {
31
48
  }
32
49
  }
33
50
 
51
+ function unit (context, dependency) {
52
+ const components = (context.components ?? []).map((component) => component.locator.label)
53
+
54
+ const variables = dependency.variables ?? {}
55
+ const mounts = dependency.mounts ?? {}
56
+
57
+ const mono = {
58
+ replicas: context.mono?.replicas,
59
+ resources: context.mono?.resources,
60
+ components,
61
+ variables: []
62
+ }
63
+
64
+ if (context.ingress !== undefined)
65
+ mono.ingress = Object.assign({}, context.ingress)
66
+
67
+ addVariables(mono, variables, Object.keys(variables))
68
+ addMounts(mono, dependency.mounts, Object.keys(mounts))
69
+
70
+ for (const service of dependency.services ?? []) {
71
+ if (service.variables !== undefined)
72
+ for (const variable of service.variables)
73
+ if (!mono.variables.some((item) => item.name === variable.name))
74
+ mono.variables.push(variable)
75
+
76
+ // every declared port is bound by the single mono process, none is primary
77
+ if (service.port !== undefined)
78
+ (mono.backends ??= []).push({ port: service.port, path: service.ingress?.path ?? '/' })
79
+
80
+ if (service.ingress !== undefined) {
81
+ const { path, hosts, ...ingress } = service.ingress
82
+
83
+ mono.ingress = Object.assign(mono.ingress ?? {}, ingress)
84
+
85
+ // one Ingress serves every path, so its hosts are the union, not the last word
86
+ if (hosts !== undefined)
87
+ mono.ingress.hosts = [...new Set([...(mono.ingress.hosts ?? []), ...hosts])]
88
+ }
89
+
90
+ if (service.probe !== undefined && service.probe !== false)
91
+ mono.probe = service.probe
92
+ }
93
+
94
+ if (mono.probe === undefined && dependency.probe !== undefined && dependency.probe !== false)
95
+ mono.probe = dependency.probe
96
+
97
+ // the more specific prefix must come first, whatever the controller's tie-break
98
+ mono.backends?.sort((a, b) => b.path.length - a.path.length)
99
+
100
+ return mono
101
+ }
102
+
34
103
  exports.describe = describe
@@ -13,14 +13,54 @@ const merge = (dependencies) => {
13
13
  /** @type {toa.deployment.dependency.Variables} */
14
14
  const variables = {}
15
15
 
16
+ const mounts = {}
17
+
18
+ /** @type {toa.deployment.dependency.Probe | false | undefined} */
19
+ let probe
20
+
16
21
  for (const dependency of dependencies) {
17
22
  if (dependency.references !== undefined) references.push(...dependency.references)
18
23
  if (dependency.services !== undefined) services.push(...dependency.services)
19
24
  if (dependency.proxies !== undefined) proxies.push(...dependency.proxies)
20
25
  if (dependency.variables !== undefined) append(variables, dependency.variables)
26
+ if (dependency.mounts !== undefined) append(mounts, dependency.mounts)
27
+ if (dependency.probe !== undefined) probe = dependency.probe
21
28
  }
22
29
 
23
- return { references, services, proxies, variables }
30
+ reserve(services, probe)
31
+
32
+ return { references, services, proxies, variables, mounts, probe }
33
+ }
34
+
35
+ /**
36
+ * In Kubernetes these are separate pods, but `toa mono` and a local run put every
37
+ * service in one process — so a port may be claimed once and only once.
38
+ */
39
+ const reserve = (services, probe) => {
40
+ const claimed = new Map()
41
+
42
+ if (probe !== undefined && probe !== false)
43
+ claimed.set(probe.port, 'the readiness probe')
44
+
45
+ for (const service of services)
46
+ for (const [port, claimant] of ports(service)) {
47
+ const conflicting = claimed.get(port)
48
+
49
+ if (conflicting !== undefined)
50
+ throw new Error(`Port ${port} is claimed by both ${conflicting} and ${claimant}`)
51
+
52
+ claimed.set(port, claimant)
53
+ }
54
+ }
55
+
56
+ function * ports (service) {
57
+ const name = `'${service.group}-${service.name}'`
58
+
59
+ if (service.port !== undefined)
60
+ yield [service.port, name]
61
+
62
+ if (service.probe !== undefined && service.probe !== false && service.probe.port !== service.port)
63
+ yield [service.probe.port, `the readiness probe of ${name}`]
24
64
  }
25
65
 
26
66
  const append = (merged, variables) => {
@@ -0,0 +1,74 @@
1
+ 'use strict'
2
+
3
+ const { merge } = require('./merge')
4
+
5
+ const service = (name, extra = {}) => ({ group: 'group', name, version: '0', ...extra })
6
+
7
+ it('should merge services of all dependencies', () => {
8
+ const merged = merge([
9
+ { services: [service('one', { port: 8000 })] },
10
+ { services: [service('two', { port: 8001 })] }
11
+ ])
12
+
13
+ expect(merged.services).toHaveLength(2)
14
+ })
15
+
16
+ /*
17
+ * In Kubernetes these are separate pods, but `toa mono` and a local run put every
18
+ * service in one process.
19
+ */
20
+ describe('port reservation', () => {
21
+ it('should reject two services claiming one port', () => {
22
+ const dependencies = [
23
+ { services: [service('one', { port: 8000 })] },
24
+ { services: [service('two', { port: 8000 })] }
25
+ ]
26
+
27
+ expect(() => merge(dependencies))
28
+ .toThrow("Port 8000 is claimed by both 'group-one' and 'group-two'")
29
+ })
30
+
31
+ it('should reject a service claiming the port of the readiness probe', () => {
32
+ const dependencies = [
33
+ { probe: { path: '/.ready', port: 8001 } },
34
+ { services: [service('one', { port: 8001 })] }
35
+ ]
36
+
37
+ expect(() => merge(dependencies))
38
+ .toThrow("Port 8001 is claimed by both the readiness probe and 'group-one'")
39
+ })
40
+
41
+ it('should reject a probe claiming the port of another service', () => {
42
+ const dependencies = [
43
+ { services: [service('one', { port: 8000 })] },
44
+ { services: [service('two', { port: 8002, probe: { path: '/.ready', port: 8000 } })] }
45
+ ]
46
+
47
+ expect(() => merge(dependencies))
48
+ .toThrow("Port 8000 is claimed by both 'group-one' and the readiness probe of 'group-two'")
49
+ })
50
+
51
+ it('should allow a service to probe its own port', () => {
52
+ const dependencies = [
53
+ { services: [service('one', { port: 8000, probe: { path: '/.ready', port: 8000 } })] }
54
+ ]
55
+
56
+ expect(() => merge(dependencies)).not.toThrow()
57
+ })
58
+
59
+ it('should ignore services without a port', () => {
60
+ const dependencies = [
61
+ { services: [service('one'), service('two')] }
62
+ ]
63
+
64
+ expect(() => merge(dependencies)).not.toThrow()
65
+ })
66
+
67
+ it('should ignore a disabled probe', () => {
68
+ const dependencies = [
69
+ { probe: false, services: [service('one', { port: 8000, probe: false })] }
70
+ ]
71
+
72
+ expect(() => merge(dependencies)).not.toThrow()
73
+ })
74
+ })
@@ -5,6 +5,9 @@ metadata:
5
5
  name: composition-{{ required "composition 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
8
11
  selector:
9
12
  matchLabels:
10
13
  toa.io/composition: {{ .name }}
@@ -19,15 +22,82 @@ spec:
19
22
  containers:
20
23
  - name: {{ .name }}
21
24
  image: {{ .image }}
25
+ {{- if .resources }}
26
+ resources:
27
+ {{- if or .resources.cpu .resources.memory }}
28
+ requests:
29
+ {{- if .resources.cpu }}
30
+ cpu: {{ index .resources.cpu 0 }}
31
+ {{- end }}
32
+ {{- if .resources.memory }}
33
+ memory: {{ index .resources.memory 0 }}
34
+ {{- end }}
35
+ limits:
36
+ {{- if .resources.cpu }}
37
+ cpu: {{ index .resources.cpu 1 }}
38
+ {{- end }}
39
+ {{- if .resources.memory }}
40
+ memory: {{ index .resources.memory 1 }}
41
+ {{- end }}
42
+ {{- end }}
43
+ {{- end }}
22
44
  {{- if .variables }}
23
45
  env:
24
46
  {{- range .variables }}
25
47
  {{- include "env.var" . | indent 12 }}
26
48
  {{- end }}
27
49
  {{- end }}
50
+ {{- if .probe }}
51
+ ports:
52
+ - containerPort: {{ .probe.port }}
53
+ startupProbe:
54
+ httpGet:
55
+ path: {{ .probe.path }}
56
+ port: {{ .probe.port }}
57
+ {{- if .probe.delay }}
58
+ initialDelaySeconds: {{ .probe.delay }}
59
+ {{- end }}
60
+ periodSeconds: 2
61
+ timeoutSeconds: 3
62
+ failureThreshold: 150
63
+ readinessProbe:
64
+ httpGet:
65
+ path: {{ .probe.path }}
66
+ port: {{ .probe.port }}
67
+ periodSeconds: 10
68
+ timeoutSeconds: 3
69
+ failureThreshold: 3
70
+ {{- end }}
71
+ {{- if .mounts }}
72
+ volumeMounts:
73
+ {{- range .mounts }}
74
+ - name: {{ .name }}
75
+ mountPath: {{ .path }}
76
+ {{- end }}
77
+ {{- end }}
78
+ lifecycle:
79
+ preStop:
80
+ exec:
81
+ command: [sleep, "5"]
82
+ terminationGracePeriodSeconds: 45
28
83
  {{- if $.Values.credentials }}
29
84
  imagePullSecrets:
30
85
  - name: {{ $.Values.credentials }}
31
86
  {{- end }}
87
+ {{- if .mounts }}
88
+ volumes:
89
+ {{- range .mounts }}
90
+ - name: {{ .name }}
91
+ persistentVolumeClaim:
92
+ claimName: {{ .claim }}
93
+ {{- end }}
94
+ {{- end }}
95
+ topologySpreadConstraints:
96
+ - maxSkew: 1
97
+ topologyKey: kubernetes.io/hostname
98
+ whenUnsatisfiable: ScheduleAnyway
99
+ labelSelector:
100
+ matchLabels:
101
+ toa.io/composition: {{ .name }}
32
102
  ---
33
103
  {{- end }}