@toa.io/operations 0.2.0-dev.3 → 0.2.1-dev.4

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 (58) hide show
  1. package/package.json +5 -3
  2. package/src/deployment/.deployment/.describe/components.js +19 -0
  3. package/src/deployment/.deployment/.describe/dependencies.js +18 -0
  4. package/src/deployment/.deployment/.describe/index.js +9 -0
  5. package/src/deployment/.deployment/.describe/variables.js +27 -0
  6. package/src/deployment/.deployment/declare.js +29 -0
  7. package/src/deployment/.deployment/describe.js +28 -0
  8. package/src/deployment/.deployment/index.js +9 -0
  9. package/src/deployment/.deployment/merge.js +42 -0
  10. package/src/deployment/{assets/chart → chart}/Chart.yaml +1 -1
  11. package/src/deployment/chart/templates/compositions.yaml +38 -0
  12. package/src/deployment/chart/templates/proxies.yaml +10 -0
  13. package/src/deployment/chart/templates/services.yaml +63 -0
  14. package/src/deployment/chart/templates/variables.yaml +12 -0
  15. package/src/deployment/chart/values.yaml +47 -0
  16. package/src/deployment/composition.js +18 -5
  17. package/src/deployment/deployment.js +55 -49
  18. package/src/deployment/factory.js +78 -15
  19. package/src/deployment/images/composition.Dockerfile +14 -0
  20. package/src/deployment/images/composition.js +50 -0
  21. package/src/deployment/images/factory.js +39 -0
  22. package/src/deployment/images/image.js +91 -0
  23. package/src/deployment/images/index.js +7 -0
  24. package/src/deployment/images/registry.js +102 -0
  25. package/src/deployment/images/service.Dockerfile +12 -0
  26. package/src/deployment/images/service.js +70 -0
  27. package/src/deployment/operator.js +85 -0
  28. package/src/deployment/service.js +25 -0
  29. package/src/process.js +25 -0
  30. package/test/deployment/deployment.fixtures.js +18 -0
  31. package/test/deployment/deployment.test.js +41 -0
  32. package/test/deployment/images/image.fixtures.js +33 -0
  33. package/test/deployment/images/image.test.js +25 -0
  34. package/types/deployment/composition.d.ts +13 -0
  35. package/types/deployment/dependency.d.ts +77 -0
  36. package/types/deployment/deployment.d.ts +67 -0
  37. package/types/deployment/factory.d.ts +12 -0
  38. package/types/deployment/images/factory.d.ts +17 -0
  39. package/types/deployment/images/image.d.ts +14 -0
  40. package/types/deployment/images/registry.d.ts +20 -0
  41. package/types/deployment/index.d.ts +1 -0
  42. package/types/deployment/operator.d.ts +21 -0
  43. package/types/deployment/service.d.ts +20 -0
  44. package/types/process.d.ts +13 -0
  45. package/LICENSE +0 -22
  46. package/src/deployment/assets/Dockerfile +0 -10
  47. package/src/deployment/assets/chart/templates/deployment.yaml +0 -23
  48. package/src/deployment/assets/chart/values.yaml +0 -18
  49. package/src/deployment/chart.js +0 -79
  50. package/src/deployment/compositions.js +0 -60
  51. package/src/deployment/copy.js +0 -7
  52. package/src/deployment/directory.js +0 -19
  53. package/src/deployment/image.js +0 -67
  54. package/test/deployment/compositions.test.js +0 -31
  55. package/test/deployment/copositions.fixtures.js +0 -42
  56. package/test/deployment/image.fixtures.js +0 -26
  57. package/test/deployment/image.test.js +0 -22
  58. /package/src/deployment/{assets/chart/templates/service.yaml → chart/templates/components.yaml} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toa.io/operations",
3
- "version": "0.2.0-dev.3+8334154",
3
+ "version": "0.2.1-dev.4",
4
4
  "description": "Toa Deployment",
5
5
  "homepage": "https://toa.io",
6
6
  "author": {
@@ -26,8 +26,10 @@
26
26
  "test": "echo \"Error: run tests from root\" && exit 1"
27
27
  },
28
28
  "dependencies": {
29
- "@toa.io/gears": "0.2.0-dev.3+8334154",
29
+ "@toa.io/filesystem": "*",
30
+ "@toa.io/generic": "*",
31
+ "@toa.io/yaml": "*",
30
32
  "execa": "5.1.1"
31
33
  },
