Feat/migrate aws sdk v3 (#698)
* chore(cloud-runner): migrate/replace deps aws-sdk v2 to v3 * chore(aws): refactor aws services to support SDK v3 * chore(aws): refactor aws runner to support SDK v3 * chore(aws): update dist * fix(aws): error handling wrap try/catch to avoid unhandled promise rejections. * fix(aws): keeping the syntax simpler for arrayspull/693/merge v4.4.0
parent
9d6bdcbdc5
commit
81ed299e10
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
|
@ -32,6 +32,11 @@
|
||||||
"@actions/core": "^1.11.1",
|
"@actions/core": "^1.11.1",
|
||||||
"@actions/exec": "^1.1.1",
|
"@actions/exec": "^1.1.1",
|
||||||
"@actions/github": "^6.0.0",
|
"@actions/github": "^6.0.0",
|
||||||
|
"@aws-sdk/client-cloudformation": "^3.777.0",
|
||||||
|
"@aws-sdk/client-cloudwatch-logs": "^3.777.0",
|
||||||
|
"@aws-sdk/client-ecs": "^3.778.0",
|
||||||
|
"@aws-sdk/client-kinesis": "^3.777.0",
|
||||||
|
"@aws-sdk/client-s3": "^3.779.0",
|
||||||
"@kubernetes/client-node": "^0.16.3",
|
"@kubernetes/client-node": "^0.16.3",
|
||||||
"@octokit/core": "^5.1.0",
|
"@octokit/core": "^5.1.0",
|
||||||
"async-wait-until": "^2.0.12",
|
"async-wait-until": "^2.0.12",
|
||||||
|
|
|
@ -1,6 +1,18 @@
|
||||||
import CloudRunnerLogger from '../../services/core/cloud-runner-logger';
|
import CloudRunnerLogger from '../../services/core/cloud-runner-logger';
|
||||||
import * as core from '@actions/core';
|
import * as core from '@actions/core';
|
||||||
import * as SDK from 'aws-sdk';
|
import {
|
||||||
|
CloudFormation,
|
||||||
|
CreateStackCommand,
|
||||||
|
CreateStackCommandInput,
|
||||||
|
DescribeStacksCommand,
|
||||||
|
DescribeStacksCommandInput,
|
||||||
|
ListStacksCommand,
|
||||||
|
Parameter,
|
||||||
|
UpdateStackCommand,
|
||||||
|
UpdateStackCommandInput,
|
||||||
|
waitUntilStackCreateComplete,
|
||||||
|
waitUntilStackUpdateComplete,
|
||||||
|
} from '@aws-sdk/client-cloudformation';
|
||||||
import { BaseStackFormation } from './cloud-formations/base-stack-formation';
|
import { BaseStackFormation } from './cloud-formations/base-stack-formation';
|
||||||
import crypto from 'node:crypto';
|
import crypto from 'node:crypto';
|
||||||
|
|
||||||
|
@ -10,51 +22,49 @@ export class AWSBaseStack {
|
||||||
}
|
}
|
||||||
private baseStackName: string;
|
private baseStackName: string;
|
||||||
|
|
||||||
async setupBaseStack(CF: SDK.CloudFormation) {
|
async setupBaseStack(CF: CloudFormation) {
|
||||||
const baseStackName = this.baseStackName;
|
const baseStackName = this.baseStackName;
|
||||||
|
|
||||||
const baseStack = BaseStackFormation.formation;
|
const baseStack = BaseStackFormation.formation;
|
||||||
|
|
||||||
// Cloud Formation Input
|
// Cloud Formation Input
|
||||||
const describeStackInput: SDK.CloudFormation.DescribeStacksInput = {
|
const describeStackInput: DescribeStacksCommandInput = {
|
||||||
StackName: baseStackName,
|
StackName: baseStackName,
|
||||||
};
|
};
|
||||||
const parametersWithoutHash: SDK.CloudFormation.Parameter[] = [
|
const parametersWithoutHash: Parameter[] = [{ ParameterKey: 'EnvironmentName', ParameterValue: baseStackName }];
|
||||||
{ ParameterKey: 'EnvironmentName', ParameterValue: baseStackName },
|
|
||||||
];
|
|
||||||
const parametersHash = crypto
|
const parametersHash = crypto
|
||||||
.createHash('md5')
|
.createHash('md5')
|
||||||
.update(baseStack + JSON.stringify(parametersWithoutHash))
|
.update(baseStack + JSON.stringify(parametersWithoutHash))
|
||||||
.digest('hex');
|
.digest('hex');
|
||||||
const parameters: SDK.CloudFormation.Parameter[] = [
|
const parameters: Parameter[] = [
|
||||||
...parametersWithoutHash,
|
...parametersWithoutHash,
|
||||||
...[{ ParameterKey: 'Version', ParameterValue: parametersHash }],
|
...[{ ParameterKey: 'Version', ParameterValue: parametersHash }],
|
||||||
];
|
];
|
||||||
const updateInput: SDK.CloudFormation.UpdateStackInput = {
|
const updateInput: UpdateStackCommandInput = {
|
||||||
StackName: baseStackName,
|
StackName: baseStackName,
|
||||||
TemplateBody: baseStack,
|
TemplateBody: baseStack,
|
||||||
Parameters: parameters,
|
Parameters: parameters,
|
||||||
Capabilities: ['CAPABILITY_IAM'],
|
Capabilities: ['CAPABILITY_IAM'],
|
||||||
};
|
};
|
||||||
const createStackInput: SDK.CloudFormation.CreateStackInput = {
|
const createStackInput: CreateStackCommandInput = {
|
||||||
StackName: baseStackName,
|
StackName: baseStackName,
|
||||||
TemplateBody: baseStack,
|
TemplateBody: baseStack,
|
||||||
Parameters: parameters,
|
Parameters: parameters,
|
||||||
Capabilities: ['CAPABILITY_IAM'],
|
Capabilities: ['CAPABILITY_IAM'],
|
||||||
};
|
};
|
||||||
|
|
||||||
const stacks = await CF.listStacks({
|
const stacks = await CF.send(
|
||||||
StackStatusFilter: ['UPDATE_COMPLETE', 'CREATE_COMPLETE', 'ROLLBACK_COMPLETE'],
|
new ListStacksCommand({ StackStatusFilter: ['UPDATE_COMPLETE', 'CREATE_COMPLETE', 'ROLLBACK_COMPLETE'] }),
|
||||||
}).promise();
|
);
|
||||||
const stackNames = stacks.StackSummaries?.map((x) => x.StackName) || [];
|
const stackNames = stacks.StackSummaries?.map((x) => x.StackName) || [];
|
||||||
const stackExists: Boolean = stackNames.includes(baseStackName) || false;
|
const stackExists: Boolean = stackNames.includes(baseStackName) || false;
|
||||||
const describeStack = async () => {
|
const describeStack = async () => {
|
||||||
return await CF.describeStacks(describeStackInput).promise();
|
return await CF.send(new DescribeStacksCommand(describeStackInput));
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
if (!stackExists) {
|
if (!stackExists) {
|
||||||
CloudRunnerLogger.log(`${baseStackName} stack does not exist (${JSON.stringify(stackNames)})`);
|
CloudRunnerLogger.log(`${baseStackName} stack does not exist (${JSON.stringify(stackNames)})`);
|
||||||
await CF.createStack(createStackInput).promise();
|
await CF.send(new CreateStackCommand(createStackInput));
|
||||||
CloudRunnerLogger.log(`created stack (version: ${parametersHash})`);
|
CloudRunnerLogger.log(`created stack (version: ${parametersHash})`);
|
||||||
}
|
}
|
||||||
const CFState = await describeStack();
|
const CFState = await describeStack();
|
||||||
|
@ -65,7 +75,13 @@ export class AWSBaseStack {
|
||||||
const stackVersion = stack.Parameters?.find((x) => x.ParameterKey === 'Version')?.ParameterValue;
|
const stackVersion = stack.Parameters?.find((x) => x.ParameterKey === 'Version')?.ParameterValue;
|
||||||
|
|
||||||
if (stack.StackStatus === 'CREATE_IN_PROGRESS') {
|
if (stack.StackStatus === 'CREATE_IN_PROGRESS') {
|
||||||
await CF.waitFor('stackCreateComplete', describeStackInput).promise();
|
await waitUntilStackCreateComplete(
|
||||||
|
{
|
||||||
|
client: CF,
|
||||||
|
maxWaitTime: 200,
|
||||||
|
},
|
||||||
|
describeStackInput,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stackExists) {
|
if (stackExists) {
|
||||||
|
@ -73,7 +89,7 @@ export class AWSBaseStack {
|
||||||
if (parametersHash !== stackVersion) {
|
if (parametersHash !== stackVersion) {
|
||||||
CloudRunnerLogger.log(`Attempting update of base stack`);
|
CloudRunnerLogger.log(`Attempting update of base stack`);
|
||||||
try {
|
try {
|
||||||
await CF.updateStack(updateInput).promise();
|
await CF.send(new UpdateStackCommand(updateInput));
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
if (error['message'].includes('No updates are to be performed')) {
|
if (error['message'].includes('No updates are to be performed')) {
|
||||||
CloudRunnerLogger.log(`No updates are to be performed`);
|
CloudRunnerLogger.log(`No updates are to be performed`);
|
||||||
|
@ -93,7 +109,13 @@ export class AWSBaseStack {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (stack.StackStatus === 'UPDATE_IN_PROGRESS') {
|
if (stack.StackStatus === 'UPDATE_IN_PROGRESS') {
|
||||||
await CF.waitFor('stackUpdateComplete', describeStackInput).promise();
|
await waitUntilStackUpdateComplete(
|
||||||
|
{
|
||||||
|
client: CF,
|
||||||
|
maxWaitTime: 200,
|
||||||
|
},
|
||||||
|
describeStackInput,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CloudRunnerLogger.log('base stack is now ready');
|
CloudRunnerLogger.log('base stack is now ready');
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
import CloudRunnerLogger from '../../services/core/cloud-runner-logger';
|
import CloudRunnerLogger from '../../services/core/cloud-runner-logger';
|
||||||
import * as SDK from 'aws-sdk';
|
import { CloudFormation, DescribeStackEventsCommand } from '@aws-sdk/client-cloudformation';
|
||||||
import * as core from '@actions/core';
|
import * as core from '@actions/core';
|
||||||
import CloudRunner from '../../cloud-runner';
|
import CloudRunner from '../../cloud-runner';
|
||||||
|
|
||||||
export class AWSError {
|
export class AWSError {
|
||||||
static async handleStackCreationFailure(error: any, CF: SDK.CloudFormation, taskDefStackName: string) {
|
static async handleStackCreationFailure(error: any, CF: CloudFormation, taskDefStackName: string) {
|
||||||
CloudRunnerLogger.log('aws error: ');
|
CloudRunnerLogger.log('aws error: ');
|
||||||
core.error(JSON.stringify(error, undefined, 4));
|
core.error(JSON.stringify(error, undefined, 4));
|
||||||
if (CloudRunner.buildParameters.cloudRunnerDebug) {
|
if (CloudRunner.buildParameters.cloudRunnerDebug) {
|
||||||
CloudRunnerLogger.log('Getting events and resources for task stack');
|
CloudRunnerLogger.log('Getting events and resources for task stack');
|
||||||
const events = (await CF.describeStackEvents({ StackName: taskDefStackName }).promise()).StackEvents;
|
const events = (await CF.send(new DescribeStackEventsCommand({ StackName: taskDefStackName }))).StackEvents;
|
||||||
CloudRunnerLogger.log(JSON.stringify(events, undefined, 4));
|
CloudRunnerLogger.log(JSON.stringify(events, undefined, 4));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,12 @@
|
||||||
import * as SDK from 'aws-sdk';
|
import {
|
||||||
|
CloudFormation,
|
||||||
|
CreateStackCommand,
|
||||||
|
CreateStackCommandInput,
|
||||||
|
DescribeStackResourcesCommand,
|
||||||
|
DescribeStacksCommand,
|
||||||
|
ListStacksCommand,
|
||||||
|
waitUntilStackCreateComplete,
|
||||||
|
} from '@aws-sdk/client-cloudformation';
|
||||||
import CloudRunnerAWSTaskDef from './cloud-runner-aws-task-def';
|
import CloudRunnerAWSTaskDef from './cloud-runner-aws-task-def';
|
||||||
import CloudRunnerSecret from '../../options/cloud-runner-secret';
|
import CloudRunnerSecret from '../../options/cloud-runner-secret';
|
||||||
import { AWSCloudFormationTemplates } from './aws-cloud-formation-templates';
|
import { AWSCloudFormationTemplates } from './aws-cloud-formation-templates';
|
||||||
|
@ -16,7 +24,7 @@ export class AWSJobStack {
|
||||||
}
|
}
|
||||||
|
|
||||||
public async setupCloudFormations(
|
public async setupCloudFormations(
|
||||||
CF: SDK.CloudFormation,
|
CF: CloudFormation,
|
||||||
buildGuid: string,
|
buildGuid: string,
|
||||||
image: string,
|
image: string,
|
||||||
entrypoint: string[],
|
entrypoint: string[],
|
||||||
|
@ -119,7 +127,7 @@ export class AWSJobStack {
|
||||||
let previousStackExists = true;
|
let previousStackExists = true;
|
||||||
while (previousStackExists) {
|
while (previousStackExists) {
|
||||||
previousStackExists = false;
|
previousStackExists = false;
|
||||||
const stacks = await CF.listStacks().promise();
|
const stacks = await CF.send(new ListStacksCommand({}));
|
||||||
if (!stacks.StackSummaries) {
|
if (!stacks.StackSummaries) {
|
||||||
throw new Error('Faild to get stacks');
|
throw new Error('Faild to get stacks');
|
||||||
}
|
}
|
||||||
|
@ -132,7 +140,7 @@ export class AWSJobStack {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const createStackInput: SDK.CloudFormation.CreateStackInput = {
|
const createStackInput: CreateStackCommandInput = {
|
||||||
StackName: taskDefStackName,
|
StackName: taskDefStackName,
|
||||||
TemplateBody: taskDefCloudFormation,
|
TemplateBody: taskDefCloudFormation,
|
||||||
Capabilities: ['CAPABILITY_IAM'],
|
Capabilities: ['CAPABILITY_IAM'],
|
||||||
|
@ -140,9 +148,15 @@ export class AWSJobStack {
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
CloudRunnerLogger.log(`Creating job aws formation ${taskDefStackName}`);
|
CloudRunnerLogger.log(`Creating job aws formation ${taskDefStackName}`);
|
||||||
await CF.createStack(createStackInput).promise();
|
await CF.send(new CreateStackCommand(createStackInput));
|
||||||
await CF.waitFor('stackCreateComplete', { StackName: taskDefStackName }).promise();
|
await waitUntilStackCreateComplete(
|
||||||
const describeStack = await CF.describeStacks({ StackName: taskDefStackName }).promise();
|
{
|
||||||
|
client: CF,
|
||||||
|
maxWaitTime: 200,
|
||||||
|
},
|
||||||
|
{ StackName: taskDefStackName },
|
||||||
|
);
|
||||||
|
const describeStack = await CF.send(new DescribeStacksCommand({ StackName: taskDefStackName }));
|
||||||
for (const parameter of parameters) {
|
for (const parameter of parameters) {
|
||||||
if (!describeStack.Stacks?.[0].Parameters?.some((x) => x.ParameterKey === parameter.ParameterKey)) {
|
if (!describeStack.Stacks?.[0].Parameters?.some((x) => x.ParameterKey === parameter.ParameterKey)) {
|
||||||
throw new Error(`Parameter ${parameter.ParameterKey} not found in stack`);
|
throw new Error(`Parameter ${parameter.ParameterKey} not found in stack`);
|
||||||
|
@ -153,7 +167,7 @@ export class AWSJobStack {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
const createCleanupStackInput: SDK.CloudFormation.CreateStackInput = {
|
const createCleanupStackInput: CreateStackCommandInput = {
|
||||||
StackName: `${taskDefStackName}-cleanup`,
|
StackName: `${taskDefStackName}-cleanup`,
|
||||||
TemplateBody: CleanupCronFormation.formation,
|
TemplateBody: CleanupCronFormation.formation,
|
||||||
Capabilities: ['CAPABILITY_IAM'],
|
Capabilities: ['CAPABILITY_IAM'],
|
||||||
|
@ -183,7 +197,7 @@ export class AWSJobStack {
|
||||||
if (CloudRunnerOptions.useCleanupCron) {
|
if (CloudRunnerOptions.useCleanupCron) {
|
||||||
try {
|
try {
|
||||||
CloudRunnerLogger.log(`Creating job cleanup formation`);
|
CloudRunnerLogger.log(`Creating job cleanup formation`);
|
||||||
await CF.createStack(createCleanupStackInput).promise();
|
await CF.send(new CreateStackCommand(createCleanupStackInput));
|
||||||
|
|
||||||
// await CF.waitFor('stackCreateComplete', { StackName: createCleanupStackInput.StackName }).promise();
|
// await CF.waitFor('stackCreateComplete', { StackName: createCleanupStackInput.StackName }).promise();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -193,12 +207,15 @@ export class AWSJobStack {
|
||||||
}
|
}
|
||||||
|
|
||||||
const taskDefResources = (
|
const taskDefResources = (
|
||||||
await CF.describeStackResources({
|
await CF.send(
|
||||||
StackName: taskDefStackName,
|
new DescribeStackResourcesCommand({
|
||||||
}).promise()
|
StackName: taskDefStackName,
|
||||||
|
}),
|
||||||
|
)
|
||||||
).StackResources;
|
).StackResources;
|
||||||
|
|
||||||
const baseResources = (await CF.describeStackResources({ StackName: this.baseStackName }).promise()).StackResources;
|
const baseResources = (await CF.send(new DescribeStackResourcesCommand({ StackName: this.baseStackName })))
|
||||||
|
.StackResources;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
taskDefStackName,
|
taskDefStackName,
|
||||||
|
|
|
@ -1,4 +1,19 @@
|
||||||
import * as AWS from 'aws-sdk';
|
import {
|
||||||
|
DescribeTasksCommand,
|
||||||
|
ECS,
|
||||||
|
RunTaskCommand,
|
||||||
|
RunTaskCommandInput,
|
||||||
|
Task,
|
||||||
|
waitUntilTasksRunning,
|
||||||
|
} from '@aws-sdk/client-ecs';
|
||||||
|
import {
|
||||||
|
DescribeStreamCommand,
|
||||||
|
DescribeStreamCommandOutput,
|
||||||
|
GetRecordsCommand,
|
||||||
|
GetRecordsCommandOutput,
|
||||||
|
GetShardIteratorCommand,
|
||||||
|
Kinesis,
|
||||||
|
} from '@aws-sdk/client-kinesis';
|
||||||
import CloudRunnerEnvironmentVariable from '../../options/cloud-runner-environment-variable';
|
import CloudRunnerEnvironmentVariable from '../../options/cloud-runner-environment-variable';
|
||||||
import * as core from '@actions/core';
|
import * as core from '@actions/core';
|
||||||
import CloudRunnerAWSTaskDef from './cloud-runner-aws-task-def';
|
import CloudRunnerAWSTaskDef from './cloud-runner-aws-task-def';
|
||||||
|
@ -12,8 +27,8 @@ import CloudRunnerOptions from '../../options/cloud-runner-options';
|
||||||
import GitHub from '../../../github';
|
import GitHub from '../../../github';
|
||||||
|
|
||||||
class AWSTaskRunner {
|
class AWSTaskRunner {
|
||||||
public static ECS: AWS.ECS;
|
public static ECS: ECS;
|
||||||
public static Kinesis: AWS.Kinesis;
|
public static Kinesis: Kinesis;
|
||||||
private static readonly encodedUnderscore = `$252F`;
|
private static readonly encodedUnderscore = `$252F`;
|
||||||
static async runTask(
|
static async runTask(
|
||||||
taskDef: CloudRunnerAWSTaskDef,
|
taskDef: CloudRunnerAWSTaskDef,
|
||||||
|
@ -60,7 +75,7 @@ class AWSTaskRunner {
|
||||||
throw new Error(`Container Overrides length must be at most 8192`);
|
throw new Error(`Container Overrides length must be at most 8192`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const task = await AWSTaskRunner.ECS.runTask(runParameters).promise();
|
const task = await AWSTaskRunner.ECS.send(new RunTaskCommand(runParameters as RunTaskCommandInput));
|
||||||
const taskArn = task.tasks?.[0].taskArn || '';
|
const taskArn = task.tasks?.[0].taskArn || '';
|
||||||
CloudRunnerLogger.log('Cloud runner job is starting');
|
CloudRunnerLogger.log('Cloud runner job is starting');
|
||||||
await AWSTaskRunner.waitUntilTaskRunning(taskArn, cluster);
|
await AWSTaskRunner.waitUntilTaskRunning(taskArn, cluster);
|
||||||
|
@ -108,7 +123,13 @@ class AWSTaskRunner {
|
||||||
|
|
||||||
private static async waitUntilTaskRunning(taskArn: string, cluster: string) {
|
private static async waitUntilTaskRunning(taskArn: string, cluster: string) {
|
||||||
try {
|
try {
|
||||||
await AWSTaskRunner.ECS.waitFor('tasksRunning', { tasks: [taskArn], cluster }).promise();
|
await waitUntilTasksRunning(
|
||||||
|
{
|
||||||
|
client: AWSTaskRunner.ECS,
|
||||||
|
maxWaitTime: 120,
|
||||||
|
},
|
||||||
|
{ tasks: [taskArn], cluster },
|
||||||
|
);
|
||||||
} catch (error_) {
|
} catch (error_) {
|
||||||
const error = error_ as Error;
|
const error = error_ as Error;
|
||||||
await new Promise((resolve) => setTimeout(resolve, 3000));
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
||||||
|
@ -124,10 +145,7 @@ class AWSTaskRunner {
|
||||||
}
|
}
|
||||||
|
|
||||||
static async describeTasks(clusterName: string, taskArn: string) {
|
static async describeTasks(clusterName: string, taskArn: string) {
|
||||||
const tasks = await AWSTaskRunner.ECS.describeTasks({
|
const tasks = await AWSTaskRunner.ECS.send(new DescribeTasksCommand({ cluster: clusterName, tasks: [taskArn] }));
|
||||||
cluster: clusterName,
|
|
||||||
tasks: [taskArn],
|
|
||||||
}).promise();
|
|
||||||
if (tasks.tasks?.[0]) {
|
if (tasks.tasks?.[0]) {
|
||||||
return tasks.tasks?.[0];
|
return tasks.tasks?.[0];
|
||||||
} else {
|
} else {
|
||||||
|
@ -169,9 +187,7 @@ class AWSTaskRunner {
|
||||||
output: string,
|
output: string,
|
||||||
shouldCleanup: boolean,
|
shouldCleanup: boolean,
|
||||||
) {
|
) {
|
||||||
const records = await AWSTaskRunner.Kinesis.getRecords({
|
const records = await AWSTaskRunner.Kinesis.send(new GetRecordsCommand({ ShardIterator: iterator }));
|
||||||
ShardIterator: iterator,
|
|
||||||
}).promise();
|
|
||||||
iterator = records.NextShardIterator || '';
|
iterator = records.NextShardIterator || '';
|
||||||
({ shouldReadLogs, output, shouldCleanup } = AWSTaskRunner.logRecords(
|
({ shouldReadLogs, output, shouldCleanup } = AWSTaskRunner.logRecords(
|
||||||
records,
|
records,
|
||||||
|
@ -184,7 +200,7 @@ class AWSTaskRunner {
|
||||||
return { iterator, shouldReadLogs, output, shouldCleanup };
|
return { iterator, shouldReadLogs, output, shouldCleanup };
|
||||||
}
|
}
|
||||||
|
|
||||||
private static checkStreamingShouldContinue(taskData: AWS.ECS.Task, timestamp: number, shouldReadLogs: boolean) {
|
private static checkStreamingShouldContinue(taskData: Task, timestamp: number, shouldReadLogs: boolean) {
|
||||||
if (taskData?.lastStatus === 'UNKNOWN') {
|
if (taskData?.lastStatus === 'UNKNOWN') {
|
||||||
CloudRunnerLogger.log('## Cloud runner job unknwon');
|
CloudRunnerLogger.log('## Cloud runner job unknwon');
|
||||||
}
|
}
|
||||||
|
@ -204,15 +220,17 @@ class AWSTaskRunner {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static logRecords(
|
private static logRecords(
|
||||||
records: AWS.Kinesis.GetRecordsOutput,
|
records: GetRecordsCommandOutput,
|
||||||
iterator: string,
|
iterator: string,
|
||||||
shouldReadLogs: boolean,
|
shouldReadLogs: boolean,
|
||||||
output: string,
|
output: string,
|
||||||
shouldCleanup: boolean,
|
shouldCleanup: boolean,
|
||||||
) {
|
) {
|
||||||
if (records.Records.length > 0 && iterator) {
|
if ((records.Records ?? []).length > 0 && iterator) {
|
||||||
for (const record of records.Records) {
|
for (const record of records.Records ?? []) {
|
||||||
const json = JSON.parse(zlib.gunzipSync(Buffer.from(record.Data as string, 'base64')).toString('utf8'));
|
const json = JSON.parse(
|
||||||
|
zlib.gunzipSync(Buffer.from(record.Data as unknown as string, 'base64')).toString('utf8'),
|
||||||
|
);
|
||||||
if (json.messageType === 'DATA_MESSAGE') {
|
if (json.messageType === 'DATA_MESSAGE') {
|
||||||
for (const logEvent of json.logEvents) {
|
for (const logEvent of json.logEvents) {
|
||||||
({ shouldReadLogs, shouldCleanup, output } = FollowLogStreamService.handleIteration(
|
({ shouldReadLogs, shouldCleanup, output } = FollowLogStreamService.handleIteration(
|
||||||
|
@ -230,19 +248,19 @@ class AWSTaskRunner {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async getLogStream(kinesisStreamName: string) {
|
private static async getLogStream(kinesisStreamName: string) {
|
||||||
return await AWSTaskRunner.Kinesis.describeStream({
|
return await AWSTaskRunner.Kinesis.send(new DescribeStreamCommand({ StreamName: kinesisStreamName }));
|
||||||
StreamName: kinesisStreamName,
|
|
||||||
}).promise();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async getLogIterator(stream: AWS.Kinesis.DescribeStreamOutput) {
|
private static async getLogIterator(stream: DescribeStreamCommandOutput) {
|
||||||
return (
|
return (
|
||||||
(
|
(
|
||||||
await AWSTaskRunner.Kinesis.getShardIterator({
|
await AWSTaskRunner.Kinesis.send(
|
||||||
ShardIteratorType: 'TRIM_HORIZON',
|
new GetShardIteratorCommand({
|
||||||
StreamName: stream.StreamDescription.StreamName,
|
ShardIteratorType: 'TRIM_HORIZON',
|
||||||
ShardId: stream.StreamDescription.Shards[0].ShardId,
|
StreamName: stream.StreamDescription?.StreamName ?? '',
|
||||||
}).promise()
|
ShardId: stream.StreamDescription?.Shards?.[0]?.ShardId || '',
|
||||||
|
}),
|
||||||
|
)
|
||||||
).ShardIterator || ''
|
).ShardIterator || ''
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import * as AWS from 'aws-sdk';
|
import { StackResource } from '@aws-sdk/client-cloudformation';
|
||||||
|
|
||||||
class CloudRunnerAWSTaskDef {
|
class CloudRunnerAWSTaskDef {
|
||||||
public taskDefStackName!: string;
|
public taskDefStackName!: string;
|
||||||
public taskDefCloudFormation!: string;
|
public taskDefCloudFormation!: string;
|
||||||
public taskDefResources: AWS.CloudFormation.StackResources | undefined;
|
public taskDefResources: StackResource[] | undefined;
|
||||||
public baseResources: AWS.CloudFormation.StackResources | undefined;
|
public baseResources: StackResource[] | undefined;
|
||||||
}
|
}
|
||||||
export default CloudRunnerAWSTaskDef;
|
export default CloudRunnerAWSTaskDef;
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
import * as SDK from 'aws-sdk';
|
import { CloudFormation, DeleteStackCommand, waitUntilStackDeleteComplete } from '@aws-sdk/client-cloudformation';
|
||||||
|
import { ECS as ECSClient } from '@aws-sdk/client-ecs';
|
||||||
|
import { Kinesis } from '@aws-sdk/client-kinesis';
|
||||||
import CloudRunnerSecret from '../../options/cloud-runner-secret';
|
import CloudRunnerSecret from '../../options/cloud-runner-secret';
|
||||||
import CloudRunnerEnvironmentVariable from '../../options/cloud-runner-environment-variable';
|
import CloudRunnerEnvironmentVariable from '../../options/cloud-runner-environment-variable';
|
||||||
import CloudRunnerAWSTaskDef from './cloud-runner-aws-task-def';
|
import CloudRunnerAWSTaskDef from './cloud-runner-aws-task-def';
|
||||||
|
@ -75,7 +77,7 @@ class AWSBuildEnvironment implements ProviderInterface {
|
||||||
defaultSecretsArray: { ParameterKey: string; EnvironmentVariable: string; ParameterValue: string }[],
|
defaultSecretsArray: { ParameterKey: string; EnvironmentVariable: string; ParameterValue: string }[],
|
||||||
) {
|
) {
|
||||||
process.env.AWS_REGION = Input.region;
|
process.env.AWS_REGION = Input.region;
|
||||||
const CF = new SDK.CloudFormation();
|
const CF = new CloudFormation({ region: Input.region });
|
||||||
await new AwsBaseStack(this.baseStackName).setupBaseStack(CF);
|
await new AwsBaseStack(this.baseStackName).setupBaseStack(CF);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,10 +91,10 @@ class AWSBuildEnvironment implements ProviderInterface {
|
||||||
secrets: CloudRunnerSecret[],
|
secrets: CloudRunnerSecret[],
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
process.env.AWS_REGION = Input.region;
|
process.env.AWS_REGION = Input.region;
|
||||||
const ECS = new SDK.ECS();
|
const ECS = new ECSClient({ region: Input.region });
|
||||||
const CF = new SDK.CloudFormation();
|
const CF = new CloudFormation({ region: Input.region });
|
||||||
AwsTaskRunner.ECS = ECS;
|
AwsTaskRunner.ECS = ECS;
|
||||||
AwsTaskRunner.Kinesis = new SDK.Kinesis();
|
AwsTaskRunner.Kinesis = new Kinesis({ region: Input.region });
|
||||||
CloudRunnerLogger.log(`AWS Region: ${CF.config.region}`);
|
CloudRunnerLogger.log(`AWS Region: ${CF.config.region}`);
|
||||||
const entrypoint = ['/bin/sh'];
|
const entrypoint = ['/bin/sh'];
|
||||||
const startTimeMs = Date.now();
|
const startTimeMs = Date.now();
|
||||||
|
@ -129,23 +131,31 @@ class AWSBuildEnvironment implements ProviderInterface {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async cleanupResources(CF: SDK.CloudFormation, taskDef: CloudRunnerAWSTaskDef) {
|
async cleanupResources(CF: CloudFormation, taskDef: CloudRunnerAWSTaskDef) {
|
||||||
CloudRunnerLogger.log('Cleanup starting');
|
CloudRunnerLogger.log('Cleanup starting');
|
||||||
await CF.deleteStack({
|
await CF.send(new DeleteStackCommand({ StackName: taskDef.taskDefStackName }));
|
||||||
StackName: taskDef.taskDefStackName,
|
|
||||||
}).promise();
|
|
||||||
if (CloudRunnerOptions.useCleanupCron) {
|
if (CloudRunnerOptions.useCleanupCron) {
|
||||||
await CF.deleteStack({
|
await CF.send(new DeleteStackCommand({ StackName: `${taskDef.taskDefStackName}-cleanup` }));
|
||||||
StackName: `${taskDef.taskDefStackName}-cleanup`,
|
|
||||||
}).promise();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await CF.waitFor('stackDeleteComplete', {
|
await waitUntilStackDeleteComplete(
|
||||||
StackName: taskDef.taskDefStackName,
|
{
|
||||||
}).promise();
|
client: CF,
|
||||||
await CF.waitFor('stackDeleteComplete', {
|
maxWaitTime: 200,
|
||||||
StackName: `${taskDef.taskDefStackName}-cleanup`,
|
},
|
||||||
}).promise();
|
{
|
||||||
|
StackName: taskDef.taskDefStackName,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
await waitUntilStackDeleteComplete(
|
||||||
|
{
|
||||||
|
client: CF,
|
||||||
|
maxWaitTime: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
StackName: `${taskDef.taskDefStackName}-cleanup`,
|
||||||
|
},
|
||||||
|
);
|
||||||
CloudRunnerLogger.log(`Deleted Stack: ${taskDef.taskDefStackName}`);
|
CloudRunnerLogger.log(`Deleted Stack: ${taskDef.taskDefStackName}`);
|
||||||
CloudRunnerLogger.log('Cleanup complete');
|
CloudRunnerLogger.log('Cleanup complete');
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,11 @@
|
||||||
import AWS from 'aws-sdk';
|
import {
|
||||||
|
CloudFormation,
|
||||||
|
DeleteStackCommand,
|
||||||
|
DeleteStackCommandInput,
|
||||||
|
DescribeStackResourcesCommand,
|
||||||
|
} from '@aws-sdk/client-cloudformation';
|
||||||
|
import { CloudWatchLogs, DeleteLogGroupCommand } from '@aws-sdk/client-cloudwatch-logs';
|
||||||
|
import { ECS, StopTaskCommand } from '@aws-sdk/client-ecs';
|
||||||
import Input from '../../../../input';
|
import Input from '../../../../input';
|
||||||
import CloudRunnerLogger from '../../../services/core/cloud-runner-logger';
|
import CloudRunnerLogger from '../../../services/core/cloud-runner-logger';
|
||||||
import { TaskService } from './task-service';
|
import { TaskService } from './task-service';
|
||||||
|
@ -12,9 +19,9 @@ export class GarbageCollectionService {
|
||||||
|
|
||||||
public static async cleanup(deleteResources = false, OneDayOlderOnly: boolean = false) {
|
public static async cleanup(deleteResources = false, OneDayOlderOnly: boolean = false) {
|
||||||
process.env.AWS_REGION = Input.region;
|
process.env.AWS_REGION = Input.region;
|
||||||
const CF = new AWS.CloudFormation();
|
const CF = new CloudFormation({ region: Input.region });
|
||||||
const ecs = new AWS.ECS();
|
const ecs = new ECS({ region: Input.region });
|
||||||
const cwl = new AWS.CloudWatchLogs();
|
const cwl = new CloudWatchLogs({ region: Input.region });
|
||||||
const taskDefinitionsInUse = new Array();
|
const taskDefinitionsInUse = new Array();
|
||||||
const tasks = await TaskService.getTasks();
|
const tasks = await TaskService.getTasks();
|
||||||
|
|
||||||
|
@ -23,14 +30,14 @@ export class GarbageCollectionService {
|
||||||
taskDefinitionsInUse.push(taskElement.taskDefinitionArn);
|
taskDefinitionsInUse.push(taskElement.taskDefinitionArn);
|
||||||
if (deleteResources && (!OneDayOlderOnly || GarbageCollectionService.isOlderThan1day(taskElement.createdAt!))) {
|
if (deleteResources && (!OneDayOlderOnly || GarbageCollectionService.isOlderThan1day(taskElement.createdAt!))) {
|
||||||
CloudRunnerLogger.log(`Stopping task ${taskElement.containers?.[0].name}`);
|
CloudRunnerLogger.log(`Stopping task ${taskElement.containers?.[0].name}`);
|
||||||
await ecs.stopTask({ task: taskElement.taskArn || '', cluster: element }).promise();
|
await ecs.send(new StopTaskCommand({ task: taskElement.taskArn || '', cluster: element }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const jobStacks = await TaskService.getCloudFormationJobStacks();
|
const jobStacks = await TaskService.getCloudFormationJobStacks();
|
||||||
for (const element of jobStacks) {
|
for (const element of jobStacks) {
|
||||||
if (
|
if (
|
||||||
(await CF.describeStackResources({ StackName: element.StackName }).promise()).StackResources?.some(
|
(await CF.send(new DescribeStackResourcesCommand({ StackName: element.StackName }))).StackResources?.some(
|
||||||
(x) => x.ResourceType === 'AWS::ECS::TaskDefinition' && taskDefinitionsInUse.includes(x.PhysicalResourceId),
|
(x) => x.ResourceType === 'AWS::ECS::TaskDefinition' && taskDefinitionsInUse.includes(x.PhysicalResourceId),
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
|
@ -39,7 +46,10 @@ export class GarbageCollectionService {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (deleteResources && (!OneDayOlderOnly || GarbageCollectionService.isOlderThan1day(element.CreationTime))) {
|
if (
|
||||||
|
deleteResources &&
|
||||||
|
(!OneDayOlderOnly || (element.CreationTime && GarbageCollectionService.isOlderThan1day(element.CreationTime)))
|
||||||
|
) {
|
||||||
if (element.StackName === 'game-ci' || element.TemplateDescription === 'Game-CI base stack') {
|
if (element.StackName === 'game-ci' || element.TemplateDescription === 'Game-CI base stack') {
|
||||||
CloudRunnerLogger.log(`Skipping ${element.StackName} ignore list`);
|
CloudRunnerLogger.log(`Skipping ${element.StackName} ignore list`);
|
||||||
|
|
||||||
|
@ -47,8 +57,8 @@ export class GarbageCollectionService {
|
||||||
}
|
}
|
||||||
|
|
||||||
CloudRunnerLogger.log(`Deleting ${element.StackName}`);
|
CloudRunnerLogger.log(`Deleting ${element.StackName}`);
|
||||||
const deleteStackInput: AWS.CloudFormation.DeleteStackInput = { StackName: element.StackName };
|
const deleteStackInput: DeleteStackCommandInput = { StackName: element.StackName };
|
||||||
await CF.deleteStack(deleteStackInput).promise();
|
await CF.send(new DeleteStackCommand(deleteStackInput));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const logGroups = await TaskService.getLogGroups();
|
const logGroups = await TaskService.getLogGroups();
|
||||||
|
@ -58,7 +68,7 @@ export class GarbageCollectionService {
|
||||||
(!OneDayOlderOnly || GarbageCollectionService.isOlderThan1day(new Date(element.creationTime!)))
|
(!OneDayOlderOnly || GarbageCollectionService.isOlderThan1day(new Date(element.creationTime!)))
|
||||||
) {
|
) {
|
||||||
CloudRunnerLogger.log(`Deleting ${element.logGroupName}`);
|
CloudRunnerLogger.log(`Deleting ${element.logGroupName}`);
|
||||||
await cwl.deleteLogGroup({ logGroupName: element.logGroupName || '' }).promise();
|
await cwl.send(new DeleteLogGroupCommand({ logGroupName: element.logGroupName || '' }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,31 @@
|
||||||
import AWS from 'aws-sdk';
|
import {
|
||||||
|
CloudFormation,
|
||||||
|
DescribeStackResourcesCommand,
|
||||||
|
DescribeStacksCommand,
|
||||||
|
ListStacksCommand,
|
||||||
|
StackSummary,
|
||||||
|
} from '@aws-sdk/client-cloudformation';
|
||||||
|
import {
|
||||||
|
CloudWatchLogs,
|
||||||
|
DescribeLogGroupsCommand,
|
||||||
|
DescribeLogGroupsCommandInput,
|
||||||
|
LogGroup,
|
||||||
|
} from '@aws-sdk/client-cloudwatch-logs';
|
||||||
|
import {
|
||||||
|
DescribeTasksCommand,
|
||||||
|
DescribeTasksCommandInput,
|
||||||
|
ECS,
|
||||||
|
ListClustersCommand,
|
||||||
|
ListTasksCommand,
|
||||||
|
ListTasksCommandInput,
|
||||||
|
Task,
|
||||||
|
} from '@aws-sdk/client-ecs';
|
||||||
|
import { ListObjectsCommand, ListObjectsCommandInput, S3 } from '@aws-sdk/client-s3';
|
||||||
import Input from '../../../../input';
|
import Input from '../../../../input';
|
||||||
import CloudRunnerLogger from '../../../services/core/cloud-runner-logger';
|
import CloudRunnerLogger from '../../../services/core/cloud-runner-logger';
|
||||||
import { BaseStackFormation } from '../cloud-formations/base-stack-formation';
|
import { BaseStackFormation } from '../cloud-formations/base-stack-formation';
|
||||||
import AwsTaskRunner from '../aws-task-runner';
|
import AwsTaskRunner from '../aws-task-runner';
|
||||||
import { ListObjectsRequest } from 'aws-sdk/clients/s3';
|
|
||||||
import CloudRunner from '../../../cloud-runner';
|
import CloudRunner from '../../../cloud-runner';
|
||||||
import { StackSummaries } from 'aws-sdk/clients/cloudformation';
|
|
||||||
import { LogGroups } from 'aws-sdk/clients/cloudwatchlogs';
|
|
||||||
|
|
||||||
export class TaskService {
|
export class TaskService {
|
||||||
static async watch() {
|
static async watch() {
|
||||||
|
@ -20,20 +39,24 @@ export class TaskService {
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
public static async getCloudFormationJobStacks() {
|
public static async getCloudFormationJobStacks() {
|
||||||
const result: StackSummaries = [];
|
const result: StackSummary[] = [];
|
||||||
CloudRunnerLogger.log(``);
|
CloudRunnerLogger.log(``);
|
||||||
CloudRunnerLogger.log(`List Cloud Formation Stacks`);
|
CloudRunnerLogger.log(`List Cloud Formation Stacks`);
|
||||||
process.env.AWS_REGION = Input.region;
|
process.env.AWS_REGION = Input.region;
|
||||||
const CF = new AWS.CloudFormation();
|
const CF = new CloudFormation({ region: Input.region });
|
||||||
const stacks =
|
const stacks =
|
||||||
(await CF.listStacks().promise()).StackSummaries?.filter(
|
(await CF.send(new ListStacksCommand({}))).StackSummaries?.filter(
|
||||||
(_x) =>
|
(_x) =>
|
||||||
_x.StackStatus !== 'DELETE_COMPLETE' && _x.TemplateDescription !== BaseStackFormation.baseStackDecription,
|
_x.StackStatus !== 'DELETE_COMPLETE' && _x.TemplateDescription !== BaseStackFormation.baseStackDecription,
|
||||||
) || [];
|
) || [];
|
||||||
CloudRunnerLogger.log(``);
|
CloudRunnerLogger.log(``);
|
||||||
CloudRunnerLogger.log(`Cloud Formation Stacks ${stacks.length}`);
|
CloudRunnerLogger.log(`Cloud Formation Stacks ${stacks.length}`);
|
||||||
for (const element of stacks) {
|
for (const element of stacks) {
|
||||||
const ageDate: Date = new Date(Date.now() - element.CreationTime.getTime());
|
if (!element.CreationTime) {
|
||||||
|
CloudRunnerLogger.log(`${element.StackName} due to undefined CreationTime`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ageDate: Date = new Date(Date.now() - (element.CreationTime?.getTime() ?? 0));
|
||||||
|
|
||||||
CloudRunnerLogger.log(
|
CloudRunnerLogger.log(
|
||||||
`Task Stack ${element.StackName} - Age D${Math.floor(
|
`Task Stack ${element.StackName} - Age D${Math.floor(
|
||||||
|
@ -43,14 +66,18 @@ export class TaskService {
|
||||||
result.push(element);
|
result.push(element);
|
||||||
}
|
}
|
||||||
const baseStacks =
|
const baseStacks =
|
||||||
(await CF.listStacks().promise()).StackSummaries?.filter(
|
(await CF.send(new ListStacksCommand({}))).StackSummaries?.filter(
|
||||||
(_x) =>
|
(_x) =>
|
||||||
_x.StackStatus !== 'DELETE_COMPLETE' && _x.TemplateDescription === BaseStackFormation.baseStackDecription,
|
_x.StackStatus !== 'DELETE_COMPLETE' && _x.TemplateDescription === BaseStackFormation.baseStackDecription,
|
||||||
) || [];
|
) || [];
|
||||||
CloudRunnerLogger.log(``);
|
CloudRunnerLogger.log(``);
|
||||||
CloudRunnerLogger.log(`Base Stacks ${baseStacks.length}`);
|
CloudRunnerLogger.log(`Base Stacks ${baseStacks.length}`);
|
||||||
for (const element of baseStacks) {
|
for (const element of baseStacks) {
|
||||||
const ageDate: Date = new Date(Date.now() - element.CreationTime.getTime());
|
if (!element.CreationTime) {
|
||||||
|
CloudRunnerLogger.log(`${element.StackName} due to undefined CreationTime`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ageDate: Date = new Date(Date.now() - (element.CreationTime?.getTime() ?? 0));
|
||||||
|
|
||||||
CloudRunnerLogger.log(
|
CloudRunnerLogger.log(
|
||||||
`Task Stack ${element.StackName} - Age D${Math.floor(
|
`Task Stack ${element.StackName} - Age D${Math.floor(
|
||||||
|
@ -64,22 +91,22 @@ export class TaskService {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
public static async getTasks() {
|
public static async getTasks() {
|
||||||
const result: { taskElement: AWS.ECS.Task; element: string }[] = [];
|
const result: { taskElement: Task; element: string }[] = [];
|
||||||
CloudRunnerLogger.log(``);
|
CloudRunnerLogger.log(``);
|
||||||
CloudRunnerLogger.log(`List Tasks`);
|
CloudRunnerLogger.log(`List Tasks`);
|
||||||
process.env.AWS_REGION = Input.region;
|
process.env.AWS_REGION = Input.region;
|
||||||
const ecs = new AWS.ECS();
|
const ecs = new ECS({ region: Input.region });
|
||||||
const clusters = (await ecs.listClusters().promise()).clusterArns || [];
|
const clusters = (await ecs.send(new ListClustersCommand({}))).clusterArns || [];
|
||||||
CloudRunnerLogger.log(`Task Clusters ${clusters.length}`);
|
CloudRunnerLogger.log(`Task Clusters ${clusters.length}`);
|
||||||
for (const element of clusters) {
|
for (const element of clusters) {
|
||||||
const input: AWS.ECS.ListTasksRequest = {
|
const input: ListTasksCommandInput = {
|
||||||
cluster: element,
|
cluster: element,
|
||||||
};
|
};
|
||||||
|
|
||||||
const list = (await ecs.listTasks(input).promise()).taskArns || [];
|
const list = (await ecs.send(new ListTasksCommand(input))).taskArns || [];
|
||||||
if (list.length > 0) {
|
if (list.length > 0) {
|
||||||
const describeInput: AWS.ECS.DescribeTasksRequest = { tasks: list, cluster: element };
|
const describeInput: DescribeTasksCommandInput = { tasks: list, cluster: element };
|
||||||
const describeList = (await ecs.describeTasks(describeInput).promise()).tasks || [];
|
const describeList = (await ecs.send(new DescribeTasksCommand(describeInput))).tasks || [];
|
||||||
if (describeList.length === 0) {
|
if (describeList.length === 0) {
|
||||||
CloudRunnerLogger.log(`No Tasks`);
|
CloudRunnerLogger.log(`No Tasks`);
|
||||||
continue;
|
continue;
|
||||||
|
@ -105,37 +132,48 @@ export class TaskService {
|
||||||
}
|
}
|
||||||
public static async awsDescribeJob(job: string) {
|
public static async awsDescribeJob(job: string) {
|
||||||
process.env.AWS_REGION = Input.region;
|
process.env.AWS_REGION = Input.region;
|
||||||
const CF = new AWS.CloudFormation();
|
const CF = new CloudFormation({ region: Input.region });
|
||||||
const stack = (await CF.listStacks().promise()).StackSummaries?.find((_x) => _x.StackName === job) || undefined;
|
try {
|
||||||
const stackInfo = (await CF.describeStackResources({ StackName: job }).promise()) || undefined;
|
const stack =
|
||||||
const stackInfo2 = (await CF.describeStacks({ StackName: job }).promise()) || undefined;
|
(await CF.send(new ListStacksCommand({}))).StackSummaries?.find((_x) => _x.StackName === job) || undefined;
|
||||||
if (stack === undefined) {
|
const stackInfo = (await CF.send(new DescribeStackResourcesCommand({ StackName: job }))) || undefined;
|
||||||
throw new Error('stack not defined');
|
const stackInfo2 = (await CF.send(new DescribeStacksCommand({ StackName: job }))) || undefined;
|
||||||
}
|
if (stack === undefined) {
|
||||||
const ageDate: Date = new Date(Date.now() - stack.CreationTime.getTime());
|
throw new Error('stack not defined');
|
||||||
const message = `
|
}
|
||||||
|
if (!stack.CreationTime) {
|
||||||
|
CloudRunnerLogger.log(`${stack.StackName} due to undefined CreationTime`);
|
||||||
|
}
|
||||||
|
const ageDate: Date = new Date(Date.now() - (stack.CreationTime?.getTime() ?? 0));
|
||||||
|
const message = `
|
||||||
Task Stack ${stack.StackName}
|
Task Stack ${stack.StackName}
|
||||||
Age D${Math.floor(ageDate.getHours() / 24)} H${ageDate.getHours()} M${ageDate.getMinutes()}
|
Age D${Math.floor(ageDate.getHours() / 24)} H${ageDate.getHours()} M${ageDate.getMinutes()}
|
||||||
${JSON.stringify(stack, undefined, 4)}
|
${JSON.stringify(stack, undefined, 4)}
|
||||||
${JSON.stringify(stackInfo, undefined, 4)}
|
${JSON.stringify(stackInfo, undefined, 4)}
|
||||||
${JSON.stringify(stackInfo2, undefined, 4)}
|
${JSON.stringify(stackInfo2, undefined, 4)}
|
||||||
`;
|
`;
|
||||||
CloudRunnerLogger.log(message);
|
CloudRunnerLogger.log(message);
|
||||||
|
|
||||||
return message;
|
return message;
|
||||||
|
} catch (error) {
|
||||||
|
CloudRunnerLogger.error(
|
||||||
|
`Failed to describe job ${job}: ${error instanceof Error ? error.message : String(error)}`,
|
||||||
|
);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
public static async getLogGroups() {
|
public static async getLogGroups() {
|
||||||
const result: LogGroups = [];
|
const result: Array<LogGroup> = [];
|
||||||
process.env.AWS_REGION = Input.region;
|
process.env.AWS_REGION = Input.region;
|
||||||
const ecs = new AWS.CloudWatchLogs();
|
const ecs = new CloudWatchLogs();
|
||||||
let logStreamInput: AWS.CloudWatchLogs.DescribeLogGroupsRequest = {
|
let logStreamInput: DescribeLogGroupsCommandInput = {
|
||||||
/* logGroupNamePrefix: 'game-ci' */
|
/* logGroupNamePrefix: 'game-ci' */
|
||||||
};
|
};
|
||||||
let logGroupsDescribe = await ecs.describeLogGroups(logStreamInput).promise();
|
let logGroupsDescribe = await ecs.send(new DescribeLogGroupsCommand(logStreamInput));
|
||||||
const logGroups = logGroupsDescribe.logGroups || [];
|
const logGroups = logGroupsDescribe.logGroups || [];
|
||||||
while (logGroupsDescribe.nextToken) {
|
while (logGroupsDescribe.nextToken) {
|
||||||
logStreamInput = { /* logGroupNamePrefix: 'game-ci',*/ nextToken: logGroupsDescribe.nextToken };
|
logStreamInput = { /* logGroupNamePrefix: 'game-ci',*/ nextToken: logGroupsDescribe.nextToken };
|
||||||
logGroupsDescribe = await ecs.describeLogGroups(logStreamInput).promise();
|
logGroupsDescribe = await ecs.send(new DescribeLogGroupsCommand(logStreamInput));
|
||||||
logGroups.push(...(logGroupsDescribe?.logGroups || []));
|
logGroups.push(...(logGroupsDescribe?.logGroups || []));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,11 +197,12 @@ export class TaskService {
|
||||||
}
|
}
|
||||||
public static async getLocks() {
|
public static async getLocks() {
|
||||||
process.env.AWS_REGION = Input.region;
|
process.env.AWS_REGION = Input.region;
|
||||||
const s3 = new AWS.S3();
|
const s3 = new S3({ region: Input.region });
|
||||||
const listRequest: ListObjectsRequest = {
|
const listRequest: ListObjectsCommandInput = {
|
||||||
Bucket: CloudRunner.buildParameters.awsStackName,
|
Bucket: CloudRunner.buildParameters.awsStackName,
|
||||||
};
|
};
|
||||||
const results = await s3.listObjects(listRequest).promise();
|
|
||||||
|
const results = await s3.send(new ListObjectsCommand(listRequest));
|
||||||
|
|
||||||
return results.Contents || [];
|
return results.Contents || [];
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue