@toa.io/operations 1.0.0-alpha.284 → 1.0.0-alpha.286
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 +4 -4
- package/readme.md +43 -0
- package/src/deployment/.deployment/.describe/compositions.js +7 -5
- package/src/deployment/.deployment/.describe/events.js +7 -7
- package/src/deployment/.deployment/.describe/fold.js +10 -3
- package/src/deployment/.deployment/.describe/fold.test.js +30 -14
- package/src/deployment/.deployment/.describe/mounts.js +4 -7
- package/src/deployment/.deployment/.describe/resources.js +7 -5
- package/src/deployment/.deployment/.describe/resources.test.js +13 -6
- package/src/deployment/.deployment/.describe/services.js +10 -9
- package/src/deployment/.deployment/.describe/services.test.js +22 -5
- package/src/deployment/.deployment/.describe/variables.js +2 -3
- package/src/deployment/.deployment/describe.js +11 -7
- package/src/deployment/.deployment/merge.js +11 -6
- package/src/deployment/.deployment/merge.test.js +41 -10
- package/src/deployment/composition.js +1 -1
- package/src/deployment/deployment.js +35 -16
- package/src/deployment/factory.js +39 -19
- package/src/deployment/images/composition.Dockerfile +1 -1
- package/src/deployment/images/composition.js +10 -9
- package/src/deployment/images/factory.js +17 -6
- package/src/deployment/images/format.js +9 -5
- package/src/deployment/images/image.fixtures.js +2 -2
- package/src/deployment/images/image.js +38 -21
- package/src/deployment/images/image.test.js +7 -2
- package/src/deployment/images/mono.Dockerfile +1 -1
- package/src/deployment/images/mono.js +10 -9
- package/src/deployment/images/publish.js +98 -0
- package/src/deployment/images/runtime-base.test.js +12 -6
- package/src/deployment/images/service.Dockerfile +1 -1
- package/src/deployment/images/service.js +4 -4
- package/src/deployment/operator.js +8 -8
- package/src/deployment/registry.js +89 -61
- package/src/deployment/registry.test.js +117 -43
- package/src/deployment/service.js +3 -1
- package/src/deployment/workspace.js +1 -1
- package/src/process.js +1 -1
- package/src/process.test.js +8 -4
- package/types/_deployment/composition.d.ts +0 -2
- package/types/_deployment/dependency.d.ts +10 -6
- package/types/_deployment/deployment.d.ts +0 -4
- package/types/_deployment/factory.d.ts +0 -2
- package/types/_deployment/images/factory.d.ts +4 -6
- package/types/_deployment/images/image.d.ts +2 -4
- package/types/_deployment/images/registry.d.ts +7 -10
- package/types/_deployment/operator.d.ts +5 -7
- package/types/_deployment/registry.d.ts +6 -8
- package/types/_deployment/service.d.ts +0 -2
- package/types/dependency.ts +3 -0
- package/CHANGELOG.md +0 -231
|
@@ -11,7 +11,7 @@ export class Deployment {
|
|
|
11
11
|
#process
|
|
12
12
|
#target
|
|
13
13
|
|
|
14
|
-
constructor
|
|
14
|
+
constructor(context, compositions, dependencies, process, image) {
|
|
15
15
|
const dependency = merge(dependencies)
|
|
16
16
|
|
|
17
17
|
this.#chart = declare(context, dependency)
|
|
@@ -19,7 +19,7 @@ export class Deployment {
|
|
|
19
19
|
this.#process = process
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
async export
|
|
22
|
+
async export(target) {
|
|
23
23
|
const chart = dump(this.#chart)
|
|
24
24
|
const values = dump(this.#values)
|
|
25
25
|
|
|
@@ -32,9 +32,9 @@ export class Deployment {
|
|
|
32
32
|
this.#target = target
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
async install
|
|
35
|
+
async install(options) {
|
|
36
36
|
if (options.target) this.#target = options.target
|
|
37
|
-
if (this.#target === undefined) throw new Error(
|
|
37
|
+
if (this.#target === undefined) throw new Error("Deployment hasn't been exported")
|
|
38
38
|
|
|
39
39
|
const args = []
|
|
40
40
|
|
|
@@ -42,25 +42,46 @@ export class Deployment {
|
|
|
42
42
|
if (options.wait === true) args.push('--wait')
|
|
43
43
|
if (options.timeout !== undefined) args.push('--timeout', options.timeout)
|
|
44
44
|
|
|
45
|
-
await this.#
|
|
46
|
-
await this.#process.execute('helm', [
|
|
45
|
+
await this.#update()
|
|
46
|
+
await this.#process.execute('helm', [
|
|
47
|
+
'upgrade',
|
|
48
|
+
this.#chart.name,
|
|
49
|
+
'-i',
|
|
50
|
+
...args,
|
|
51
|
+
this.#target
|
|
52
|
+
])
|
|
47
53
|
}
|
|
48
54
|
|
|
49
|
-
async template
|
|
50
|
-
if (this.#target === undefined) throw new Error(
|
|
55
|
+
async template(options) {
|
|
56
|
+
if (this.#target === undefined) throw new Error("Deployment hasn't been exported")
|
|
51
57
|
|
|
52
|
-
await this.#
|
|
58
|
+
await this.#update({ silently: true })
|
|
53
59
|
|
|
54
60
|
const args = []
|
|
55
61
|
|
|
56
62
|
if (options.namespace !== undefined) args.push('-n', options.namespace)
|
|
57
63
|
|
|
58
|
-
return await this.#process.execute(
|
|
64
|
+
return await this.#process.execute(
|
|
65
|
+
'helm',
|
|
59
66
|
['template', this.#chart.name, ...args, this.#target],
|
|
60
|
-
{ silently: true }
|
|
67
|
+
{ silently: true }
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* A chart with no subcharts has nothing to resolve, and the call still reaches out for
|
|
73
|
+
* the repositories it would have read.
|
|
74
|
+
*
|
|
75
|
+
* @param {object} [options]
|
|
76
|
+
* @returns {Promise<void>}
|
|
77
|
+
*/
|
|
78
|
+
async #update(options = {}) {
|
|
79
|
+
if (this.#chart.dependencies.length === 0) return
|
|
80
|
+
|
|
81
|
+
await this.#process.execute('helm', ['dependency', 'update', this.#target], options)
|
|
61
82
|
}
|
|
62
83
|
|
|
63
|
-
variables
|
|
84
|
+
variables() {
|
|
64
85
|
const variables = []
|
|
65
86
|
const used = new Set()
|
|
66
87
|
|
|
@@ -74,7 +95,7 @@ export class Deployment {
|
|
|
74
95
|
}
|
|
75
96
|
}
|
|
76
97
|
|
|
77
|
-
function addVariables
|
|
98
|
+
function addVariables(list, variables, used = new Set()) {
|
|
78
99
|
for (const item of list) {
|
|
79
100
|
if (item.variables === undefined) continue
|
|
80
101
|
|
|
@@ -89,9 +110,7 @@ function addVariables (list, variables, used = new Set()) {
|
|
|
89
110
|
|
|
90
111
|
const TEMPLATES = join(import.meta.dirname, 'chart/templates')
|
|
91
112
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
function dump (object) {
|
|
113
|
+
function dump(object) {
|
|
95
114
|
// js-yaml writes plain objects only, and the values carry locators and images
|
|
96
115
|
return jsyaml.dump(JSON.parse(JSON.stringify(object)), { noRefs: true, lineWidth: -1 })
|
|
97
116
|
}
|
|
@@ -29,14 +29,18 @@ export class Factory {
|
|
|
29
29
|
* @type {Map<string, string[]>} */
|
|
30
30
|
#claims
|
|
31
31
|
|
|
32
|
-
constructor
|
|
32
|
+
constructor(context, options = {}) {
|
|
33
33
|
this.#context = context
|
|
34
34
|
this.#mono = options.mono === true
|
|
35
35
|
this.#process = new Process()
|
|
36
36
|
|
|
37
|
-
const imagesFactory = new ImagesFactory(
|
|
37
|
+
const imagesFactory = new ImagesFactory(
|
|
38
|
+
context.name,
|
|
39
|
+
context.runtime,
|
|
40
|
+
context.registry
|
|
41
|
+
)
|
|
38
42
|
|
|
39
|
-
this.#registry = new Registry(context.
|
|
43
|
+
this.#registry = new Registry(context.registry, imagesFactory, this.#process)
|
|
40
44
|
this.#claims = claims(context)
|
|
41
45
|
this.#dependencies = this.#getDependencies()
|
|
42
46
|
this.#compositions = []
|
|
@@ -46,10 +50,12 @@ export class Factory {
|
|
|
46
50
|
components: context.components
|
|
47
51
|
})
|
|
48
52
|
else
|
|
49
|
-
this.#compositions = context.compositions.map((composition) =>
|
|
53
|
+
this.#compositions = context.compositions.map((composition) =>
|
|
54
|
+
this.#composition(composition)
|
|
55
|
+
)
|
|
50
56
|
}
|
|
51
57
|
|
|
52
|
-
async operator
|
|
58
|
+
async operator() {
|
|
53
59
|
const deployment = new Deployment(
|
|
54
60
|
this.#context,
|
|
55
61
|
this.#compositions,
|
|
@@ -62,17 +68,17 @@ export class Factory {
|
|
|
62
68
|
return new Operator(deployment, this.#registry)
|
|
63
69
|
}
|
|
64
70
|
|
|
65
|
-
registry
|
|
71
|
+
registry() {
|
|
66
72
|
return this.#registry
|
|
67
73
|
}
|
|
68
74
|
|
|
69
|
-
#composition
|
|
75
|
+
#composition(composition) {
|
|
70
76
|
const image = this.#registry.composition(composition)
|
|
71
77
|
|
|
72
78
|
return new Composition(composition, image)
|
|
73
79
|
}
|
|
74
80
|
|
|
75
|
-
async #getDependencies
|
|
81
|
+
async #getDependencies() {
|
|
76
82
|
/** @type {toa.deployment.Dependency[]} */
|
|
77
83
|
const dependencies = []
|
|
78
84
|
|
|
@@ -95,17 +101,21 @@ export class Factory {
|
|
|
95
101
|
// point where a reference that yields nothing can be told from one that yields a service
|
|
96
102
|
for (const [reference, compositions] of this.#claims)
|
|
97
103
|
if (!contributing.has(reference))
|
|
98
|
-
throw new Error(
|
|
99
|
-
'
|
|
104
|
+
throw new Error(
|
|
105
|
+
`Composition '${compositions[0]}' lists '${reference}', ` +
|
|
106
|
+
'which contributes no service.'
|
|
107
|
+
)
|
|
100
108
|
|
|
101
109
|
return dependencies
|
|
102
110
|
}
|
|
103
111
|
|
|
104
|
-
async #getDependency
|
|
112
|
+
async #getDependency(reference, instances) {
|
|
105
113
|
// a dependency may be named the way a package is or written as a directory,
|
|
106
114
|
// and a module is loaded by file
|
|
107
115
|
const module = await import(pathToFileURL(require.resolve(reference)).href)
|
|
108
|
-
const pkg = JSON.parse(
|
|
116
|
+
const pkg = JSON.parse(
|
|
117
|
+
readFileSync(require.resolve(join(reference, 'package.json')), 'utf8')
|
|
118
|
+
)
|
|
109
119
|
|
|
110
120
|
if (module.deployment === undefined) return
|
|
111
121
|
|
|
@@ -121,9 +131,10 @@ export class Factory {
|
|
|
121
131
|
/** @type {toa.deployment.Service[]} */
|
|
122
132
|
const services = dependency.services?.map((service) =>
|
|
123
133
|
workload === undefined
|
|
124
|
-
? this.#service(reference, service)
|
|
125
|
-
// named the way `Service` would name it, since it skips that wrapper
|
|
126
|
-
|
|
134
|
+
? this.#service(reference, service) // its own deployment, its own image
|
|
135
|
+
: // named the way `Service` would name it, since it skips that wrapper
|
|
136
|
+
{ ...service, name: `${service.group}-${service.name}`, workload }
|
|
137
|
+
)
|
|
127
138
|
|
|
128
139
|
return { ...dependency, services }
|
|
129
140
|
}
|
|
@@ -133,13 +144,20 @@ export class Factory {
|
|
|
133
144
|
* @param service {toa.deployment.dependency.Service}
|
|
134
145
|
* @returns {Service}
|
|
135
146
|
*/
|
|
136
|
-
#service
|
|
137
|
-
|
|
147
|
+
#service(path, service) {
|
|
148
|
+
// an extension that publishes its service's image is taken at its word: nothing is
|
|
149
|
+
// added to the registry, so nothing is prepared, probed, built or pushed for it
|
|
150
|
+
const published =
|
|
151
|
+
service.image !== undefined && this.#context.registry?.services === PUBLISHED
|
|
152
|
+
|
|
153
|
+
const image = published
|
|
154
|
+
? { reference: `${service.image}:${this.#context.runtime.version}` }
|
|
155
|
+
: this.#registry.service(path, service)
|
|
138
156
|
|
|
139
157
|
return new Service(service, image)
|
|
140
158
|
}
|
|
141
159
|
|
|
142
|
-
static async create
|
|
160
|
+
static async create(path, environment, options = {}) {
|
|
143
161
|
const context = await load(path, environment)
|
|
144
162
|
|
|
145
163
|
return new Factory(context, options)
|
|
@@ -148,11 +166,13 @@ export class Factory {
|
|
|
148
166
|
|
|
149
167
|
const MONO = 'mono'
|
|
150
168
|
|
|
169
|
+
const PUBLISHED = 'published'
|
|
170
|
+
|
|
151
171
|
/**
|
|
152
172
|
* @param {toa.norm.Context} context
|
|
153
173
|
* @returns {Map<string, string[]>}
|
|
154
174
|
*/
|
|
155
|
-
function claims
|
|
175
|
+
function claims(context) {
|
|
156
176
|
const map = new Map()
|
|
157
177
|
|
|
158
178
|
for (const composition of context.compositions ?? [])
|
|
@@ -12,7 +12,7 @@ COPY --chown=node:node . /composition
|
|
|
12
12
|
{{build.run}}
|
|
13
13
|
|
|
14
14
|
# run 'npm i' in each component
|
|
15
|
-
RUN --mount=type=cache,target=/root/.npm \
|
|
15
|
+
RUN --mount=type=cache,target=/root/.npm,sharing=locked \
|
|
16
16
|
for entry in *; do if grep -qs '"dependencies"' "$entry/package.json"; then (cd $entry && npm i --omit=dev); fi; done
|
|
17
17
|
|
|
18
18
|
USER node
|
|
@@ -12,7 +12,7 @@ export class Composition extends Image {
|
|
|
12
12
|
#image
|
|
13
13
|
#components
|
|
14
14
|
|
|
15
|
-
constructor
|
|
15
|
+
constructor(scope, runtime, registry, composition) {
|
|
16
16
|
super(scope, runtime, registry)
|
|
17
17
|
|
|
18
18
|
this.#name = composition.name
|
|
@@ -20,11 +20,11 @@ export class Composition extends Image {
|
|
|
20
20
|
this.#components = composition.components
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
get name
|
|
23
|
+
get name() {
|
|
24
24
|
return 'composition-' + this.#name
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
get version
|
|
27
|
+
get version() {
|
|
28
28
|
const hash = createHash('sha256')
|
|
29
29
|
|
|
30
30
|
for (const component of this.#components) {
|
|
@@ -35,31 +35,32 @@ export class Composition extends Image {
|
|
|
35
35
|
return hash.digest('hex').slice(0, 8)
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
get base
|
|
38
|
+
get base() {
|
|
39
39
|
if (this.#image !== undefined) return this.#image
|
|
40
40
|
|
|
41
41
|
const images = new Set(this.#components.map((component) => component.build?.image))
|
|
42
42
|
|
|
43
43
|
if (images.size > 1)
|
|
44
|
-
throw new Error(
|
|
44
|
+
throw new Error(
|
|
45
|
+
`Composition '${this.#name}' requires different base images for its components. Specify base image for the composition in the context.`
|
|
46
|
+
)
|
|
45
47
|
|
|
46
48
|
return images.values().next().value
|
|
47
49
|
}
|
|
48
50
|
|
|
49
|
-
get run
|
|
51
|
+
get run() {
|
|
50
52
|
const commands = []
|
|
51
53
|
|
|
52
54
|
for (const component of this.#components) {
|
|
53
55
|
const run = component.build?.run
|
|
54
56
|
|
|
55
|
-
if (run !== undefined)
|
|
56
|
-
commands.push(run)
|
|
57
|
+
if (run !== undefined) commands.push(run)
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
return commands.join('\n')
|
|
60
61
|
}
|
|
61
62
|
|
|
62
|
-
async prepare
|
|
63
|
+
async prepare(root) {
|
|
63
64
|
const context = await super.prepare(root)
|
|
64
65
|
|
|
65
66
|
for (const component of this.#components) {
|
|
@@ -17,7 +17,7 @@ export class Factory {
|
|
|
17
17
|
* @param {toa.norm.context.Runtime} runtime
|
|
18
18
|
* @param {toa.norm.context.Registry} registry
|
|
19
19
|
*/
|
|
20
|
-
constructor
|
|
20
|
+
constructor(scope, runtime, registry) {
|
|
21
21
|
this.#scope = scope
|
|
22
22
|
this.#runtime = runtime
|
|
23
23
|
this.#registry = registry
|
|
@@ -26,8 +26,13 @@ export class Factory {
|
|
|
26
26
|
/**
|
|
27
27
|
* @returns {Composition}
|
|
28
28
|
*/
|
|
29
|
-
composition
|
|
30
|
-
const instance = new Composition(
|
|
29
|
+
composition(composition) {
|
|
30
|
+
const instance = new Composition(
|
|
31
|
+
this.#scope,
|
|
32
|
+
this.#runtime,
|
|
33
|
+
this.#registry,
|
|
34
|
+
composition
|
|
35
|
+
)
|
|
31
36
|
|
|
32
37
|
instance.tag()
|
|
33
38
|
|
|
@@ -37,8 +42,14 @@ export class Factory {
|
|
|
37
42
|
/**
|
|
38
43
|
* @returns {Service}
|
|
39
44
|
*/
|
|
40
|
-
service
|
|
41
|
-
const instance = new Service(
|
|
45
|
+
service(path, service) {
|
|
46
|
+
const instance = new Service(
|
|
47
|
+
this.#scope,
|
|
48
|
+
this.#runtime,
|
|
49
|
+
this.#registry,
|
|
50
|
+
path,
|
|
51
|
+
service
|
|
52
|
+
)
|
|
42
53
|
|
|
43
54
|
instance.tag()
|
|
44
55
|
|
|
@@ -48,7 +59,7 @@ export class Factory {
|
|
|
48
59
|
/**
|
|
49
60
|
* @returns {Mono}
|
|
50
61
|
*/
|
|
51
|
-
mono
|
|
62
|
+
mono(composition) {
|
|
52
63
|
const instance = new Mono(this.#scope, this.#runtime, this.#registry, composition)
|
|
53
64
|
|
|
54
65
|
instance.tag()
|
|
@@ -14,11 +14,13 @@ import { dirname, join, parse } from 'node:path'
|
|
|
14
14
|
* @param {string} target where it was copied
|
|
15
15
|
* @param {string} name
|
|
16
16
|
*/
|
|
17
|
-
export async function declare
|
|
17
|
+
export async function declare(source, target, name) {
|
|
18
18
|
if (existsSync(join(target, MANIFEST))) return
|
|
19
19
|
|
|
20
|
-
await writeFile(
|
|
21
|
-
|
|
20
|
+
await writeFile(
|
|
21
|
+
join(target, MANIFEST),
|
|
22
|
+
JSON.stringify({ name, private: true, type: format(source) }, null, 2) + '\n'
|
|
23
|
+
)
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
/**
|
|
@@ -28,7 +30,7 @@ export async function declare (source, target, name) {
|
|
|
28
30
|
* @param {string} directory
|
|
29
31
|
* @returns {'module' | 'commonjs'}
|
|
30
32
|
*/
|
|
31
|
-
export function format
|
|
33
|
+
export function format(directory) {
|
|
32
34
|
const { root } = parse(directory)
|
|
33
35
|
|
|
34
36
|
let current = directory
|
|
@@ -37,7 +39,9 @@ export function format (directory) {
|
|
|
37
39
|
const manifest = join(current, MANIFEST)
|
|
38
40
|
|
|
39
41
|
if (existsSync(manifest))
|
|
40
|
-
return JSON.parse(readFileSync(manifest, 'utf8')).type === 'module'
|
|
42
|
+
return JSON.parse(readFileSync(manifest, 'utf8')).type === 'module'
|
|
43
|
+
? 'module'
|
|
44
|
+
: 'commonjs'
|
|
41
45
|
|
|
42
46
|
current = dirname(current)
|
|
43
47
|
}
|
|
@@ -21,13 +21,13 @@ export class Image {
|
|
|
21
21
|
build: {}
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
constructor
|
|
24
|
+
constructor(scope, runtime, registry) {
|
|
25
25
|
this.#scope = scope
|
|
26
26
|
this.#registry = registry
|
|
27
27
|
this.#runtime = runtime
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
tag
|
|
30
|
+
tag() {
|
|
31
31
|
const hash = createHash('sha256')
|
|
32
32
|
|
|
33
33
|
hash.update(this.#runtime.version)
|
|
@@ -35,25 +35,37 @@ export class Image {
|
|
|
35
35
|
|
|
36
36
|
const tag = hash.digest('hex').slice(0, 8)
|
|
37
37
|
|
|
38
|
-
this.reference = posix.join(
|
|
38
|
+
this.reference = posix.join(
|
|
39
|
+
this.#registry.base ?? '',
|
|
40
|
+
this.#scope,
|
|
41
|
+
`${this.name}:${tag}`
|
|
42
|
+
)
|
|
39
43
|
}
|
|
40
44
|
|
|
41
45
|
/** @returns {string | undefined} */
|
|
42
|
-
get name
|
|
46
|
+
get name() {
|
|
47
|
+
return undefined
|
|
48
|
+
}
|
|
43
49
|
|
|
44
50
|
/** @returns {string | undefined} */
|
|
45
|
-
get version
|
|
51
|
+
get version() {
|
|
52
|
+
return undefined
|
|
53
|
+
}
|
|
46
54
|
|
|
47
55
|
/** The image to build `FROM`. Undefined takes the runtime's, which is what a service does.
|
|
48
56
|
* @returns {string | undefined} */
|
|
49
|
-
get base
|
|
57
|
+
get base() {
|
|
58
|
+
return undefined
|
|
59
|
+
}
|
|
50
60
|
|
|
51
61
|
/** Build commands to add. Undefined adds none, which is what a service does.
|
|
52
62
|
* @returns {string | undefined} */
|
|
53
|
-
get run
|
|
63
|
+
get run() {
|
|
64
|
+
return undefined
|
|
65
|
+
}
|
|
54
66
|
|
|
55
|
-
async prepare
|
|
56
|
-
if (this.dockerfile === undefined) throw new Error(
|
|
67
|
+
async prepare(root) {
|
|
68
|
+
if (this.dockerfile === undefined) throw new Error("Dockerfile isn't specified")
|
|
57
69
|
|
|
58
70
|
this.#setValues()
|
|
59
71
|
|
|
@@ -73,38 +85,43 @@ export class Image {
|
|
|
73
85
|
return path
|
|
74
86
|
}
|
|
75
87
|
|
|
76
|
-
#setValues
|
|
88
|
+
#setValues() {
|
|
77
89
|
this.#values.runtime = this.#runtime
|
|
78
|
-
this.#values.build = overwrite(
|
|
79
|
-
|
|
80
|
-
|
|
90
|
+
this.#values.build = overwrite(
|
|
91
|
+
{
|
|
92
|
+
image: `${RUNTIME_IMAGE}:${this.#runtime.version}`
|
|
93
|
+
},
|
|
94
|
+
this.#registry.build
|
|
95
|
+
)
|
|
81
96
|
|
|
82
97
|
const image = this.base
|
|
83
98
|
|
|
84
|
-
if (image !== undefined)
|
|
85
|
-
this.#values.build.image = image
|
|
99
|
+
if (image !== undefined) this.#values.build.image = image
|
|
86
100
|
|
|
87
101
|
const run = this.run
|
|
88
102
|
|
|
89
103
|
if (run !== undefined)
|
|
90
|
-
this.#values.build.run =
|
|
104
|
+
this.#values.build.run =
|
|
105
|
+
(this.#values.build.run === undefined ? '' : this.#values.build.run + '\n') + run
|
|
91
106
|
|
|
92
|
-
if (this.#values.build.arguments !== undefined)
|
|
93
|
-
|
|
107
|
+
if (this.#values.build.arguments !== undefined)
|
|
108
|
+
this.#values.build.arguments = createArguments(this.#values.build.arguments)
|
|
109
|
+
if (this.#values.build.run !== undefined)
|
|
110
|
+
this.#values.build.run = createRunCommands(this.#values.build.run)
|
|
94
111
|
}
|
|
95
112
|
|
|
96
113
|
/**
|
|
97
114
|
* @param key {string}
|
|
98
115
|
* @returns {string}
|
|
99
116
|
*/
|
|
100
|
-
#value
|
|
117
|
+
#value(key) {
|
|
101
118
|
const [source, property] = key.split('.')
|
|
102
119
|
|
|
103
120
|
return this.#values[source]?.[property] ?? ''
|
|
104
121
|
}
|
|
105
122
|
}
|
|
106
123
|
|
|
107
|
-
function createRunCommands
|
|
124
|
+
function createRunCommands(input) {
|
|
108
125
|
const lines = input.split('\n')
|
|
109
126
|
|
|
110
127
|
return lines.reduce((commands, command) => {
|
|
@@ -114,7 +131,7 @@ function createRunCommands (input) {
|
|
|
114
131
|
}, '')
|
|
115
132
|
}
|
|
116
133
|
|
|
117
|
-
function createArguments
|
|
134
|
+
function createArguments(variables) {
|
|
118
135
|
const args = []
|
|
119
136
|
|
|
120
137
|
for (const variable of variables) {
|
|
@@ -14,11 +14,16 @@ beforeEach(() => {
|
|
|
14
14
|
it('should assign url', () => {
|
|
15
15
|
instance.tag()
|
|
16
16
|
|
|
17
|
-
assert.deepStrictEqual(
|
|
17
|
+
assert.deepStrictEqual(
|
|
18
|
+
instance.reference,
|
|
19
|
+
`${fixtures.registry.base}/${fixtures.scope}/${fixtures.name}:${fixtures.version}`
|
|
20
|
+
)
|
|
18
21
|
})
|
|
19
22
|
|
|
20
23
|
describe('prepare', () => {
|
|
21
24
|
it('should throw error if no dockerfile specified', async () => {
|
|
22
|
-
await assert.rejects(instance.prepare(generate()), (error) =>
|
|
25
|
+
await assert.rejects(instance.prepare(generate()), (error) =>
|
|
26
|
+
/Dockerfile isn't specified/.test(error.message)
|
|
27
|
+
)
|
|
23
28
|
})
|
|
24
29
|
})
|
|
@@ -11,7 +11,7 @@ COPY --chown=node:node . /composition
|
|
|
11
11
|
|
|
12
12
|
{{build.run}}
|
|
13
13
|
|
|
14
|
-
RUN --mount=type=cache,target=/root/.npm \
|
|
14
|
+
RUN --mount=type=cache,target=/root/.npm,sharing=locked \
|
|
15
15
|
for entry in *; do if grep -qs '"dependencies"' "$entry/package.json"; then (cd $entry && npm i --omit=dev); fi; done
|
|
16
16
|
|
|
17
17
|
USER node
|
|
@@ -11,18 +11,18 @@ export class Mono extends Image {
|
|
|
11
11
|
#image
|
|
12
12
|
#components
|
|
13
13
|
|
|
14
|
-
constructor
|
|
14
|
+
constructor(scope, runtime, registry, composition) {
|
|
15
15
|
super(scope, runtime, registry)
|
|
16
16
|
|
|
17
17
|
this.#image = composition.image
|
|
18
18
|
this.#components = composition.components
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
get name
|
|
21
|
+
get name() {
|
|
22
22
|
return 'mono'
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
get version
|
|
25
|
+
get version() {
|
|
26
26
|
const hash = createHash('sha256')
|
|
27
27
|
|
|
28
28
|
for (const component of this.#components) {
|
|
@@ -33,31 +33,32 @@ export class Mono extends Image {
|
|
|
33
33
|
return hash.digest('hex').slice(0, 8)
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
get base
|
|
36
|
+
get base() {
|
|
37
37
|
if (this.#image !== undefined) return this.#image
|
|
38
38
|
|
|
39
39
|
const images = new Set(this.#components.map((component) => component.build?.image))
|
|
40
40
|
|
|
41
41
|
if (images.size > 1)
|
|
42
|
-
throw new Error(
|
|
42
|
+
throw new Error(
|
|
43
|
+
'Mono deployment requires different base images for its components. Specify base image for the composition in the context.'
|
|
44
|
+
)
|
|
43
45
|
|
|
44
46
|
return images.values().next().value
|
|
45
47
|
}
|
|
46
48
|
|
|
47
|
-
get run
|
|
49
|
+
get run() {
|
|
48
50
|
const commands = []
|
|
49
51
|
|
|
50
52
|
for (const component of this.#components) {
|
|
51
53
|
const run = component.build?.run
|
|
52
54
|
|
|
53
|
-
if (run !== undefined)
|
|
54
|
-
commands.push(run)
|
|
55
|
+
if (run !== undefined) commands.push(run)
|
|
55
56
|
}
|
|
56
57
|
|
|
57
58
|
return commands.join('\n')
|
|
58
59
|
}
|
|
59
60
|
|
|
60
|
-
async prepare
|
|
61
|
+
async prepare(root) {
|
|
61
62
|
const context = await super.prepare(root)
|
|
62
63
|
|
|
63
64
|
for (const component of this.#components) {
|