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

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 (62) 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/chart/Chart.yaml +7 -0
  11. package/src/deployment/chart/templates/components.yaml +16 -0
  12. package/src/deployment/chart/templates/compositions.yaml +38 -0
  13. package/src/deployment/chart/templates/proxies.yaml +10 -0
  14. package/src/deployment/chart/templates/services.yaml +63 -0
  15. package/src/deployment/chart/templates/variables.yaml +12 -0
  16. package/src/deployment/chart/values.yaml +47 -0
  17. package/src/deployment/composition.js +30 -0
  18. package/src/deployment/deployment.js +82 -0
  19. package/src/deployment/factory.js +100 -0
  20. package/src/deployment/images/composition.Dockerfile +14 -0
  21. package/src/deployment/images/composition.js +50 -0
  22. package/src/deployment/images/factory.js +39 -0
  23. package/src/deployment/images/image.js +91 -0
  24. package/src/deployment/images/index.js +7 -0
  25. package/src/deployment/images/registry.js +102 -0
  26. package/src/deployment/images/service.Dockerfile +12 -0
  27. package/src/deployment/images/service.js +70 -0
  28. package/src/deployment/index.js +5 -0
  29. package/src/deployment/operator.js +85 -0
  30. package/src/deployment/service.js +25 -0
  31. package/src/index.js +1 -5
  32. package/src/process.js +25 -0
  33. package/test/deployment/deployment.fixtures.js +18 -0
  34. package/test/deployment/deployment.test.js +41 -0
  35. package/test/deployment/images/image.fixtures.js +33 -0
  36. package/test/deployment/images/image.test.js +25 -0
  37. package/types/deployment/composition.d.ts +13 -0
  38. package/types/deployment/dependency.d.ts +77 -0
  39. package/types/deployment/deployment.d.ts +67 -0
  40. package/types/deployment/factory.d.ts +12 -0
  41. package/types/deployment/images/factory.d.ts +17 -0
  42. package/types/deployment/images/image.d.ts +14 -0
  43. package/types/deployment/images/registry.d.ts +20 -0
  44. package/types/deployment/index.d.ts +1 -0
  45. package/types/deployment/operator.d.ts +21 -0
  46. package/types/deployment/service.d.ts +20 -0
  47. package/types/process.d.ts +13 -0
  48. package/LICENSE +0 -22
  49. package/src/deployment/chart.js +0 -15
  50. package/src/deployment/dependencies.js +0 -25
  51. package/src/deployment/directory.js +0 -21
  52. package/src/deployment/values.js +0 -7
  53. package/src/deployment.js +0 -64
  54. package/src/images/Dockerfile +0 -10
  55. package/src/images/image.js +0 -38
  56. package/src/images.js +0 -26
  57. package/test/deployment.fixtures.js +0 -34
  58. package/test/deployment.test.js +0 -88
  59. package/test/image.fixtures.js +0 -24
  60. package/test/image.test.js +0 -45
  61. package/test/images.fixtures.js +0 -29
  62. package/test/images.test.js +0 -35
