@toa.io/operations 1.0.0-alpha.26 → 1.0.0-alpha.261

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 (33) hide show
  1. package/CHANGELOG.md +38 -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 +6 -1
  8. package/src/deployment/.deployment/.describe/variables.js +6 -5
  9. package/src/deployment/.deployment/describe.js +60 -5
  10. package/src/deployment/.deployment/merge.js +8 -1
  11. package/src/deployment/chart/templates/compositions.yaml +70 -0
  12. package/src/deployment/chart/templates/mono.yaml +168 -0
  13. package/src/deployment/chart/templates/services.yaml +67 -2
  14. package/src/deployment/chart/values.yaml +11 -1
  15. package/src/deployment/composition.js +1 -0
  16. package/src/deployment/deployment.js +6 -2
  17. package/src/deployment/factory.js +26 -8
  18. package/src/deployment/images/composition.Dockerfile +2 -2
  19. package/src/deployment/images/composition.js +16 -6
  20. package/src/deployment/images/factory.js +12 -3
  21. package/src/deployment/images/image.js +16 -7
  22. package/src/deployment/images/mono.Dockerfile +18 -0
  23. package/src/deployment/images/mono.js +82 -0
  24. package/src/deployment/images/runtime-base.test.js +90 -0
  25. package/src/deployment/images/service.Dockerfile +3 -3
  26. package/src/deployment/operator.js +4 -0
  27. package/src/deployment/registry.js +64 -16
  28. package/src/deployment/registry.test.js +175 -0
  29. package/types/_deployment/images/image.d.ts +4 -2
  30. package/types/_deployment/registry.d.ts +9 -7
  31. package/types/dependency.ts +67 -0
  32. package/types/dependency.d.ts +0 -39
  33. /package/types/{index.d.ts → index.ts} +0 -0
