@toa.io/operations 0.20.0-dev.30 → 0.20.0-dev.34

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toa.io/operations",
3
- "version": "0.20.0-dev.30",
3
+ "version": "0.20.0-dev.34",
4
4
  "description": "Toa Deployment",
5
5
  "homepage": "https://toa.io",
6
6
  "author": {
@@ -27,10 +27,10 @@
27
27
  "test": "echo \"Error: run tests from root\" && exit 1"
28
28
  },
29
29
  "dependencies": {
30
- "@toa.io/filesystem": "0.20.0-dev.30",
31
- "@toa.io/generic": "0.20.0-dev.30",
32
- "@toa.io/yaml": "0.20.0-dev.30",
30
+ "@toa.io/filesystem": "0.20.0-dev.34",
31
+ "@toa.io/generic": "0.20.0-dev.34",
32
+ "@toa.io/yaml": "0.20.0-dev.34",
33
33
  "execa": "5.1.1"
34
34
  },
35
- "gitHead": "61c8e74a336385265767e9fd37d49128cb51b17f"
35
+ "gitHead": "7035b1985fe9bb844069308a272d061bfbd38bf0"
36
36
  }
@@ -16,4 +16,5 @@ const components = (compositions) => {
16
16
 
17
17
  return Array.from(components)
18
18
  }
19
+
19
20
  exports.components = components
@@ -0,0 +1,26 @@
1
+ 'use strict'
2
+
3
+ function compositions (compositions, variables) {
4
+ for (const composition of compositions) {
5
+ addVariables(composition, variables)
6
+ }
7
+ }
8
+
9
+ function addVariables (composition, variables) {
10
+ const used = new Set()
11
+
12
+ composition.variables ??= []
13
+
14
+ for (const [key, set] of Object.entries(variables)) {
15
+ if (key !== 'global' && !composition.components.includes(key)) continue
16
+
17
+ for (const variable of set) {
18
+ if (used.has(variable.name)) continue
19
+
20
+ composition.variables.push(variable)
21
+ used.add(variable.name)
22
+ }
23
+ }
24
+ }
25
+
26
+ exports.compositions = compositions
@@ -2,8 +2,12 @@
2
2
 
3
3
  const { dependencies } = require('./dependencies')
4
4
  const { components } = require('./components')
5
+ const { compositions } = require('./compositions')
5
6
  const { variables } = require('./variables')
7
+ const { services } = require('./services')
6
8
 
7
9
  exports.dependencies = dependencies
8
10
  exports.components = components
11
+ exports.compositions = compositions
9
12
  exports.variables = variables
13
+ exports.services = services
@@ -0,0 +1,9 @@
1
+ 'use strict'
2
+
3
+ function services (services, variables) {
4
+ for (const service of services) {
5
+ service.variables = variables.global
6
+ }
7
+ }
8
+
9
+ exports.services = services
@@ -2,26 +2,23 @@
2
2
 
3
3
  const get = require('./.describe')
4
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
5
  const describe = (context, compositions, dependency) => {
12
- const { references, services, proxies } = dependency
6
+ const { references, services } = dependency
7
+
8
+ dependency.variables.global ??= []
9
+ dependency.variables.global.unshift({ name: 'TOA_ENV', value: context.environment })
13
10
 
14
11
  const components = get.components(compositions)
15
12
  const dependencies = get.dependencies(references)
16
- const variables = get.variables(context, dependency.variables)
17
13
  const credentials = context.registry?.credentials
18
14
 
15
+ get.compositions(compositions, dependency.variables, context.environment)
16
+ get.services(services, dependency.variables)
17
+
19
18
  return {
20
19
  compositions,
21
20
  components,
22
21
  services,
23
- proxies,
24
- variables,
25
22
  credentials,
26
23
  ...dependencies
27
24
  }
@@ -2,7 +2,7 @@
2
2
  apiVersion: apps/v1
3
3
  kind: Deployment
4
4
  metadata:
5
- name: composition-{{ required "deployment name is required" .name }}
5
+ name: composition-{{ required "composition name is required" .name }}
6
6
  spec:
7
7
  replicas: {{ .replicas | default 2 }}
8
8
  selector:
@@ -19,18 +19,9 @@ spec:
19
19
  containers:
20
20
  - name: {{ .name }}
21
21
  image: {{ .image }}
22
- {{- if $.Values.variables }}
22
+ {{- if .variables }}
23
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 }}
24
+ {{- range .variables }}
34
25
  {{- include "env.var" . | indent 12 }}
