@agilecustoms/envctl 0.17.0 → 0.18.1
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/dist/client/AwsCredsHelper.js +5 -3
- package/dist/client/TerraformAdapter.js +1 -0
- package/dist/commands/apiCreateEphemeral.js +20 -0
- package/dist/commands/delete.js +3 -2
- package/dist/commands/index.js +1 -0
- package/dist/index.js +2 -1
- package/dist/service/EnvCtl.js +43 -24
- package/package.json +7 -3
|
@@ -4,11 +4,13 @@ import { KnownException } from '../exceptions.js';
|
|
|
4
4
|
export class AwsCredsHelper {
|
|
5
5
|
creds;
|
|
6
6
|
async ensureCredentials() {
|
|
7
|
-
await this.getCredentials();
|
|
7
|
+
await this.getCredentials(true);
|
|
8
8
|
}
|
|
9
|
-
async getCredentials() {
|
|
9
|
+
async getCredentials(printLogs = false) {
|
|
10
10
|
if (!this.creds) {
|
|
11
|
-
|
|
11
|
+
if (printLogs) {
|
|
12
|
+
console.log('Fetching AWS credentials...');
|
|
13
|
+
}
|
|
12
14
|
this.creds = await this._getCredentials();
|
|
13
15
|
}
|
|
14
16
|
return this.creds;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { envCtl } from '../container.js';
|
|
3
|
+
import { _keys } from './_keys.js';
|
|
4
|
+
import { wrap } from './_utils.js';
|
|
5
|
+
export function apiCreateEphemeral(program) {
|
|
6
|
+
program
|
|
7
|
+
.command('api-create-ephemeral')
|
|
8
|
+
.description('Create bare env w/o resources. Used in CI as first step just to pre-generate token for extension')
|
|
9
|
+
.option('--project <project>', _keys.PROJECT)
|
|
10
|
+
.requiredOption('--env <env>', _keys.ENV)
|
|
11
|
+
.requiredOption('--owner <owner>', _keys.OWNER)
|
|
12
|
+
.requiredOption('--size <size>', _keys.SIZE)
|
|
13
|
+
.requiredOption('--type <type>', _keys.TYPE)
|
|
14
|
+
.action(wrap(handler));
|
|
15
|
+
}
|
|
16
|
+
async function handler(options) {
|
|
17
|
+
const { project, env, owner, size, type } = options;
|
|
18
|
+
const token = await envCtl.createEphemeral(project, env, owner, size, type);
|
|
19
|
+
console.log(token);
|
|
20
|
+
}
|
package/dist/commands/delete.js
CHANGED
|
@@ -8,11 +8,12 @@ export function deleteIt(program) {
|
|
|
8
8
|
.description('Delete a development environment')
|
|
9
9
|
.option('--project <project>', _keys.PROJECT)
|
|
10
10
|
.option('--env <env>', _keys.ENV)
|
|
11
|
+
.option('--force', 'Force deletion without confirmation')
|
|
11
12
|
.option('--cwd <cwd>', _keys.CWD)
|
|
12
13
|
.action(wrap(handler));
|
|
13
14
|
}
|
|
14
15
|
async function handler(options) {
|
|
15
|
-
const { project, env, cwd } = options;
|
|
16
|
+
const { project, env, force, cwd } = options;
|
|
16
17
|
const envName = cliHelper.ensureEnv(env);
|
|
17
|
-
await envCtl.delete(project, envName, cwd);
|
|
18
|
+
await envCtl.delete(project, envName, Boolean(force), cwd);
|
|
18
19
|
}
|
package/dist/commands/index.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { createRequire } from 'module';
|
|
3
3
|
import { Command } from 'commander';
|
|
4
4
|
import updateNotifier from 'update-notifier';
|
|
5
|
-
import { configure, deleteIt, deploy, destroy, init, plan, status } from './commands/index.js';
|
|
5
|
+
import { apiCreateEphemeral, configure, deleteIt, deploy, destroy, init, plan, status } from './commands/index.js';
|
|
6
6
|
const require = createRequire(import.meta.url);
|
|
7
7
|
const pkg = require('../package.json');
|
|
8
8
|
updateNotifier({ pkg, updateCheckInterval: 0 }).notify();
|
|
@@ -11,6 +11,7 @@ program
|
|
|
11
11
|
.name('envctl')
|
|
12
12
|
.description('CLI to manage environments')
|
|
13
13
|
.version(pkg.version);
|
|
14
|
+
apiCreateEphemeral(program);
|
|
14
15
|
configure(program);
|
|
15
16
|
deleteIt(program);
|
|
16
17
|
deploy(program);
|
package/dist/service/EnvCtl.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { KnownException, TerraformInitRequired } from '../exceptions.js';
|
|
2
|
+
import { EnvType, EnvSize } from '../model/index.js';
|
|
2
3
|
import { EnvSizeAvgTime, EnvStatus } from '../model/index.js';
|
|
3
4
|
export class EnvCtl {
|
|
4
5
|
cliHelper;
|
|
@@ -20,7 +21,7 @@ export class EnvCtl {
|
|
|
20
21
|
console.log(`Env ${key} does not exist`);
|
|
21
22
|
return;
|
|
22
23
|
}
|
|
23
|
-
const kind = this.cliHelper.ensureKind(undefined, cwd);
|
|
24
|
+
const kind = env.kind ? this.cliHelper.ensureKind(undefined, cwd) : undefined;
|
|
24
25
|
console.log(`Env ${key} status: ${env.status}`);
|
|
25
26
|
if (env.kind && env.kind !== kind) {
|
|
26
27
|
console.warn(`Env ${key} kind (dir-name): ${env.kind} - looks like this env was deployed from a different directory`);
|
|
@@ -46,9 +47,6 @@ export class EnvCtl {
|
|
|
46
47
|
return { envName, owner, key, env };
|
|
47
48
|
}
|
|
48
49
|
checkInput(envName, env, input, cwd) {
|
|
49
|
-
if (env.ephemeral) {
|
|
50
|
-
throw new KnownException(`Attempted to convert ephemeral env to non-ephemeral`);
|
|
51
|
-
}
|
|
52
50
|
if (input.owner && env.owner !== input.owner) {
|
|
53
51
|
throw new KnownException(`Can not change env owner ${env.owner} -> ${input.owner}`);
|
|
54
52
|
}
|
|
@@ -86,17 +84,29 @@ export class EnvCtl {
|
|
|
86
84
|
const { envName, owner, key, env } = await this.tryGetEnv(input);
|
|
87
85
|
if (env == null) {
|
|
88
86
|
const envInput = await this.cliHelper.parseEnvInput(input, envName, owner, cwd);
|
|
87
|
+
const envTerraform = { ...envInput, ephemeral: false };
|
|
89
88
|
await this.terraformAdapter.init(key, cwd);
|
|
90
|
-
await this.terraformAdapter.plan(
|
|
89
|
+
await this.terraformAdapter.plan(envTerraform, tfArgs, cwd);
|
|
91
90
|
return;
|
|
92
91
|
}
|
|
93
92
|
const envInput = this.checkInput(envName, env, input, cwd);
|
|
93
|
+
const envTerraform = { ...envInput, ephemeral: false };
|
|
94
94
|
this.checkStatus(env);
|
|
95
95
|
await this.promptUnlock(env);
|
|
96
96
|
if (env.status !== EnvStatus.Active) {
|
|
97
97
|
throw new KnownException(`Env ${env.key} status is ${env.status}, can not run plan`);
|
|
98
98
|
}
|
|
99
|
-
await this.terraformAdapter.plan(
|
|
99
|
+
await this.terraformAdapter.plan(envTerraform, tfArgs, cwd);
|
|
100
|
+
}
|
|
101
|
+
async createEphemeral(project, envName, owner, size, type) {
|
|
102
|
+
const key = this.key(project, envName);
|
|
103
|
+
const env = await this.envApi.get(key);
|
|
104
|
+
if (env !== null) {
|
|
105
|
+
throw new KnownException(`Env ${key} already exists`);
|
|
106
|
+
}
|
|
107
|
+
const createEnv = { key, ephemeral: true, owner, size, type };
|
|
108
|
+
const newEnv = await this.envApi.create(createEnv);
|
|
109
|
+
return newEnv.token;
|
|
100
110
|
}
|
|
101
111
|
async deploy(input, tfArgs, cwd) {
|
|
102
112
|
const { envName, owner, key, env } = await this.tryGetEnv(input);
|
|
@@ -110,7 +120,7 @@ export class EnvCtl {
|
|
|
110
120
|
}
|
|
111
121
|
this.checkInput(envName, env, input, cwd);
|
|
112
122
|
this.checkStatus(env);
|
|
113
|
-
if (env.status == EnvStatus.
|
|
123
|
+
if (env.status == EnvStatus.Updating) {
|
|
114
124
|
const answerYes = await this.cliHelper.promptYesNo(`Env status is ${env.status}, likely to be an error from a previous run\n`
|
|
115
125
|
+ 'Do you want to proceed with deployment?');
|
|
116
126
|
if (!answerYes) {
|
|
@@ -126,8 +136,8 @@ export class EnvCtl {
|
|
|
126
136
|
}
|
|
127
137
|
async runDeploy(env, envName, tfArgs, cwd) {
|
|
128
138
|
console.log('Deploying resources');
|
|
129
|
-
const { owner, size, type } = env;
|
|
130
|
-
const envTf = { owner, size, type, env: envName };
|
|
139
|
+
const { owner, size, type, ephemeral } = env;
|
|
140
|
+
const envTf = { owner, size, type, ephemeral, env: envName };
|
|
131
141
|
try {
|
|
132
142
|
await this.terraformAdapter.apply(envTf, tfArgs, cwd);
|
|
133
143
|
}
|
|
@@ -143,24 +153,33 @@ export class EnvCtl {
|
|
|
143
153
|
console.log('Activating env (to finish creation)');
|
|
144
154
|
await this.envApi.activate(env);
|
|
145
155
|
}
|
|
146
|
-
async delete(project, envName, cwd) {
|
|
156
|
+
async delete(project, envName, force, cwd) {
|
|
147
157
|
const env = await this.get(project, envName);
|
|
148
158
|
this.checkStatus(env);
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
if (!answerYes) {
|
|
155
|
-
console.log('Aborting deletion');
|
|
156
|
-
return;
|
|
159
|
+
if (force) {
|
|
160
|
+
console.log('Force deletion');
|
|
161
|
+
if (env.status !== EnvStatus.Active) {
|
|
162
|
+
console.log(`Env status is ${env.status}, will unlock it`);
|
|
163
|
+
await this.envApi.activate(env);
|
|
157
164
|
}
|
|
158
165
|
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
166
|
+
else {
|
|
167
|
+
const kind = this.cliHelper.ensureKind(undefined, cwd);
|
|
168
|
+
if (env.kind && env.kind !== kind) {
|
|
169
|
+
const answerYes = await this.cliHelper.promptYesNo(`Env ${env.key} kind (dir-name): ${env.kind}\n`
|
|
170
|
+
+ 'You\'re deleting env deployed from different dir\n'
|
|
171
|
+
+ 'Do you want to proceed?');
|
|
172
|
+
if (!answerYes) {
|
|
173
|
+
console.log('Aborting deletion');
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
await this.promptUnlock(env);
|
|
178
|
+
if (env.status !== EnvStatus.Active) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
console.log('Deleting env');
|
|
162
182
|
}
|
|
163
|
-
console.log('Deleting env');
|
|
164
183
|
const statusMessage = await this.envApi.delete(env);
|
|
165
184
|
console.log(statusMessage);
|
|
166
185
|
}
|
|
@@ -176,8 +195,8 @@ export class EnvCtl {
|
|
|
176
195
|
console.log('Lock env to run destroy');
|
|
177
196
|
await this.envApi.lockForUpdate(env);
|
|
178
197
|
}
|
|
179
|
-
const { owner, size, type } = env;
|
|
180
|
-
const tfEnv = { owner, size, type, env: envName };
|
|
198
|
+
const { owner, size, type, ephemeral } = env;
|
|
199
|
+
const tfEnv = { owner, size, type, ephemeral, env: envName };
|
|
181
200
|
console.log('Destroying env resources');
|
|
182
201
|
await this.terraformAdapter.destroy(tfEnv, tfArgs, cwd);
|
|
183
202
|
console.log('Unlock env');
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agilecustoms/envctl",
|
|
3
3
|
"description": "node.js CLI client for manage environments",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.18.1",
|
|
5
5
|
"author": "Alex Chekulaev",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
@@ -28,11 +28,15 @@
|
|
|
28
28
|
"run-version": "tsc --sourceMap true && npm run run -- --version",
|
|
29
29
|
"run-configure": "tsc --sourceMap true && npm run run -- configure",
|
|
30
30
|
"run-status": "tsc --sourceMap true && npm run run -- status --cwd ../tt-core",
|
|
31
|
-
"run-init": "tsc --sourceMap true && npm run run -- init --cwd ../tt-core",
|
|
32
31
|
"run-plan": "tsc --sourceMap true && npm run run -- plan --size min --cwd ../tt-core",
|
|
33
32
|
"run-deploy": "tsc --sourceMap true && npm run run -- deploy --size min --cwd ../tt-core",
|
|
34
33
|
"run-delete": "tsc --sourceMap true && npm run run -- delete --cwd ../tt-core",
|
|
35
|
-
"run-destroy": "tsc --sourceMap true && npm run run -- destroy --cwd ../tt-core"
|
|
34
|
+
"run-destroy": "tsc --sourceMap true && npm run run -- destroy --cwd ../tt-core",
|
|
35
|
+
"run-api-status": "tsc --sourceMap true && npm run run -- status --env laxa1986",
|
|
36
|
+
"run-api-create-ephemeral": "tsc --sourceMap true && npm run run -- api-create-ephemeral --env laxa1986 --owner laxa1986 --size min --type dev",
|
|
37
|
+
"run-api-init": "tsc --sourceMap true && npm run run -- init --env laxa1986 --cwd ../tt-gitops",
|
|
38
|
+
"run-api-deploy": "tsc --sourceMap true && npm run run -- deploy --env laxa1986 -var-file=versions.tfvars --cwd ../tt-gitops",
|
|
39
|
+
"run-api-delete": "tsc --sourceMap true && npm run run -- delete --force --cwd ../tt-gitops"
|
|
36
40
|
},
|
|
37
41
|
"dependencies": {
|
|
38
42
|
"@aws-sdk/client-sts": "^3.716.0",
|