-
Notifications
You must be signed in to change notification settings - Fork 0
/
stress-test-eks.js
296 lines (256 loc) · 8.6 KB
/
stress-test-eks.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
#!/usr/bin/env node
const { program } = require('commander');
const chalk = require('chalk');
const util = require('util');
const exec = util.promisify(require('child_process').exec);
async function checkRequirements(debugEnabled) {
try {
await exec('docker info');
} catch {
throw new Error('Docker missing or not running');
}
try {
await exec('helm');
} catch {
throw new Error('Helm missing');
}
try {
await exec('kubectl');
} catch {
throw new Error('Kubectl missing');
}
try {
await exec('go version');
} catch {
throw new Error('Go missing');
}
try {
await exec('aws help');
} catch {
throw new Error('Aws cli missing');
}
}
async function setupInfrastructureServices(debugEnabled) {
try {
console.log('Setting up helm...');
await exec('helm repo add datadog https://helm.datadoghq.com');
await exec('helm repo add bitnami https://charts.bitnami.com/bitnami');
await exec('helm repo update');
console.log('Helm setup completed ✅');
console.log('Starting Datadog...');
try {
await exec(
`helm upgrade --install datadog -f local/k6-cluster/datadog-values.yml datadog/datadog`,
);
console.log('Datadog running ✅');
} catch (error) {
if (debugEnabled) {
console.log(error);
}
console.error(
'Datadog is not set up correctly ❌. Cluster will be set up without It...',
);
}
console.log('Starting Kubernetes metrics server...');
await exec(
`helm upgrade --install metrics-server -f local/k6-cluster/metrics-server.values.yml bitnami/metrics-server`,
);
console.log('Metrics server running ✅');
console.log('Starting RabbitMQ...');
await exec(
`helm upgrade --install rabbitmq -f local/k6-cluster/rabbitmq-values.yml bitnami/rabbitmq`,
);
console.log('RabbitMQ running ✅');
console.log('Starting PostgresSQL...');
await exec(
`helm upgrade --install postgresql -f local/k6-cluster/postgresql-values.yml bitnami/postgresql`,
);
console.log('PostgresSQL running ✅');
console.log('Starting Redis...');
await exec(
`helm upgrade --install redis -f local/k6-cluster/redis-values.yml bitnami/redis`,
);
console.log('Redis running ✅');
console.log('Starting K6 operator...');
await exec(
'git clone https://github.com/grafana/k6-operator --depth 1 && cd k6-operator && make deploy && cd .. && rm -rf k6-operator',
);
console.log('K6 operator running ✅');
} catch (error) {
if (debugEnabled) {
console.error(error);
}
throw new Error(
'Issue while setting up infrastructure services, is your docker running?',
);
}
}
async function deployApi(service, build, debugEnabled) {
const readline = require('readline').createInterface({
input: process.stdin,
output: process.stdout,
});
const inputQuestion = util.promisify(readline.question).bind(readline);
try {
const platformTag = 'linux-amd64';
const repositoryUrl =
'108827241267.dkr.ecr.us-east-1.amazonaws.com/archie-testing-container-repository';
const availableProfiles = (await exec('aws configure list-profiles'))
.stdout;
console.log('AWS profiles: ');
console.log(availableProfiles);
const awsProfile = await inputQuestion(
`AWS profile for archie testing cluster: `,
);
if (build) {
let dockerfilePath;
if (service.endsWith('test-api')) {
dockerfilePath = 'apps/tests';
} else if (service.endsWith('api')) {
dockerfilePath = 'apps/api';
} else {
throw new Error('Invalid service name');
}
console.log('Building service... (This might take a while)');
await exec(
`DOCKER_DEFAULT_PLATFORM=linux/amd64 docker build -f ${dockerfilePath}/${service}/Dockerfile.local -t ${service}-${platformTag} --build-arg LOCAL=true .`,
);
console.log('Service built ✅');
}
console.log('Logging in the ECR repository');
await exec(
`aws ecr get-login-password --region=us-east-1 --profile=${awsProfile} | docker login --username AWS --password-stdin ${repositoryUrl}`,
);
console.log('Logged in ✅');
console.log('Pushing docker image...');
const imageTag = `${service}-${Date.now()}`;
const ecrImageName = `${repositoryUrl}:${imageTag}`;
await exec(`docker tag ${service}-${platformTag}:latest ${ecrImageName}`);
await exec(`docker push ${ecrImageName}`);
console.log(`Docker image pushed ✅. Image name: ${ecrImageName}`);
console.log('Deploying service ...');
await exec(
`helm upgrade --install ${service} local/k6-cluster/eks-deploy-chart --set environment=stress-test --set service=${service} --set tag=${imageTag} --set image=${repositoryUrl}`,
);
console.log(chalk.inverse.bold('Service deployed'));
} catch (error) {
readline.close();
if (debugEnabled) {
console.error(error);
}
throw new Error(
'Issue while deploying api, is your docker running? Try running command with --build argument to build the image. This is required first time and on any service updates',
);
}
readline.close();
}
async function cleanupK6Script() {
console.log(`Removing previous test config...`);
try {
await exec(
'kubectl delete -f local/k6-cluster/k6-operator-custom-resource.yml',
);
} catch (error) {
console.warn(
'Error deleting k6 custom resource. This is expected in case you run this script on clean cluster',
error.message,
);
}
try {
await exec('kubectl delete configmap stress-test');
} catch (error) {
console.warn(
'Error stress test config map. This is expected in case you run this script on clean cluster',
error.message,
);
}
console.log(`Test config removed ✅`);
}
program
.command('setup')
.alias('s')
.option('-d, --debug')
.option('-b, --build')
.action(async ({ debug, build }) => {
console.log(chalk.inverse.bold(`Starting K6 cluster setup...`));
try {
await checkRequirements(debug);
await setupInfrastructureServices(debug);
await deployApi('utils-test-api', build, debug);
} catch (error) {
console.log(chalk.red.bold(error.message));
}
console.log(chalk.inverse.bold('K6 cluster setup complete ✅'));
});
program
.command('clean')
.alias('c')
.option('-d, --debug')
.action(async ({ debug }) => {
console.log(chalk.inverse.bold(`Starting cluster cleanup...`));
await cleanupK6Script();
try {
console.log('Uninstalling K6 operator...');
await exec(
'git clone https://github.com/grafana/k6-operator --depth 1 && cd k6-operator && make delete && cd .. && rm -rf k6-operator',
);
console.log('K6 operator uninstalled ✅');
} catch (error) {
await exec('cd .. && rm -rf k6-operator');
console.error('K6 operator could not be cleaned ❌', error);
}
try {
console.log('Uninstalling all Helm services...');
await exec('helm uninstall $(helm list --short)');
console.log('All services uninstalled ✅');
} catch (error) {
console.error('Services could not be cleaned ❌', error);
}
console.log(chalk.inverse.bold('Cluster cleanup complete'));
});
program
.command('deploy')
.alias('d')
.option('-d, --debug')
.option('-b, --build')
.requiredOption('-s, --service <service>', 'Service name')
.action(async ({ debug, build, service }) => {
await deployApi(service, build, debug);
});
program
.command('run')
.requiredOption(
'-s, --script <script>',
'Path to script to build and execute',
)
.option('-d, --debug')
.action(async ({ script, debug }) => {
const scriptPath = `apps/tests/stress/src/${script}`;
try {
await cleanupK6Script();
console.log(`Building script at path: ${scriptPath}`);
await exec(
`nx run stress-tests:build --main=${scriptPath} --skip-nx-cache`,
);
console.log(`Script built ✅`);
console.log(`Starting script`);
await exec(
'kubectl create configmap stress-test --from-file dist/apps/tests/stress/main.js --from-file dist/apps/tests/stress/main.js.map',
);
await exec(
'kubectl apply -f local/k6-cluster/k6-operator-custom-resource.yml',
);
console.log(`Script started ✅. Check kubernetes pods for more info.`);
} catch (error) {
console.log(chalk.red.bold(error.message));
}
});
program
.command('stop')
.option('-d, --debug')
.action(async ({ debug }) => {
console.log(`Stopping test execution...`);
await cleanupK6Script();
console.log(`Script stopped ✅`);
});
program.parse();