@toa.io/operations 1.0.0-alpha.0 → 1.0.0-alpha.11

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": "1.0.0-alpha.0",
3
+ "version": "1.0.0-alpha.11",
4
4
  "description": "Toa Deployment",
5
5
  "homepage": "https://toa.io",
6
6
  "author": {
@@ -27,10 +27,12 @@
27
27
  "test": "echo \"Error: run tests from root\" && exit 1"
28
28
  },
29
29
  "dependencies": {
30
- "@toa.io/filesystem": "1.0.0-alpha.0",
31
- "@toa.io/generic": "1.0.0-alpha.0",
32
- "@toa.io/yaml": "1.0.0-alpha.0",
33
- "execa": "5.1.1"
30
+ "@toa.io/filesystem": "1.0.0-alpha.11",
31
+ "@toa.io/generic": "1.0.0-alpha.11",
32
+ "@toa.io/norm": "1.0.0-alpha.11",
33
+ "@toa.io/yaml": "1.0.0-alpha.11",
34
+ "execa": "5.1.1",
35
+ "fs-extra": "11.1.1"
34
36
  },
35
- "gitHead": "06c64546f6292cc07c52f74b31415101037f7616"
37
+ "gitHead": "e343ac81eef12957cfa5e520119b1276b8ec0ad2"
36
38
  }
@@ -8,5 +8,8 @@
8
8
  secretKeyRef:
9
9
  name: {{ .secret.name }}
10
10
  key: {{ .secret.key }}
11
+ {{- if .secret.optional }}
12
+ optional: {{ .secret.optional }}
13
+ {{- end }}
11
14
  {{- end }}
12
15
  {{- end }}
@@ -12,6 +12,7 @@ compositions:
12
12
  secret:
13
13
  name: secret-name
14
14
  key: secret-key
15
+ optional: false
15
16
  - name: users
16
17
  image: localhost:5000/composition-users:0.0.0
17
18
  replicas: 3
@@ -2,8 +2,8 @@
2
2
 
3
3
  const { join } = require('node:path')
4
4
  const { writeFile: write } = require('node:fs/promises')
5
- const { directory: { copy } } = require('@toa.io/filesystem')
6
5
  const { dump } = require('@toa.io/yaml')
6
+ const fs = require('fs-extra')
7
7
 
8
8
  const { merge, declare, describe } = require('./.deployment')
9
9
 
@@ -28,7 +28,7 @@ class Deployment {
28
28
  await Promise.all([
29
29
  write(join(target, 'Chart.yaml'), chart),
30
30
  write(join(target, 'values.yaml'), values),
31
- copy(TEMPLATES, join(target, 'templates'))
31
+ fs.copy(TEMPLATES, join(target, 'templates'))
32
32
  ])
33
33
 
34
34
  this.#target = target
@@ -1,5 +1,6 @@
1
1
  'use strict'
2
2
 
3
+ const { context: load } = require('@toa.io/norm')
3
4
  const { Process } = require('../process')
4
5
  const { Operator } = require('./operator')
5
6
  const { Factory: ImagesFactory } = require('./images')
@@ -85,6 +86,12 @@ class Factory {
85
86
 
86
87
  return new Service(service, image)
87
88
  }
89
+
90
+ static async create (path, environment) {
91
+ const context = await load(path, environment)
92
+
93
+ return new Factory(context)
94
+ }
88
95
  }
89
96
 
90
97
  exports.Factory = Factory
@@ -1,4 +1,4 @@
1
- FROM node:20.9.0-alpine3.18
1
+ FROM {{build.image}}
2
2
 
3
3
  {{build.arguments}}
4
4
 
@@ -1,30 +1,23 @@
1
1
  'use strict'
2
2
 
3
3
  const { join } = require('node:path')
4
- const { hash } = require('@toa.io/generic')
5
- const { directory: { copy } } = require('@toa.io/filesystem')
4
+ const fs = require('fs-extra')
5
+ const { createHash } = require('node:crypto')
6
6
 
7
7
  const { Image } = require('./image')
8
8
 
