@agilecustoms/envctl 0.10.0 → 0.11.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 CHANGED
@@ -33,7 +33,7 @@ npm view @agilecustoms/envctl version # show latest version available (without i
33
33
  `env-api` is a microservice hosted in 'maintenance' account and working as garbage collector: every environment first
34
34
  created in `env-api` and then 'managed' by `env-api`: it deletes env when it is not in use anymore OR can extend lifetime.
35
35
  Creation API yields unique ID, so you can safely manage env (delete, extend lifetime) via this ID. But creation API
36
- need to be secured. There are two main use cases:
36
+ needs to be secured. There are two main use cases:
37
37
  1. create environment from CI (mainly ephemeral envs)
38
38
  2. create env from dev machine
39
39
 
@@ -9,7 +9,9 @@ export class ProcessRunner {
9
9
  return this.run(scriptPath, args, cwd, scanner);
10
10
  }
11
11
  async run(command, args, cwd, scanner) {
12
- const spawnOptions = {};
12
+ const spawnOptions = {
13
+ stdio: ['inherit', 'pipe', 'pipe'],
14
+ };
13
15
  if (cwd) {
14
16
  spawnOptions.cwd = cwd;
15
17
  }
@@ -26,6 +28,10 @@ export class ProcessRunner {
26
28
  scanner(line);
27
29
  console.log(`> ${line}`);
28
30
  }
31
+ if (buffer.includes('Enter a value:')) {
32
+ console.log(buffer);
33
+ buffer = '';
34
+ }
29
35
  });
30
36
  let errorBuffer = '';
