@agilecustoms/envctl 0.25.0 → 0.27.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 +2 -2
- package/dist/client/CliHelper.js +3 -34
- package/dist/client/ProcessRunner.js +0 -7
- package/dist/client/TerraformAdapter.js +32 -44
- package/dist/commands/delete.js +2 -3
- package/dist/commands/deploy.js +2 -5
- package/dist/commands/destroy.js +4 -5
- package/dist/commands/index.js +0 -1
- package/dist/commands/plan.js +2 -4
- package/dist/commands/status.js +2 -3
- package/dist/container.js +3 -3
- package/dist/index.js +1 -2
- package/dist/service/ConfigService.js +6 -1
- package/dist/service/EnvCtl.js +47 -42
- package/package.json +7 -8
- package/dist/commands/init.js +0 -17
- package/scripts/get-acc-alias.sh +0 -8
package/README.md
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
|
|
8
8
|
```shell
|
|
9
9
|
terraform init -backend-config=key=laxa1986
|
|
10
|
-
|
|
11
|
-
envctl
|
|
10
|
+
envctl deploy -var-file=versions.tfvars -var="log_level=debug"
|
|
11
|
+
envctl delete
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
## setup/update
|
package/dist/client/CliHelper.js
CHANGED
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
2
|
import inquirer from 'inquirer';
|
|
3
|
-
import { KnownException } from '../exceptions.js';
|
|
4
|
-
import { ConfigService } from '../service/index.js';
|
|
5
3
|
export class CliHelper {
|
|
6
|
-
|
|
7
|
-
constructor(configService) {
|
|
8
|
-
this.configService = configService;
|
|
9
|
-
}
|
|
4
|
+
constructor() { }
|
|
10
5
|
async promptYesNo(message, defaultValue = false) {
|
|
11
6
|
const { answer } = await inquirer.prompt([{
|
|
12
7
|
type: 'confirm',
|
|
@@ -16,34 +11,8 @@ export class CliHelper {
|
|
|
16
11
|
}]);
|
|
17
12
|
return answer;
|
|
18
13
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
return key;
|
|
22
|
-
}
|
|
23
|
-
const owner = this.configService.getOwner();
|
|
24
|
-
if (!owner) {
|
|
25
|
-
throw new KnownException('--key argument is not provided\n'
|
|
26
|
-
+ 'default to owner from local configuration, but it is not configured\n'
|
|
27
|
-
+ 'please call with --key argument or run \'envctl configure\' to configure owner');
|
|
28
|
-
}
|
|
29
|
-
console.log(`Key is not provided, default to owner name ${owner}`);
|
|
30
|
-
return owner;
|
|
31
|
-
}
|
|
32
|
-
ensureOwner(owner) {
|
|
33
|
-
if (!owner) {
|
|
34
|
-
owner = this.configService.getOwner();
|
|
35
|
-
if (!owner) {
|
|
36
|
-
throw new KnownException('when called without --owner option, first call \'envctl configure\'');
|
|
37
|
-
}
|
|
38
|
-
console.log(`Owner not provided, default to configured owner ${owner}`);
|
|
39
|
-
}
|
|
40
|
-
return owner;
|
|
41
|
-
}
|
|
42
|
-
ensureKind(kind, cwd) {
|
|
43
|
-
if (kind) {
|
|
44
|
-
return kind;
|
|
45
|
-
}
|
|
46
|
-
kind = getDirName(cwd);
|
|
14
|
+
getKind(cwd) {
|
|
15
|
+
const kind = getDirName(cwd);
|
|
47
16
|
console.log(`Inferred kind from directory name: ${kind}`);
|
|
48
17
|
return kind;
|
|
49
18
|
}
|
|
@@ -1,14 +1,7 @@
|
|
|
1
1
|
import { spawn } from 'child_process';
|
|
2
|
-
import path from 'path';
|
|
3
2
|
import * as readline from 'readline';
|
|
4
|
-
import { fileURLToPath } from 'url';
|
|
5
3
|
import { ProcessException } from '../exceptions.js';
|
|
6
4
|
export class ProcessRunner {
|
|
7
|
-
async runScript(script, args = [], cwd, scanner) {
|
|
8
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
-
const scriptPath = path.join(__dirname, `../../scripts/${script}`);
|
|
10
|
-
await this.run(scriptPath, args, cwd, scanner);
|
|
11
|
-
}
|
|
12
5
|
async run(command, args, cwd, out_scanner, in_scanner) {
|
|
13
6
|
const spawnOptions = {
|
|
14
7
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
@@ -14,6 +14,38 @@ export class TerraformAdapter {
|
|
|
14
14
|
this.processRunner = processRunner;
|
|
15
15
|
this.cliHelper = cliHelper;
|
|
16
16
|
}
|
|
17
|
+
getKey(cwd) {
|
|
18
|
+
const dir = cwd ?? process.cwd();
|
|
19
|
+
const statePath = path.join(dir, '.terraform', 'terraform.tfstate');
|
|
20
|
+
if (!fs.existsSync(statePath)) {
|
|
21
|
+
throw new KnownException(`Terraform state file not found at: ${statePath}`);
|
|
22
|
+
}
|
|
23
|
+
let content;
|
|
24
|
+
try {
|
|
25
|
+
content = fs.readFileSync(statePath, 'utf8');
|
|
26
|
+
}
|
|
27
|
+
catch (err) {
|
|
28
|
+
throw new KnownException(`Failed to read terraform state file: ${statePath}`, { cause: err });
|
|
29
|
+
}
|
|
30
|
+
let tfstate;
|
|
31
|
+
try {
|
|
32
|
+
tfstate = JSON.parse(content);
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
throw new KnownException(`Failed to parse terraform state file: ${statePath}`, { cause: err });
|
|
36
|
+
}
|
|
37
|
+
const backend = tfstate.backend;
|
|
38
|
+
const type = backend?.type;
|
|
39
|
+
if (type !== 's3') {
|
|
40
|
+
throw new KnownException(`Unsupported terraform backend type: ${type}. Only 's3' is supported for key inference`);
|
|
41
|
+
}
|
|
42
|
+
const config = backend?.config;
|
|
43
|
+
const key = config?.key;
|
|
44
|
+
if (!key) {
|
|
45
|
+
throw new KnownException(`Terraform backend config does not contain 'key' attribute`);
|
|
46
|
+
}
|
|
47
|
+
return key;
|
|
48
|
+
}
|
|
17
49
|
printTime() {
|
|
18
50
|
const now = new Date();
|
|
19
51
|
const utc = now.toISOString();
|
|
@@ -22,50 +54,6 @@ export class TerraformAdapter {
|
|
|
22
54
|
});
|
|
23
55
|
console.log(`\nTime EST: ${est}, UTC: ${utc}\n`);
|
|
24
56
|
}
|
|
25
|
-
async init(key, cwd, upgrade = false) {
|
|
26
|
-
let accAlias = '';
|
|
27
|
-
const accAliasScanner = (line) => {
|
|
28
|
-
accAlias = line;
|
|
29
|
-
};
|
|
30
|
-
await this.processRunner.runScript('get-acc-alias.sh', [], cwd, accAliasScanner);
|
|
31
|
-
console.log('Run terraform init to download providers, this doesn\'t create any resources in AWS even S3 object');
|
|
32
|
-
let emptyDir = false;
|
|
33
|
-
const scanner = (line) => {
|
|
34
|
-
if (line.includes('Terraform initialized in an empty directory!')) {
|
|
35
|
-
emptyDir = true;
|
|
36
|
-
}
|
|
37
|
-
};
|
|
38
|
-
const bucket = `agilecustoms-${accAlias}-tf-state`;
|
|
39
|
-
const args = [
|
|
40
|
-
'init',
|
|
41
|
-
'-reconfigure',
|
|
42
|
-
`-backend-config=bucket=${bucket}`,
|
|
43
|
-
`-backend-config=key=${key}`
|
|
44
|
-
];
|
|
45
|
-
if (upgrade) {
|
|
46
|
-
args.push('-upgrade');
|
|
47
|
-
}
|
|
48
|
-
this.printTime();
|
|
49
|
-
try {
|
|
50
|
-
await this.processRunner.run('terraform', args, cwd, scanner);
|
|
51
|
-
this.printTime();
|
|
52
|
-
}
|
|
53
|
-
catch (error) {
|
|
54
|
-
if (error instanceof ProcessException) {
|
|
55
|
-
if (!upgrade && error.message.includes('Failed to query available provider packages')) {
|
|
56
|
-
args.push('-upgrade');
|
|
57
|
-
await this.processRunner.run('terraform', args, cwd);
|
|
58
|
-
this.printTime();
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
throw new KnownException(`terraform init failed with code ${error.code}:\n${error.message}`, { cause: error });
|
|
62
|
-
}
|
|
63
|
-
throw error;
|
|
64
|
-
}
|
|
65
|
-
if (emptyDir) {
|
|
66
|
-
throw new KnownException('Can not find terraform files. Command needs to be run in a directory with terraform files');
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
57
|
tfArgs(env, cwd) {
|
|
70
58
|
const varDefs = this.getVarDefs(cwd);
|
|
71
59
|
const argVars = this.getArgVars(env.args);
|
package/dist/commands/delete.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
import {
|
|
2
|
+
import { envCtl } from '../container.js';
|
|
3
3
|
import { _keys } from './_keys.js';
|
|
4
4
|
import { wrap } from './_utils.js';
|
|
5
5
|
export function deleteIt(program) {
|
|
@@ -13,6 +13,5 @@ export function deleteIt(program) {
|
|
|
13
13
|
}
|
|
14
14
|
async function handler(options) {
|
|
15
15
|
const { key, force, cwd } = options;
|
|
16
|
-
|
|
17
|
-
await envCtl.delete(theKey, Boolean(force), cwd);
|
|
16
|
+
await envCtl.delete(Boolean(force), key, cwd);
|
|
18
17
|
}
|
package/dist/commands/deploy.js
CHANGED
|
@@ -6,9 +6,6 @@ export function deploy(program) {
|
|
|
6
6
|
program
|
|
7
7
|
.command('deploy')
|
|
8
8
|
.description('Create new or update existing dev environment')
|
|
9
|
-
.option('--key <key>', _keys.KEY)
|
|
10
|
-
.option('--owner <owner>', _keys.OWNER)
|
|
11
|
-
.option('--kind <kind>', _keys.KIND)
|
|
12
9
|
.option('--cwd <cwd>', _keys.CWD)
|
|
13
10
|
.allowUnknownOption(true)
|
|
14
11
|
.argument('[args...]')
|
|
@@ -16,6 +13,6 @@ export function deploy(program) {
|
|
|
16
13
|
}
|
|
17
14
|
async function handler(tfArgs, options) {
|
|
18
15
|
await awsCredsHelper.ensureCredentials();
|
|
19
|
-
const { cwd
|
|
20
|
-
await envCtl.deploy(
|
|
16
|
+
const { cwd } = options;
|
|
17
|
+
await envCtl.deploy(tfArgs, cwd);
|
|
21
18
|
}
|
package/dist/commands/destroy.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
import {
|
|
2
|
+
import { envCtl } from '../container.js';
|
|
3
3
|
import { _keys } from './_keys.js';
|
|
4
4
|
import { wrap } from './_utils.js';
|
|
5
5
|
export function destroy(program) {
|
|
@@ -7,8 +7,8 @@ export function destroy(program) {
|
|
|
7
7
|
.command('destroy')
|
|
8
8
|
.description('Destroy environment resources. This is thin wrapper for terraform destroy.'
|
|
9
9
|
+ ' Unlike "delete" command (which just schedule deletion) this command deletes resources synchronously.'
|
|
10
|
+
+ ' Unlike "delete" command, this command doesn\'t have --key option, it can only delete env after key you provided during terraform init.'
|
|
10
11
|
+ ' Main use case - test deletion process, basically that you have enough permissions to delete resources')
|
|
11
|
-
.option('--key <key>', _keys.KEY)
|
|
12
12
|
.option('--force', _keys.FORCE)
|
|
13
13
|
.option('--cwd <cwd>', _keys.CWD)
|
|
14
14
|
.allowUnknownOption(true)
|
|
@@ -16,7 +16,6 @@ export function destroy(program) {
|
|
|
16
16
|
.action(wrap(handler));
|
|
17
17
|
}
|
|
18
18
|
async function handler(tfArgs, options) {
|
|
19
|
-
const {
|
|
20
|
-
|
|
21
|
-
await envCtl.destroy(theKey, tfArgs, Boolean(force), cwd);
|
|
19
|
+
const { force, cwd } = options;
|
|
20
|
+
await envCtl.destroy(tfArgs, Boolean(force), cwd);
|
|
22
21
|
}
|
package/dist/commands/index.js
CHANGED
|
@@ -3,6 +3,5 @@ export { createEphemeral } from './createEphemeral.js';
|
|
|
3
3
|
export { deleteIt } from './delete.js';
|
|
4
4
|
export { deploy } from './deploy.js';
|
|
5
5
|
export { destroy } from './destroy.js';
|
|
6
|
-
export { init } from './init.js';
|
|
7
6
|
export { plan } from './plan.js';
|
|
8
7
|
export { status } from './status.js';
|
package/dist/commands/plan.js
CHANGED
|
@@ -7,8 +7,6 @@ export function plan(program) {
|
|
|
7
7
|
.command('plan')
|
|
8
8
|
.description('High level wrapper for terraform plan. Compliments deploy command: if you plan to deploy env with envctl deploy,'
|
|
9
9
|
+ ' then it is recommended to do plan with envctl plan to guarantee consistent behavior')
|
|
10
|
-
.option('--key <key>', _keys.KEY)
|
|
11
|
-
.option('--owner <owner>', _keys.OWNER)
|
|
12
10
|
.option('--cwd <cwd>', _keys.CWD)
|
|
13
11
|
.allowUnknownOption(true)
|
|
14
12
|
.argument('[args...]')
|
|
@@ -16,6 +14,6 @@ export function plan(program) {
|
|
|
16
14
|
}
|
|
17
15
|
async function handler(tfArgs, options) {
|
|
18
16
|
await awsCredsHelper.ensureCredentials();
|
|
19
|
-
const { cwd
|
|
20
|
-
await envCtl.plan(
|
|
17
|
+
const { cwd } = options;
|
|
18
|
+
await envCtl.plan(tfArgs, cwd);
|
|
21
19
|
}
|
package/dist/commands/status.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
import {
|
|
2
|
+
import { envCtl } from '../container.js';
|
|
3
3
|
import { _keys } from './_keys.js';
|
|
4
4
|
import { wrap } from './_utils.js';
|
|
5
5
|
export function status(program) {
|
|
@@ -12,6 +12,5 @@ export function status(program) {
|
|
|
12
12
|
}
|
|
13
13
|
async function handler(options) {
|
|
14
14
|
const { key, cwd } = options;
|
|
15
|
-
|
|
16
|
-
await envCtl.status(theKey, cwd);
|
|
15
|
+
await envCtl.status(key, cwd);
|
|
17
16
|
}
|
package/dist/container.js
CHANGED
|
@@ -2,12 +2,12 @@ import { AwsCredsHelper } from './client/AwsCredsHelper.js';
|
|
|
2
2
|
import { CliHelper, EnvApiClient, HttpClient, TerraformAdapter } from './client/index.js';
|
|
3
3
|
import { ProcessRunner } from './client/ProcessRunner.js';
|
|
4
4
|
import { ConfigService, EnvCtl } from './service/index.js';
|
|
5
|
-
const
|
|
6
|
-
const cliHelper = new CliHelper(configService);
|
|
5
|
+
const cliHelper = new CliHelper();
|
|
7
6
|
const awsCredsHelper = new AwsCredsHelper();
|
|
8
7
|
const httpClient = new HttpClient(awsCredsHelper);
|
|
9
8
|
const envApiClient = new EnvApiClient(httpClient);
|
|
10
9
|
const processRunner = new ProcessRunner();
|
|
11
10
|
const terraformAdapter = new TerraformAdapter(processRunner, cliHelper);
|
|
12
|
-
const
|
|
11
|
+
const configService = new ConfigService();
|
|
12
|
+
const envCtl = new EnvCtl(cliHelper, envApiClient, terraformAdapter, configService);
|
|
13
13
|
export { awsCredsHelper, cliHelper, configService, envCtl };
|
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, createEphemeral, deleteIt, deploy, destroy,
|
|
5
|
+
import { configure, createEphemeral, deleteIt, deploy, destroy, 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();
|
|
@@ -16,7 +16,6 @@ createEphemeral(program);
|
|
|
16
16
|
deleteIt(program);
|
|
17
17
|
deploy(program);
|
|
18
18
|
destroy(program);
|
|
19
|
-
init(program);
|
|
20
19
|
plan(program);
|
|
21
20
|
status(program);
|
|
22
21
|
program.parse(process.argv);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as fs from 'node:fs';
|
|
2
2
|
import * as os from 'node:os';
|
|
3
3
|
import path from 'path';
|
|
4
|
+
import { KnownException } from '../exceptions.js';
|
|
4
5
|
const CONFIG_PATH = path.join(os.homedir(), '.envctl', 'config.json');
|
|
5
6
|
export class ConfigService {
|
|
6
7
|
config;
|
|
@@ -23,6 +24,10 @@ export class ConfigService {
|
|
|
23
24
|
return this.config;
|
|
24
25
|
}
|
|
25
26
|
getOwner() {
|
|
26
|
-
|
|
27
|
+
const owner = this.loadConfig()?.owner;
|
|
28
|
+
if (!owner) {
|
|
29
|
+
throw new KnownException('please first set owner via calling \'envctl configure\'');
|
|
30
|
+
}
|
|
31
|
+
return owner;
|
|
27
32
|
}
|
|
28
33
|
}
|
package/dist/service/EnvCtl.js
CHANGED
|
@@ -4,32 +4,37 @@ export class EnvCtl {
|
|
|
4
4
|
cliHelper;
|
|
5
5
|
envApi;
|
|
6
6
|
terraformAdapter;
|
|
7
|
-
|
|
7
|
+
configService;
|
|
8
|
+
constructor(cliHelper, envApi, terraformAdapter, configService) {
|
|
8
9
|
this.cliHelper = cliHelper;
|
|
9
10
|
this.envApi = envApi;
|
|
10
11
|
this.terraformAdapter = terraformAdapter;
|
|
12
|
+
this.configService = configService;
|
|
13
|
+
}
|
|
14
|
+
getKey(key, cwd) {
|
|
15
|
+
if (!key) {
|
|
16
|
+
console.log('Key is not provided, inferring from .terraform/terraform.tfstate file');
|
|
17
|
+
key = this.terraformAdapter.getKey(cwd);
|
|
18
|
+
}
|
|
19
|
+
return key;
|
|
11
20
|
}
|
|
12
21
|
async status(key, cwd) {
|
|
22
|
+
key = this.getKey(key, cwd);
|
|
13
23
|
console.log(`Retrieve env ${key}`);
|
|
14
24
|
const env = await this.envApi.get(key);
|
|
15
25
|
if (env === null) {
|
|
16
26
|
console.log(`Env ${key} does not exist`);
|
|
17
27
|
return;
|
|
18
28
|
}
|
|
19
|
-
const kind = env.kind ? this.cliHelper.ensureKind(undefined, cwd) : undefined;
|
|
20
29
|
console.log(`Env ${key} status: ${env.status}`);
|
|
21
|
-
if (env.kind
|
|
22
|
-
|
|
30
|
+
if (env.kind) {
|
|
31
|
+
const kind = this.cliHelper.getKind(cwd);
|
|
32
|
+
if (env.kind !== kind) {
|
|
33
|
+
console.warn(`Env ${key} kind (dir-name): ${env.kind} - looks like this env was deployed from a different directory`);
|
|
34
|
+
}
|
|
23
35
|
}
|
|
24
36
|
}
|
|
25
|
-
async tryGetEnv(
|
|
26
|
-
let key = input.key;
|
|
27
|
-
let owner = input.owner;
|
|
28
|
-
if (!key) {
|
|
29
|
-
owner = this.cliHelper.ensureOwner(owner);
|
|
30
|
-
console.log(`Env name not provided, default to owner name ${owner}`);
|
|
31
|
-
key = owner;
|
|
32
|
-
}
|
|
37
|
+
async tryGetEnv(key) {
|
|
33
38
|
console.log(`Check if env ${key} already exists`);
|
|
34
39
|
const env = await this.envApi.get(key);
|
|
35
40
|
if (env) {
|
|
@@ -38,15 +43,17 @@ export class EnvCtl {
|
|
|
38
43
|
else {
|
|
39
44
|
console.log(`Env ${key} does not exist`);
|
|
40
45
|
}
|
|
41
|
-
return
|
|
46
|
+
return env;
|
|
42
47
|
}
|
|
43
|
-
checkInput(env,
|
|
44
|
-
if (
|
|
45
|
-
throw new KnownException(`Can not change env owner ${env.owner} -> ${
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
checkInput(env, owner, cwd) {
|
|
49
|
+
if (env.owner !== owner) {
|
|
50
|
+
throw new KnownException(`Can not change env owner ${env.owner} -> ${owner}`);
|
|
51
|
+
}
|
|
52
|
+
if (env.kind) {
|
|
53
|
+
const kind = this.cliHelper.getKind(cwd);
|
|
54
|
+
if (kind !== env.kind) {
|
|
55
|
+
throw new KnownException(`Can not change env kind ${env.kind} -> ${kind}`);
|
|
56
|
+
}
|
|
50
57
|
}
|
|
51
58
|
}
|
|
52
59
|
checkStatus(env, availableStatuses = [EnvStatus.Init, EnvStatus.Deploying, EnvStatus.Active, EnvStatus.Updating]) {
|
|
@@ -58,18 +65,16 @@ export class EnvCtl {
|
|
|
58
65
|
}
|
|
59
66
|
throw new KnownException(`Env ${env.key} status is ${env.status}, can not run this command`);
|
|
60
67
|
}
|
|
61
|
-
async
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const { anOwner, key, env } = await this.tryGetEnv(input);
|
|
68
|
+
async plan(tfArgs, cwd) {
|
|
69
|
+
const key = this.terraformAdapter.getKey(cwd);
|
|
70
|
+
const owner = this.configService.getOwner();
|
|
71
|
+
const env = await this.tryGetEnv(key);
|
|
66
72
|
if (env == null) {
|
|
67
|
-
const
|
|
68
|
-
await this.runPlan(key, owner, tfArgs,
|
|
73
|
+
const vars = undefined;
|
|
74
|
+
await this.runPlan(key, owner, tfArgs, vars, cwd);
|
|
69
75
|
return;
|
|
70
76
|
}
|
|
71
|
-
this.checkInput(env,
|
|
72
|
-
const owner = env.owner;
|
|
77
|
+
this.checkInput(env, owner, cwd);
|
|
73
78
|
this.checkStatus(env);
|
|
74
79
|
await this.promptUnlock(env);
|
|
75
80
|
if (env.status !== EnvStatus.Active) {
|
|
@@ -79,7 +84,6 @@ export class EnvCtl {
|
|
|
79
84
|
}
|
|
80
85
|
async runPlan(key, owner, args, vars, cwd) {
|
|
81
86
|
const envTerraform = { key, ephemeral: false, owner, args, vars };
|
|
82
|
-
await this.terraformAdapter.init(key, cwd);
|
|
83
87
|
await this.terraformAdapter.plan(envTerraform, cwd);
|
|
84
88
|
}
|
|
85
89
|
async createEphemeral(key, owner) {
|
|
@@ -90,17 +94,18 @@ export class EnvCtl {
|
|
|
90
94
|
const createEnv = { key, owner };
|
|
91
95
|
return await this.envApi.createEphemeral(createEnv);
|
|
92
96
|
}
|
|
93
|
-
async deploy(
|
|
94
|
-
const
|
|
97
|
+
async deploy(tfArgs, cwd) {
|
|
98
|
+
const key = this.terraformAdapter.getKey(cwd);
|
|
99
|
+
const owner = this.configService.getOwner();
|
|
100
|
+
const env = await this.tryGetEnv(key);
|
|
95
101
|
if (env === null) {
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
console.log('Creating env tracking record in DynamoDB');
|
|
102
|
+
const kind = this.cliHelper.getKind(cwd);
|
|
103
|
+
console.log('Creating env metadata');
|
|
99
104
|
const createEnv = { key, owner, kind };
|
|
100
105
|
const newEnv = await this.envApi.create(createEnv);
|
|
101
106
|
return await this.runDeploy(newEnv, key, tfArgs, cwd);
|
|
102
107
|
}
|
|
103
|
-
this.checkInput(env,
|
|
108
|
+
this.checkInput(env, owner, cwd);
|
|
104
109
|
this.checkStatus(env);
|
|
105
110
|
if (env.status === EnvStatus.Init) {
|
|
106
111
|
await this.envApi.lockForUpdate(env);
|
|
@@ -120,7 +125,6 @@ export class EnvCtl {
|
|
|
120
125
|
await this.runDeploy(env, key, tfArgs, cwd);
|
|
121
126
|
}
|
|
122
127
|
async runDeploy(env, key, tfArgs, cwd) {
|
|
123
|
-
await this.terraformAdapter.init(env.key, cwd);
|
|
124
128
|
console.log('Deploying resources');
|
|
125
129
|
const { ephemeral, vars } = env;
|
|
126
130
|
const envTerraform = { key, ephemeral, owner: env.owner || 'system', args: tfArgs, vars };
|
|
@@ -139,7 +143,8 @@ export class EnvCtl {
|
|
|
139
143
|
console.log('Activating env (to finish creation)');
|
|
140
144
|
await this.envApi.activate(env);
|
|
141
145
|
}
|
|
142
|
-
async delete(
|
|
146
|
+
async delete(force, key, cwd) {
|
|
147
|
+
key = this.getKey(key, cwd);
|
|
143
148
|
const env = await this.get(key);
|
|
144
149
|
if (!force) {
|
|
145
150
|
this.checkStatus(env);
|
|
@@ -148,7 +153,7 @@ export class EnvCtl {
|
|
|
148
153
|
console.log(`Env ${env.key} is deleted`);
|
|
149
154
|
return;
|
|
150
155
|
}
|
|
151
|
-
const kind = this.cliHelper.
|
|
156
|
+
const kind = this.cliHelper.getKind(cwd);
|
|
152
157
|
if (env.kind && env.kind !== kind) {
|
|
153
158
|
const answerYes = await this.cliHelper.promptYesNo(`Env ${env.key} kind (dir-name): ${env.kind}\n`
|
|
154
159
|
+ 'You\'re deleting env deployed from different dir\n'
|
|
@@ -167,7 +172,8 @@ export class EnvCtl {
|
|
|
167
172
|
const message = await this.envApi.delete(env);
|
|
168
173
|
console.log(message);
|
|
169
174
|
}
|
|
170
|
-
async destroy(
|
|
175
|
+
async destroy(tfArgs, force, cwd) {
|
|
176
|
+
const key = this.terraformAdapter.getKey(cwd);
|
|
171
177
|
const env = await this.get(key);
|
|
172
178
|
this.checkStatus(env);
|
|
173
179
|
if (env.status === EnvStatus.Init) {
|
|
@@ -175,7 +181,7 @@ export class EnvCtl {
|
|
|
175
181
|
return;
|
|
176
182
|
}
|
|
177
183
|
if (!force) {
|
|
178
|
-
const kind = this.cliHelper.
|
|
184
|
+
const kind = this.cliHelper.getKind(cwd);
|
|
179
185
|
if (env.kind && env.kind !== kind) {
|
|
180
186
|
throw new KnownException(`Env ${env.key} kind (dir-name): ${env.kind} - make sure you run destroy from the same directory`);
|
|
181
187
|
}
|
|
@@ -191,7 +197,6 @@ export class EnvCtl {
|
|
|
191
197
|
const { ephemeral, owner, vars } = env;
|
|
192
198
|
const envTerraform = { key, ephemeral, owner: owner || 'system', args: tfArgs, vars };
|
|
193
199
|
console.log('Destroying env resources');
|
|
194
|
-
await this.terraformAdapter.init(env.key, cwd);
|
|
195
200
|
await this.terraformAdapter.destroy(envTerraform, force, cwd);
|
|
196
201
|
console.log('Unlock env');
|
|
197
202
|
await this.envApi.activate(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.27.0",
|
|
5
5
|
"author": "Alex Chekulaev",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
@@ -30,19 +30,18 @@
|
|
|
30
30
|
"run-configure": "tsc --sourceMap true && npm run run -- configure",
|
|
31
31
|
"~": "",
|
|
32
32
|
"run-init": "cd ../tt-core && terraform init -backend-config=key=laxa1986 -upgrade",
|
|
33
|
-
"*": "",
|
|
34
33
|
"run-status": " tsc --sourceMap true && npm run run -- status --cwd ../tt-core",
|
|
35
34
|
"run-plan": " tsc --sourceMap true && npm run run -- plan --cwd ../tt-core",
|
|
36
35
|
"run-deploy": " tsc --sourceMap true && npm run run -- deploy --cwd ../tt-core -var=\"env_size=min\"",
|
|
37
36
|
"run-delete": " tsc --sourceMap true && npm run run -- delete --cwd ../tt-core",
|
|
38
37
|
"run-destroy": "tsc --sourceMap true && npm run run -- destroy --cwd ../tt-core",
|
|
39
38
|
"-": "",
|
|
40
|
-
"run-ephemeral-
|
|
41
|
-
"run-ephemeral-
|
|
42
|
-
"run-ephemeral-
|
|
43
|
-
"run-ephemeral-deploy": " tsc --sourceMap true && npm run run -- deploy --
|
|
44
|
-
"run-ephemeral-delete": " tsc --sourceMap true && npm run run -- delete --
|
|
45
|
-
"run-ephemeral-destroy": "tsc --sourceMap true && npm run run -- destroy --
|
|
39
|
+
"run-ephemeral-init": "cd ../tt-gitops && terraform init -backend-config=key=test",
|
|
40
|
+
"run-ephemeral-create": " tsc --sourceMap true && npm run run -- create-ephemeral",
|
|
41
|
+
"run-ephemeral-status": " tsc --sourceMap true && npm run run -- status --key test",
|
|
42
|
+
"run-ephemeral-deploy": " tsc --sourceMap true && npm run run -- deploy --cwd ../tt-gitops -var-file=versions.tfvars",
|
|
43
|
+
"run-ephemeral-delete": " tsc --sourceMap true && npm run run -- delete --cwd ../tt-gitops --force",
|
|
44
|
+
"run-ephemeral-destroy": "tsc --sourceMap true && npm run run -- destroy --cwd ../tt-gitops --force -var-file=versions.tfvars"
|
|
46
45
|
},
|
|
47
46
|
"dependencies": {
|
|
48
47
|
"@aws-sdk/client-sts": "^3.716.0",
|
package/dist/commands/init.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import { cliHelper, envCtl } from '../container.js';
|
|
3
|
-
import { _keys } from './_keys.js';
|
|
4
|
-
import { wrap } from './_utils.js';
|
|
5
|
-
export function init(program) {
|
|
6
|
-
program
|
|
7
|
-
.command('init')
|
|
8
|
-
.description('Terraform init wrapper')
|
|
9
|
-
.option('--key <key>', _keys.KEY)
|
|
10
|
-
.option('--cwd <cwd>', _keys.CWD)
|
|
11
|
-
.action(wrap(handler));
|
|
12
|
-
}
|
|
13
|
-
async function handler(options) {
|
|
14
|
-
const { key, cwd } = options;
|
|
15
|
-
const theKey = cliHelper.ensureEnv(key);
|
|
16
|
-
await envCtl.init(theKey, cwd);
|
|
17
|
-
}
|
package/scripts/get-acc-alias.sh
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
set -euo pipefail
|
|
3
|
-
|
|
4
|
-
# TF state is stored in S3 in format: {company}-{acc-alias}-tf-state/{key}
|
|
5
|
-
# (for non ephemeral environments the {key} typically has form of {project-code}-{env-name} like tt-dev)
|
|
6
|
-
# Retrieve AWS account information
|
|
7
|
-
acc_id=$(aws sts get-caller-identity --query "Account" --output text)
|
|
8
|
-
aws organizations list-tags-for-resource --resource-id "$acc_id" --query "Tags[?Key=='Alias'].Value" --output text
|