@agilecustoms/envctl 0.36.2 → 0.37.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/container.js
CHANGED
|
@@ -3,6 +3,7 @@ import { AwsCredsHelper } from './client/AwsCredsHelper.js';
|
|
|
3
3
|
import { CliHelper, EnvApiClient, HttpClient, TerraformAdapter } from './client/index.js';
|
|
4
4
|
import { ProcessRunner } from './client/ProcessRunner.js';
|
|
5
5
|
import { ConfigService, EnvService } from './service/index.js';
|
|
6
|
+
import { LocalStateService } from './service/LocalStateService.js';
|
|
6
7
|
import { LogService } from './service/LogService.js';
|
|
7
8
|
const cliHelper = new CliHelper();
|
|
8
9
|
const configService = new ConfigService();
|
|
@@ -14,6 +15,7 @@ const httpClient = new HttpClient(awsCredsHelper);
|
|
|
14
15
|
const envApiClient = new EnvApiClient(httpClient);
|
|
15
16
|
const processRunner = new ProcessRunner();
|
|
16
17
|
const terraformAdapter = new TerraformAdapter(processRunner, cliHelper, backends);
|
|
17
|
-
const
|
|
18
|
+
const localStateService = new LocalStateService();
|
|
19
|
+
const envService = new EnvService(cliHelper, envApiClient, terraformAdapter, localStateService);
|
|
18
20
|
const logService = new LogService(envApiClient, terraformAdapter, processRunner);
|
|
19
21
|
export { awsCredsHelper, configService, envService, logService };
|
|
@@ -1,13 +1,17 @@
|
|
|
1
|
+
import { CliHelper, EnvApiClient, TerraformAdapter } from '../client/index.js';
|
|
1
2
|
import { KnownException } from '../exceptions.js';
|
|
2
|
-
import { EnvStatus
|
|
3
|
+
import { EnvStatus } from '../model/index.js';
|
|
4
|
+
import { LocalStateService } from './LocalStateService.js';
|
|
3
5
|
export class EnvService {
|
|
4
6
|
cliHelper;
|
|
5
7
|
envApi;
|
|
6
8
|
terraformAdapter;
|
|
7
|
-
|
|
9
|
+
localStateService;
|
|
10
|
+
constructor(cliHelper, envApi, terraformAdapter, localStateService) {
|
|
8
11
|
this.cliHelper = cliHelper;
|
|
9
12
|
this.envApi = envApi;
|
|
10
13
|
this.terraformAdapter = terraformAdapter;
|
|
14
|
+
this.localStateService = localStateService;
|
|
11
15
|
}
|
|
12
16
|
async status(key, cwd) {
|
|
13
17
|
key = this.terraformAdapter.getKey(key, cwd);
|
|
@@ -81,15 +85,33 @@ export class EnvService {
|
|
|
81
85
|
return await this.envApi.createEphemeral(createEnv);
|
|
82
86
|
}
|
|
83
87
|
async lockTerraform(env, cwd) {
|
|
88
|
+
console.log('load local state config');
|
|
89
|
+
const config = this.localStateService.load(cwd);
|
|
84
90
|
console.log('validate terraform.tfstate file');
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
91
|
+
const stateFileContent = this.terraformAdapter.getTerraformBackend(cwd).validateAndGetStateFileContent();
|
|
92
|
+
const stateHash = this.localStateService.hash(stateFileContent);
|
|
93
|
+
if (config.stateHash !== stateHash) {
|
|
94
|
+
config.stateHash = stateHash;
|
|
95
|
+
env.stateFile = stateFileContent;
|
|
96
|
+
}
|
|
97
|
+
const linuxArm64 = process.platform === 'linux' && process.arch === 'arm64';
|
|
98
|
+
if (!linuxArm64) {
|
|
99
|
+
let lockFileContent = this.terraformAdapter.getLockFile(cwd);
|
|
100
|
+
const lockFileHash = this.localStateService.hash(lockFileContent);
|
|
101
|
+
if (config.lockHash !== lockFileHash) {
|
|
102
|
+
await this.terraformAdapter.lockProviders(cwd);
|
|
103
|
+
lockFileContent = this.terraformAdapter.getLockFile(cwd);
|
|
104
|
+
config.lockHash = this.localStateService.hash(lockFileContent);
|
|
105
|
+
env.lockFile = lockFileContent;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
this.localStateService.save(config, cwd);
|
|
88
109
|
}
|
|
89
110
|
async deploy(tfArgs, cwd) {
|
|
90
111
|
const key = this.terraformAdapter.getTerraformBackend(cwd).getKey();
|
|
91
112
|
let env = await this.tryGetEnv(key);
|
|
92
113
|
if (env === null) {
|
|
114
|
+
this.localStateService.init(cwd);
|
|
93
115
|
const kind = this.cliHelper.getKind(cwd);
|
|
94
116
|
console.log(`Inferred kind from directory name: ${kind}`);
|
|
95
117
|
const createEnv = { key, kind };
|
|
@@ -102,6 +124,10 @@ export class EnvService {
|
|
|
102
124
|
this.checkKind(env, cwd);
|
|
103
125
|
switch (env.status) {
|
|
104
126
|
case EnvStatus.Init:
|
|
127
|
+
this.localStateService.init(cwd);
|
|
128
|
+
await this.lockTerraform(env, cwd);
|
|
129
|
+
await this.envApi.lockForUpdate(env);
|
|
130
|
+
break;
|
|
105
131
|
case EnvStatus.Active:
|
|
106
132
|
if (env.status === EnvStatus.Active) {
|
|
107
133
|
console.log('Env status is ACTIVE\nWill lock for update and run terraform apply (to update resources)');
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
const CURRENT_VERSION = 1;
|
|
5
|
+
export class LocalStateService {
|
|
6
|
+
config;
|
|
7
|
+
constructor() {
|
|
8
|
+
}
|
|
9
|
+
filePath(cwd) {
|
|
10
|
+
cwd = cwd || process.cwd();
|
|
11
|
+
return path.join(cwd, '.terraform', 'envctl.json');
|
|
12
|
+
}
|
|
13
|
+
hash(data) {
|
|
14
|
+
return createHash('sha256').update(data).digest('hex');
|
|
15
|
+
}
|
|
16
|
+
createEmptyConfig(filePath) {
|
|
17
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
18
|
+
this.config = { version: CURRENT_VERSION };
|
|
19
|
+
fs.writeFileSync(filePath, JSON.stringify(this.config, null, 2));
|
|
20
|
+
return this.config;
|
|
21
|
+
}
|
|
22
|
+
init(cwd) {
|
|
23
|
+
const filePath = this.filePath(cwd);
|
|
24
|
+
if (!fs.existsSync(filePath)) {
|
|
25
|
+
this.createEmptyConfig(filePath);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const data = fs.readFileSync(filePath, 'utf-8');
|
|
29
|
+
const config = JSON.parse(data);
|
|
30
|
+
if (config.version == CURRENT_VERSION) {
|
|
31
|
+
this.config = config;
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
console.log('local state file version mismatch, re-initializing');
|
|
35
|
+
this.createEmptyConfig(filePath);
|
|
36
|
+
}
|
|
37
|
+
load(cwd) {
|
|
38
|
+
if (this.config)
|
|
39
|
+
return this.config;
|
|
40
|
+
const filePath = this.filePath(cwd);
|
|
41
|
+
if (!fs.existsSync(filePath)) {
|
|
42
|
+
console.warn('Local state file does not exist, must have been accidentally deleted, re-initializing');
|
|
43
|
+
return this.createEmptyConfig(filePath);
|
|
44
|
+
}
|
|
45
|
+
const data = fs.readFileSync(filePath, 'utf-8');
|
|
46
|
+
this.config = JSON.parse(data);
|
|
47
|
+
return this.config;
|
|
48
|
+
}
|
|
49
|
+
save(config, cwd) {
|
|
50
|
+
if (!this.config) {
|
|
51
|
+
throw new Error('call init or load first');
|
|
52
|
+
}
|
|
53
|
+
const mergedConfig = { ...this.load(cwd), ...config };
|
|
54
|
+
const data = JSON.stringify(mergedConfig, null, 2);
|
|
55
|
+
const filePath = this.filePath(cwd);
|
|
56
|
+
fs.writeFileSync(filePath, data);
|
|
57
|
+
}
|
|
58
|
+
}
|
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.37.0",
|
|
5
5
|
"author": "Alex Chekulaev",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"engines": {
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"run-version": " tsc --sourceMap true && npm run run -- --version",
|
|
33
33
|
"run-configure": "tsc --sourceMap true && npm run run -- configure",
|
|
34
34
|
"~": "",
|
|
35
|
-
"run-core-init": "cd ../tt-core && terraform init -upgrade -backend-config=key=laxa1986",
|
|
35
|
+
"run-core-init": "cd ../tt-core && terraform init -upgrade -backend-config=key=laxa1986 -reconfigure",
|
|
36
36
|
"run-core-status": " tsc --sourceMap true && npm run run -- status --cwd ../tt-core",
|
|
37
37
|
"run-core-plan": " tsc --sourceMap true && npm run run -- plan --cwd ../tt-core",
|
|
38
38
|
"run-core-deploy": " tsc --sourceMap true && npm run run -- deploy --cwd ../tt-core -var=\"env_size=min\"",
|