32
- "gitHead": "8334154c1b8a8268ad90adfb15b43a876459014f"
34
+ "gitHead": "2be07592325b2e4dc823e81d882a4e50bf50de24"
33
35
  }
@@ -0,0 +1,19 @@
1
+ 'use strict'
2
+
3
+ /**
4
+ * @param {toa.deployment.Composition[]} compositions
5
+ * @returns {string[]}
6
+ */
7
+ const components = (compositions) => {
8
+ /** @type {Set<string>} */
9
+ const components = new Set()
10
+
11
+ for (const composition of compositions) {
12
+ for (const component of composition.components) {
13
+ components.add(component)
14
+ }
15
+ }
16
+
17
+ return Array.from(components)
18
+ }
19
+ exports.components = components
@@ -0,0 +1,18 @@
1
+ 'use strict'
2
+
3
+ /**
4
+ * @param {toa.deployment.dependency.Reference[]} references
5
+ * @returns {*}
6
+ */
7
+ const dependencies = (references) => {
8
+ return references?.reduce((map, reference) => {
9
+ const { name, alias, values } = reference
10
+
11
+ map[alias || name] = values
12
+
13
+ return map
14
+ }, {})
15
+ }
16
+
17
+
18
+ exports.dependencies = dependencies
@@ -0,0 +1,9 @@
1
+ 'use strict'
2
+
3
+ const { dependencies } = require('./dependencies')
4
+ const { components } = require('./components')
5
+ const { variables } = require('./variables')
6
+
7
+ exports.dependencies = dependencies
8
+ exports.components = components
9
+ exports.variables = variables
@@ -0,0 +1,27 @@
1
+ 'use strict'
2
+
3
+ /**
4
+ * @param {toa.norm.Context} context
5
+ * @param {toa.deployment.dependency.Variables} variables
6
+ * @returns {toa.deployment.dependency.Variables}
7
+ */
8
+ const variables = (context, variables) => {
9
+ if (variables.global === undefined) variables.global = []
10
+
11
+ if (context.environment !== undefined) {
12
+ const variable = format('TOA_ENV', context.environment)
13
+
14
+ variables.global.push(variable)
15
+ }
16
+
17
+ return variables
18
+ }
19
+
20
+ /**
21
+ * @param {string} name
22
+ * @param {string} value
23
+ * @returns {toa.deployment.dependency.Variable}
24
+ */
25
+ const format = (name, value) => ({ name, value })
26
+
27
+ exports.variables = variables
@@ -0,0 +1,29 @@
1
+ 'use strict'
2
+
3
+ /**
4
+ * @param {toa.norm.Context} context
5
+ * @param {toa.deployment.Dependency} dependency
6
+ * @returns {toa.deployment.Declaration}
7
+ */
8
+ const declare = (context, dependency) => {
9
+ const { references } = dependency
10
+ const { name, description, version } = context
11
+
12
+ const dependencies = references.map(({ values, ...rest }) => rest)
13
+
14
+ return {
15
+ ...DECLARATION,
16
+ name,
17
+ description,
18
+ version,
19
+ appVersion: version,
20
+ dependencies
21
+ }
22
+ }
23
+
24
+ const DECLARATION = {
25
+ apiVersion: 'v2',
26
+ type: 'application'
27
+ }
28
+
29
+ exports.declare = declare
@@ -0,0 +1,28 @@
1
+ 'use strict'
2
+
3
+ const get = require('./.describe')
4
+
5
+ /**
6
+ * @param {toa.norm.Context} context
7
+ * @param {toa.deployment.Composition[]} compositions
8
+ * @param {toa.deployment.Dependency} dependency
9
+ * @returns {toa.deployment.Contents}
10
+ */
11
+ const describe = (context, compositions, dependency) => {
12
+ const { references, services, proxies } = dependency
13
+
14
+ const components = get.components(compositions)
15
+ const dependencies = get.dependencies(references)
16
+ const variables = get.variables(context, dependency.variables)
17
+
18
+ return {
19
+ compositions,
20
+ components,
21
+ services,
22
+ proxies,
23
+ variables,
24
+ ...dependencies
25
+ }
26
+ }
27
+
28
+ exports.describe = describe
@@ -0,0 +1,9 @@
1
+ 'use strict'
2
+
3
+ const { declare } = require('./declare')
4
+ const { describe } = require('./describe')
5
+ const { merge } = require('./merge')
6
+
7
+ exports.declare = declare
8
+ exports.describe = describe
9
+ exports.merge = merge
@@ -0,0 +1,42 @@
1
+ 'use strict'
2
+
3
+ /**
4
+ * @param {toa.deployment.Dependency[]} dependencies
5
+ * @returns {toa.deployment.Dependency}
6
+ */
7
+ const merge = (dependencies) => {
8
+ /** @type {toa.deployment.dependency.Reference[]} */
9
+ const references = []
10
+
11
+ /** @type {toa.deployment.Service[]} */
12
+ const services = []
13
+
14
+ /** @type {toa.deployment.dependency.Proxy[]} */
15
+ const proxies = []
16
+
17
+ /** @type {toa.deployment.dependency.Variables} */
18
+ const variables = {}
19
+
20
+ for (const dependency of dependencies) {
21
+ if (dependency.references !== undefined) references.push(...dependency.references)
22
+ if (dependency.services !== undefined) services.push(...dependency.services)
23
+ if (dependency.proxies !== undefined) proxies.push(...dependency.proxies)
24
+ if (dependency.variables !== undefined) append(variables, dependency.variables)
25
+ }
26
+
27
+ return { references, services, proxies, variables }
28
+ }
29
+
30
+ /**
31
+ * @param {toa.deployment.dependency.Variables} merged
32
+ * @param {toa.deployment.dependency.Variables} variables
33
+ */
34
+ const append = (merged, variables) => {
35
+ for (const [component, vars] of Object.entries(variables)) {
36
+ if (merged[component] === undefined) merged[component] = []
37
+
38
+ merged[component].push(...vars)
39
+ }
40
+ }
41
+
42
+ exports.merge = merge
@@ -1,4 +1,4 @@
1
- # This file just helps with IDE autocompletion and helm debugging. Actually dynamically replaced by ../chart.js.
1
+ # This file helps with IDE autocompletion
2
2
  apiVersion: v2