9
9
  class Composition extends Image {
10
10
  dockerfile = join(__dirname, 'composition.Dockerfile')
11
11
 
12
- /** @type {string} */
13
12
  #name
14
-
15
- /** @type {toa.norm.Component[]} */
13
+ #image
16
14
  #components
17
15
 
18
- /**
19
- * @param {string} scope
20
- * @param {toa.norm.context.Runtime} runtime
21
- * @param {toa.norm.context.Registry} registry
22
- * @param {toa.norm.context.Composition} composition
23
- */
24
16
  constructor (scope, runtime, registry, composition) {
25
17
  super(scope, runtime, registry)
26
18
 
27
19
  this.#name = composition.name
20
+ this.#image = composition.image
28
21
  this.#components = composition.components
29
22
  }
30
23
 
@@ -33,16 +26,41 @@ class Composition extends Image {
33
26
  }
34
27
 
35
28
  get version () {
36
- const tags = this.#components.map((component) => component.locator.id + ':' + component.version)
29
+ const hash = createHash('sha256')
30
+
31
+ for (const component of this.#components) {
32
+ hash.update(component.locator.id)
33
+ hash.update(component.version)
34
+ }
35
+
36
+ return hash.digest('hex').slice(0, 8)
37
+ }
38
+
39
+ get base () {
40
+ if (this.#image !== undefined) {
41
+ return this.#image
42
+ }
43
+
44
+ let image = null
45
+
46
+ for (const component of this.#components) {
47
+ const value = component.build?.image
48
+
49
+ if (image !== null && image !== value) {
50
+ throw new Error(`Composition '${this.#name}' requires different base images for its components. Specify base image for the composition in the context.`)
51
+ }
52
+
53
+ image = value
54
+ }
37
55
 
38
- return hash(tags.join(';'))
56
+ return image ?? undefined
39
57
  }
40
58
 
41
59
  async prepare (root) {
42
60
  const context = await super.prepare(root)
43
61
 
44
62
  for (const component of this.#components) {
45
- await copy(component.path, join(context, component.locator.label))
63
+ await fs.copy(component.path, join(context, component.locator.label))
46
64
  }
47
65
 
48
66
  return context
@@ -2,9 +2,8 @@
2
2
 
3
3
  const { Image } = require('./image')
4
4
  const { generate } = require('randomstring')
5
- const { hash } = require('@toa.io/generic')
6
5
 
7
- const version = generate()
6
+ const version = '168b04ff'
8
7
  const name = generate()
9
8
 
10
9
  /**
@@ -22,17 +21,17 @@ class Class extends Image {
22
21
 
23
22
  /** @type {toa.norm.context.Runtime} */
24
23
  const runtime = {
25
- version: generate()
24
+ version: '0.0.0'
26
25
  }
27
26
 
28
27
  /** @type {toa.norm.context.Registry} */
29
28
  const registry = {
30
- base: generate()
29
+ base: 'node:alpine'
31
30
  }
32
31
 
33
32
  exports.scope = generate()
34
33
  exports.name = name
35
- exports.version = hash(runtime.version + ';' + version)
34
+ exports.version = 'ba2409fc'
36
35
  exports.Class = Class
37
36
  exports.runtime = runtime
38
37
  exports.registry = registry
@@ -1,10 +1,17 @@
1
1
  'use strict'
2
2
 
3
- const { join, posix } = require('node:path')
4
- const { readFile: read, writeFile: write } = require('node:fs/promises')
5
-
6
- const { hash, overwrite } = require('@toa.io/generic')
7
- const { directory } = require('@toa.io/filesystem')
3
+ const {
4
+ join,
5
+ posix
6
+ } = require('node:path')
7
+ const {
8
+ readFile: read,
9
+ writeFile: write
10
+ } = require('node:fs/promises')
11
+ const { createHash } = require('node:crypto')
12
+
13
+ const { overwrite } = require('@toa.io/generic')
14
+ const { mkdir } = require('node:fs/promises')
8
15
 
9
16
  /**
10
17
  * @implements {toa.deployment.images.Image}
@@ -13,64 +20,48 @@ const { directory } = require('@toa.io/filesystem')
13
20
  class Image {
14
21
  context
15
22
  reference
16
-
17
- /**
18
- * @protected
19
- * @type {string}
20
- */
21
23
  dockerfile
22
24
 
23
- /** @type {string} */
24
25
  #scope
25
-
26
- /** @type {toa.norm.context.Registry} */
27
26
  #registry
28
-
29
- /** @type {toa.norm.context.Runtime} */
30
27
  #runtime
28
+ #values = {
29
+ build: {
30
+ image: 'node:20.9.0-alpine3.18'
31
+ }
32
+ }
31
33
 
32
- /** @type {{ runtime?: Partial<toa.norm.context.Runtime>, build: object }} */
33
- #values = { build: { command: 'echo hello' } }
34
-
35
- /**
36
- * @param {string} scope
37
- * @param {toa.norm.context.Runtime} runtime
38
- * @param {toa.norm.context.Registry} registry
39
- */
40
34
  constructor (scope, runtime, registry) {
41
35
  this.#scope = scope
42
36
  this.#registry = registry
43
37
  this.#runtime = runtime
44
-
45
- this.#setValues()
46
38
  }
47
39
 
48
40
  tag () {
49
- const tag = hash(this.#runtime?.version + ';' + this.version)
41
+ const hash = createHash('sha256')
42
+
43
+ hash.update(this.#runtime.version)
44
+ hash.update(this.version)
45
+
46
+ const tag = hash.digest('hex').slice(0, 8)
50
47
 
51
48
  this.reference = posix.join(this.#registry.base ?? '', this.#scope, `${this.name}:${tag}`)
52
49
  }
53
50
 
54
- /**
55
- * @abstract
56
- * @protected
57
- * @type {string}
58
- */
59
51
  get name () {}
60
52
 
61
- /**
62
- * @abstract
63
- * @protected
64
- * @type {string}
65
- */
66
53
  get version () {}
67
54
 
55
+ get base () {}
56
+
68
57
  async prepare (root) {
69
58
  if (this.dockerfile === undefined) throw new Error('Dockerfile isn\'t specified')
70
59
 
60
+ this.#setValues()
61
+
71
62
  const path = join(root, `${this.name}.${this.version}`)
72
63
 
73
- await directory.ensure(path)
64
+ await mkdir(path, { recursive: true })
74
65
 
75
66
  const template = await read(this.dockerfile, 'utf-8')
76
67
  const contents = template.replace(/{{(\S{1,32})}}/g, (_, key) => this.#value(key))
@@ -88,6 +79,12 @@ class Image {
88
79
  this.#values.runtime = this.#runtime
89
80
  this.#values.build = overwrite(this.#values.build, this.#registry.build)
90
81
 
82
+ const image = this.base
83
+
84
+ if (image !== undefined) {
85
+ this.#values.build.image = image
86
+ }
87
+
91
88
  if (this.#values.build.arguments !== undefined) this.#values.build.arguments = createArguments(this.#values.build.arguments)
92
89
  if (this.#values.build.run !== undefined) this.#values.build.run = createRunCommands(this.#values.build.run)
93
90
  }
@@ -3,7 +3,7 @@
3
3
  const { join, dirname } = require('node:path')
4
4
 
5
5
  const { Image } = require('./image')
6
- const { directory: { copy } } = require('@toa.io/filesystem')
6
+ const fs = require('fs-extra')
7
7
 
8
8
  class Service extends Image {
9
9
  dockerfile = join(__dirname, 'service.Dockerfile')
@@ -57,7 +57,7 @@ class Service extends Image {
57
57
  async prepare (root) {
58
58
  const context = await super.prepare(root)
59
59
 
60
- await copy(this.#path, context)
60
+ await fs.copy(this.#path, context)
61
61
 
62
62
  return context
63
63
  }
@@ -6,23 +6,14 @@ const workspace = require('./workspace')
6
6
  * @implements {toa.deployment.Registry}
7
7
  */
8
8
  class Registry {
9
- /** @type {toa.norm.context.Registry} */
10
9
  #registry
11
10
 
12
- /** @type {toa.deployment.images.Factory} */
13
11
  #factory
14
12
 
15
- /** @type {toa.operations.Process} */
16
13
  #process
17
14
 
18
- /** @type {toa.deployment.images.Image[]} */
19
15
  #images = []
20
16
 
21
- /**
22
- * @param {toa.norm.context.Registry} registry
23
- * @param {toa.deployment.images.Factory} factory
24
- * @param {toa.operations.Process} process
25
- */
26
17
  constructor (registry, factory, process) {
27
18
  this.#registry = registry
28
19
  this.#factory = factory
@@ -48,7 +39,9 @@ class Registry {
48
39
  async build () {
49
40
  await this.prepare()
50
41
 
51
- for (const image of this.#images) await this.#build(image)
42
+ for (const image of this.#images) {
43
+ await this.#build(image)
44
+ }
52
45
  }
53
46
 
54
47
  async push () {
@@ -58,7 +51,7 @@ class Registry {
58
51
  }
59
52
 
60
53
  /**
61
- * @param {"composition" | "service"} type
54
+ * @param {'composition' | 'service'} type
62
55
  * @param {...any} args
63
56
  * @returns {toa.deployment.images.Image}
64
57
  */
@@ -78,7 +71,11 @@ class Registry {
78
71
  async #build (image, push = false) {
79
72
  const args = ['--context=default', 'buildx', 'build']
80
73
 
81
- if (push) args.push('--push')
74
+ if (push) {
75
+ args.push('--push')
76
+ } else {
77
+ args.push('--load')
78
+ }
82
79
 
83
80
  args.push('--tag', image.reference, image.context)
84
81
 
@@ -95,7 +92,11 @@ class Registry {
95
92
  args.push('--builder', BUILDER)
96
93
 
97
94
  await this.#ensureBuilder()
98
- } else args.push('--builder', 'default')
95
+ } else {
96
+ args.push('--builder', 'default')
97
+ }
98
+
99
+ args.push('--progress', 'plain')
99
100
 
100
101
  await this.#process.execute('docker', args)
101
102
  }
@@ -18,6 +18,7 @@ export type Variable = {
18
18
  secret?: {
19
19
  name: string,
20
20
  key: string
21
+ optional?: boolean
21
22
  }
22
23
  }
23
24