@@ -0,0 +1,175 @@
1
+ 'use strict'
2
+
3
+ const { Registry } = require('./registry')
4
+
5
+ /** @type {toa.operations.Process} */
6
+ let process
7
+
8
+ /** @type {toa.deployment.images.Factory} */
9
+ let factory
10
+
11
+ /** @type {toa.deployment.images.Image[]} */
12
+ let images
13
+
14
+ beforeEach(() => {
15
+ images = []
16
+ process = /** @type {toa.operations.Process} */ {
17
+ execute: jest.fn(async (cmd, args) => {
18
+ if (args[0] === 'manifest')
19
+ throw new Error('manifest unknown')
20
+
21
+ if (args[0] === 'buildx' && args[1] === 'inspect')
22
+ throw new Error('builder not found')
23
+
24
+ return ''
25
+ })
26
+ }
27
+
28
+ factory = /** @type {toa.deployment.images.Factory} */ {
29
+ composition: () => createImage('composition-mono'),
30
+ service: () => createImage('extension-realtime')
31
+ }
32
+ })
33
+
34
+ it('should reuse named builder across images', async () => {
35
+ const registry = createRegistry({ base: 'example.com/reg', platforms: ['linux/amd64'] })
36
+
37
+ registry.composition(/** @type {any} */ ({}))
38
+ registry.service('.', /** @type {any} */ ({}))
39
+
40
+ await registry.build()
41
+
42
+ const creates = process.execute.mock.calls.filter(([, args]) =>
43
+ args[0] === 'buildx' && args[1] === 'create')
44
+
45
+ expect(creates).toHaveLength(1)
46
+ expect(creates[0][1]).toEqual(['buildx', 'create', '--name', 'toa', '--bootstrap'])
47
+
48
+ const builds = process.execute.mock.calls.filter(([, args]) =>
49
+ args[0] === '--context=default' && args[1] === 'buildx' && args[2] === 'build')
50
+
51
+ expect(builds).toHaveLength(2)
52
+
53
+ for (const [, args] of builds) {
54
+ expect(args).toContain('--builder')
55
+ expect(args[args.indexOf('--builder') + 1]).toBe('toa')
56
+ }
57
+ })
58
+
59
+ it('should not create builder when it already exists', async () => {
60
+ process.execute = jest.fn(async (cmd, args) => {
61
+ if (args[0] === 'manifest')
62
+ throw new Error('manifest unknown')
63
+
64
+ return ''
65
+ })
66
+
67
+ const registry = createRegistry({ base: 'example.com/reg', platforms: ['linux/amd64'] })
68
+
69
+ registry.composition(/** @type {any} */ ({}))
70
+
71
+ await registry.build()
72
+
73
+ const creates = process.execute.mock.calls.filter(([, args]) =>
74
+ args[0] === 'buildx' && args[1] === 'create')
75
+
76
+ expect(creates).toHaveLength(0)
77
+ })
78
+
79
+ it('should add shared registry cache flags when base is set', async () => {
80
+ const registry = createRegistry({ base: 'example.com/reg', platforms: ['linux/amd64'] })
81
+
82
+ registry.composition(/** @type {any} */ ({}))
83
+ registry.service('.', /** @type {any} */ ({}))
84
+
85
+ await registry.build()
86
+
87
+ const builds = process.execute.mock.calls.filter(([, args]) =>
88
+ args[0] === '--context=default' && args[2] === 'build')
89
+
90
+ expect(builds).toHaveLength(2)
91
+
92
+ for (const [, args] of builds) {
93
+ expect(args).toContain('--cache-from')
94
+ expect(args).toContain('type=registry,ref=example.com/reg/acme/buildcache')
95
+ expect(args).toContain('--cache-to')
96
+ expect(args).toContain('type=registry,ref=example.com/reg/acme/buildcache,mode=max,image-manifest=true')
97
+ }
98
+ })
99
+
100
+ it('should omit cache flags when base is not set', async () => {
101
+ const registry = createRegistry({ platforms: ['linux/amd64'] })
102
+
103
+ registry.composition(/** @type {any} */ ({}))
104
+
105
+ await registry.build()
106
+
107
+ const builds = process.execute.mock.calls.filter(([, args]) =>
108
+ args[0] === '--context=default' && args[2] === 'build')
109
+
110
+ expect(builds).toHaveLength(1)
111
+
112
+ const [, args] = builds[0]
113
+
114
+ expect(args).not.toContain('--cache-from')
115
+ expect(args).not.toContain('--cache-to')
116
+ })
117
+
118
+ it('should use default builder when platforms is null', async () => {
119
+ const registry = createRegistry({ base: 'example.com/reg', platforms: null })
120
+
121
+ registry.composition(/** @type {any} */ ({}))
122
+
123
+ await registry.build()
124
+
125
+ const builds = process.execute.mock.calls.filter(([, args]) =>
126
+ args[0] === '--context=default' && args[2] === 'build')
127
+
128
+ expect(builds).toHaveLength(1)
129
+
130
+ const [, args] = builds[0]
131
+
132
+ expect(args[args.indexOf('--builder') + 1]).toBe('default')
133
+ expect(args).not.toContain('--cache-from')
134
+ expect(args).not.toContain('--platform')
135
+ })
136
+
137
+ it('should skip build when image already exists', async () => {
138
+ process.execute = jest.fn(async () => '')
139
+
140
+ const registry = createRegistry({ base: 'example.com/reg', platforms: ['linux/amd64'] })
141
+
142
+ registry.composition(/** @type {any} */ ({}))
143
+
144
+ await registry.build()
145
+
146
+ const builds = process.execute.mock.calls.filter(([, args]) =>
147
+ args[0] === '--context=default' && args[2] === 'build')
148
+
149
+ expect(builds).toHaveLength(0)
150
+ expect(process.execute).toHaveBeenCalledWith('docker', ['manifest', 'inspect', images[0].reference], { silently: true })
151
+ })
152
+
153
+ /**
154
+ * @param {object} [registry]
155
+ * @returns {Registry}
156
+ */
157
+ function createRegistry (registry = {}) {
158
+ return new Registry('acme', registry, factory, process)
159
+ }
160
+
161
+ /**
162
+ * @param {string} name
163
+ * @returns {toa.deployment.images.Image}
164
+ */
165
+ function createImage (name) {
166
+ const image = /** @type {toa.deployment.images.Image} */ {
167
+ reference: `example.com/reg/acme/${name}:abcdef12`,
168
+ context: `/tmp/${name}`,
169
+ prepare: jest.fn(async () => `/tmp/${name}`)
170
+ }
171
+
172
+ images.push(image)
173
+
174
+ return image
175
+ }
@@ -4,9 +4,11 @@ declare namespace toa.deployment.images {
4
4
  readonly reference: string
5
5
  readonly context: string
6
6
 
7
- tag(): void
7
+ name: string
8
8
 
9
- prepare(root: string): Promise<string>
9
+ tag (): void
10
+
11
+ prepare (root: string): Promise<string>
10
12
  }