3
3
  name: toa-application
4
4
  description: Toa Application Chart
@@ -0,0 +1,38 @@
1
+ {{- range .Values.compositions }}
2
+ apiVersion: apps/v1
3
+ kind: Deployment
4
+ metadata:
5
+ name: composition-{{ required "deployment name is required" .name }}
6
+ spec:
7
+ replicas: {{ .replicas | default 2 }}
8
+ selector:
9
+ matchLabels:
10
+ toa.io/composition: {{ .name }}
11
+ template:
12
+ metadata:
13
+ labels:
14
+ toa.io/composition: {{ .name }}
15
+ {{- range .components }}
16
+ {{ . }}: "1"
17
+ {{- end }}
18
+ spec:
19
+ containers:
20
+ - name: {{ .name }}
21
+ image: {{ .image }}
22
+ {{- if $.Values.variables }}
23
+ env:
24
+ {{- range $component := .components }}
25
+ {{- range $key, $vars := $.Values.variables }}
26
+ {{- if eq $component $key }}
27
+ {{- range $vars }}
28
+ {{- include "env.var" . | indent 12 }}
29
+ {{- end }}
30
+ {{- end }}
31
+ {{- end }}
32
+ {{- end }}
33
+ {{- range $.Values.variables.global }}
34
+ {{- include "env.var" . | indent 12 }}
35
+ {{- end }}
36
+ {{- end }}
37
+ ---
38
+ {{- end }}
@@ -0,0 +1,10 @@
1
+ {{- range .Values.proxies }}
2
+ apiVersion: v1
3
+ kind: Service
4
+ metadata:
5
+ name: {{ .name }}
6
+ spec:
7
+ type: ExternalName
8
+ externalName: {{ .target }}
9
+ ---
10
+ {{- end }}
@@ -0,0 +1,63 @@
1
+ {{- range .Values.services }}
2
+ apiVersion: apps/v1
3
+ kind: Deployment
4
+ metadata:
5
+ name: service-{{ required "deployment name is required" .name }}
6
+ spec:
7
+ replicas: {{ .replicas | default 2 }}
8
+ selector:
9
+ matchLabels:
10
+ toa.io/service: {{ .name }}
11
+ template:
12
+ metadata:
13
+ labels:
14
+ toa.io/service: {{ .name }}
15
+ spec:
16
+ containers:
17
+ - name: {{ .name }}
18
+ image: {{ .image }}
19
+ {{- if $.Values.variables }}
20
+ env:
21
+ {{- range $.Values.variables.global }}
22
+ {{- include "env.var" . | indent 12 }}
23
+ {{- end }}
24
+ {{- end }}
25
+ ---
26
+ apiVersion: v1
27
+ kind: Service
28
+ metadata:
29
+ name: service-{{ .name }}
30
+ spec:
31
+ type: ClusterIP
32
+ selector:
33
+ toa.io/service: {{ .name }}
34
+ ports:
35
+ - name: port-{{ .port }}
36
+ protocol: TCP
37
+ port: {{ .port }}
38
+ targetPort: {{ .port }}
39
+ ---
40
+ {{- if .ingress }}
41
+ apiVersion: networking.k8s.io/v1
42
+ kind: Ingress
43
+ metadata:
44
+ name: {{ .name }}
45
+ {{- if .ingress.annotations }}
46
+ annotations:
47
+ {{ toYaml .ingress.annotations | indent 4 }}
48
+ {{- end }}
49
+ spec:
50
+ ingressClassName: {{ required "ingress.class is required" .ingress.class }}
51
+ rules:
52
+ - host: {{ required "ingress.host is required" .ingress.host }}
53
+ http:
54
+ paths:
55
+ - path: /
56
+ pathType: Prefix
57
+ backend:
58
+ service:
59
+ name: service-{{ .name }}
60
+ port:
61
+ number: 8000
62
+ {{- end }}
63
+ {{- end }}
@@ -0,0 +1,12 @@
1
+ {{- define "env.var" }}
2
+ - name: {{ .name }}
3
+ {{- if .value }}
4
+ value: {{ .value | quote }}
5
+ {{- end }}
6
+ {{- if .secret }}
7
+ valueFrom:
8
+ secretKeyRef:
9
+ name: {{ .secret.name }}
10
+ key: {{ .secret.key }}
11
+ {{- end }}
12
+ {{- end }}
@@ -0,0 +1,47 @@
1
+ # This file helps with IDE autocompletion
2
+ compositions:
3
+ - name: todos
4
+ image: localhost:5000/composition-todos:0.0.0
5
+ replicas: 2
6
+ components:
7
+ - todos-tasks
8
+ - todos-stats
9
+ - name: users
10
+ image: localhost:5000/composition-users:0.0.0
11
+ replicas: 3
12
+ components:
13
+ - users-users
14
+ # TODO: create component services only if sync binding is being used
15
+ components:
16
+ - todos-tasks
17
+ - todos-stats
18
+ - users-users
19
+ variables:
20
+ global:
21
+ - name: TOA_BINDINGS_AMQP_SYSTEM_USERNAME
22
+ value: foo
23
+ secret:
24
+ name: secret-name
25
+ key: secret-key
26
+ todos-tasks:
27
+ - name: TOA_CONFIGURATION_TODOS_TASKS
28
+ value: foo
29
+ secret:
30
+ name: secret-name
31
+ key: secret-key
32
+ services:
33
+ - name: resources-gateway
34
+ image: localhost:5000/resources-gateway:0.0.0
35
+ port: 8000
36
+ replicas: 2
37
+ ingress:
38
+ host: dummies.toa.io
39
+ class: alb
40
+ annotations:
41
+ alb.ingress.kubernetes.io/scheme: internet-facing
42
+ alb.ingress.kubernetes.io/target-type: ip
43
+ alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS": 443}]'
44
+ proxies:
45
+ - name: storage-proxy
46
+ target: host.docker.internal
47
+ environment: development
@@ -1,17 +1,30 @@
1
1
  'use strict'