31
37
  child.stderr.on('data', (data) => {
@@ -40,15 +40,23 @@ export class TerraformAdapter {
40
40
  throw new KnownException('Can not find terraform files. Command needs to be run in a directory with terraform files');
41
41
  }
42
42
  }
43
- async deploy(envAttrs, tfArgs, cwd, attemptNo = 1) {
44
- const args = [
43
+ tfArgs(envAttrs, tfArgs) {
44
+ return [
45
45
  `-var=env=${envAttrs.env}`,
46
46
  `-var=owner=${envAttrs.owner}`,
47
47
  `-var=env_size=${envAttrs.size}`,
48
48
  `-var=env_type=${envAttrs.type}`,
49
49
  ...tfArgs
50
50
  ];
51
- console.log('Running:', 'terraform apply -auto-approve', ...args);
51
+ }
52
+ async plan(envAttrs, tfArgs, cwd) {
53
+ const args = this.tfArgs(envAttrs, tfArgs);
54
+ console.log('Running: terraform plan -auto-approve', ...args);
55
+ await this.processRunner.run('terraform', ['plan', ...args], cwd);
56
+ }
57
+ async deploy(envAttrs, tfArgs, cwd, attemptNo = 1) {
58
+ const args = this.tfArgs(envAttrs, tfArgs);
59
+ console.log('Running: terraform apply -auto-approve', ...args);
52
60
  this.printTime();
53
61
  try {
54
62
  await this.processRunner.run('terraform', ['apply', '-auto-approve', ...args], cwd);
@@ -21,6 +21,10 @@ export function deploy(program) {
21
21
  .action(wrap(handler));
22
22
  }
23
23
  async function handler(tfArgs, options) {
24
+ const envDto = await parseEnvDto(options);
25
+ await envCtl.deploy(envDto, tfArgs, options.cwd);
26
+ }
27
+ export async function parseEnvDto(options) {
24
28
  let { project, env, owner, size, type, kind, cwd } = options;
25
29
  if (!owner) {
26
30
  owner = configService.getOwner();
@@ -51,6 +55,5 @@ async function handler(tfArgs, options) {
51
55
  envSize = answer.size;
52
56
  }
53
57
  const envType = ensureEnumValue(EnvType, type, 'type');
54
- const envAttributes = { project, env, owner, size: envSize, type: envType, kind };
55
- await envCtl.deploy(envAttributes, tfArgs, options.cwd);
58
+ return { project, env, owner, size: envSize, type: envType, kind };
56
59
  }
@@ -1,3 +1,4 @@
1
1
  export { configure } from './configure.js';
2
2
  export { deploy } from './deploy.js';
3
3
  export { deleteIt } from './delete.js';
4
+ export { plan } from './plan.js';
@@ -0,0 +1,22 @@
1
+ import { Command } from 'commander';
2
+ import { envCtl } from '../container.js';
3
+ import { parseEnvDto } from './deploy.js';
4
+ import { wrap } from './utils.js';
5
+ export function plan(program) {
6
+ program
7
+ .command('plan')
8
+ .description('High level wrapper for terraform plan. Compliments deploy command: if you plan to deploy env with envctl deploy,'
9
+ + ' then it is recommended to do plan with envctl plan to guarantee consistent behavior')
10
+ .option('--env <env>', 'Environment name (can be git hash). {project}-{env} give env key used to store env state (s3 key in case of AWS)')
11
+ .option('--owner <owner>', 'Environment owner (GH username)')
12
+ .option('--size <size>', 'Environment size: min, small, full')
13
+ .option('--type <type>', 'Environment type: dev, prod', 'dev')
14
+ .option('--cwd <cwd>', 'Working directory (default: current directory)')
15
+ .allowUnknownOption(true)
16
+ .argument('[args...]')
17
+ .action(wrap(handler));
18
+ }
19
+ async function handler(tfArgs, options) {
20
+ const envDto = await parseEnvDto(options);
21
+ await envCtl.plan(envDto, tfArgs, options.cwd);
22
+ }
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 } from './commands/index.js';
5
+ import { configure, deleteIt, deploy, plan } 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();
@@ -14,4 +14,5 @@ program
14
14
  configure(program);
15
15
  deploy(program);
16
16
  deleteIt(program);
17
+ plan(program);
17
18
  program.parse(process.argv);
@@ -8,8 +8,15 @@ export class EnvCtl {
8
8
  this.envApi = envApi;
9
9
  this.terraformAdapter = terraformAdapter;
10
10
  }
11
+ key(project, env) {
12
+ return project ? `${project}-${env}` : env;
13
+ }
14
+ async plan(envDto, tfArgs, cwd) {
15
+ console.log('Deploying resources');
16
+ await this.terraformAdapter.plan(envDto, tfArgs, cwd);
17
+ }
11
18
  async deploy(envDto, tfArgs, cwd) {
12
- const key = envDto.project ? `${envDto.project}-${envDto.env}` : envDto.env;
19
+ const key = this.key(envDto.project, envDto.env);
13
20
  console.log(`Check if env ${key} already exists`);
14
21
  const env = await this.envApi.get(key);
15
22
  if (env === null) {
@@ -40,8 +47,8 @@ export class EnvCtl {
40
47
  switch (env.status) {
41
48
  case EnvStatus.Creating:
42
49
  case EnvStatus.Updating: {
43
- const answerYes = await promptYesNo(`Env status is ${env.status}, likely to be an error from a previous run\n
44
- Do you want to proceed with deployment? (y/n)`);
50
+ const answerYes = await promptYesNo(`Env status is ${env.status}, likely to be an error from a previous run\n`
51
+ + 'Do you want to proceed with deployment?');
45
52
  if (answerYes) {
46
53
  await this.runDeploy(key, envDto, tfArgs, cwd);
47
54
  }
@@ -61,9 +68,9 @@ export class EnvCtl {
61
68
  throw new KnownException(`Unsupported environment status: ${env.status}`);
62
69
  }
63
70
  }
64
- async runDeploy(key, envAttrs, tfArgs, cwd) {
71
+ async runDeploy(key, envDto, tfArgs, cwd) {
65
72
  console.log('Deploying resources');
66
- await this.terraformAdapter.deploy(envAttrs, tfArgs, cwd);
73
+ await this.terraformAdapter.deploy(envDto, tfArgs, cwd);
67
74
  console.log('Activating env (to finish creation)');
68
75
  await this.envApi.activate(key);
69
76
  console.log('Lock env to run db evolution');
@@ -72,14 +79,14 @@ export class EnvCtl {
72
79
  await this.envApi.activate(key);
73
80
  }
74
81
  async delete(envName, project) {
75
- const key = project ? `${project}-${envName}` : envName;
82
+ const key = this.key(project, envName);
76
83
  console.log(`Retrieve env`);
77
84
  const env = await this.envApi.get(key);
78
85
  if (env === null) {
79
86
  throw new KnownException(`Environment ${key} does not exist`);
80
87
  }
81
88
  if (env.status === EnvStatus.Creating) {
82
- const answerYes = await promptYesNo('Environment is still being created.\nDo you want to delete it? (y/n) ');
89
+ const answerYes = await promptYesNo('Environment is still being created.\nDo you want to delete it?');
83
90
  if (!answerYes) {
84
91
  throw new KnownException('Aborting env deletion');
85
92
  }
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.10.0",
4
+ "version": "0.11.0",
5
5
  "author": "Alex Chekulaev",
6
6
  "type": "module",
7
7
  "bin": {
@@ -27,6 +27,7 @@
27
27
  "run": "node dist/index.js",
28
28
  "run-version": "tsc --sourceMap true && npm run run -- --version",
29
29
  "run-configure": "tsc --sourceMap true && npm run run -- configure",
30
+ "run-plan": "tsc --sourceMap true && npm run run -- plan --size min --cwd ../tt-core",
30
31
  "run-deploy": "tsc --sourceMap true && npm run run -- deploy --size min --cwd ../tt-core",
31
32
  "run-delete": "tsc --sourceMap true && npm run run -- delete"
32
33
  },