@agilecustoms/envctl 0.15.0 → 0.16.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/dist/client/TerraformAdapter.js +42 -39
- package/dist/exceptions.js +6 -0
- package/dist/service/EnvCtl.js +14 -4
- package/package.json +2 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AbortedException, KnownException, ProcessException } from '../exceptions.js';
|
|
1
|
+
import { AbortedException, KnownException, ProcessException, TerraformInitRequired } from '../exceptions.js';
|
|
2
2
|
const MAX_ATTEMPTS = 2;
|
|
3
3
|
const RETRYABLE_ERRORS = [
|
|
4
4
|
'ConcurrentModificationException',
|
|
@@ -53,50 +53,53 @@ export class TerraformAdapter {
|
|
|
53
53
|
}
|
|
54
54
|
async plan(env, tfArgs, cwd, attemptNo = 1) {
|
|
55
55
|
const args = this.tfArgs(env, tfArgs);
|
|
56
|
-
console.log('Running: terraform plan -auto-approve', ...args);
|
|
56
|
+
console.log('Running: terraform plan -auto-approve', ...args, '\n');
|
|
57
57
|
try {
|
|
58
58
|
await this.processRunner.run('terraform', ['plan', ...args], cwd);
|
|
59
59
|
}
|
|
60
60
|
catch (error) {
|
|
61
|
-
if (error instanceof ProcessException) {
|
|
62
|
-
|
|
63
|
-
console.warn(`Retrying terraform plan due to error: ${error.message}`);
|
|
64
|
-
return this.plan(env, tfArgs, cwd, attemptNo + 1);
|
|
65
|
-
}
|
|
66
|
-
const lockId = this.lockId(error, attemptNo);
|
|
67
|
-
if (lockId) {
|
|
68
|
-
await this.promptUnlock(lockId, cwd);
|
|
69
|
-
console.info('State unlocked, retrying terraform plan');
|
|
70
|
-
return this.plan(env, tfArgs, cwd, attemptNo + 1);
|
|
71
|
-
}
|
|
72
|
-
throw new KnownException(`terraform plan failed with code ${error.code}:\n${error.message}`, { cause: error });
|
|
61
|
+
if (!(error instanceof ProcessException)) {
|
|
62
|
+
throw error;
|
|
73
63
|
}
|
|
74
|
-
|
|
64
|
+
if (attemptNo < MAX_ATTEMPTS && RETRYABLE_ERRORS.some(err => error.message.includes(err))) {
|
|
65
|
+
console.warn(`Retrying terraform plan due to error: ${error.message}`);
|
|
66
|
+
return this.plan(env, tfArgs, cwd, attemptNo + 1);
|
|
67
|
+
}
|
|
68
|
+
const lockId = this.lockId(error, attemptNo);
|
|
69
|
+
if (lockId) {
|
|
70
|
+
await this.promptUnlock(lockId, cwd);
|
|
71
|
+
console.info('State unlocked, retrying terraform plan');
|
|
72
|
+
return this.plan(env, tfArgs, cwd, attemptNo + 1);
|
|
73
|
+
}
|
|
74
|
+
throw new KnownException(`terraform plan failed with code ${error.code}:\n${error.message}`, { cause: error });
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
77
|
async apply(env, tfArgs, cwd, attemptNo = 1) {
|
|
78
78
|
const args = this.tfArgs(env, tfArgs);
|
|
79
|
-
console.log('Running: terraform apply -auto-approve', ...args);
|
|
79
|
+
console.log('Running: terraform apply -auto-approve', ...args, '\n');
|
|
80
80
|
this.printTime();
|
|
81
81
|
try {
|
|
82
82
|
await this.processRunner.run('terraform', ['apply', '-auto-approve', ...args], cwd);
|
|
83
83
|
this.printTime();
|
|
84
84
|
}
|
|
85
85
|
catch (error) {
|
|
86
|
-
if (error instanceof ProcessException) {
|
|
87
|
-
|
|
88
|
-
console.warn(`Retrying terraform apply due to error: ${error.message}`);
|
|
89
|
-
return this.apply(env, tfArgs, cwd, attemptNo + 1);
|
|
90
|
-
}
|
|
91
|
-
const lockId = this.lockId(error, attemptNo);
|
|
92
|
-
if (lockId) {
|
|
93
|
-
await this.promptUnlock(lockId, cwd);
|
|
94
|
-
console.info('State unlocked, retrying terraform apply');
|
|
95
|
-
return this.apply(env, tfArgs, cwd, attemptNo + 1);
|
|
96
|
-
}
|
|
97
|
-
throw new KnownException(`terraform apply failed with code ${error.code}:\n${error.message}`, { cause: error });
|
|
86
|
+
if (!(error instanceof ProcessException)) {
|
|
87
|
+
throw error;
|
|
98
88
|
}
|
|
99
|
-
|
|
89
|
+
if (error.message.includes('Backend initialization required')) {
|
|
90
|
+
throw new TerraformInitRequired();
|
|
91
|
+
}
|
|
92
|
+
if (attemptNo < MAX_ATTEMPTS && RETRYABLE_ERRORS.some(err => error.message.includes(err))) {
|
|
93
|
+
console.warn(`Retrying terraform apply due to error: ${error.message}`);
|
|
94
|
+
return this.apply(env, tfArgs, cwd, attemptNo + 1);
|
|
95
|
+
}
|
|
96
|
+
const lockId = this.lockId(error, attemptNo);
|
|
97
|
+
if (lockId) {
|
|
98
|
+
await this.promptUnlock(lockId, cwd);
|
|
99
|
+
console.info('State unlocked, retrying terraform apply');
|
|
100
|
+
return this.apply(env, tfArgs, cwd, attemptNo + 1);
|
|
101
|
+
}
|
|
102
|
+
throw new KnownException(`terraform apply failed with code ${error.code}:\n${error.message}`, { cause: error });
|
|
100
103
|
}
|
|
101
104
|
}
|
|
102
105
|
async destroy(env, tfArgs, cwd, attemptNo = 1) {
|
|
@@ -107,21 +110,21 @@ export class TerraformAdapter {
|
|
|
107
110
|
}
|
|
108
111
|
};
|
|
109
112
|
const args = this.tfArgs(env, tfArgs);
|
|
110
|
-
console.log('Running: terraform destroy -auto-approve');
|
|
113
|
+
console.log('Running: terraform destroy -auto-approve', ...args, '\n');
|
|
111
114
|
try {
|
|
112
115
|
await this.processRunner.run('terraform', ['destroy', '-auto-approve', ...args], cwd, scanner);
|
|
113
116
|
}
|
|
114
117
|
catch (error) {
|
|
115
|
-
if (error instanceof ProcessException) {
|
|
116
|
-
|
|
117
|
-
if (lockId) {
|
|
118
|
-
await this.promptUnlock(lockId, cwd);
|
|
119
|
-
console.info('State unlocked, retrying terraform destroy');
|
|
120
|
-
return this.destroy(env, tfArgs, cwd, attemptNo + 1);
|
|
121
|
-
}
|
|
122
|
-
throw new KnownException(`terraform destroy failed with code ${error.code}:\n${error.message}`, { cause: error });
|
|
118
|
+
if (!(error instanceof ProcessException)) {
|
|
119
|
+
throw error;
|
|
123
120
|
}
|
|
124
|
-
|
|
121
|
+
const lockId = this.lockId(error, attemptNo);
|
|
122
|
+
if (lockId) {
|
|
123
|
+
await this.promptUnlock(lockId, cwd);
|
|
124
|
+
console.info('State unlocked, retrying terraform destroy');
|
|
125
|
+
return this.destroy(env, tfArgs, cwd, attemptNo + 1);
|
|
126
|
+
}
|
|
127
|
+
throw new KnownException(`terraform destroy failed with code ${error.code}:\n${error.message}`, { cause: error });
|
|
125
128
|
}
|
|
126
129
|
if (wrongDir) {
|
|
127
130
|
throw new KnownException('Can not find terraform files. Command needs to be run in a directory with terraform files');
|
package/dist/exceptions.js
CHANGED
package/dist/service/EnvCtl.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { KnownException } from '../exceptions.js';
|
|
1
|
+
import { KnownException, TerraformInitRequired } from '../exceptions.js';
|
|
2
2
|
import { EnvSizeAvgTime, EnvStatus } from '../model/index.js';
|
|
3
3
|
export class EnvCtl {
|
|
4
4
|
cliHelper;
|
|
@@ -98,14 +98,13 @@ export class EnvCtl {
|
|
|
98
98
|
const { envName, owner, key, env } = await this.tryGetEnv(input);
|
|
99
99
|
if (env === null) {
|
|
100
100
|
const envInput = await this.cliHelper.parseEnvInput(input, envName, owner, cwd);
|
|
101
|
-
await this.terraformAdapter.init(key, cwd);
|
|
102
101
|
console.log('Creating env tracking record in DynamoDB');
|
|
103
102
|
const { size, type, kind } = envInput;
|
|
104
103
|
const createEnv = { key, ephemeral: false, owner: envInput.owner, size, type, kind };
|
|
105
104
|
const newEnv = await this.envApi.create(createEnv);
|
|
106
105
|
return await this.runDeploy(newEnv, envName, tfArgs, cwd);
|
|
107
106
|
}
|
|
108
|
-
this.checkInput(envName, env, input);
|
|
107
|
+
this.checkInput(envName, env, input, cwd);
|
|
109
108
|
this.checkStatus(env);
|
|
110
109
|
if (env.status == EnvStatus.Creating || env.status == EnvStatus.Updating) {
|
|
111
110
|
const answerYes = await this.cliHelper.promptYesNo(`Env status is ${env.status}, likely to be an error from a previous run\n`
|
|
@@ -125,7 +124,18 @@ export class EnvCtl {
|
|
|
125
124
|
console.log('Deploying resources');
|
|
126
125
|
const { owner, size, type } = env;
|
|
127
126
|
const envTf = { owner, size, type, env: envName };
|
|
128
|
-
|
|
127
|
+
try {
|
|
128
|
+
await this.terraformAdapter.apply(envTf, tfArgs, cwd);
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
if (error instanceof TerraformInitRequired) {
|
|
132
|
+
await this.terraformAdapter.init(env.key, cwd);
|
|
133
|
+
await this.terraformAdapter.apply(envTf, tfArgs, cwd);
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
throw error;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
129
139
|
console.log('Activating env (to finish creation)');
|
|
130
140
|
await this.envApi.activate(env);
|
|
131
141
|
console.log('Lock env to run db evolution');
|
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.16.0",
|
|
5
5
|
"author": "Alex Chekulaev",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"run-status": "tsc --sourceMap true && npm run run -- status --cwd ../tt-core",
|
|
31
31
|
"run-plan": "tsc --sourceMap true && npm run run -- plan --size min --cwd ../tt-core",
|
|
32
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-
|
|
33
|
+
"run-delete": "tsc --sourceMap true && npm run run -- delete --cwd ../tt-core",
|
|
34
34
|
"run-destroy": "tsc --sourceMap true && npm run run -- destroy --cwd ../tt-core"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|