2
2
 
3
+ // noinspection JSClosureCompilerSyntax
4
+ /**
5
+ * @implements {toa.deployment.Composition}
6
+ */
3
7
  class Composition {
4
8
  name
5
- components
6
- replicas
7
9
  image
10
+ /** @type {string[]} */
11
+ components
8
12
 
13
+ /**
14
+ * @param composition {toa.norm.context.Composition}
15
+ * @param image {toa.deployment.images.Image}
16
+ */
9
17
  constructor (composition, image) {
10
18
  this.name = composition.name
11
- this.components = composition.components
12
- this.replicas = composition.replicas
13
- this.image = image
19
+ this.image = image.reference
20
+ this.components = composition.components.map(component)
14
21
  }
15
22
  }
16
23
 
24
+ /**
25
+ * @param {toa.norm.Component} component
26
+ * @returns {string}
27
+ */
28
+ const component = (component) => component.locator.label
29
+
17
30
  exports.Composition = Composition
@@ -1,76 +1,82 @@
1
1
  'use strict'
2
2
 
3
- const { writeFile: write, rm: remove } = require('node:fs/promises')
4
- const execa = require('execa')
5
3
  const { join } = require('node:path')
6
- const { yaml } = require('@toa.io/gears')
4
+ const { writeFile: write } = require('node:fs/promises')
5
+ const { directory: { copy } } = require('@toa.io/filesystem')
6
+ const { dump } = require('@toa.io/yaml')
7
7
 
