stepzen 0.44.0 → 0.45.0-beta.0
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/README.md +76 -71
- package/bin/dev.cmd +1 -1
- package/bin/dev.js +6 -0
- package/bin/{run → run.js} +6 -20
- package/lib/commands/delete.d.ts +3 -3
- package/lib/commands/delete.d.ts.map +1 -1
- package/lib/commands/delete.js +32 -33
- package/lib/commands/delete.js.map +1 -1
- package/lib/commands/deploy.d.ts +10 -12
- package/lib/commands/deploy.d.ts.map +1 -1
- package/lib/commands/deploy.js +98 -98
- package/lib/commands/deploy.js.map +1 -1
- package/lib/commands/import/curl.d.ts +17 -24
- package/lib/commands/import/curl.d.ts.map +1 -1
- package/lib/commands/import/curl.js +85 -85
- package/lib/commands/import/curl.js.map +1 -1
- package/lib/commands/import/db2.d.ts +20 -28
- package/lib/commands/import/db2.d.ts.map +1 -1
- package/lib/commands/import/db2.js +59 -62
- package/lib/commands/import/db2.js.map +1 -1
- package/lib/commands/import/flow.d.ts +10 -71
- package/lib/commands/import/flow.d.ts.map +1 -1
- package/lib/commands/import/flow.js +39 -43
- package/lib/commands/import/flow.js.map +1 -1
- package/lib/commands/import/graphql.d.ts +10 -18
- package/lib/commands/import/graphql.d.ts.map +1 -1
- package/lib/commands/import/graphql.js +38 -42
- package/lib/commands/import/graphql.js.map +1 -1
- package/lib/commands/import/index.d.ts +7 -10
- package/lib/commands/import/index.d.ts.map +1 -1
- package/lib/commands/import/index.js +52 -53
- package/lib/commands/import/index.js.map +1 -1
- package/lib/commands/import/mysql.d.ts +14 -22
- package/lib/commands/import/mysql.d.ts.map +1 -1
- package/lib/commands/import/mysql.js +53 -56
- package/lib/commands/import/mysql.js.map +1 -1
- package/lib/commands/import/openapi.d.ts +9 -16
- package/lib/commands/import/openapi.d.ts.map +1 -1
- package/lib/commands/import/openapi.js +38 -41
- package/lib/commands/import/openapi.js.map +1 -1
- package/lib/commands/import/oracle.d.ts +19 -26
- package/lib/commands/import/oracle.d.ts.map +1 -1
- package/lib/commands/import/oracle.js +56 -59
- package/lib/commands/import/oracle.js.map +1 -1
- package/lib/commands/import/postgresql.d.ts +15 -23
- package/lib/commands/import/postgresql.d.ts.map +1 -1
- package/lib/commands/import/postgresql.js +54 -57
- package/lib/commands/import/postgresql.js.map +1 -1
- package/lib/commands/import/presto.d.ts +14 -22
- package/lib/commands/import/presto.d.ts.map +1 -1
- package/lib/commands/import/presto.js +57 -60
- package/lib/commands/import/presto.js.map +1 -1
- package/lib/commands/import/snowflake.d.ts +16 -24
- package/lib/commands/import/snowflake.d.ts.map +1 -1
- package/lib/commands/import/snowflake.js +61 -64
- package/lib/commands/import/snowflake.js.map +1 -1
- package/lib/commands/init.d.ts +5 -7
- package/lib/commands/init.d.ts.map +1 -1
- package/lib/commands/init.js +22 -22
- package/lib/commands/init.js.map +1 -1
- package/lib/commands/lint.d.ts +2 -4
- package/lib/commands/lint.d.ts.map +1 -1
- package/lib/commands/lint.js +15 -18
- package/lib/commands/lint.js.map +1 -1
- package/lib/commands/list.d.ts +4 -6
- package/lib/commands/list.d.ts.map +1 -1
- package/lib/commands/list.js +66 -63
- package/lib/commands/list.js.map +1 -1
- package/lib/commands/login.d.ts +8 -10
- package/lib/commands/login.d.ts.map +1 -1
- package/lib/commands/login.js +94 -92
- package/lib/commands/login.js.map +1 -1
- package/lib/commands/logout.d.ts +2 -3
- package/lib/commands/logout.d.ts.map +1 -1
- package/lib/commands/logout.js +8 -11
- package/lib/commands/logout.js.map +1 -1
- package/lib/commands/request.d.ts +10 -12
- package/lib/commands/request.d.ts.map +1 -1
- package/lib/commands/request.js +127 -129
- package/lib/commands/request.js.map +1 -1
- package/lib/commands/service.d.ts +9 -11
- package/lib/commands/service.d.ts.map +1 -1
- package/lib/commands/service.js +203 -203
- package/lib/commands/service.js.map +1 -1
- package/lib/commands/start.d.ts +13 -15
- package/lib/commands/start.d.ts.map +1 -1
- package/lib/commands/start.js +61 -60
- package/lib/commands/start.js.map +1 -1
- package/lib/commands/transpile.d.ts +8 -10
- package/lib/commands/transpile.d.ts.map +1 -1
- package/lib/commands/transpile.js +31 -34
- package/lib/commands/transpile.js.map +1 -1
- package/lib/commands/upload.d.ts +6 -8
- package/lib/commands/upload.d.ts.map +1 -1
- package/lib/commands/upload.js +41 -44
- package/lib/commands/upload.js.map +1 -1
- package/lib/commands/validate.d.ts +3 -4
- package/lib/commands/validate.d.ts.map +1 -1
- package/lib/commands/validate.js +23 -26
- package/lib/commands/validate.js.map +1 -1
- package/lib/commands/whoami.d.ts +6 -8
- package/lib/commands/whoami.d.ts.map +1 -1
- package/lib/commands/whoami.js +39 -48
- package/lib/commands/whoami.js.map +1 -1
- package/lib/generate/curl2sdl.d.ts +8 -8
- package/lib/generate/curl2sdl.d.ts.map +1 -1
- package/lib/generate/curl2sdl.js +16 -20
- package/lib/generate/curl2sdl.js.map +1 -1
- package/lib/generate/flags.d.ts +11 -11
- package/lib/generate/flags.d.ts.map +1 -1
- package/lib/generate/flags.js +16 -17
- package/lib/generate/flags.js.map +1 -1
- package/lib/generate/flow2sdl.d.ts +5 -5
- package/lib/generate/flow2sdl.d.ts.map +1 -1
- package/lib/generate/flow2sdl.js +13 -19
- package/lib/generate/flow2sdl.js.map +1 -1
- package/lib/generate/graphql2sdl.d.ts +5 -5
- package/lib/generate/graphql2sdl.d.ts.map +1 -1
- package/lib/generate/graphql2sdl.js +22 -28
- package/lib/generate/graphql2sdl.js.map +1 -1
- package/lib/generate/helpers.d.ts +5 -5
- package/lib/generate/helpers.d.ts.map +1 -1
- package/lib/generate/helpers.js +39 -44
- package/lib/generate/helpers.js.map +1 -1
- package/lib/generate/import-command.d.ts +7 -13
- package/lib/generate/import-command.d.ts.map +1 -1
- package/lib/generate/import-command.js +51 -52
- package/lib/generate/import-command.js.map +1 -1
- package/lib/generate/openapi2sdl.d.ts +4 -4
- package/lib/generate/openapi2sdl.d.ts.map +1 -1
- package/lib/generate/openapi2sdl.js +10 -16
- package/lib/generate/openapi2sdl.js.map +1 -1
- package/lib/generate/questions.d.ts.map +1 -1
- package/lib/generate/questions.js +4 -7
- package/lib/generate/questions.js.map +1 -1
- package/lib/generate/sql2sdl.d.ts +13 -13
- package/lib/generate/sql2sdl.d.ts.map +1 -1
- package/lib/generate/sql2sdl.js +13 -16
- package/lib/generate/sql2sdl.js.map +1 -1
- package/lib/hooks/init/version.d.ts +1 -1
- package/lib/hooks/init/version.d.ts.map +1 -1
- package/lib/hooks/init/version.js +11 -9
- package/lib/hooks/init/version.js.map +1 -1
- package/lib/hooks/prerun/check-upgrade.d.ts +1 -1
- package/lib/hooks/prerun/check-upgrade.d.ts.map +1 -1
- package/lib/hooks/prerun/check-upgrade.js +42 -46
- package/lib/hooks/prerun/check-upgrade.js.map +1 -1
- package/lib/hooks/prerun/ensure-config-file.d.ts +1 -1
- package/lib/hooks/prerun/ensure-config-file.d.ts.map +1 -1
- package/lib/hooks/prerun/ensure-config-file.js +9 -11
- package/lib/hooks/prerun/ensure-config-file.js.map +1 -1
- package/lib/hooks/prerun/ensure-permissions.d.ts +1 -1
- package/lib/hooks/prerun/ensure-permissions.d.ts.map +1 -1
- package/lib/hooks/prerun/ensure-permissions.js +15 -11
- package/lib/hooks/prerun/ensure-permissions.js.map +1 -1
- package/lib/index.js +1 -5
- package/lib/index.js.map +1 -1
- package/lib/shared/actions.d.ts +2 -2
- package/lib/shared/actions.d.ts.map +1 -1
- package/lib/shared/actions.js +15 -21
- package/lib/shared/actions.js.map +1 -1
- package/lib/shared/configuration.d.ts +5 -3
- package/lib/shared/configuration.d.ts.map +1 -1
- package/lib/shared/configuration.js +39 -52
- package/lib/shared/configuration.js.map +1 -1
- package/lib/shared/constants.d.ts +7 -4
- package/lib/shared/constants.d.ts.map +1 -1
- package/lib/shared/constants.js +70 -86
- package/lib/shared/constants.js.map +1 -1
- package/lib/shared/curl-parser.d.ts +2 -2
- package/lib/shared/curl-parser.d.ts.map +1 -1
- package/lib/shared/curl-parser.js +9 -13
- package/lib/shared/curl-parser.js.map +1 -1
- package/lib/shared/docker.d.ts +2 -4
- package/lib/shared/docker.d.ts.map +1 -1
- package/lib/shared/docker.js +40 -48
- package/lib/shared/docker.js.map +1 -1
- package/lib/shared/errors.js +7 -11
- package/lib/shared/errors.js.map +1 -1
- package/lib/shared/header-params-parser.d.ts +2 -4
- package/lib/shared/header-params-parser.d.ts.map +1 -1
- package/lib/shared/header-params-parser.js +23 -29
- package/lib/shared/header-params-parser.js.map +1 -1
- package/lib/shared/header.d.ts +7 -7
- package/lib/shared/header.d.ts.map +1 -1
- package/lib/shared/header.js +5 -10
- package/lib/shared/header.js.map +1 -1
- package/lib/shared/inquirer.d.ts +7 -6
- package/lib/shared/inquirer.d.ts.map +1 -1
- package/lib/shared/inquirer.js +29 -37
- package/lib/shared/inquirer.js.map +1 -1
- package/lib/shared/moniker.js +4 -9
- package/lib/shared/moniker.js.map +1 -1
- package/lib/shared/path-params-parser.d.ts +2 -2
- package/lib/shared/path-params-parser.d.ts.map +1 -1
- package/lib/shared/path-params-parser.js +6 -12
- package/lib/shared/path-params-parser.js.map +1 -1
- package/lib/shared/request-variables-parser.d.ts +4 -4
- package/lib/shared/request-variables-parser.d.ts.map +1 -1
- package/lib/shared/request-variables-parser.js +10 -14
- package/lib/shared/request-variables-parser.js.map +1 -1
- package/lib/shared/rmtemp.d.ts +1 -0
- package/lib/shared/rmtemp.d.ts.map +1 -1
- package/lib/shared/rmtemp.js +7 -10
- package/lib/shared/rmtemp.js.map +1 -1
- package/lib/shared/segment.d.ts +10 -0
- package/lib/shared/segment.d.ts.map +1 -0
- package/lib/shared/segment.js +162 -0
- package/lib/shared/segment.js.map +1 -0
- package/lib/shared/stepzen-sdk.d.ts +8 -8
- package/lib/shared/stepzen-sdk.d.ts.map +1 -1
- package/lib/shared/stepzen-sdk.js +27 -36
- package/lib/shared/stepzen-sdk.js.map +1 -1
- package/lib/shared/types.d.ts +13 -11
- package/lib/shared/types.d.ts.map +1 -1
- package/lib/shared/types.js +1 -2
- package/lib/shared/url-helpers.d.ts.map +1 -1
- package/lib/shared/url-helpers.js +7 -14
- package/lib/shared/url-helpers.js.map +1 -1
- package/lib/shared/utils.d.ts +2 -2
- package/lib/shared/utils.d.ts.map +1 -1
- package/lib/shared/utils.js +55 -71
- package/lib/shared/utils.js.map +1 -1
- package/lib/shared/validation.js +12 -17
- package/lib/shared/validation.js.map +1 -1
- package/lib/shared/workspace.d.ts +1 -1
- package/lib/shared/workspace.d.ts.map +1 -1
- package/lib/shared/workspace.js +34 -45
- package/lib/shared/workspace.js.map +1 -1
- package/lib/shared/zen-command.d.ts +5 -10
- package/lib/shared/zen-command.d.ts.map +1 -1
- package/lib/shared/zen-command.js +132 -113
- package/lib/shared/zen-command.js.map +1 -1
- package/oclif.manifest.json +1281 -945
- package/package.json +35 -33
- package/bin/dev +0 -23
package/lib/commands/service.js
CHANGED
|
@@ -1,31 +1,28 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
// Copyright IBM Corp. 2020, 2024
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const utils_1 = require("../shared/utils");
|
|
22
|
-
const zen_command_1 = require("../shared/zen-command");
|
|
2
|
+
import { Args, Flags, ux } from '@oclif/core';
|
|
3
|
+
import { CLIError } from '@oclif/core/errors';
|
|
4
|
+
import { parseDsn } from '@soluble/dsn-parser';
|
|
5
|
+
import fetch from '@stepzen/fetch';
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import debug from 'debug';
|
|
8
|
+
import { isEqual } from 'lodash-es';
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
// @ts-expect-error this package has no TS types
|
|
11
|
+
import { sh } from 'puka';
|
|
12
|
+
import shell from 'shelljs';
|
|
13
|
+
import { getStepZenDockerImage, readConfiguration, removeCredentialsAndServiceFromConfigFile, writeConfiguration, } from '../shared/configuration.js';
|
|
14
|
+
import { STEPZEN_AUTODB_CONTAINER_NAME, STEPZEN_AUTODB_DATABASE, STEPZEN_AUTODB_DSN, STEPZEN_AUTODB_IMAGE, STEPZEN_AUTODB_PASSWORD, STEPZEN_AUTODB_USER, STEPZEN_CONFIG_DIRECTORY, STEPZEN_CONTAINER_NAME, STEPZEN_CONTAINER_NETWORK, STEPZEN_DISCORD_URL, STEPZEN_LOCAL_CONFIG_FILE, } from '../shared/constants.js';
|
|
15
|
+
import { getDockerImageVersions } from '../shared/docker.js';
|
|
16
|
+
import * as inquirer from '../shared/inquirer.js';
|
|
17
|
+
import { getPortFromUrl, isLocalhost, parseHost } from '../shared/url-helpers.js';
|
|
18
|
+
import { homeRelative, maskPasswordInDsn, prependEachLine, } from '../shared/utils.js';
|
|
19
|
+
import ZenCommand from '../shared/zen-command.js';
|
|
23
20
|
const DEFAULT_ZEN_PORT = 9000;
|
|
24
21
|
const dsnQuestions = [
|
|
25
22
|
{
|
|
26
23
|
name: 'host',
|
|
27
24
|
message: 'What is the database host?',
|
|
28
|
-
validate: input =>
|
|
25
|
+
validate: input => parseHost(input).isValid || `Could not parse "${input}" as a hostname.`,
|
|
29
26
|
},
|
|
30
27
|
{
|
|
31
28
|
name: 'database',
|
|
@@ -43,11 +40,66 @@ const dsnQuestions = [
|
|
|
43
40
|
type: 'password',
|
|
44
41
|
},
|
|
45
42
|
];
|
|
46
|
-
class Service extends
|
|
43
|
+
export default class Service extends ZenCommand {
|
|
44
|
+
static description = 'Manage the local StepZen service instance (requires Docker).' +
|
|
45
|
+
'\n' +
|
|
46
|
+
'You can use a local StepZen service instance instead of the StepZen' +
|
|
47
|
+
' cloud to develop and test GraphQL endpoints locally. This way you can' +
|
|
48
|
+
' try StepZen out without giving the StepZen cloud access to your data' +
|
|
49
|
+
' sources and API endpoints.' +
|
|
50
|
+
'\n\n' +
|
|
51
|
+
'Local StepZen service instances are not suitable for production use.' +
|
|
52
|
+
' Please refer to the documentation at' +
|
|
53
|
+
' https://stepzen.com/docs/deployment/local-docker for more information' +
|
|
54
|
+
' and examples.';
|
|
55
|
+
static flags = {
|
|
56
|
+
...ZenCommand.flags,
|
|
57
|
+
dsn: Flags.string({
|
|
58
|
+
description: chalk `DSN of a StepZen metadata database, e.g.` +
|
|
59
|
+
chalk ` {bold postgres://user:password@172.17.0.1:5432/zenctl}`,
|
|
60
|
+
exclusive: ['reset-auto', 'prompt'],
|
|
61
|
+
}),
|
|
62
|
+
'reset-auto': Flags.boolean({
|
|
63
|
+
description: 'Create fresh empty metadata database. Note: all existing endpoints will be deleted and you will be logged out.',
|
|
64
|
+
exclusive: ['dsn', 'prompt'],
|
|
65
|
+
}),
|
|
66
|
+
prompt: Flags.boolean({
|
|
67
|
+
description: 'Opens an interactive prompt to ask for database details (or fails, if running' +
|
|
68
|
+
' in a non-interactive mode).',
|
|
69
|
+
exclusive: ['dsn', 'non-interactive'],
|
|
70
|
+
}),
|
|
71
|
+
port: Flags.integer({
|
|
72
|
+
description: `Override the TCP port on which the local StepZen service instance will listen` +
|
|
73
|
+
` for connections (port ${DEFAULT_ZEN_PORT} by default).`,
|
|
74
|
+
}),
|
|
75
|
+
};
|
|
76
|
+
static args = {
|
|
77
|
+
action: Args.string({
|
|
78
|
+
options: ['start', 'stop', 'upgrade'],
|
|
79
|
+
required: false,
|
|
80
|
+
description: chalk `
|
|
81
|
+
\t{bold start}\tstart a local StepZen service instance in a Docker container
|
|
82
|
+
\t\tThis command pulls the latest StepZen service instance Docker image so that the
|
|
83
|
+
\t\tversion of StepZen you run locally is the same as the one running on
|
|
84
|
+
\t\tstepzen.net.
|
|
85
|
+
|
|
86
|
+
\t{bold stop}\tstop the local StepZen service instance (if running)
|
|
87
|
+
\t\tThis command deletes the Docker container with the StepZen service instance,
|
|
88
|
+
\t\tbut all your endpoints are persistently stored in the metadata database. They
|
|
89
|
+
\t\tremain available when running {bold stepzen service start} next time.
|
|
90
|
+
|
|
91
|
+
\t{bold upgrade}\tupgrade the local StepZen service instance if it is running
|
|
92
|
+
\t\tThis command checks if a newer version of the StepZen service instance Docker
|
|
93
|
+
\t\timage is available, and if it is then stops and deletes the currently running
|
|
94
|
+
\t\tcontainer, pulls the latest image, and starts it. All endpoints are preserved.
|
|
95
|
+
|
|
96
|
+
If no action is provided, print the service status and exit.`,
|
|
97
|
+
}),
|
|
98
|
+
};
|
|
47
99
|
async run() {
|
|
48
100
|
const { args, flags } = await this.parse(Service);
|
|
49
101
|
await this.ensureDockerInPath();
|
|
50
|
-
let status = await this.getDockerContainerStatus(
|
|
102
|
+
let status = await this.getDockerContainerStatus(STEPZEN_CONTAINER_NAME);
|
|
51
103
|
switch (args.action) {
|
|
52
104
|
case 'start':
|
|
53
105
|
switch (status) {
|
|
@@ -56,7 +108,7 @@ class Service extends zen_command_1.default {
|
|
|
56
108
|
// eslint-disable-next-line no-fallthrough
|
|
57
109
|
case 'created':
|
|
58
110
|
case 'exited': {
|
|
59
|
-
const port =
|
|
111
|
+
const { port } = await this.getPorts(flags);
|
|
60
112
|
// this would be the case when Docker is restarted
|
|
61
113
|
const result = await this.startDockerContainers(port);
|
|
62
114
|
await this.createLocalCredentialsFile({
|
|
@@ -75,7 +127,7 @@ class Service extends zen_command_1.default {
|
|
|
75
127
|
switch (status) {
|
|
76
128
|
case 'running':
|
|
77
129
|
status = await this.stopDockerContainers();
|
|
78
|
-
// eslint-disable-next-line no-fallthrough
|
|
130
|
+
// eslint-disable-next-line no-fallthrough, default-case-last
|
|
79
131
|
default:
|
|
80
132
|
status = await this.removeDockerContainers();
|
|
81
133
|
this.log();
|
|
@@ -85,9 +137,9 @@ class Service extends zen_command_1.default {
|
|
|
85
137
|
}
|
|
86
138
|
break;
|
|
87
139
|
case 'upgrade': {
|
|
88
|
-
const { result: versions, error } = await
|
|
140
|
+
const { result: versions, error } = await getDockerImageVersions();
|
|
89
141
|
if (error) {
|
|
90
|
-
throw new
|
|
142
|
+
throw new CLIError(`Could not upgrade the local StepZen service. ` + error.message);
|
|
91
143
|
}
|
|
92
144
|
if (versions.local === versions.latest) {
|
|
93
145
|
this.log('The local StepZen docker image is already the latest.');
|
|
@@ -96,16 +148,16 @@ class Service extends zen_command_1.default {
|
|
|
96
148
|
const initialStatus = status;
|
|
97
149
|
switch (status) {
|
|
98
150
|
case 'running':
|
|
99
|
-
|
|
151
|
+
ux.action.start(chalk `Stopping the {bold ${STEPZEN_CONTAINER_NAME}} Docker container`);
|
|
100
152
|
try {
|
|
101
|
-
await this.stopDockerContainer(
|
|
153
|
+
await this.stopDockerContainer(STEPZEN_CONTAINER_NAME);
|
|
102
154
|
}
|
|
103
155
|
catch (error) {
|
|
104
|
-
|
|
156
|
+
ux.action.stop('fail');
|
|
105
157
|
throw error;
|
|
106
158
|
}
|
|
107
|
-
|
|
108
|
-
// eslint-disable-next-line no-fallthrough
|
|
159
|
+
ux.action.stop('done');
|
|
160
|
+
// eslint-disable-next-line no-fallthrough, default-case-last
|
|
109
161
|
default: {
|
|
110
162
|
const dsn = await this.getDsnFromDockerContainer();
|
|
111
163
|
await this.removeDockerContainers();
|
|
@@ -114,15 +166,15 @@ class Service extends zen_command_1.default {
|
|
|
114
166
|
dsn: this.validateProvidedDsn(flags) || dsn,
|
|
115
167
|
});
|
|
116
168
|
if (initialStatus === 'running') {
|
|
117
|
-
|
|
169
|
+
ux.action.start(chalk `Starting the {bold ${STEPZEN_CONTAINER_NAME}} Docker container`);
|
|
118
170
|
try {
|
|
119
|
-
status = await this.startDockerContainer(
|
|
171
|
+
status = await this.startDockerContainer(STEPZEN_CONTAINER_NAME);
|
|
120
172
|
}
|
|
121
173
|
catch (error) {
|
|
122
|
-
|
|
174
|
+
ux.action.stop('fail');
|
|
123
175
|
throw error;
|
|
124
176
|
}
|
|
125
|
-
|
|
177
|
+
ux.action.stop('done');
|
|
126
178
|
}
|
|
127
179
|
}
|
|
128
180
|
// eslint-disable-next-line no-fallthrough
|
|
@@ -136,12 +188,12 @@ class Service extends zen_command_1.default {
|
|
|
136
188
|
}
|
|
137
189
|
}
|
|
138
190
|
async getPorts(flags) {
|
|
139
|
-
const config = await
|
|
191
|
+
const config = await readConfiguration();
|
|
140
192
|
const legacyPorts = new Set();
|
|
141
193
|
if (config.serviceInstance &&
|
|
142
194
|
'zenctl2' in config.serviceInstance &&
|
|
143
|
-
|
|
144
|
-
const zenservPort =
|
|
195
|
+
isLocalhost(config.serviceInstance.zenctl2)) {
|
|
196
|
+
const zenservPort = getPortFromUrl(config.serviceInstance.zenctl2);
|
|
145
197
|
if (!flags.port && zenservPort !== DEFAULT_ZEN_PORT) {
|
|
146
198
|
// see https://github.com/steprz/stepzen-cli/issues/956
|
|
147
199
|
legacyPorts.add(zenservPort);
|
|
@@ -150,13 +202,13 @@ class Service extends zen_command_1.default {
|
|
|
150
202
|
'introspection' in config.serviceInstance &&
|
|
151
203
|
config.serviceInstance.introspection) {
|
|
152
204
|
// backwards compatibility
|
|
153
|
-
legacyPorts.add(
|
|
205
|
+
legacyPorts.add(getPortFromUrl(config.serviceInstance.introspection));
|
|
154
206
|
}
|
|
155
207
|
if (!flags.port &&
|
|
156
208
|
'dbintrospection' in config.serviceInstance &&
|
|
157
209
|
config.serviceInstance.dbintrospection) {
|
|
158
210
|
// backwards compatibility
|
|
159
|
-
legacyPorts.add(
|
|
211
|
+
legacyPorts.add(getPortFromUrl(config.serviceInstance.dbintrospection));
|
|
160
212
|
}
|
|
161
213
|
}
|
|
162
214
|
const port = flags.port || DEFAULT_ZEN_PORT;
|
|
@@ -166,7 +218,7 @@ class Service extends zen_command_1.default {
|
|
|
166
218
|
}
|
|
167
219
|
async ensureDockerInPath() {
|
|
168
220
|
if (!shell.which('docker')) {
|
|
169
|
-
throw new
|
|
221
|
+
throw new CLIError(chalk `{bold stepzen service} needs Docker CLI to be installed, but could not find it.` +
|
|
170
222
|
chalk `\nPlease check that the {bold docker} binary is in PATH and try again.`);
|
|
171
223
|
}
|
|
172
224
|
}
|
|
@@ -175,15 +227,15 @@ class Service extends zen_command_1.default {
|
|
|
175
227
|
debug('stepzen:service')(chalk `running {cyan ${command}}`);
|
|
176
228
|
const result = shell.exec(command, { silent: true });
|
|
177
229
|
if (result.code) {
|
|
178
|
-
throw new
|
|
230
|
+
throw new CLIError(`failed to connect to Docker` +
|
|
179
231
|
`\nCommand failed: ${command}` +
|
|
180
232
|
`\n` +
|
|
181
|
-
`\n${chalk.dim(
|
|
233
|
+
`\n${chalk.dim(prependEachLine(result.stderr, '[docker] '))}` +
|
|
182
234
|
`\n` +
|
|
183
235
|
`\nPlease make sure Docker is running, and you are using the latest` +
|
|
184
236
|
` StepZen CLI version.` +
|
|
185
237
|
`\nIf the issue persists contact support via the StepZen discord ` +
|
|
186
|
-
`channel (${
|
|
238
|
+
`channel (${STEPZEN_DISCORD_URL}).`);
|
|
187
239
|
}
|
|
188
240
|
const status = (result.stdout.trim() ||
|
|
189
241
|
'not-created');
|
|
@@ -192,58 +244,58 @@ class Service extends zen_command_1.default {
|
|
|
192
244
|
}
|
|
193
245
|
async ensureDockerNetwork() {
|
|
194
246
|
// set up stepzen-network for container networking if it does not exist
|
|
195
|
-
const inspectCommand = `docker network list --filter "name=^${
|
|
247
|
+
const inspectCommand = `docker network list --filter "name=^${STEPZEN_CONTAINER_NETWORK}$" --format "{{.Driver}}"`;
|
|
196
248
|
debug('stepzen:service')(chalk `running {cyan ${inspectCommand}}`);
|
|
197
249
|
const inspectResult = shell.exec(inspectCommand, { silent: true });
|
|
198
250
|
if (inspectResult.code) {
|
|
199
|
-
throw new
|
|
251
|
+
throw new CLIError(`failed to connect to Docker` +
|
|
200
252
|
`\nCommand failed: ${inspectCommand}` +
|
|
201
253
|
`\n` +
|
|
202
|
-
`\n${chalk.dim(
|
|
254
|
+
`\n${chalk.dim(prependEachLine(inspectResult.stderr, '[docker] '))}` +
|
|
203
255
|
`\n` +
|
|
204
256
|
`\nPlease make sure Docker is running, and you are using the latest` +
|
|
205
257
|
` StepZen CLI version.` +
|
|
206
258
|
`\nIf the issue persists contact support via the StepZen discord ` +
|
|
207
|
-
`channel (${
|
|
259
|
+
`channel (${STEPZEN_DISCORD_URL}).`);
|
|
208
260
|
}
|
|
209
261
|
if (inspectResult.stdout.trim()) {
|
|
210
262
|
// stepzen-network already exists
|
|
211
263
|
return;
|
|
212
264
|
}
|
|
213
|
-
|
|
214
|
-
const createCommand = `docker network create -d bridge ${
|
|
265
|
+
ux.action.start(chalk `Creating the {bold ${STEPZEN_CONTAINER_NETWORK}} network`);
|
|
266
|
+
const createCommand = `docker network create -d bridge ${STEPZEN_CONTAINER_NETWORK}`;
|
|
215
267
|
debug('stepzen:service')(chalk `running {cyan ${createCommand}}`);
|
|
216
268
|
const createResult = shell.exec(createCommand, { silent: true });
|
|
217
269
|
if (createResult.code !== 0) {
|
|
218
|
-
|
|
219
|
-
throw new
|
|
270
|
+
ux.action.stop('fail');
|
|
271
|
+
throw new CLIError(`failed to create a StepZen Docker network.` +
|
|
220
272
|
`\nCommand failed: ${createCommand}` +
|
|
221
273
|
`\n` +
|
|
222
|
-
`\n${chalk.dim(
|
|
274
|
+
`\n${chalk.dim(prependEachLine(createResult.stderr, '[docker] '))}` +
|
|
223
275
|
`\n` +
|
|
224
276
|
`\nPlease make sure Docker is running, and you are using the` +
|
|
225
277
|
` latest StepZen CLI version.` +
|
|
226
278
|
`\nIf the issue persists contact support via the StepZen discord ` +
|
|
227
|
-
`channel (${
|
|
279
|
+
`channel (${STEPZEN_DISCORD_URL}).`);
|
|
228
280
|
}
|
|
229
|
-
|
|
281
|
+
ux.action.stop('done');
|
|
230
282
|
}
|
|
231
283
|
async createDockerContainers(flags) {
|
|
232
|
-
const useAutoDb = (flags.dsn === undefined || flags.dsn ===
|
|
284
|
+
const useAutoDb = (flags.dsn === undefined || flags.dsn === STEPZEN_AUTODB_DSN) &&
|
|
233
285
|
!flags.prompt;
|
|
234
286
|
// For auto-provisioned metadata db, use `stepzen-network` so that the
|
|
235
287
|
// db port doesn't need to be exposed on the host. This reduces the risk
|
|
236
288
|
// of port clashes.
|
|
237
|
-
const network = useAutoDb ?
|
|
289
|
+
const network = useAutoDb ? STEPZEN_CONTAINER_NETWORK : undefined;
|
|
238
290
|
let dsn;
|
|
239
291
|
if (useAutoDb) {
|
|
240
292
|
this.log(`StepZen CLI uses an automatically-managed metadata database, running in a` +
|
|
241
293
|
` local Docker container.`);
|
|
242
|
-
let dbStatus = await this.getDockerContainerStatus(
|
|
294
|
+
let dbStatus = await this.getDockerContainerStatus(STEPZEN_AUTODB_CONTAINER_NAME);
|
|
243
295
|
if (flags['reset-auto'] && dbStatus !== 'not-created') {
|
|
244
|
-
|
|
296
|
+
removeCredentialsAndServiceFromConfigFile();
|
|
245
297
|
this.removeDockerContainer({
|
|
246
|
-
containerName:
|
|
298
|
+
containerName: STEPZEN_AUTODB_CONTAINER_NAME,
|
|
247
299
|
ignoreErrors: true,
|
|
248
300
|
});
|
|
249
301
|
dbStatus = 'not-created';
|
|
@@ -251,41 +303,42 @@ class Service extends zen_command_1.default {
|
|
|
251
303
|
if (dbStatus === 'not-created') {
|
|
252
304
|
await this.ensureDockerNetwork();
|
|
253
305
|
await this.createDockerContainer({
|
|
254
|
-
image:
|
|
255
|
-
containerName:
|
|
306
|
+
image: STEPZEN_AUTODB_IMAGE,
|
|
307
|
+
containerName: STEPZEN_AUTODB_CONTAINER_NAME,
|
|
256
308
|
environment: {
|
|
257
|
-
POSTGRES_USER:
|
|
258
|
-
POSTGRES_PASSWORD:
|
|
259
|
-
POSTGRES_DB:
|
|
309
|
+
POSTGRES_USER: STEPZEN_AUTODB_USER,
|
|
310
|
+
POSTGRES_PASSWORD: STEPZEN_AUTODB_PASSWORD,
|
|
311
|
+
POSTGRES_DB: STEPZEN_AUTODB_DATABASE,
|
|
260
312
|
},
|
|
261
|
-
secretMasker: { STEPZEN_CONTROL_DB_DSN:
|
|
313
|
+
secretMasker: { STEPZEN_CONTROL_DB_DSN: maskPasswordInDsn },
|
|
262
314
|
ports: [],
|
|
263
315
|
pull: 'missing',
|
|
264
316
|
network,
|
|
265
317
|
hostGatewayParameter: false,
|
|
266
318
|
});
|
|
267
319
|
}
|
|
268
|
-
dsn =
|
|
320
|
+
dsn = STEPZEN_AUTODB_DSN;
|
|
269
321
|
}
|
|
270
322
|
else if (flags.dsn === '' || flags.prompt) {
|
|
271
323
|
this.log(`StepZen Docker container is stateless. It needs a connection to a` +
|
|
272
324
|
` StepZen metadata database to run.`);
|
|
273
325
|
const answers = await inquirer.prompt('service', dsnQuestions);
|
|
274
326
|
this.log();
|
|
275
|
-
const { host, port } =
|
|
327
|
+
const { host, port } = parseHost(answers.host);
|
|
276
328
|
dsn = `postgres://${encodeURIComponent(answers.user)}:${encodeURIComponent(answers.password)}@${encodeURIComponent(host)}:${port || 5432}/${encodeURIComponent(answers.database)}`;
|
|
277
329
|
}
|
|
278
330
|
else {
|
|
279
331
|
dsn = this.validateProvidedDsn(flags);
|
|
280
332
|
}
|
|
281
|
-
const image = await
|
|
333
|
+
const image = await getStepZenDockerImage();
|
|
282
334
|
await this.ensureDockerNetwork();
|
|
283
335
|
const { port, legacyPorts } = await this.getPorts(flags);
|
|
284
336
|
await this.createDockerContainer({
|
|
285
337
|
image,
|
|
286
|
-
containerName:
|
|
338
|
+
containerName: STEPZEN_CONTAINER_NAME,
|
|
339
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
|
287
340
|
environment: { STEPZEN_CONTROL_DB_DSN: dsn },
|
|
288
|
-
secretMasker: { STEPZEN_CONTROL_DB_DSN:
|
|
341
|
+
secretMasker: { STEPZEN_CONTROL_DB_DSN: maskPasswordInDsn },
|
|
289
342
|
ports: [
|
|
290
343
|
{ host: port, container: 9000 },
|
|
291
344
|
...[...legacyPorts].map(port => ({ host: port, container: port })),
|
|
@@ -307,7 +360,7 @@ class Service extends zen_command_1.default {
|
|
|
307
360
|
? ' --add-host host.docker.internal:host-gateway'
|
|
308
361
|
: '') +
|
|
309
362
|
Object.entries(env)
|
|
310
|
-
.map(([name, val]) =>
|
|
363
|
+
.map(([name, val]) => sh ` --env ${name}=${val}`)
|
|
311
364
|
.join('') +
|
|
312
365
|
args.ports
|
|
313
366
|
.map(port => ` --publish ${port.host}:${port.container}`)
|
|
@@ -315,103 +368,104 @@ class Service extends zen_command_1.default {
|
|
|
315
368
|
` ${args.image}`;
|
|
316
369
|
const command = fmtCommand(args.environment);
|
|
317
370
|
debug('stepzen:service')(chalk `running {cyan ${command}}`);
|
|
318
|
-
|
|
371
|
+
ux.action.start(chalk `Creating a {bold ${args.containerName}} Docker container`);
|
|
319
372
|
const result = shell.exec(command, { silent: true });
|
|
320
373
|
if (result.code !== 0) {
|
|
321
|
-
|
|
374
|
+
ux.action.stop(chalk.red('fail'));
|
|
322
375
|
const maskedEnv = Object.fromEntries(Object.entries(args.environment).map(([name, val]) => [
|
|
323
376
|
name,
|
|
324
377
|
(args.secretMasker[name] || (x => x))(val),
|
|
325
378
|
]));
|
|
326
379
|
const maskedCommand = fmtCommand(maskedEnv);
|
|
327
|
-
throw new
|
|
380
|
+
throw new CLIError(`failed to create a StepZen Docker container.` +
|
|
328
381
|
`\nCommand failed: ${maskedCommand}` +
|
|
329
382
|
`\n` +
|
|
330
|
-
`\n${chalk.dim(
|
|
383
|
+
`\n${chalk.dim(prependEachLine(result.stderr, '[docker] '))}` +
|
|
331
384
|
`\n` +
|
|
332
385
|
`\nPlease make sure Docker is running, your network connection is` +
|
|
333
386
|
` stable, and you are using the latest StepZen CLI version.` +
|
|
334
387
|
`\nIf the issue persists contact support via the StepZen discord ` +
|
|
335
|
-
`channel (${
|
|
388
|
+
`channel (${STEPZEN_DISCORD_URL}).`);
|
|
336
389
|
}
|
|
337
|
-
|
|
390
|
+
ux.action.stop('done');
|
|
338
391
|
}
|
|
339
392
|
async startDockerContainer(containerName) {
|
|
340
393
|
const command = `docker container start ${containerName}`;
|
|
341
394
|
debug('stepzen:service')(chalk `running {cyan ${command}}`);
|
|
342
395
|
const result = shell.exec(command, { silent: true });
|
|
343
396
|
if (result.code !== 0) {
|
|
344
|
-
throw new
|
|
397
|
+
throw new CLIError(`failed to start a StepZen Docker container.` +
|
|
345
398
|
`\nCommand failed: ${command}` +
|
|
346
399
|
`\n` +
|
|
347
|
-
`\n${chalk.dim(
|
|
400
|
+
`\n${chalk.dim(prependEachLine(result.stderr, '[docker] '))}`);
|
|
348
401
|
}
|
|
349
402
|
const status = await this.getDockerContainerStatus(containerName);
|
|
350
403
|
if (status !== 'running') {
|
|
351
404
|
await this.removeDockerContainers({ ignoreErrors: true });
|
|
352
|
-
throw new
|
|
405
|
+
throw new CLIError(`failed to start a Docker container.` +
|
|
353
406
|
`\nThe container status changed to '${status}' instead of 'running'.`);
|
|
354
407
|
}
|
|
355
408
|
return 'running';
|
|
356
409
|
}
|
|
357
410
|
async startDockerContainers(port) {
|
|
358
411
|
const dsn = await this.getDsnFromDockerContainer();
|
|
359
|
-
const toStart = [
|
|
360
|
-
if (dsn ===
|
|
412
|
+
const toStart = [STEPZEN_CONTAINER_NAME];
|
|
413
|
+
if (dsn === STEPZEN_AUTODB_DSN) {
|
|
361
414
|
// start stepzen-metadata container if it exists and auto-managed DB has been requested
|
|
362
|
-
const autodbStatus = await this.getDockerContainerStatus(
|
|
415
|
+
const autodbStatus = await this.getDockerContainerStatus(STEPZEN_AUTODB_CONTAINER_NAME);
|
|
363
416
|
if (autodbStatus === 'created' || autodbStatus === 'exited') {
|
|
364
|
-
toStart.push(
|
|
417
|
+
toStart.push(STEPZEN_AUTODB_CONTAINER_NAME);
|
|
365
418
|
}
|
|
366
419
|
}
|
|
367
|
-
|
|
420
|
+
ux.action.start(chalk `Starting the ${toStart
|
|
368
421
|
.map(name => chalk `{bold ${name}}`)
|
|
369
422
|
.join(', ')} Docker container${toStart.length > 1 ? 's' : ''}`);
|
|
370
423
|
try {
|
|
371
424
|
await Promise.all(toStart.map(name => this.startDockerContainer(name)));
|
|
372
425
|
}
|
|
373
426
|
catch (error) {
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
427
|
+
ux.action.stop('fail');
|
|
428
|
+
// eslint-disable-next-line unicorn/prefer-ternary
|
|
429
|
+
if (error instanceof CLIError) {
|
|
430
|
+
throw new CLIError(error.message +
|
|
377
431
|
`\n` +
|
|
378
432
|
`\nPlease make sure Docker is running, port ${port} is free on your` +
|
|
379
433
|
` localhost, and you are using the latest StepZen CLI version.` +
|
|
380
434
|
`\nIf the issue persists contact support via the StepZen discord ` +
|
|
381
|
-
`channel (${
|
|
435
|
+
`channel (${STEPZEN_DISCORD_URL}).`);
|
|
382
436
|
}
|
|
383
437
|
else {
|
|
384
438
|
throw error;
|
|
385
439
|
}
|
|
386
440
|
}
|
|
387
|
-
|
|
388
|
-
|
|
441
|
+
ux.action.stop('done');
|
|
442
|
+
ux.action.start('Waiting for a StepZen service instance to start');
|
|
389
443
|
const timeout = 30 * 1000; // 30 sec
|
|
390
444
|
const { isHealthy, credentials } = await this.waitForStepZenToStart({
|
|
391
445
|
port,
|
|
392
446
|
timeoutMs: timeout,
|
|
393
447
|
});
|
|
394
448
|
if (!isHealthy || !credentials) {
|
|
395
|
-
|
|
449
|
+
ux.action.stop(chalk.red('fail'));
|
|
396
450
|
const logs = await this.getDockerContainerLogs({ ignoreErrors: true });
|
|
397
451
|
await this.removeDockerContainers({ ignoreErrors: true });
|
|
398
|
-
throw new
|
|
452
|
+
throw new CLIError(`failed to start a StepZen service instance.` +
|
|
399
453
|
`\nThe StepZen service instance has not reported healthy within ${timeout / 1000} seconds.` +
|
|
400
454
|
(logs.stdout || logs.stderr
|
|
401
|
-
? chalk `\n\nLogs from the {bold ${
|
|
455
|
+
? chalk `\n\nLogs from the {bold ${STEPZEN_CONTAINER_NAME}} Docker container:`
|
|
402
456
|
: '') +
|
|
403
457
|
(logs.stdout
|
|
404
|
-
? `\n${chalk.dim(
|
|
458
|
+
? `\n${chalk.dim(prependEachLine(logs.stdout, '[stdout] '))}\n`
|
|
405
459
|
: '') +
|
|
406
460
|
(logs.stderr
|
|
407
|
-
? `\n${chalk.dim(
|
|
461
|
+
? `\n${chalk.dim(prependEachLine(logs.stderr, '[stderr] '))}\n`
|
|
408
462
|
: '') +
|
|
409
463
|
`\nPlease make sure the StepZen metadata database details are correct,` +
|
|
410
464
|
` and you are using the latest StepZen CLI version.` +
|
|
411
465
|
`\nIf the issue persists contact support via the StepZen discord ` +
|
|
412
|
-
`channel (${
|
|
466
|
+
`channel (${STEPZEN_DISCORD_URL}).`);
|
|
413
467
|
}
|
|
414
|
-
|
|
468
|
+
ux.action.stop('done');
|
|
415
469
|
return {
|
|
416
470
|
status: 'running',
|
|
417
471
|
credentials,
|
|
@@ -419,21 +473,21 @@ class Service extends zen_command_1.default {
|
|
|
419
473
|
}
|
|
420
474
|
async stopDockerContainers() {
|
|
421
475
|
try {
|
|
422
|
-
const toStop = [
|
|
423
|
-
const dbStatus = await this.getDockerContainerStatus(
|
|
476
|
+
const toStop = [STEPZEN_CONTAINER_NAME];
|
|
477
|
+
const dbStatus = await this.getDockerContainerStatus(STEPZEN_AUTODB_CONTAINER_NAME);
|
|
424
478
|
if (dbStatus === 'running') {
|
|
425
|
-
toStop.push(
|
|
479
|
+
toStop.push(STEPZEN_AUTODB_CONTAINER_NAME);
|
|
426
480
|
}
|
|
427
|
-
|
|
481
|
+
ux.action.start(chalk `Stopping the ${toStop
|
|
428
482
|
.map(name => chalk `{bold ${name}}`)
|
|
429
483
|
.join(', ')} Docker container${toStop.length > 1 ? 's' : ''}`);
|
|
430
484
|
await Promise.all(toStop.map(name => this.stopDockerContainer(name)));
|
|
431
485
|
}
|
|
432
486
|
catch (error) {
|
|
433
|
-
|
|
487
|
+
ux.action.stop('fail');
|
|
434
488
|
throw error;
|
|
435
489
|
}
|
|
436
|
-
|
|
490
|
+
ux.action.stop('done');
|
|
437
491
|
return 'exited';
|
|
438
492
|
}
|
|
439
493
|
async stopDockerContainer(containerName) {
|
|
@@ -441,21 +495,21 @@ class Service extends zen_command_1.default {
|
|
|
441
495
|
debug('stepzen:service')(chalk `running {cyan ${command}}`);
|
|
442
496
|
const result = shell.exec(command, { silent: true });
|
|
443
497
|
if (result.code !== 0) {
|
|
444
|
-
throw new
|
|
498
|
+
throw new CLIError(`failed to stop a StepZen Docker container.` +
|
|
445
499
|
`\nCommand failed: ${command}` +
|
|
446
500
|
`\n` +
|
|
447
|
-
`\n${chalk.dim(
|
|
501
|
+
`\n${chalk.dim(prependEachLine(result.stderr, '[docker] '))}` +
|
|
448
502
|
`\n` +
|
|
449
503
|
`\nPlease make sure Docker is running, and you are using the` +
|
|
450
504
|
` latest StepZen CLI version.` +
|
|
451
505
|
`\nIf the issue persists contact support via the StepZen discord ` +
|
|
452
|
-
`channel (${
|
|
506
|
+
`channel (${STEPZEN_DISCORD_URL}).`);
|
|
453
507
|
}
|
|
454
508
|
return 'exited';
|
|
455
509
|
}
|
|
456
510
|
async removeDockerContainers({ ignoreErrors, } = {}) {
|
|
457
511
|
const status = await this.removeDockerContainer({
|
|
458
|
-
containerName:
|
|
512
|
+
containerName: STEPZEN_CONTAINER_NAME,
|
|
459
513
|
ignoreErrors,
|
|
460
514
|
});
|
|
461
515
|
return status;
|
|
@@ -463,28 +517,28 @@ class Service extends zen_command_1.default {
|
|
|
463
517
|
async removeDockerContainer({ containerName, ignoreErrors, }) {
|
|
464
518
|
const command = `docker container rm --force ${containerName}`;
|
|
465
519
|
debug('stepzen:service')(chalk `running {cyan ${command}}`);
|
|
466
|
-
|
|
520
|
+
ux.action.start(chalk `Removing the {bold ${containerName}} Docker container`);
|
|
467
521
|
const result = shell.exec(command, { silent: true });
|
|
468
522
|
if (result.code !== 0) {
|
|
469
|
-
|
|
523
|
+
ux.action.stop(chalk.red('fail'));
|
|
470
524
|
if (ignoreErrors) {
|
|
471
525
|
return this.getDockerContainerStatus(containerName);
|
|
472
526
|
}
|
|
473
|
-
throw new
|
|
527
|
+
throw new CLIError(`failed to remove a StepZen Docker container.` +
|
|
474
528
|
`\nCommand failed: ${command}` +
|
|
475
529
|
`\n` +
|
|
476
|
-
`\n${chalk.dim(
|
|
530
|
+
`\n${chalk.dim(prependEachLine(result.stderr, '[docker] '))}` +
|
|
477
531
|
`\n` +
|
|
478
532
|
`\nPlease make sure Docker is running, and you are using the` +
|
|
479
533
|
` latest StepZen CLI version.` +
|
|
480
534
|
`\nIf the issue persists contact support via the StepZen discord ` +
|
|
481
|
-
`channel (${
|
|
535
|
+
`channel (${STEPZEN_DISCORD_URL}).`);
|
|
482
536
|
}
|
|
483
|
-
|
|
537
|
+
ux.action.stop('done');
|
|
484
538
|
return 'not-created';
|
|
485
539
|
}
|
|
486
540
|
async getDockerContainerLogs({ ignoreErrors, } = {}) {
|
|
487
|
-
const command = `docker logs --tail 24 ${
|
|
541
|
+
const command = `docker logs --tail 24 ${STEPZEN_CONTAINER_NAME}`;
|
|
488
542
|
debug('stepzen:service')(chalk `running {cyan ${command}}`);
|
|
489
543
|
const result = shell.exec(command, { silent: true });
|
|
490
544
|
if (result.code !== 0) {
|
|
@@ -492,18 +546,18 @@ class Service extends zen_command_1.default {
|
|
|
492
546
|
debug('stepzen:service')(`failed to retrieve logs from the StepZen Docker container.` +
|
|
493
547
|
`\nCommand failed: ${command}` +
|
|
494
548
|
`\n` +
|
|
495
|
-
`\n${chalk.dim(
|
|
549
|
+
`\n${chalk.dim(prependEachLine(result.stderr, '[docker] '))}`);
|
|
496
550
|
return {};
|
|
497
551
|
}
|
|
498
|
-
throw new
|
|
552
|
+
throw new CLIError(`failed to retrieve logs from the StepZen Docker container.` +
|
|
499
553
|
`\nCommand failed: ${command}` +
|
|
500
554
|
`\n` +
|
|
501
|
-
`\n${chalk.dim(
|
|
555
|
+
`\n${chalk.dim(prependEachLine(result.stderr, '[docker] '))}` +
|
|
502
556
|
`\n` +
|
|
503
557
|
`\nPlease make sure Docker is running, and you are using the` +
|
|
504
558
|
` latest StepZen CLI version.` +
|
|
505
559
|
`\nIf the issue persists contact support via the StepZen discord ` +
|
|
506
|
-
`channel (${
|
|
560
|
+
`channel (${STEPZEN_DISCORD_URL}).`);
|
|
507
561
|
}
|
|
508
562
|
return {
|
|
509
563
|
stdout: result.stdout,
|
|
@@ -511,19 +565,19 @@ class Service extends zen_command_1.default {
|
|
|
511
565
|
};
|
|
512
566
|
}
|
|
513
567
|
async execInDockerContainer(command) {
|
|
514
|
-
const dockerCommand = `docker container exec ${
|
|
568
|
+
const dockerCommand = `docker container exec ${STEPZEN_CONTAINER_NAME} ${command}`;
|
|
515
569
|
debug('stepzen:service')(chalk `running {cyan ${dockerCommand}}`);
|
|
516
570
|
return shell.exec(dockerCommand, { silent: true });
|
|
517
571
|
}
|
|
518
572
|
async getDsnFromDockerContainer() {
|
|
519
|
-
const command = `docker container inspect -f "{{json .Config.Env}}" ${
|
|
573
|
+
const command = `docker container inspect -f "{{json .Config.Env}}" ${STEPZEN_CONTAINER_NAME}`;
|
|
520
574
|
debug('stepzen:service')(chalk `running {cyan ${command}}`);
|
|
521
575
|
const result = shell.exec(command, { silent: true });
|
|
522
576
|
if (result.code !== 0) {
|
|
523
577
|
const message = `Failed to get the metadata database details from the existing local StepZen image.` +
|
|
524
578
|
`\nCommand failed: ${command}` +
|
|
525
579
|
`\n` +
|
|
526
|
-
`\n${chalk.dim(
|
|
580
|
+
`\n${chalk.dim(prependEachLine(result.stderr, '[docker] '))}`;
|
|
527
581
|
debug('stepzen:service')(message + `\nreturning 'undefined', meaning 'no DSN found'`);
|
|
528
582
|
return;
|
|
529
583
|
}
|
|
@@ -546,23 +600,23 @@ class Service extends zen_command_1.default {
|
|
|
546
600
|
switch (status) {
|
|
547
601
|
case 'running': {
|
|
548
602
|
this.log(chalk `Local StepZen service instance is running in the ` +
|
|
549
|
-
chalk `{bold ${
|
|
603
|
+
chalk `{bold ${STEPZEN_CONTAINER_NAME}} Docker container`);
|
|
550
604
|
let isLoggedIn;
|
|
551
605
|
{
|
|
552
606
|
const [current, local] = await Promise.all([
|
|
553
|
-
|
|
554
|
-
|
|
607
|
+
readConfiguration(),
|
|
608
|
+
readConfiguration(STEPZEN_LOCAL_CONFIG_FILE),
|
|
555
609
|
]);
|
|
556
610
|
// disregard the uuid property for comparison
|
|
557
611
|
delete current.uuid;
|
|
558
612
|
delete local.uuid;
|
|
559
|
-
isLoggedIn =
|
|
613
|
+
isLoggedIn = isEqual(current, local);
|
|
560
614
|
if (current.serviceInstance &&
|
|
561
615
|
'zenctl2' in current.serviceInstance &&
|
|
562
616
|
local.serviceInstance &&
|
|
563
617
|
'zenctl2' in local.serviceInstance) {
|
|
564
|
-
const currentPort =
|
|
565
|
-
const localPort =
|
|
618
|
+
const currentPort = getPortFromUrl(current.serviceInstance.zenctl2);
|
|
619
|
+
const localPort = getPortFromUrl(local.serviceInstance.zenctl2);
|
|
566
620
|
if (currentPort !== localPort) {
|
|
567
621
|
this.warn(`You are currently logged in to a service instance on port` +
|
|
568
622
|
` ${currentPort}. Please log in again to use port ${localPort}.`);
|
|
@@ -573,7 +627,7 @@ class Service extends zen_command_1.default {
|
|
|
573
627
|
this.log(chalk ` - to use: {bold stepzen [command]}`);
|
|
574
628
|
}
|
|
575
629
|
else {
|
|
576
|
-
let localConfigPath = path.join(
|
|
630
|
+
let localConfigPath = path.join(STEPZEN_CONFIG_DIRECTORY, STEPZEN_LOCAL_CONFIG_FILE);
|
|
577
631
|
if (process.platform !== 'win32') {
|
|
578
632
|
// On UNIX-like platforms a `~/.stepzen` format universally works in any shell
|
|
579
633
|
// On Windows it depends on the shell and looks much uglier.
|
|
@@ -582,7 +636,7 @@ class Service extends zen_command_1.default {
|
|
|
582
636
|
//
|
|
583
637
|
// The output would look better with an absolute path, like
|
|
584
638
|
// C:\Users\sam\.stepzen\stepzen-config.local.yaml
|
|
585
|
-
localConfigPath =
|
|
639
|
+
localConfigPath = homeRelative(localConfigPath);
|
|
586
640
|
}
|
|
587
641
|
this.log(chalk ` - to use: login with {bold {green stepzen login --config ${localConfigPath}}}` +
|
|
588
642
|
chalk ` then use normally with {bold stepzen [command]}`);
|
|
@@ -599,7 +653,7 @@ class Service extends zen_command_1.default {
|
|
|
599
653
|
const url = `http://127.0.0.1:${port}/healthz`;
|
|
600
654
|
try {
|
|
601
655
|
debug('stepzen:service')(chalk `fetching {cyan ${url}}`);
|
|
602
|
-
const response = await (
|
|
656
|
+
const response = await fetch(url);
|
|
603
657
|
debug('stepzen:service')(chalk `\t--> ${response.status}`);
|
|
604
658
|
return response.status === 200;
|
|
605
659
|
}
|
|
@@ -609,20 +663,22 @@ class Service extends zen_command_1.default {
|
|
|
609
663
|
}
|
|
610
664
|
}
|
|
611
665
|
async waitForStepZenToStart({ port, timeoutMs, }) {
|
|
612
|
-
const waitingSince =
|
|
666
|
+
const waitingSince = Date.now();
|
|
613
667
|
let [status, isHealthy, credentials] = await Promise.all([
|
|
614
|
-
this.getDockerContainerStatus(
|
|
668
|
+
this.getDockerContainerStatus(STEPZEN_CONTAINER_NAME),
|
|
615
669
|
this.getStepZenHealthCheck(port),
|
|
616
670
|
this.getStepZenDefaultCredentials(),
|
|
617
671
|
]);
|
|
618
672
|
while (status === 'running' &&
|
|
619
673
|
!(isHealthy && credentials) &&
|
|
620
|
-
|
|
674
|
+
Date.now() - waitingSince < timeoutMs) {
|
|
621
675
|
// eslint-disable-next-line no-await-in-loop
|
|
622
|
-
await new Promise(resolve =>
|
|
676
|
+
await new Promise(resolve => {
|
|
677
|
+
setTimeout(resolve, 300);
|
|
678
|
+
});
|
|
623
679
|
// eslint-disable-next-line no-await-in-loop
|
|
624
680
|
const checks = await Promise.all([
|
|
625
|
-
this.getDockerContainerStatus(
|
|
681
|
+
this.getDockerContainerStatus(STEPZEN_CONTAINER_NAME),
|
|
626
682
|
this.getStepZenHealthCheck(port),
|
|
627
683
|
this.getStepZenDefaultCredentials(),
|
|
628
684
|
]);
|
|
@@ -652,7 +708,7 @@ class Service extends zen_command_1.default {
|
|
|
652
708
|
: undefined;
|
|
653
709
|
}
|
|
654
710
|
async createLocalCredentialsFile({ port, credentials, }) {
|
|
655
|
-
await
|
|
711
|
+
await writeConfiguration({
|
|
656
712
|
account: credentials.account,
|
|
657
713
|
adminkey: credentials.adminKey,
|
|
658
714
|
apikey: credentials.apiKeys[0],
|
|
@@ -662,87 +718,31 @@ class Service extends zen_command_1.default {
|
|
|
662
718
|
introspection: `http://127.0.0.1:${port}/introspection/api/graphql`,
|
|
663
719
|
dbintrospection: `http://127.0.0.1:${port}/dbintrospection/graphql`,
|
|
664
720
|
},
|
|
665
|
-
},
|
|
721
|
+
}, STEPZEN_LOCAL_CONFIG_FILE);
|
|
666
722
|
}
|
|
667
723
|
validateProvidedDsn(flags) {
|
|
668
724
|
if (!flags.dsn) {
|
|
669
725
|
return undefined;
|
|
670
726
|
}
|
|
671
|
-
const parsed =
|
|
727
|
+
const parsed = parseDsn(flags.dsn);
|
|
672
728
|
if (parsed.success) {
|
|
673
729
|
const value = parsed.value;
|
|
674
730
|
if (value.db === undefined) {
|
|
675
|
-
throw new
|
|
731
|
+
throw new CLIError('DSN missing database');
|
|
676
732
|
}
|
|
677
733
|
if (value.user === undefined) {
|
|
678
|
-
throw new
|
|
734
|
+
throw new CLIError('DSN missing user');
|
|
679
735
|
}
|
|
680
736
|
if (value.pass === undefined) {
|
|
681
|
-
throw new
|
|
737
|
+
throw new CLIError('DSN missing password');
|
|
682
738
|
}
|
|
683
|
-
if (
|
|
739
|
+
if (isLocalhost(value.host)) {
|
|
684
740
|
value.host = 'host.docker.internal';
|
|
685
741
|
return `${value.driver}://${value.user}:${value.pass}@${value.host}${(value.port && ':' + value.port) || ''}/${value.db}`;
|
|
686
742
|
}
|
|
687
743
|
return flags.dsn;
|
|
688
744
|
}
|
|
689
|
-
throw new
|
|
745
|
+
throw new CLIError(parsed.message);
|
|
690
746
|
}
|
|
691
747
|
}
|
|
692
|
-
exports.default = Service;
|
|
693
|
-
Service.description = 'Manage the local StepZen service instance (requires Docker).' +
|
|
694
|
-
'\n' +
|
|
695
|
-
'You can use a local StepZen service instance instead of the StepZen' +
|
|
696
|
-
' cloud to develop and test GraphQL endpoints locally. This way you can' +
|
|
697
|
-
' try StepZen out without giving the StepZen cloud access to your data' +
|
|
698
|
-
' sources and API endpoints.' +
|
|
699
|
-
'\n\n' +
|
|
700
|
-
'Local StepZen service instances are not suitable for production use.' +
|
|
701
|
-
' Please refer to the documentation at' +
|
|
702
|
-
' https://stepzen.com/docs/deployment/local-docker for more information' +
|
|
703
|
-
' and examples.';
|
|
704
|
-
Service.flags = {
|
|
705
|
-
...zen_command_1.default.flags,
|
|
706
|
-
dsn: core_1.Flags.string({
|
|
707
|
-
description: chalk `DSN of a StepZen metadata database, e.g.` +
|
|
708
|
-
chalk ` {bold postgres://user:password@172.17.0.1:5432/zenctl}`,
|
|
709
|
-
exclusive: ['reset-auto', 'prompt'],
|
|
710
|
-
}),
|
|
711
|
-
'reset-auto': core_1.Flags.boolean({
|
|
712
|
-
description: 'Create fresh empty metadata database. Note: all existing endpoints will be deleted and you will be logged out.',
|
|
713
|
-
exclusive: ['dsn', 'prompt'],
|
|
714
|
-
}),
|
|
715
|
-
prompt: core_1.Flags.boolean({
|
|
716
|
-
description: 'Opens an interactive prompt to ask for database details (or fails, if running' +
|
|
717
|
-
' in a non-interactive mode).',
|
|
718
|
-
exclusive: ['dsn', 'non-interactive'],
|
|
719
|
-
}),
|
|
720
|
-
port: core_1.Flags.integer({
|
|
721
|
-
description: `Override the TCP port on which the local StepZen service instance will listen` +
|
|
722
|
-
` for connections (port ${DEFAULT_ZEN_PORT} by default).`,
|
|
723
|
-
}),
|
|
724
|
-
};
|
|
725
|
-
Service.args = {
|
|
726
|
-
action: core_1.Args.string({
|
|
727
|
-
options: ['start', 'stop', 'upgrade'],
|
|
728
|
-
required: false,
|
|
729
|
-
description: chalk `
|
|
730
|
-
\t{bold start}\tstart a local StepZen service instance in a Docker container
|
|
731
|
-
\t\tThis command pulls the latest StepZen service instance Docker image so that the
|
|
732
|
-
\t\tversion of StepZen you run locally is the same as the one running on
|
|
733
|
-
\t\tstepzen.net.
|
|
734
|
-
|
|
735
|
-
\t{bold stop}\tstop the local StepZen service instance (if running)
|
|
736
|
-
\t\tThis command deletes the Docker container with the StepZen service instance,
|
|
737
|
-
\t\tbut all your endpoints are persistently stored in the metadata database. They
|
|
738
|
-
\t\tremain available when running {bold stepzen service start} next time.
|
|
739
|
-
|
|
740
|
-
\t{bold upgrade}\tupgrade the local StepZen service instance if it is running
|
|
741
|
-
\t\tThis command checks if a newer version of the StepZen service instance Docker
|
|
742
|
-
\t\timage is available, and if it is then stops and deletes the currently running
|
|
743
|
-
\t\tcontainer, pulls the latest image, and starts it. All endpoints are preserved.
|
|
744
|
-
|
|
745
|
-
If no action is provided, print the service status and exit.`,
|
|
746
|
-
}),
|
|
747
|
-
};
|
|
748
748
|
//# sourceMappingURL=service.js.map
|