@agilecustoms/envctl 0.18.2 → 0.19.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.
@@ -18,16 +18,17 @@ export class ProcessRunner {
18
18
  const child = spawn(command, args, spawnOptions);
19
19
  child.stdout.setEncoding('utf8');
20
20
  child.stderr.setEncoding('utf8');
21
+ function processLine(line) {
22
+ if (scanner)
23
+ scanner(line);
24
+ console.log(line);
25
+ }
21
26
  let buffer = '';
22
27
  child.stdout.on('data', (data) => {
23
28
  buffer += data;
24
29
  const lines = buffer.split('\n');
25
30
  buffer = lines.pop() || '';
26
- for (const line of lines) {
27
- if (scanner)
28
- scanner(line);
29
- console.log(line);
30
- }
31
+ lines.forEach(processLine);
31
32
  if (buffer.includes('Enter a value:')) {
32
33
  console.log(buffer);
33
34
  buffer = '';
@@ -39,9 +40,7 @@ export class ProcessRunner {
39
40
  });
40
41
  return new Promise((resolve, reject) => {
41
42
  child.on('close', (code) => {
42
- if (scanner)
43
- scanner(buffer);
44
- console.log(buffer);
43
+ processLine(buffer);
45
44
  if (code === 0) {
46
45
  if (errorBuffer) {
47
46
  console.warn('Process completed successfully, but there were errors:\n' + errorBuffer);
@@ -103,7 +103,7 @@ export class TerraformAdapter {
103
103
  throw new KnownException(`terraform apply failed with code ${error.code}:\n${error.message}`, { cause: error });
104
104
  }
105
105
  }
106
- async destroy(env, tfArgs, cwd, attemptNo = 1) {
106
+ async destroy(env, tfArgs, force, cwd, attemptNo = 1) {
107
107
  let wrongDir = false;
108
108
  const scanner = (line) => {
109
109
  if (line.includes('Either you have not created any objects yet or the existing objects were')) {
@@ -121,9 +121,14 @@ export class TerraformAdapter {
121
121
  }
122
122
  const lockId = this.lockId(error, attemptNo);
123
123
  if (lockId) {
124
- await this.promptUnlock(lockId, cwd);
124
+ if (force) {
125
+ await this.forceUnlock(lockId, cwd);
126
+ }
127
+ else {
128
+ await this.promptUnlock(lockId, cwd);
129
+ }
125
130
  console.info('State unlocked, retrying terraform destroy');
126
- return this.destroy(env, tfArgs, cwd, attemptNo + 1);
131
+ return this.destroy(env, tfArgs, force, cwd, attemptNo + 1);
127
132
  }
128
133
  throw new KnownException(`terraform destroy failed with code ${error.code}:\n${error.message}`, { cause: error });
129
134
  }
@@ -147,6 +152,9 @@ export class TerraformAdapter {
147
152
  if (!answerYes) {
148
153
  throw new AbortedException();
149
154
  }
155
+ await this.forceUnlock(id, cwd);
156
+ }
157
+ async forceUnlock(id, cwd) {
150
158
  console.log('Force unlocking state');
151
159
  await this.processRunner.run('terraform', ['force-unlock', '-force', id], cwd);
152
160
  }
@@ -1,6 +1,7 @@
1
1
  export const _keys = {
2
2
  CWD: 'Working directory (default: current directory)',
3
3
  ENV: 'Environment name (can be git hash). {project}-{env} give env key used to store env state (s3 key in case of AWS)',
4
+ FORCE: 'Force deletion without confirmation',
4
5
  KIND: 'Environment kind: complete project (default) or some slice such as tt-core + tt-web',
5
6
  OWNER: 'Environment owner (GH username)',
6
7
  PROJECT: 'Project code (like tt). Used when multiple projects deployed in same AWS account',
@@ -8,7 +8,7 @@ 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
+ .option('--force', _keys.FORCE)
12
12
  .option('--cwd <cwd>', _keys.CWD)
13
13
  .action(wrap(handler));
14
14
  }
@@ -10,13 +10,14 @@ export function destroy(program) {
10
10
  + ' Main use case - test deletion process, basically that you have enough permissions to delete resources')
11
11
  .option('--project <project>', _keys.PROJECT)
12
12
  .option('--env <env>', _keys.ENV)
13
+ .option('--force', _keys.FORCE)
13
14
  .option('--cwd <cwd>', _keys.CWD)
14
15
  .allowUnknownOption(true)
15
16
  .argument('[args...]')
16
17
  .action(wrap(handler));
17
18
  }
18
19
  async function handler(tfArgs, options) {
19
- const { project, env, cwd } = options;
20
+ const { project, env, force, cwd } = options;
20
21
  const envName = cliHelper.ensureEnv(env);
21
- await envCtl.destroy(project, envName, tfArgs, cwd);
22
+ await envCtl.destroy(project, envName, tfArgs, Boolean(force), cwd);
22
23
  }
@@ -183,14 +183,16 @@ export class EnvCtl {
183
183
  const statusMessage = await this.envApi.delete(env);
184
184
  console.log(statusMessage);
185
185
  }
186
- async destroy(project, envName, tfArgs, cwd) {
186
+ async destroy(project, envName, tfArgs, force, cwd) {
187
187
  const env = await this.get(project, envName);
188
- const kind = this.cliHelper.ensureKind(undefined, cwd);
189
- if (env.kind && env.kind !== kind) {
190
- throw new KnownException(`Env ${env.key} kind (dir-name): ${env.kind} - make sure you run destroy from the same directory`);
191
- }
192
188
  this.checkStatus(env);
193
- await this.promptUnlock(env, [EnvStatus.Creating]);
189
+ if (!force) {
190
+ const kind = this.cliHelper.ensureKind(undefined, cwd);
191
+ if (env.kind && env.kind !== kind) {
192
+ throw new KnownException(`Env ${env.key} kind (dir-name): ${env.kind} - make sure you run destroy from the same directory`);
193
+ }
194
+ await this.promptUnlock(env, [EnvStatus.Creating]);
195
+ }
194
196
  if (env.status === EnvStatus.Active) {
195
197
  console.log('Lock env to run destroy');
196
198
  await this.envApi.lockForUpdate(env);
@@ -198,13 +200,13 @@ export class EnvCtl {
198
200
  const { owner, size, type, ephemeral } = env;
199
201
  const tfEnv = { owner, size, type, ephemeral, env: envName };
200
202
  console.log('Destroying env resources');
201
- await this.terraformAdapter.destroy(tfEnv, tfArgs, cwd);
203
+ await this.terraformAdapter.destroy(tfEnv, tfArgs, force, cwd);
202
204
  console.log('Unlock env');
203
205
  await this.envApi.activate(env);
204
206
  console.log('Schedule env metadata deletion');
205
207
  const statusMessage = await this.envApi.delete(env);
206
208
  console.log(statusMessage);
207
- console.log('Please wait for ~1 min before you can create env with same name');
209
+ console.log('Please wait for ~15 sec before you can create env with same name');
208
210
  }
209
211
  async get(project, envName) {
210
212
  const key = this.key(project, envName);
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.18.2",
4
+ "version": "0.19.1",
5
5
  "author": "Alex Chekulaev",
6
6
  "type": "module",
7
7
  "bin": {
@@ -25,18 +25,20 @@
25
25
  "test": "vitest run --coverage",
26
26
  "build": "tsc",
27
27
  "run": "node dist/index.js",
28
- "run-version": "tsc --sourceMap true && npm run run -- --version",
29
- "run-configure": "tsc --sourceMap true && npm run run -- configure",
30
- "run-status": "tsc --sourceMap true && npm run run -- status --cwd ../tt-core",
31
- "run-plan": "tsc --sourceMap true && npm run run -- plan --size min --cwd ../tt-core",
32
- "run-deploy": "tsc --sourceMap true && npm run run -- deploy --size min --cwd ../tt-core",
33
- "run-delete": "tsc --sourceMap true && npm run run -- delete --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"
28
+ "run-version": " tsc --sourceMap true && npm run run -- --version",
29
+ "run-configure": " tsc --sourceMap true && npm run run -- configure",
30
+ "run-status": " tsc --sourceMap true && npm run run -- status --cwd ../tt-core",
31
+ "run-plan": " tsc --sourceMap true && npm run run -- plan --cwd ../tt-core --size min",
32
+ "run-deploy": " tsc --sourceMap true && npm run run -- deploy --cwd ../tt-core --size min",
33
+ "run-delete": " tsc --sourceMap true && npm run run -- delete --cwd ../tt-core",
34
+ "run-destroy": " tsc --sourceMap true && npm run run -- destroy --cwd ../tt-core",
35
+ "-": "",
36
+ "run-api-create-ephemeral": "tsc --sourceMap true && npm run run -- api-create-ephemeral --env test --owner laxa1986 --size min --type dev",
37
+ "run-api-status": " tsc --sourceMap true && npm run run -- status --env test",
38
+ "run-api-init": " tsc --sourceMap true && npm run run -- init --env test --cwd ../tt-gitops",
39
+ "run-api-deploy": " tsc --sourceMap true && npm run run -- deploy --env test --cwd ../tt-gitops -var-file=versions.tfvars",
40
+ "run-api-delete": " tsc --sourceMap true && npm run run -- delete --env test --cwd ../tt-gitops --force",
41
+ "run-api-destroy": "tsc --sourceMap true && npm run run -- destroy --env test --cwd ../tt-gitops --force -var-file=versions.tfvars"
40
42
  },
41
43
  "dependencies": {
42
44
  "@aws-sdk/client-sts": "^3.716.0",