35
26
  {{- end }}
36
27
  {{- end }}
@@ -16,9 +16,9 @@ spec:
16
16
  containers:
17
17
  - name: {{ .name }}
18
18
  image: {{ .image }}
19
- {{- if $.Values.variables }}
19
+ {{- if .variables }}
20
20
  env:
21
- {{- range $.Values.variables.global }}
21
+ {{- range .variables }}
22
22
  {{- include "env.var" . | indent 12 }}
23
23
  {{- end }}
24
24
  {{- end }}
@@ -6,6 +6,12 @@ compositions:
6
6
  components:
7
7
  - todos-tasks
8
8
  - todos-stats
9
+ variables:
10
+ - name: TOA_CONFIGURATION_TODOS_TASKS
11
+ value: foo
12
+ secret:
13
+ name: secret-name
14
+ key: secret-key
9
15
  - name: users
10
16
  image: localhost:5000/composition-users:0.0.0
11
17
  replicas: 3
@@ -16,19 +22,6 @@ components:
16
22
  - todos-tasks
17
23
  - todos-stats
18
24
  - 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
25
  services:
33
26
  - name: resources-gateway
34
27
  image: localhost:5000/resources-gateway:0.0.0
@@ -41,6 +34,12 @@ services:
41
34
  alb.ingress.kubernetes.io/scheme: internet-facing
42
35
  alb.ingress.kubernetes.io/target-type: ip
43
36
  alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS": 443}]'
37
+ variables:
38
+ - name: TOA_CONFIGURATION_TODOS_TASKS
39
+ value: foo
40
+ secret:
41
+ name: secret-name
42
+ key: secret-key
44
43
  proxies:
45
44
  - name: storage-proxy
46
45
  target: host.docker.internal
@@ -7,39 +7,23 @@ const { dump } = require('@toa.io/yaml')
7
7
 
8
8
  const { merge, declare, describe } = require('./.deployment')
9
9
 
