@toa.io/cli 1.0.0-alpha.7 → 1.0.0-alpha.70

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/cli",
3
- "version": "1.0.0-alpha.7",
3
+ "version": "1.0.0-alpha.70",
4
4
  "description": "Toa CLI",
5
5
  "author": "temich <tema.gurtovoy@gmail.com>",
6
6
  "homepage": "https://github.com/toa-io/toa#readme",
@@ -22,16 +22,17 @@
22
22
  "@toa.io/runtime": "*"
23
23
  },
24
24
  "dependencies": {
25
- "@toa.io/console": "1.0.0-alpha.7",
26
- "@toa.io/generic": "1.0.0-alpha.7",
27
- "@toa.io/kubernetes": "1.0.0-alpha.7",
28
- "@toa.io/norm": "1.0.0-alpha.7",
29
- "@toa.io/operations": "1.0.0-alpha.7",
30
- "@toa.io/yaml": "1.0.0-alpha.7",
25
+ "@toa.io/generic": "1.0.0-alpha.63",
26
+ "@toa.io/kubernetes": "1.0.0-alpha.63",
27
+ "@toa.io/norm": "1.0.0-alpha.67",
28
+ "@toa.io/operations": "1.0.0-alpha.70",
29
+ "@toa.io/yaml": "1.0.0-alpha.63",
31
30
  "dotenv": "16.1.1",
32
31
  "find-up": "5.0.0",
32
+ "jsonpath": "1.1.1",
33
+ "openspan": "1.0.0-alpha.67",
33
34
  "paseto": "3.1.4",
34
35
  "yargs": "17.6.2"
35
36
  },
36
- "gitHead": "4f5ac0bc342d4b7bd469fbe5c74266f050b55c9f"
37
+ "gitHead": "3fe12fa8b6a82fecc9205e9f6dd4b39e03184c7b"
37
38
  }
package/readme.md CHANGED
@@ -60,54 +60,39 @@ Credentials specified in the output file are preserved.
60
60
 
61
61
  > It is recommended to add `.env*` to `.gitignore`.
62
62
 
63
- ### replay
64
-
65
- [Replay](/extensions/sampling/docs/replay.md) samples. Reports in [TAP](https://testanything.org)
66
- format.
63
+ ### export manifest
67
64
 
68
65
  <dl>
69
- <dt><code>toa replay [paths...]</code></dt>
70
- <dd>
71
- <code>paths</code> Path(s) to Component(s) or a Context (default <code>.</code>).<br/>
72
- <code>--component &lt;id&gt;</code> Replay samples for a specified component <code>id</code>.<br/>
73
- <code>--integration</code> Replay integration tests only.<br/>
74
- <code>--autonomous</code> Replay autonomous tests only.<br/>
75
- <code>--operation &lt;name&gt;</code> Replay samples for specified operation.<br/>
76
- <code>--title &lt;regexp&gt;</code> Regexp to match sample titles.<br/>
77
- <code>--dock</code> Run in Docker. Applicable only for component samples.
66
+ <dt><code>toa export manifest</code></dt>
67
+ <dd>Print normalized manifest.
68
+
69
+ <code>--path</code> path to a component (default <code>.</code>)<br/>
70
+ <code>--jsonpath</code> JSONPath expression to filter the output<br/>
71
+ <code>--error</code> print errors only<br/>
72
+ <code>--output</code> output format (default <code>yaml</code>)
78
73
  </dd>
79
74
  </dl>
80
75
 
81
- #### Examples
76
+ ### export secrets
82
77
 
83
- ```shell
84
- $ toa replay
85
- $ toa replay ./path/to/component
86
- $ toa replay ./components/a ./components/b --dock
87
- $ toa replay ./components/*
88
- $ toa replay ./path/to/context
89
- $ toa replay --title "should add numbers"
90
- ```
78
+ <dl>
79
+ <dt><code>toa export secrets &lt;environment&gt;</code></dt>
80
+ <dd>Print deployment secrets.
91
81
 
92
- If the path is a Context root (containing `context.toa.yaml` file), samples for components within
93
- the Context will be found and replayed sequentially.
82
+ <code>--path</code> path to context (default <code>.</code>)<br/>
83
+ </dd>
84
+ </dl>
94
85
 
95
- ### export manifest
86
+ ### export image tags
96
87
 
97
88
  <dl>
98
- <dt><code>toa export manifest</code></dt>
99
- <dd>Print normalized manifest.
89
+ <dt><code>toa export tags &lt;environment&gt;</code></dt>
90
+ <dd>Print image tags.
100
91
 
101
- <code>--path</code> path to component (default <code>.</code>)<br/>
102
- <code>--error</code> print errors only<br/>
103
- <code>--output</code> output format (default <code>yaml</code>)
92
+ <code>--path</code> path to context (default <code>.</code>)<br/>
104
93
  </dd>
105
94
  </dl>
106
95
 
107
- ### export entity
108
-
109
- Same as `exprot manifest` but outputs only the `entity`.
110
-
111
96
  ## Operations
112
97
 
113
98
  > Some commands use current `kubectl` and `docker` context.
@@ -17,6 +17,12 @@ const builder = (yargs) => {
17
17
  desc: 'Path to a component',
18
18
  default: '.'
19
19
  })