@@ -0,0 +1,67 @@
1
+ // noinspection ES6UnusedImports,JSUnusedGlobalSymbols
2
+
3
+ import type { Composition } from './composition'
4
+ import type { Service } from './service'
5
+ import type { dependency } from './dependency'
6
+
7
+ declare namespace toa.deployment {
8
+
9
+ interface Declaration {
10
+ apiVersion: string
11
+ type: string
12
+ name: string
13
+ description?: string
14
+ version: string
15
+ appVersion: string
16
+ dependencies: dependency.Reference[]
17
+ }
18
+
19
+ interface Contents {
20
+ compositions?: Composition[]
21
+ components?: string[]
22
+ services?: Service[]
23
+ proxies?: dependency.Proxy[]
24
+ variables?: dependency.Variables
25
+ [key: string]: Object
26
+ }
27
+
28
+ namespace installation {
29
+
30
+ interface Options {
31
+ wait?: boolean
32
+ target?: string
33
+ namespace?: string
34
+ }
35
+
36
+ }
37
+
38
+ namespace template {
39
+ interface Options {
40
+ namespace: string
41
+ }
42
+ }
43
+
44
+ interface Deployable {
45
+ name: string
46
+ image: string
47
+ }
48
+
49
+ interface Deployment {
50
+ export(target: string): Promise<void>
51
+
52
+ install(options: installation.Options): Promise<void>
53
+
54
+ template(options: template.Options): Promise<string>
55
+ }
56
+
57
+ }
58
+
59
+ export namespace installation {
60
+ export type Options = toa.deployment.installation.Options
61
+ }
62
+
63
+ export namespace template {
64
+ export type Options = toa.deployment.template.Options
65
+ }
66
+
67
+ export type Deployable = toa.deployment.Deployable
@@ -0,0 +1,12 @@
1
+ // noinspection ES6UnusedImports
2
+ import type { Operator } from './operator'
3
+
4
+ declare namespace toa.deployment {
5
+
6
+ interface Factory {
7
+ operator(): Operator
8
+ }
9
+
10
+ }
11
+
12
+ export type Factory = toa.deployment.Factory
@@ -0,0 +1,17 @@
1
+ // noinspection ES6UnusedImports
2
+
3
+ import type { Composition } from '@toa.io/norm/types'
4
+ import type { Image } from './image'
5
+ import type { dependency } from '../dependency'
6
+
7
+ declare namespace toa.deployment.images {
8
+
9
+ interface Factory {
10
+ composition(composition: Composition): Image
11
+
12
+ service(path: string, service: dependency.Service): Image
13
+ }
14
+
15
+ }
16
+
17
+ export type Factory = toa.deployment.images.Factory
@@ -0,0 +1,14 @@
1
+ declare namespace toa.deployment.images {
2
+
3
+ export interface Image {
4
+ readonly reference: string
5
+ readonly context: string
6
+
7
+ tag(base: string): void
8
+
9
+ prepare(root: string): Promise<string>
10
+ }
11
+
12
+ }
13
+
14
+ export type Image = toa.deployment.images.Image
@@ -0,0 +1,20 @@
1
+ // noinspection ES6UnusedImports
2
+
3
+ import type { Composition } from '@toa.io/norm'
4
+ import type { dependency } from '../dependency'
5
+ import type { Image } from "./image"
6
+
7
+ declare namespace toa.deployment.images {
8
+
9
+ interface Registry {
10
+ composition(composition: Composition): Image
11
+
12
+ service(path: string, service: dependency.Service): Image
13
+
14
+ prepare(target: string): Promise<void>
15
+
16
+ push(): Promise<void>
17
+ }
18
+
19
+ }
20
+
@@ -0,0 +1 @@
1
+ export { Dependency, dependency } from './dependency'
@@ -0,0 +1,21 @@
1
+ // noinspection ES6UnusedImports
2
+
3
+ import { installation, template } from './deployment'
4
+
5
+ declare namespace toa.deployment {
6
+
7
+ interface Operator {
8
+ export(path?: string): Promise<string>
9
+
10
+ prepare(path?: string): Promise<string>
11
+
12
+ build(): Promise<void>
13
+
14
+ install(options?: installation.Options): Promise<void>
15
+
16
+ template(options?: template.Options): Promise<string>
17
+ }
18
+
19
+ }
20
+
21
+ export type Operator = toa.deployment.Operator
@@ -0,0 +1,20 @@
1
+ // noinspection ES6UnusedImports
2
+
3
+ import type { Deployable } from './deployment'
4
+
5
+ declare namespace toa.deployment {
6
+
7
+ interface Ingress {
8
+ host: string
9
+ class: string
10
+ annotations?: object
11
+ }
12
+
13
+ interface Service extends Deployable {
14
+ port: number
15
+ ingress?: Ingress
16
+ }
17
+
18
+ }
19
+
20
+ export type Service = toa.deployment.Service
@@ -0,0 +1,13 @@
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
+ }
package/LICENSE DELETED
@@ -1,22 +0,0 @@
1
- Copyright (c) 2020-present Artem Gurtovoi
2
-
3
- MIT License
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining
6
- a copy of this software and associated documentation files (the
7
- "Software"), to deal in the Software without restriction, including
8
- without limitation the rights to use, copy, modify, merge, publish,
9
- distribute, sublicense, and/or sell copies of the Software, and to
10
- permit persons to whom the Software is furnished to do so, subject to
11
- the following conditions:
12
-
13
- The above copyright notice and this permission notice shall be
14
- included in all copies or substantial portions of the Software.
15
-
16
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -1,15 +0,0 @@
1
- 'use strict'
2
-
3
- const chart = (context, dependencies) => {
4
- return {
5
- apiVersion: 'v2',
6
- type: 'application',
7
- name: context.name,
8
- description: context.description,
9
- version: context.version,
10
- appVersion: context.version,
11
- dependencies: dependencies.map((dependency) => dependency.chart)
12
- }
13
- }
14
-
15
- exports.chart = chart
@@ -1,25 +0,0 @@
1
- 'use strict'
2
-
3
- const dependencies = (context) => {
4
- const dependencies = map(context.connectors)
5
-
6
- if (context.extensions !== undefined) dependencies.push(...map(context.extensions))
7
-
8
- return dependencies
9
- }
10
-
11
- const map = (map) => {
12
- const list = []
13
-
14
- for (const [key, values] of Object.entries(map)) {
15
- const dependency = require(key)
16
-
17
- if (dependency.deployments !== undefined) {
18
- list.push(...dependency.deployments(values))
19
- }
20
- }
21
-
22
- return list
23
- }
24
-
25
- exports.dependencies = dependencies
@@ -1,21 +0,0 @@
1
- 'use strict'
2
-
3
- const fs = require('node:fs/promises')
4
- const { join } = require('node:path')
5
- const { tmpdir } = require('node:os')
6
-
7
- const directory = async (path) => {
8
- if (path === undefined) {
9
- path = await fs.mkdtemp(join(tmpdir(), 'toa-deployment-'))
10
- } else {
11
- await fs.mkdir(path, { recursive: true })
12
-
13
- const entries = await fs.readdir(path)
14
-
15
- if (entries.length > 0) throw new Error('Target directory must be empty')
16
- }
17
-
18
- return path
19
- }
20
-
21
- exports.directory = directory
@@ -1,7 +0,0 @@
1
- 'use strict'
2
-
3
- const values = (context, dependencies) => {
4
- return Object.fromEntries(dependencies.map(({ chart, values }) => [chart.alias || chart.name, values]))
5
- }
6
-
7
- exports.values = values
package/src/deployment.js DELETED
@@ -1,64 +0,0 @@
1
- 'use strict'
2
-
3
- const fs = require('node:fs/promises')
4
- const execa = require('execa')
5
- const { join } = require('node:path')
6
- const { yaml } = require('@toa.io/gears')
7
-
8
- const { dependencies } = require('./deployment/dependencies')
9
- const { directory } = require('./deployment/directory')
10
- const { chart } = require('./deployment/chart')
11
- const { values } = require('./deployment/values')
12
-
13
- class Deployment {
14
- #context
15
- #chart
16
- #values
17
-
18
- constructor (context) {
19
- const deps = dependencies(context)
20
-
21
- this.#context = context
22
- this.#chart = chart(context, deps)
23
- this.#values = values(context, deps)
24
- }
25
-
26
- async export (path) {
27
- path = await directory(path)
28
-
29
- await this.#dump(path)
30
-
31
- return path
32
- }
33
-
34
- async install (wait) {
35
- const path = await this.export()
36
- const args = []
37
-
38
- if (wait === true) args.push('--wait')
39
-
40
- const update = execa('helm', ['dependency', 'update', path])
41
-
42
- update.stdout.pipe(process.stdout)
43
- await update
44
-
45
- const upgrade = execa('helm', ['upgrade', this.#context.name, '-i', ...args, path])
46
-
47
- upgrade.stdout.pipe(process.stdout)
48
- await upgrade
49
-
50
- await fs.rm(path, { recursive: true })
51
- }
52
-
53
- async #dump (path) {
54
- const chart = yaml.dump(this.#chart)
55
- const values = yaml.dump(this.#values)
56
-
57
- await Promise.all([
58
- fs.writeFile(join(path, 'Chart.yaml'), chart),
59
- fs.writeFile(join(path, 'values.yaml'), values)
60
- ])
61
- }
62
- }
63
-
64
- exports.Deployment = Deployment
@@ -1,10 +0,0 @@
1
- #TODO: bridge specifics
2
-
3
- FROM node:alpine
4
-
5
- RUN npm i -g @toa.io/runtime@0.1.0-dev.13
6
-
7
- WORKDIR /app
8
- ADD . .
9
-
10
- CMD toa compose .
@@ -1,38 +0,0 @@
1
- 'use strict'
2
-
3
- const { join } = require('node:path')
4
- const execa = require('execa')
5
-
6
- class Image {
7
- #manifest
8
- #registry
9
- #tag
10
-
11
- constructor (manifest, registry) {
12
- this.#manifest = manifest
13
- this.#registry = registry
14
-
15
- const { domain, name, version } = this.#manifest
16
- this.#tag = this.#registry + '/' + domain + '-' + name + ':' + version
17
- }
18
-
19
- async build () {
20
- const build = execa('docker', ['build', this.#manifest.path, '-f', DOCKERFILE, '-t', this.#tag])
21
-
22
- build.stdout.pipe(process.stdout)
23
-
24
- await build
25
- }
26
-
27
- async push () {
28
- const push = execa('docker', ['push', this.#tag])
29
-
30
- push.stdout.pipe(process.stdout)
31
-
32
- await push
33
- }
34
- }
35
-
36
- const DOCKERFILE = join(__dirname, 'Dockerfile')
37
-
38
- exports.Image = Image
package/src/images.js DELETED
@@ -1,26 +0,0 @@
1
- 'use strict'
2
-
3
- const { Image } = require('./images/image')
4
-
5
- class Images {
6
- #images
7
-
8
- constructor (context) {
9
- this.#images = context.manifests.map((manifest) => new Image(manifest, context.registry))
10
- }
11
-
12
- async push () {
13
- await this.#build()
14
- await this.#push()
15
- }
16
-
17
- async #build () {
18
- await Promise.all(this.#images.map((image) => image.build()))
19
- }
20
-
21
- async #push () {
22
- await Promise.all(this.#images.map((image) => image.push()))
23
- }
24
- }
25
-
26
- exports.Images = Images
@@ -1,34 +0,0 @@
1
- 'use strict'
2
-
3
- const { generate } = require('randomstring')
4
-
5
- const context = {
6
- name: generate(),
7
- description: generate(),
8
- version: '0.0.1',
9
- runtime: '0.1.0',
10
- packages: './path/to/' + generate()
11
- }
12
-
13
- const mock = {
14
- dependencies: {
15
- dependencies: jest.fn(() => [generate(), generate()])
16
- },
17
- directory: {
18
- directory: jest.fn(() => generate())
19
- },
20
- chart: {
21
- chart: jest.fn(() => ({ [generate()]: generate() }))
22
- },
23
- values: {
24
- values: jest.fn(() => ({ [generate()]: generate() }))
25
- },
26
- fs: {
27
- writeFile: jest.fn(),
28
- rm: jest.fn()
29
- },
30
- execa: jest.fn(() => ({ stdout: { pipe: jest.fn() } }))
31
- }
32
-
33
- exports.context = context
34
- exports.mock = mock
@@ -1,88 +0,0 @@
1
- 'use strict'
2
-
3
- const fixtures = require('./deployment.fixtures')
4
- const mock = fixtures.mock
5
-
6
- jest.mock('node:fs/promises', () => mock.fs)
7
- jest.mock('execa', () => mock.execa)
8
- jest.mock('../src/deployment/dependencies', () => mock.dependencies)
9
- jest.mock('../src/deployment/directory', () => mock.directory)
10
- jest.mock('../src/deployment/chart', () => mock.chart)
11
- jest.mock('../src/deployment/values', () => mock.values)
12
-
13
- const { Deployment } = require('../src')
14
- const { join } = require('node:path')
15
- const { generate } = require('randomstring')
16
- const { yaml } = require('@toa.io/gears')
17
-
18
- let deployment
19
-
20
- beforeEach(() => {
21
- deployment = new Deployment(fixtures.context)
22
- })
23
-
24
- describe('export', () => {
25
- const test = async (arg) => {
26
- const result = await deployment.export(arg)
27
-
28
- if (arg !== undefined) expect(arg).toBe(mock.directory.directory.mock.calls[0][0])
29
-
30
- const path = mock.directory.directory.mock.results[0].value
31
- const chart = yaml.dump(mock.chart.chart.mock.results[0].value)
32
- const values = yaml.dump(mock.values.values.mock.results[0].value)
33
-
34
- expect(mock.fs.writeFile).toHaveBeenNthCalledWith(1, join(path, 'Chart.yaml'), chart)
35
- expect(mock.fs.writeFile).toHaveBeenNthCalledWith(2, join(path, 'values.yaml'), values)
36
- expect(result).toBe(path)
37
- }
38
-
39
- it('should export chart', async () => {
40
- await test()
41
- })
42
-
43
- it('should export chart to given path', async () => {
44
- await test(generate())
45
- })
46
- })
47
-
48
- describe('install', () => {
49
- let path
50
-
51
- beforeEach(async () => {
52
- await deployment.install()
53
-
54
- path = mock.directory.directory.mock.results[0].value
55
- })
56
-
57
- it('should update', () => {
58
- expect(mock.execa).toHaveBeenNthCalledWith(1, 'helm', ['dependency', 'update', path])
59
- })
60
-
61
- it('should upgrade', () => {
62
- expect(mock.execa).toHaveBeenNthCalledWith(2,
63
- 'helm', ['upgrade', fixtures.context.name, '-i', path])
64
- })
65
-
66
- it('should wait on upgrade', async () => {
67
- jest.clearAllMocks()
68
-
69
- await deployment.install(true)
70
-
71
- path = mock.directory.directory.mock.results[0].value
72
-
73
- expect(mock.execa).toHaveBeenNthCalledWith(2,
74
- 'helm', ['upgrade', fixtures.context.name, '-i', '--wait', path])
75
- })
76
-
77
- it('should pipe stdout', () => {
78
- const update = mock.execa.mock.results[0].value
79
- const upgrade = mock.execa.mock.results[1].value
80
-
81
- expect(update.stdout.pipe).toHaveBeenCalledWith(process.stdout)
82
- expect(upgrade.stdout.pipe).toHaveBeenCalledWith(process.stdout)
83
- })
84
-
85
- it('should clear', () => {
86
- expect(mock.fs.rm).toHaveBeenCalledWith(path, { recursive: true })
87
- })
88
- })
@@ -1,24 +0,0 @@
1
- 'use strict'
2
-
3
- const { newid, random } = require('@toa.io/gears')
4
- const { join } = require('node:path')
5
-
6
- const mock = {
7
- execa: jest.fn(() => ({ stdout: { pipe: jest.fn() } }))
8
- }
9
-
10
- const manifest = {
11
- domain: 'domain' + newid(),
12
- name: 'component' + newid(),
13
- version: '0.0.' + random(9),
14
- path: newid()
15
- }
16
-
17
- const registry = `registry-${newid()}:${random(999) + 5000}`
18
-
19
- const DOCKERFILE = join(__dirname, '../src/images/Dockerfile')
20
-
21
- exports.mock = mock
22
- exports.manifest = manifest
23
- exports.registry = registry
24
- exports.DOCKERFILE = DOCKERFILE
@@ -1,45 +0,0 @@
1
- 'use strict'
2
-
3
- const fixtures = require('./image.fixtures')
4
- const mock = fixtures.mock
5
-
6
- jest.mock('execa', () => mock.execa)
7
-
8
- const { Image } = require('../src/images/image')
9
-
10
- let image
11
- let tag
12
-
13
- beforeAll(() => {
14
- const { domain, name, version } = fixtures.manifest
15
-
16
- tag = `${fixtures.registry}/${domain}-${name}:${version}`
17
- })
18
-
19
- beforeEach(() => {
20
- image = new Image(fixtures.manifest, fixtures.registry)
21
- })
22
-
23
- it('should build', async () => {
24
- await image.build()
25
-
26
- expect(mock.execa).toHaveBeenCalledWith('docker',
27
- ['build', fixtures.manifest.path, '-f', fixtures.DOCKERFILE, '-t', tag])
28
- })
29
-
30
- it('should push', async () => {
31
- await image.push()
32
-
33
- expect(mock.execa).toHaveBeenCalledWith('docker', ['push', tag])
34
- })
35
-
36
- it('should pipe stdout', async () => {
37
- await image.build()
38
- await image.push()
39
-
40
- const build = mock.execa.mock.results[0].value
41
- const push = mock.execa.mock.results[1].value
42
-
43
- expect(build.stdout.pipe).toHaveBeenCalledWith(process.stdout)
44
- expect(push.stdout.pipe).toHaveBeenCalledWith(process.stdout)
45
- })
@@ -1,29 +0,0 @@
1
- 'use strict'
2
-
3
- const { random, repeat, newid } = require('@toa.io/gears')
4
-
5
- const mock = {
6
- image: {
7
- Image: jest.fn(() => {
8
- return {
9
- build: jest.fn(),
10
- push: jest.fn()
11
- }
12
- })
13
- }
14
- }
15
-
16
- const context = {
17
- name: 'context-' + newid(),
18
- registry: `registry-${newid()}:${random(999) + 5000}`,
19
- manifests: repeat(
20
- () => ({
21
- domain: 'domain-' + newid(),
22
- name: 'component-' + newid(),
23
- version: '0.0.0'
24
- }),
25
- random(9) + 1)
26
- }
27
-
28
- exports.mock = mock
29
- exports.context = context