11
13
 
12
14
  }
@@ -1,19 +1,21 @@
1
1
  import type * as _norm from '@toa.io/norm/types'
2
2
  import type * as _dependency from './dependency'
3
- import type * as _image from "./images/image"
3
+ import type * as _image from './images/image'
4
4
 
5
5
  declare namespace toa.deployment {
6
-
6
+
7
7
  interface Registry {
8
- composition(composition: _norm.Composition): _image.Image
8
+ composition (composition: _norm.Composition): _image.Image
9
+
10
+ service (path: string, service: _dependency.Service): _image.Image
9
11
 
10
- service(path: string, service: _dependency.Service): _image.Image
12
+ prepare (path: string): Promise<string>
11
13
 
12
- prepare(path: string): Promise<string>
14
+ build (): Promise<void>
13
15
 
14
- build(): Promise<void>
16
+ push (): Promise<void>
15
17
 
16
- push(): Promise<void>
18
+ tags (): string[]
17
19
  }
18
20
 
19
21
  }
@@ -0,0 +1,67 @@
1
+ import type { Manifest } from '@toa.io/norm'
2
+ import type { Locator } from '@toa.io/core'
3
+
4
+ export interface Service {
5
+ group: string
6
+ name: string
7
+ version: string
8
+ port?: number
9
+ ingress?: Ingress
10
+ resources?: Resources
11
+ variables?: Variable[]
12
+ components?: string[]
13
+ probe?: Probe | false
14
+ }
15
+
16
+ export interface Variable {
17
+ name: string
18
+ value?: string
19
+ secret?: {
20
+ name: string
21
+ key: string
22
+ optional?: boolean
23
+ }
24
+ }
25
+
26
+ export interface Instance<T> {
27
+ locator: Locator
28
+ manifest: T
29
+ component: Manifest
30
+ }
31
+
32
+ export type Instances<T> = Array<Instance<T>>
33
+
34
+ export type Variables = Record<'global' | string, Variable[]>
35
+ export type Mounts = Record<'global' | string, Mount[]>
36
+
37
+ export interface Dependency {
38
+ services?: Service[]
39
+ variables?: Variables
40
+ mounts?: Mounts
41
+ /** Default probe for compositions and services without their own probe. `false` disables. */
42
+ probe?: Probe | false
43
+ }
44
+
45
+ interface Ingress {
46
+ default?: boolean
47
+ hosts?: string[]
48
+ class?: string
49
+ annotations?: object
50
+ }
51
+
52
+ export interface Probe {
53
+ port: number
54
+ path: string
55
+ delay?: number
56
+ }
57
+
58
+ interface Mount {
59
+ name: string
60
+ path: string
61
+ claim: string
62
+ }
63
+
64
+ export interface Resources {
65
+ cpu: string[]
66
+ memory: string[]
67
+ }
@@ -1,39 +0,0 @@
1
- export type Service = {
2
- group: string
3
- name: string
4
- version: string
5
- port: number
6
- ingress: Ingress
7
- variables: Variable[]
8
- components?: string[]
9
- probe?: Probe
10
- }
11
-
12
- export type Variable = {
13
- name: string
14
- value?: string
15
- secret?: {
16
- name: string,
17
- key: string
18
- optional?: boolean
19
- }
20
- }
21
-
22
- export type Variables = Record<'global' | string, Variable[]>
23
-
24
- export type Dependency = {
25
- services?: Service[]
26
- variables?: Variables
27
- }
28
-
29
- type Ingress = {
30
- hosts: string[]
31
- class?: string
32
- annotations?: object
33
- }
34
-
35
- interface Probe {
36
- port: number
37
- path: string
38
- delay?: number
39
- }
File without changes