20
+ .option('jsonpath', {
21
+ alias: 'j',
22
+ group: 'Command options:',
23
+ type: 'string',
24
+ desc: 'JSONPath expression'
25
+ })
20
26
  .option('output', {
21
27
  alias: 'o',
22
28
  group: 'Command options:',
@@ -0,0 +1,23 @@
1
+ 'use strict'
2
+
3
+ const { secrets } = require('../../handlers/export/secrets')
4
+
5
+ const builder = (yargs) => {
6
+ yargs
7
+ .positional('environment', {
8
+ type: 'string',
9
+ desc: 'Deployment environment'
10
+ })
11
+ .option('path', {
12
+ alias: 'p',
13
+ group: 'Command options:',
14
+ type: 'string',
15
+ desc: 'Path to context',
16
+ default: '.'
17
+ })
18
+ }
19
+
20
+ exports.command = ['secrets <environment>']
21
+ exports.desc = 'Export deployment secrets'
22
+ exports.builder = builder
23
+ exports.handler = secrets
@@ -0,0 +1,23 @@
1
+ 'use strict'
2
+
3
+ const { tags } = require('../../handlers/export/tags')
4
+
5
+ const builder = (yargs) => {
6
+ yargs
7
+ .positional('environment', {
8
+ type: 'string',
9
+ desc: 'Deployment environment'
10
+ })
11
+ .option('path', {
12
+ alias: 'p',
13
+ group: 'Command options:',
14
+ type: 'string',
15
+ desc: 'Path to context',
16
+ default: '.'
17
+ })
18
+ }
19
+
20
+ exports.command = ['tags <environment>']
21
+ exports.desc = 'Export image tags'
22
+ exports.builder = builder
23
+ exports.handler = tags
@@ -5,6 +5,7 @@ const boot = require('@toa.io/boot')
5
5
  const { version } = require('@toa.io/runtime')
6
6
 
7
7
  const docker = require('./docker')
8
+ const { graceful } = require('./lib/graceful')
8
9
  const { components: find } = require('../util/find')
9
10
 
10
11
  /**
@@ -22,6 +23,8 @@ async function compose (argv) {
22
23
  await composition.connect()
23
24
 
24
25
  if (argv.kill === true) await composition.disconnect()
26
+
27
+ return graceful(composition)
25
28
  }
26
29
 
27
30
  /**
@@ -14,8 +14,9 @@ async function build (contextPath, componentPatterns) {
14
14
  await registry.build()
15
15
 
16
16
  const composition = context.compositions[0].name
17
+ const base = context.registry.base === undefined ? '' : context.registry.base + '/'
17
18
 
18
- return `${context.registry.base === undefined ? '' : context.registry.base + '/'}${context.name}/composition-${composition}`
19
+ return `${base}${context.name}/composition-${composition}`
19
20
  }
20
21
 
21
22
  async function createContext (contextPath, componentPatterns) {
@@ -24,10 +25,13 @@ async function createContext (contextPath, componentPatterns) {
24
25
  const paths = componentPatterns.map((pattern) => find.components(pattern))
25
26
  const components = await loadComponents(paths)
26
27
  const rnd = newid().substring(0, 6)
27
- const name = 'replay-' + rnd
28
+ const name = 'temp-' + rnd
28
29
 
29
30
  context.name += '-' + rnd
30
- context.compositions = [{ name, components }]
31
+ context.compositions = [{
32
+ name,
33
+ components
34
+ }]
31
35
 
32
36
  return context
33
37
  }
@@ -1,6 +1,5 @@
1
1
  'use strict'
2
2
 
3
- const { console } = require('@toa.io/console')
4
3
  const { context: find } = require('../../util/find')
5
4
  const { deployment: { Factory } } = require('@toa.io/operations')
6
5
 
@@ -1,6 +1,5 @@
1
1
  'use strict'
2
2
 
3
- const { console } = require('@toa.io/console')
4
3
  const { context: find } = require('../../util/find')
5
4
  const { deployment: { Factory } } = require('@toa.io/operations')
6
5
 
@@ -1,7 +1,7 @@
1
1
  'use strict'
2
2
 
3
+ const jsonpath = require('jsonpath')
3
4
  const { component } = require('@toa.io/norm')
4
- const { console } = require('@toa.io/console')
5
5
  const yaml = require('@toa.io/yaml')
6
6
 
7
7
  const { components: find } = require('../../util/find')
@@ -11,7 +11,10 @@ const print = async (argv) => {
11
11
 
12
12
  if (path === undefined) throw new Error(`No component found in ${argv.path}`)
13
13
 
14
- const manifest = await component(path)
14
+ let manifest = await component(path)
15
+
16
+ if (argv.jsonpath !== undefined)
17
+ manifest = jsonpath.value(manifest, argv.jsonpath)
15
18
 
16
19
  if (argv.error !== true) {
17
20
  const result = argv.output === 'json'
@@ -0,0 +1,40 @@
1
+ 'use strict'
2
+
3
+ const { context: find } = require('../../util/find')
4
+ const { deployment: { Factory } } = require('@toa.io/operations')
5
+
6
+ /**
7
+ * @param {{ path: string, target: string, environment: string }} argv
8
+ * @returns {Promise<void>}
9
+ */
10
+ const secrets = async (argv) => {
11
+ const path = find(argv.path)
12
+ const factory = await Factory.create(path, argv.environment)
13
+ const operator = factory.operator()
14
+ const secrets = operator
15
+ .variables()
16
+ .filter(({ secret }) => secret !== undefined)
17
+ .map(({ secret }) => secret)
18
+ .sort((a, b) => a.name.localeCompare(b.name))
19
+
20
+ const groups = []
21
+ let current = null
22
+
23
+ for (const secret of secrets) {
24
+ if (current === null || current.name !== secret.name) {
25
+ current = { name: secret.name, keys: new Map() }
26
+ groups.push(current)
27
+ }
28
+
29
+ current.keys.set(secret.key, secret.optional)
30
+ }
31
+
32
+ for (const group of groups) {
33
+ console.log(`${group.name}:`)
34
+
35
+ for (const [key, optional] of group.keys)
36
+ console.log(' ' + key + (optional ? ' (optional)' : ''))
37
+ }
38
+ }
39
+
40
+ exports.secrets = secrets
@@ -0,0 +1,20 @@
1
+ 'use strict'
2
+
3
+ const { context: find } = require('../../util/find')
4
+ const { deployment: { Factory } } = require('@toa.io/operations')
5
+
6
+ /**
7
+ * @param {{ path: string, target: string, environment: string }} argv
8
+ * @returns {Promise<void>}
9
+ */
10
+ const tags = async (argv) => {
11
+ const path = find(argv.path)
12
+ const factory = await Factory.create(path, argv.environment)
13
+ const operator = factory.operator()
14
+ const tags = operator.tags()
15
+
16
+ for (const tag of tags)
17
+ console.log(tag)
18
+ }
19
+
20
+ exports.tags = tags
@@ -0,0 +1,14 @@
1
+ 'use strict'
2
+
3
+ const { console } = require('openspan')
4
+
5
+ function graceful (connector) {
6
+ ['SIGTERM', 'SIGINT']
7
+ .forEach(signal => process.once(signal, async () => {
8
+ console.info('Shutting down', { signal })
9
+
10
+ await connector.disconnect()
11
+ }))
12
+ }
13
+
14
+ exports.graceful = graceful
@@ -4,6 +4,7 @@ const boot = require('@toa.io/boot')
4
4
  const { shortcuts } = require('@toa.io/norm')
5
5
  const { directory: { find } } = require('@toa.io/filesystem')
6
6
  const { version } = require('@toa.io/runtime')
7
+ const { graceful } = require('./lib/graceful')
7
8
 
8
9
  const serve = async (argv) => {
9
10
  console.log('Runtime', version)
@@ -22,8 +23,7 @@ const serve = async (argv) => {
22
23
 
23
24
  await service.connect()
24
25
 
25
- // for tests
26
- return service
26
+ return graceful(service)
27
27
  }
28
28
 
29
29
  exports.serve = serve
package/src/program.js CHANGED
@@ -4,7 +4,6 @@
4
4
 
5
5
  const yargs = require('yargs/yargs')
6
6
 
7
- const { console } = require('@toa.io/console')
8
7
  const { version } = require('@toa.io/runtime')
9
8
 
10
9
  yargs(process.argv.slice(2))
@@ -13,8 +12,6 @@ yargs(process.argv.slice(2))
13
12
  })
14
13
  .middleware((argv) => {
15
14
  if (argv.log === undefined) argv.log = process.env.TOA_DEBUG === '1' ? 'debug' : 'info'
16
-
17
- console.level(argv.log)
18
15
  })
19
16
  .middleware(async (argv) => {
20
17
  if (argv.env === undefined) return
@@ -1,26 +0,0 @@
1
- 'use strict'
2
-
3
- const { manifest } = require('../../handlers/export/entity')
4
-
5
- const builder = (yargs) => {
6
- yargs
7
- .option('path', {
8
- alias: 'p',
9
- group: 'Command options:',
10
- type: 'string',
11
- desc: 'Path to a component',
12
- default: '.'
13
- })
14
- .option('output', {
15
- alias: 'o',
16
- group: 'Command options:',
17
- choices: ['yaml', 'json'],
18
- desc: 'Output format',
19
- default: 'yaml'
20
- })
21
- }
22
-
23
- exports.command = 'entity'
24
- exports.desc = 'Print entity'
25
- exports.builder = builder
26
- exports.handler = manifest
@@ -1,66 +0,0 @@
1
- // noinspection JSCheckFunctionSignatures
2
-
3
- 'use strict'
4
-
5
- const { replay } = require('../handlers/replay')
6
-
7
- /*
8
- !!! OPTIONS MUST BE SYNCHRONIZED WITH ../handlers/.replay/args !!!
9
- */
10
-
11
- const builder = (yargs) => {
12
- yargs
13
- .positional('paths', {
14
- type: 'string',
15
- desc: 'Paths to components or context',
16
- default: '.'
17
- })
18
- .option('component', {
19
- alias: 'c',
20
- type: 'string',
21
- group: 'Command options:',
22
- describe: 'Replay samples for specified component'
23
- })
24
- .option('autonomous', {
25
- alias: 'a',
26
- type: 'boolean',
27
- group: 'Command options:',
28
- describe: 'Replay autonomous tests only'
29
- })
30
- .option('integration', {
31
- alias: 'i',
32
- type: 'boolean',
33
- group: 'Command options:',
34
- describe: 'Replay integration tests only'
35
- })
36
- .option('operation', {
37
- alias: 'o',
38
- type: 'string',
39
- group: 'Command options:',
40
- describe: 'Replay samples for specified operation'
41
- })
42
- .option('title', {
43
- alias: 't',
44
- type: 'string',
45
- group: 'Command options:',
46
- describe: 'Replay samples with titles matching given regexp'
47
- })
48
- .option('dock', {
49
- alias: 'd',
50
- type: 'boolean',
51
- default: false,
52
- group: 'Command options:',
53
- describe: 'Replay inside Docker container'
54
- })
55
- .option('context', {
56
- group: 'Command options:',
57
- type: 'string',
58
- desc: 'Path to the Context (used with --dock)',
59
- default: '.'
60
- })
61
- }
62
-
63
- exports.command = 'replay [paths...]'
64
- exports.desc = 'Replay samples'
65
- exports.builder = builder
66
- exports.handler = replay
@@ -1,27 +0,0 @@
1
- 'use strict'
2
-
3
- const { component } = require('@toa.io/norm')
4
- const { console } = require('@toa.io/console')
5
- const yaml = require('@toa.io/yaml')
6
-
7
- const { components: find } = require('../../util/find')
8
-
9
- const print = async (argv) => {
10
- const path = find(argv.path)
11
-
12
- if (path === undefined) throw new Error(`No component found in ${argv.path}`)
13
-
14
- const manifest = await component(path)
15
- const entity = manifest.entity
16
-
17
- if (entity === undefined)
18
- return
19
-
20
- const result = argv.output === 'json'
21
- ? JSON.stringify(entity, null, 2)
22
- : yaml.dump(entity)
23
-
24
- console.log(result)
25
- }
26
-
27
- exports.manifest = print
@@ -1,64 +0,0 @@
1
- 'use strict'
2
-
3
- const { pick } = require('@toa.io/generic')
4
- const { context, components } = require('@toa.io/userland/samples')
5
-
6
- const find = require('../util/find')
7
- const docker = require('./docker')
8
-
9
- /**
10
- * @param {Record<string, string | string[] | boolean>} argv
11
- * @return {Promise<void>}
12
- */
13
- async function replay (argv) {
14
- if (argv.dock) return dock(argv)
15
-
16
- /** @type {boolean} */
17
- let ok
18
-
19
- const paths = find.components(argv.paths, true)
20
-
21
- /** @type {toa.samples.suite.Options} */
22
- const options = {
23
- component: argv.component,
24
- autonomous: argv.autonomous,
25
- integration: argv.integration,
26
- operation: argv.operation,
27
- title: argv.title,
28
- runner: { bail: true }
29
- }
30
-
31
- if (paths !== null) {
32
- ok = await components(paths, options)
33
- } else {
34
- // no components found, checking context
35
- const path = find.context(argv.paths[0], true)
36
-
37
- if (path === null) throw new Error('Neither components nor context found in ' + argv.paths.join(','))
38
-
39
- ok = await context(path, options)
40
- }
41
-
42
- const message = (ok ? GREEN + 'PASSED' : RED + 'FAILED') + RESET
43
-
44
- // print after tap's output
45
- process.on('beforeExit', () => console.log(message))
46
- }
47
-
48
- /**
49
- * @param {Record<string, string | string[] | boolean>} argv
50
- * @return {Promise<void>}
51
- */
52
- async function dock (argv) {
53
- const repository = await docker.build(argv.context, argv.paths)
54
- const args = pick(argv, ['component', 'operation', 'integration', 'title'])
55
- const command = docker.command('toa replay *', args)
56
-
57
- await docker.run(repository, command, argv.env)
58
- }
59
-
60
- const GREEN = '\x1b[32m'
61
- const RED = '\x1b[31m'
62
- const RESET = '\x1b[0m'
63
-
64
- exports.replay = replay