8
- const { directory } = require('./directory')
9
- const { copy } = require('./copy')
8
+ const { merge, declare, describe } = require('./.deployment')
10
9
 
10
+ /**
11
+ * @implements {toa.deployment.Deployment}
12
+ */
11
13
  class Deployment {
12
- #chart
13
- #images
14
-
15
- constructor (chart, images) {
16
- this.#chart = chart
17
- this.#images = images
18
- }
19
-
20
- async export (path) {
21
- if (path === undefined) path = await directory.temp('deployment')
22
- else await directory(path)
23
-
24
- await this.#dump(path)
25
-
26
- return path
27
- }
28
-
29
- async install (options = {}) {
30
- await this.#push()
31
- return await this.#upgrade(options)
14
+ /** @type {toa.deployment.Declaration} */
15
+ #declaration
16
+ /** @type {toa.deployment.Contents} */
17
+ #contents
18
+ /** @type {toa.operations.Process} */
19
+ #process
20
+ /** @type {string} */
21
+ #target
22
+
23
+ /**
24
+ * @param context {toa.norm.Context}
25
+ * @param compositions {toa.deployment.Composition[]}
26
+ * @param dependencies {toa.deployment.Dependency[]}
27
+ * @param process {toa.operations.Process}
28
+ */
29
+ constructor (context, compositions, dependencies, process) {
30
+ const dependency = merge(dependencies)
31
+
32
+ if (context.environment === 'local') throw new Error('Deployment environment name \'local\' is not allowed.')
33
+
34
+ this.#declaration = declare(context, dependency)
35
+ this.#contents = describe(context, compositions, dependency)
36
+ this.#process = process
32
37
  }
33
38
 
34
- async #dump (path) {
35
- const chart = yaml.dump(this.#chart.declaration)
36
- const values = yaml.dump(this.#chart.values)
37
-
38
- await copy(join(__dirname, 'assets/chart/templates'), join(path, 'templates'))
39
+ async export (target) {
40
+ const chart = dump(this.#declaration)
41
+ const values = dump(this.#contents)
39
42
 
40
43
  await Promise.all([
41
- write(join(path, 'Chart.yaml'), chart),
42
- write(join(path, 'values.yaml'), values)
44
+ write(join(target, 'Chart.yaml'), chart),
45
+ write(join(target, 'values.yaml'), values),
46
+ copy(TEMPLATES, join(target, 'templates'))
43
47
  ])
44
- }
45
48
 
46
- async #push () {
47
- for (const image of this.#images) {
48
- await image.build()
49
- await image.push()
50
- }
49
+ this.#target = target
51
50
  }
52
51
 
53
- async #upgrade (options) {
54
- const path = await this.export()
52
+ async install (options) {
53
+ if (options.target) this.#target = options.target
54
+ if (this.#target === undefined) throw new Error('Deployment hasn\'t been exported')
55
+
55
56
  const args = []
56
57
 
58
+ if (options.namespace !== undefined) args.push('-n', options.namespace)
57
59
  if (options.wait === true) args.push('--wait')
58
- if (options.dry === true) args.push('--dry-run')
59
60
 
60
- const update = execa('helm', ['dependency', 'update', path])
61
+ await this.#process.execute('helm', ['dependency', 'update', this.#target])
62
+ await this.#process.execute('helm', ['upgrade', this.#declaration.name, '-i', ...args, this.#target])
63
+ }
61
64
 
62
- update.stdout.pipe(process.stdout)
63
- await update
65
+ async template (options) {
66
+ if (this.#target === undefined) throw new Error('Deployment hasn\'t been exported')
64
67
 
65
- const upgrade = execa('helm', ['upgrade', this.#chart.name, '-i', ...args, path])
68
+ await this.#process.execute('helm', ['dependency', 'update', this.#target], { silently: true })
66
69
 
67
- upgrade.stdout.pipe(process.stdout)
68
- const output = await upgrade
70
+ const args = []
69
71
 
70
- await remove(path, { recursive: true })
72
+ if (options.namespace !== undefined) args.push('-n', options.namespace)
71
73
 
72
- return output
74
+ return await this.#process.execute('helm',
75
+ ['template', this.#declaration.name, ...args, this.#target],
76
+ { silently: true })
73
77
  }
74
78
  }
75
79
 
80
+ const TEMPLATES = join(__dirname, 'chart/templates')
81
+
76
82
  exports.Deployment = Deployment
@@ -1,37 +1,100 @@
1
1
  'use strict'
2
2
 
3
- const { Composition } = require('./composition')
4
- const { Compositions } = require('./compositions')
5
- const { Image } = require('./image')
3
+ const { Process } = require('../process')
4
+ const { Operator } = require('./operator')
5
+ const { Factory: Images, Registry } = require('./images')
6
6
  const { Deployment } = require('./deployment')
7
- const { Chart } = require('./chart')
7
+ const { Composition } = require('./composition')
8
+ const { Service } = require('./service')
8
9
 
10
+ /**
11
+ * @implements {toa.deployment.Factory}
12
+ */
9
13
  class Factory {
14
+ /** @type {toa.norm.Context} */
10
15
  #context
16
+ /** @type {toa.deployment.images.Registry} */
17
+ #registry
18
+ /** @type {toa.operations.Process} */
19
+ #process
11
20
 
21
+ /**
22
+ * @param context {toa.norm.Context}
23
+ */
12
24
  constructor (context) {
13
25
  this.#context = context
14
- }
26
+ this.#process = new Process()
15
27
 
16
- deployment () {
17
- const compositions = this.#compositions()
18
- const images = Array.from(compositions).map((composition) => composition.image)
19
- const chart = new Chart(this.#context, compositions)
20
-
21
- return new Deployment(chart, images)
28
+ const images = new Images(context.name, context.runtime)
29
+ this.#registry = new Registry(context.registry, images, this.#process)
22
30
  }
23
31
 
24
- #compositions () {
25
- return new Compositions(this.#context, (composition) => this.#composition(composition))
32
+ operator () {
33
+ const compositions = this.#context.compositions.map((composition) => this.#composition(composition))
34
+ const dependencies = this.#dependencies()
35
+ const deployment = new Deployment(this.#context, compositions, dependencies, this.#process)
36
+
37
+ return new Operator(deployment, this.#registry)
26
38
  }
27
39
 
40
+ /**
41
+ * @param composition {toa.norm.context.Composition}
42
+ * @returns {Composition}
43
+ */
28
44
  #composition (composition) {
29
- const image = this.#image(composition)
45
+ const image = this.#registry.composition(composition)
30
46
 
31
47
  return new Composition(composition, image)
32
48
  }
33
49
 
34
- #image = (composition) => new Image(composition, this.#context)
50
+ /**
51
+ * @returns {toa.deployment.Dependency[]}
52
+ */
53
+ #dependencies () {
54
+ /** @type {toa.deployment.Dependency[]} */
55
+ const dependencies = []
56
+
57
+ for (const [reference, instances] of Object.entries(this.#context.dependencies)) {
58
+ const dependency = this.#dependency(reference, instances)
59
+
60
+ if (dependency !== undefined) dependencies.push(dependency)
61
+ }
62
+
63
+ return dependencies
64
+ }
65
+
66
+ /**
67
+ * @param {string} path
68
+ * @param {toa.norm.context.dependencies.Instance[]} instances
69
+ * @returns {toa.deployment.Dependency | undefined}
70
+ */
71
+ #dependency (path, instances) {
72
+ const module = require(path)
73
+ const pkg = require(path + '/package.json')
74
+
75
+ if (module.deployment === undefined) return
76
+
77
+ const annotations = this.#context.annotations?.[pkg.name]
78
+
79
+ /** @type {toa.deployment.dependency.Declaration} */
80
+ const dependency = module.deployment(instances, annotations)
81
+
82
+ /** @type {toa.deployment.Service[]} */
83
+ const services = dependency.services?.map((service) => this.#service(path, service))
84
+
85
+ return { ...dependency, services }
86
+ }
87
+
88
+ /**
89
+ * @param path {string}
90
+ * @param service {toa.deployment.dependency.Service}
91
+ * @returns {Service}
92
+ */
93
+ #service (path, service) {
94
+ const image = this.#registry.service(path, service)
95
+
96
+ return new Service(service, image)
97
+ }
35
98
  }
36
99
 
37
100
  exports.Factory = Factory