10
- /**
11
- * @implements {toa.deployment.Deployment}
12
- */
13
10
  class Deployment {
14
- /** @type {toa.deployment.Declaration} */
15
- #declaration
16
-
17
- /** @type {toa.deployment.Contents} */
18
- #contents
19
-
20
- /** @type {toa.operations.Process} */
11
+ #chart
12
+ #values
21
13
  #process
22
-
23
- /** @type {string} */
24
14
  #target
25
15
 
26
- /**
27
- * @param context {toa.norm.Context}
28
- * @param compositions {toa.deployment.Composition[]}
29
- * @param dependencies {toa.deployment.Dependency[]}
30
- * @param process {toa.operations.Process}
31
- */
32
16
  constructor (context, compositions, dependencies, process) {
33
17
  const dependency = merge(dependencies)
34
18
 
35
- this.#declaration = declare(context, dependency)
36
- this.#contents = describe(context, compositions, dependency)
19
+ this.#chart = declare(context, dependency)
20
+ this.#values = describe(context, compositions, dependency)
37
21
  this.#process = process
38
22
  }
39
23
 
40
24
  async export (target) {
41
- const chart = dump(this.#declaration)
42
- const values = dump(this.#contents)
25
+ const chart = dump(this.#chart)
26
+ const values = dump(this.#values)
43
27
 
44
28
  await Promise.all([
45
29
  write(join(target, 'Chart.yaml'), chart),
@@ -60,7 +44,7 @@ class Deployment {
60
44
  if (options.wait === true) args.push('--wait')
61
45
 
62
46
  await this.#process.execute('helm', ['dependency', 'update', this.#target])
63
- await this.#process.execute('helm', ['upgrade', this.#declaration.name, '-i', ...args, this.#target])
47
+ await this.#process.execute('helm', ['upgrade', this.#chart.name, '-i', ...args, this.#target])
64
48
  }
65
49
 
66
50
  async template (options) {
@@ -73,12 +57,31 @@ class Deployment {
73
57
  if (options.namespace !== undefined) args.push('-n', options.namespace)
74
58
 
75
59
  return await this.#process.execute('helm',
76
- ['template', this.#declaration.name, ...args, this.#target],
60
+ ['template', this.#chart.name, ...args, this.#target],
77
61
  { silently: true })
78
62
  }
79
63
 
80
64
  variables () {
81
- return this.#contents.variables
65
+ const variables = []
66
+ const used = new Set()
67
+
68
+ addVariables(this.#values.compositions, variables, used)
69
+ addVariables(this.#values.services, variables, used)
70
+
71
+ return variables
72
+ }
73
+ }
74
+
75
+ function addVariables (list, variables, used = new Set()) {
76
+ for (const item of list) {
77
+ if (item.variables === undefined) continue
78
+
79
+ for (const variable of item.variables) {
80
+ if (used.has(variable.name)) continue
81
+
82
+ variables.push(variable)
83
+ used.add(variable.name)
84
+ }
82
85
  }
83
86
  }
84
87
 
@@ -8,9 +8,6 @@ const { Registry } = require('./registry')
8
8
  const { Composition } = require('./composition')
9
9
  const { Service } = require('./service')
10
10
 
11
- /**
12
- * @implements {toa.deployment.Factory}
13
- */
14
11
  class Factory {
15
12
  /** @type {toa.norm.Context} */
16
13
  #context
@@ -90,10 +87,10 @@ class Factory {
90
87
 
91
88
  if (module.deployment === undefined) return
92
89
 
93
- const annotations = this.#context.annotations?.[pkg.name]
90
+ const annotation = this.#context.annotations?.[pkg.name]
94
91
 
95
92
  /** @type {toa.deployment.dependency.Declaration} */
96
- const dependency = module.deployment(instances, annotations)
93
+ const dependency = module.deployment(instances, annotation)
97
94
 
98
95
  /** @type {toa.deployment.Service[]} */
99
96
  const services = dependency.services?.map((service) => this.#service(path, service))
@@ -2,9 +2,6 @@
2
2
 
3
3
  const workspace = require('./workspace')
4
4
 
5
- /**
6
- * @implements {toa.deployment.Operator}
7
- */
8
5
  class Operator {
9
6
  /** @type {toa.deployment.Deployment} */
10
7
  #deployment
@@ -4,13 +4,15 @@ import * as _dependency from './dependency'
4
4
  declare namespace toa.deployment {
5
5
 
6
6
  interface Operator {
7
- export(path?: string): Promise<string>
7
+ export (path?: string): Promise<string>
8
8
 
9
- install(options?: _deployment.installation.Options): Promise<void>
9
+ install (options?: _deployment.installation.Options): Promise<void>
10
10
 
11
- template(options?: _deployment.template.Options): Promise<string>
11
+ template (options?: _deployment.template.Options): Promise<string>
12
12
 
13
- variables(): _dependency.Variables
13
+ variables (): _dependency.Variables
14
+
15
+ listVariables (): _dependency.Variable[]
14
16
  }
15
17
 
16
18
  }
@@ -18,7 +18,7 @@ type Service = {
18
18
  }
19
19
  }
20
20
 
21
- type Variable = {
21
+ export type Variable = {
22
22
  name: string
23
23
  value?: string
24
24
  secret?: {
@@ -27,7 +27,7 @@ type Variable = {
27
27
  }
28
28
  }
29
29
 
30
- type Variables = Record<string, Variable[]>
30
+ export type Variables = Record<string, Variable[]>
31
31
 
32
32
  export type Dependency = {
33
33
  references?: Reference[]
package/types/index.d.ts CHANGED
@@ -1 +1 @@
1
- export * as deployment from './deployment'
1
+ export * from './dependency'
@@ -1,10 +0,0 @@
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 }}
@@ -1,42 +0,0 @@
1
- 'use strict'
2
-
3
- const { generate } = require('randomstring')
4
-
5
- const context = /** @type {toa.norm.Context} */ { name: generate() }
6
- const compositions = []
7
- /** @type {toa.deployment.Dependency[]} */
8
-
9
- /** @type {toa.deployment.dependency.Variable} */
10
- const bindingVariable = {
11
- name: 'TOA_BINDINGS_AMQP_POINTER',
12
- value: 'eyJkZWZhdWx0IjoiYW1xcDovL3doYXRldmVyIiwic3lzdGVtIjoiYW1xcDovL2hvc3QwIn0='
13
- }
14
-
15
- /** @type {toa.deployment.dependency.Variable} */
16
- const secretVariable = {
17
- name: 'TOA_BINDINGS_AMQP_DEFAULT_USERNAME',
18
- secret: {
19
- name: 'toa-bindings-amqp-default',
20
- key: 'username'
21
- }
22
- }
23
-
24
- /** @type {toa.deployment.Dependency[]} */
25
- const dependencies = [{
26
- variables: { global: [bindingVariable, secretVariable] }
27
- }]
28
-
29
- const process = /** @type {toa.operations.Process} */ { execute: jest.fn() }
30
-
31
- const options = /** @type {toa.deployment.installation.Options} */ {
32
- namespace: generate(),
33
- target: generate()
34
- }
35
-
36
- exports.context = context
37
- exports.compositions = compositions
38
- exports.dependencies = dependencies
39
- exports.process = process
40
- exports.options = options
41
- exports.bindingVariable = bindingVariable
42
- exports.secretVariable = secretVariable
@@ -1,78 +0,0 @@
1
- 'use strict'
2
-
3
- const clone = require('clone-deep')
4
-
5
- const fixtures = require('./deployment.fixtures')
6
- const { Deployment } = require('../../src/deployment/deployment')
7
-
8
- /** @type {toa.deployment.Deployment} */
9
- let deployment
10
- /** @type {toa.deployment.installation.Options} */
11
- let options
12
-
13
- beforeEach(() => {
14
- deployment = new Deployment(fixtures.context, fixtures.compositions, fixtures.dependencies, fixtures.process)
15
- options = clone(fixtures.options)
16
- })
17
-
18
- it('should pass -n argument if options.namespace is set', async () => {
19
- await deployment.install(options)
20
-
21
- const call = fixtures.process.execute.mock.calls.find((call) => call[0] === 'helm' && call[1][0] === 'upgrade')
22
-
23
- expect(call).toBeDefined()
24
-
25
- const args = call[1]
26
- const index = args.findIndex((value) => value === '-n')
27
- const namespace = args[index + 1]
28
-
29
- expect(index).not.toStrictEqual(-1)
30
- expect(namespace).toStrictEqual(fixtures.options.namespace)
31
- })
32
-
33
- describe('variables', () => {
34
- let deployment /** @type {toa.deployment.Deployment} */
35
-
36
- it('should be define', () => {
37
- const context = clone(fixtures.context)
38
-
39
- deployment = new Deployment(context, fixtures.compositions, fixtures.dependencies, fixtures.process)
40
-
41
- expect(typeof deployment.variables).toBe('function')
42
- })
43
-
44
- it('should return variables', () => {
45
- const context = clone(fixtures.context)
46
- const [{ variables }] = fixtures.dependencies
47
-
48
- deployment = new Deployment(context, fixtures.compositions, fixtures.dependencies, fixtures.process)
49
- expect(deployment.variables()).toEqual(variables)
50
- })
51
-
52
- it('should return variables', () => {
53
- const context = clone(fixtures.context)
54
- const [{ variables }] = fixtures.dependencies
55
-
56
- deployment = new Deployment(context, fixtures.compositions, fixtures.dependencies, fixtures.process)
57
-
58
- expect(deployment.variables()).toEqual(variables)
59
- })
60
-
61
- it('should merge all variables', () => {
62
- const context = clone(fixtures.context)
63
-
64
- /** @type {toa.deployment.Dependency} */
65
- const dep1 = { variables: { global: [fixtures.secretVariable] } }
66
-
67
- /** @type {toa.deployment.Dependency} */
68
- const dep2 = { variables: { global: [fixtures.bindingVariable] } }
69
-
70
- deployment = new Deployment(context, fixtures.compositions, [dep1, dep2], fixtures.process)
71
-
72
- const result = deployment.variables()
73
- const expectedVariables = [fixtures.bindingVariable, fixtures.secretVariable]
74
-
75
- expect(result.global.length).toBe(expectedVariables.length)
76
- expect(result.global).toStrictEqual(expect.arrayContaining(expectedVariables))
77
- })
78
- })
@@ -1 +0,0 @@
1
- export * from './dependency'
@@ -1,13 +0,0 @@
1
- declare namespace toa.operations {
2
-
3
- namespace process {
4
- interface Options {
5
- silently?: boolean
6
- }
7
- }
8
-
9
- interface Process {
10
- execute(cmd: string, args: Array<string>, options?: process.Options): Promise<string>
11
- }
12
-
13
- }