Refactor state and steps out of cloud-runner main class
parent
c664a315c1
commit
c121a1f2f6
|
|
@ -312,7 +312,7 @@ exports.default = Cache;
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
|
|
||||||
/***/ 81683:
|
/***/ 28730:
|
||||||
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
|
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
@ -349,203 +349,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||||
};
|
};
|
||||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||||
const SDK = __importStar(__webpack_require__(71786));
|
exports.AWSBaseStack = void 0;
|
||||||
const nanoid_1 = __webpack_require__(39140);
|
|
||||||
const fs = __importStar(__webpack_require__(35747));
|
|
||||||
const core = __importStar(__webpack_require__(42186));
|
|
||||||
const cloud_runner_constants_1 = __importDefault(__webpack_require__(82394));
|
|
||||||
const aws_build_runner_1 = __importDefault(__webpack_require__(29102));
|
|
||||||
const cloud_runner_logger_1 = __importDefault(__webpack_require__(17375));
|
const cloud_runner_logger_1 = __importDefault(__webpack_require__(17375));
|
||||||
|
const core = __importStar(__webpack_require__(42186));
|
||||||
|
const fs = __importStar(__webpack_require__(35747));
|
||||||
const crypto = __webpack_require__(33373);
|
const crypto = __webpack_require__(33373);
|
||||||
class AWSBuildEnvironment {
|
class AWSBaseStack {
|
||||||
constructor(buildParameters) {
|
constructor(baseStackName) {
|
||||||
this.baseStackName = buildParameters.awsBaseStackName;
|
this.baseStackName = baseStackName;
|
||||||
}
|
|
||||||
cleanupSharedBuildResources(
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
buildGuid,
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
buildParameters,
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
branchName,
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
defaultSecretsArray) { }
|
|
||||||
setupSharedBuildResources(
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
buildGuid,
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
buildParameters,
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
branchName,
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
defaultSecretsArray) { }
|
|
||||||
runBuildTask(buildId, image, commands, mountdir, workingdir, environment, secrets) {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
const ECS = new SDK.ECS();
|
|
||||||
const CF = new SDK.CloudFormation();
|
|
||||||
const entrypoint = ['/bin/sh'];
|
|
||||||
const t0 = Date.now();
|
|
||||||
const taskDef = yield this.setupCloudFormations(CF, buildId, image, entrypoint, commands, mountdir, workingdir, secrets);
|
|
||||||
let t2;
|
|
||||||
try {
|
|
||||||
const t1 = Date.now();
|
|
||||||
cloud_runner_logger_1.default.log(`Setup job time: ${Math.floor((t1 - t0) / 1000)}s`);
|
|
||||||
yield aws_build_runner_1.default.runTask(taskDef, ECS, CF, environment, buildId, commands);
|
|
||||||
t2 = Date.now();
|
|
||||||
cloud_runner_logger_1.default.log(`Run job time: ${Math.floor((t2 - t1) / 1000)}s`);
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
yield this.cleanupResources(CF, taskDef);
|
|
||||||
const t3 = Date.now();
|
|
||||||
if (t2 !== undefined)
|
|
||||||
cloud_runner_logger_1.default.log(`Cleanup job time: ${Math.floor((t3 - t2) / 1000)}s`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
getParameterTemplate(p1) {
|
|
||||||
return `
|
|
||||||
${p1}:
|
|
||||||
Type: String
|
|
||||||
Default: ''
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
getSecretTemplate(p1) {
|
|
||||||
return `
|
|
||||||
${p1}Secret:
|
|
||||||
Type: AWS::SecretsManager::Secret
|
|
||||||
Properties:
|
|
||||||
Name: !Join [ "", [ '${p1}', !Ref BUILDID ] ]
|
|
||||||
SecretString: !Ref ${p1}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
getSecretDefinitionTemplate(p1, p2) {
|
|
||||||
return `
|
|
||||||
- Name: '${p1}'
|
|
||||||
ValueFrom: !Ref ${p2}Secret
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
insertAtTemplate(template, insertionKey, insertion) {
|
|
||||||
const index = template.search(insertionKey) + insertionKey.length + '\n'.length;
|
|
||||||
template = [template.slice(0, index), insertion, template.slice(index)].join('');
|
|
||||||
return template;
|
|
||||||
}
|
|
||||||
setupCloudFormations(CF, buildGuid, image, entrypoint, commands, mountdir, workingdir, secrets) {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
const logGuid = nanoid_1.customAlphabet(cloud_runner_constants_1.default.alphabet, 9)();
|
|
||||||
commands[1] += `
|
|
||||||
echo "${logGuid}"
|
|
||||||
`;
|
|
||||||
yield this.setupBaseStack(CF);
|
|
||||||
const taskDefStackName = `${this.baseStackName}-${buildGuid}`;
|
|
||||||
let taskDefCloudFormation = this.readTaskCloudFormationTemplate();
|
|
||||||
const cleanupTaskDefStackName = `${taskDefStackName}-cleanup`;
|
|
||||||
const cleanupCloudFormation = fs.readFileSync(`${__dirname}/cloud-formations/cloudformation-stack-ttl.yml`, 'utf8');
|
|
||||||
for (const secret of secrets) {
|
|
||||||
if (typeof secret.ParameterValue == 'number') {
|
|
||||||
secret.ParameterValue = `${secret.ParameterValue}`;
|
|
||||||
}
|
|
||||||
if (!secret.ParameterValue || secret.ParameterValue === '') {
|
|
||||||
secrets = secrets.filter((x) => x !== secret);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
taskDefCloudFormation = this.insertAtTemplate(taskDefCloudFormation, 'p1 - input', this.getParameterTemplate(secret.ParameterKey.replace(/[^\dA-Za-z]/g, '')));
|
|
||||||
taskDefCloudFormation = this.insertAtTemplate(taskDefCloudFormation, 'p2 - secret', this.getSecretTemplate(secret.ParameterKey.replace(/[^\dA-Za-z]/g, '')));
|
|
||||||
taskDefCloudFormation = this.insertAtTemplate(taskDefCloudFormation, 'p3 - container def', this.getSecretDefinitionTemplate(secret.EnvironmentVariable, secret.ParameterKey.replace(/[^\dA-Za-z]/g, '')));
|
|
||||||
}
|
|
||||||
const secretsMappedToCloudFormationParameters = secrets.map((x) => {
|
|
||||||
return { ParameterKey: x.ParameterKey.replace(/[^\dA-Za-z]/g, ''), ParameterValue: x.ParameterValue };
|
|
||||||
});
|
|
||||||
const parameters = [
|
|
||||||
{
|
|
||||||
ParameterKey: 'EnvironmentName',
|
|
||||||
ParameterValue: this.baseStackName,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ParameterKey: 'ImageUrl',
|
|
||||||
ParameterValue: image,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ParameterKey: 'ServiceName',
|
|
||||||
ParameterValue: taskDefStackName,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ParameterKey: 'Command',
|
|
||||||
ParameterValue: 'echo "this template should be overwritten when running a task"',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ParameterKey: 'EntryPoint',
|
|
||||||
ParameterValue: entrypoint.join(','),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ParameterKey: 'WorkingDirectory',
|
|
||||||
ParameterValue: workingdir,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ParameterKey: 'EFSMountDirectory',
|
|
||||||
ParameterValue: mountdir,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ParameterKey: 'BUILDID',
|
|
||||||
ParameterValue: buildGuid,
|
|
||||||
},
|
|
||||||
...secretsMappedToCloudFormationParameters,
|
|
||||||
];
|
|
||||||
try {
|
|
||||||
yield CF.createStack({
|
|
||||||
StackName: taskDefStackName,
|
|
||||||
TemplateBody: taskDefCloudFormation,
|
|
||||||
Capabilities: ['CAPABILITY_IAM'],
|
|
||||||
Parameters: parameters,
|
|
||||||
}).promise();
|
|
||||||
cloud_runner_logger_1.default.log('Creating cloud runner job');
|
|
||||||
yield CF.createStack({
|
|
||||||
StackName: cleanupTaskDefStackName,
|
|
||||||
TemplateBody: cleanupCloudFormation,
|
|
||||||
Capabilities: ['CAPABILITY_IAM'],
|
|
||||||
Parameters: [
|
|
||||||
{
|
|
||||||
ParameterKey: 'StackName',
|
|
||||||
ParameterValue: taskDefStackName,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ParameterKey: 'DeleteStackName',
|
|
||||||
ParameterValue: cleanupTaskDefStackName,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ParameterKey: 'TTL',
|
|
||||||
ParameterValue: '100',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ParameterKey: 'BUILDID',
|
|
||||||
ParameterValue: buildGuid,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ParameterKey: 'EnvironmentName',
|
|
||||||
ParameterValue: this.baseStackName,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}).promise();
|
|
||||||
yield CF.waitFor('stackCreateComplete', { StackName: taskDefStackName }).promise();
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
yield this.handleStackCreationFailure(error, CF, taskDefStackName, taskDefCloudFormation, parameters, secrets);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
const taskDefResources = (yield CF.describeStackResources({
|
|
||||||
StackName: taskDefStackName,
|
|
||||||
}).promise()).StackResources;
|
|
||||||
const baseResources = (yield CF.describeStackResources({ StackName: this.baseStackName }).promise()).StackResources;
|
|
||||||
// TODO: offer a parameter to decide if you want the guaranteed shutdown or fastest startup time possible
|
|
||||||
return {
|
|
||||||
taskDefStackName,
|
|
||||||
taskDefCloudFormation,
|
|
||||||
taskDefStackNameTTL: cleanupTaskDefStackName,
|
|
||||||
ttlCloudFormation: cleanupCloudFormation,
|
|
||||||
taskDefResources,
|
|
||||||
baseResources,
|
|
||||||
logid: logGuid,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
setupBaseStack(CF) {
|
setupBaseStack(CF) {
|
||||||
var _a, _b, _c, _d, _e;
|
var _a, _b, _c, _d, _e;
|
||||||
|
|
@ -625,25 +436,97 @@ class AWSBuildEnvironment {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
handleStackCreationFailure(error, CF, taskDefStackName, taskDefCloudFormation, parameters, secrets) {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
cloud_runner_logger_1.default.log('aws stack parameters: ');
|
|
||||||
cloud_runner_logger_1.default.log(JSON.stringify(parameters, undefined, 4));
|
|
||||||
cloud_runner_logger_1.default.log('aws stack secrets: ');
|
|
||||||
cloud_runner_logger_1.default.log(JSON.stringify(secrets, undefined, 4));
|
|
||||||
cloud_runner_logger_1.default.log('aws stack: ');
|
|
||||||
cloud_runner_logger_1.default.log(taskDefCloudFormation);
|
|
||||||
cloud_runner_logger_1.default.log('aws error: ');
|
|
||||||
core.error(error);
|
|
||||||
cloud_runner_logger_1.default.log('Getting events and resources for task stack');
|
|
||||||
const events = (yield CF.describeStackEvents({ StackName: taskDefStackName }).promise()).StackEvents;
|
|
||||||
const resources = (yield CF.describeStackResources({ StackName: taskDefStackName }).promise()).StackResources;
|
|
||||||
cloud_runner_logger_1.default.log(JSON.stringify(events, undefined, 4));
|
|
||||||
cloud_runner_logger_1.default.log(JSON.stringify(resources, undefined, 4));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
readTaskCloudFormationTemplate() {
|
exports.AWSBaseStack = AWSBaseStack;
|
||||||
return fs.readFileSync(`${__dirname}/cloud-formations/task-def-formation.yml`, 'utf8');
|
|
||||||
|
|
||||||
|
/***/ }),
|
||||||
|
|
||||||
|
/***/ 81683:
|
||||||
|
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||||
|
if (k2 === undefined) k2 = k;
|
||||||
|
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
||||||
|
}) : (function(o, m, k, k2) {
|
||||||
|
if (k2 === undefined) k2 = k;
|
||||||
|
o[k2] = m[k];
|
||||||
|
}));
|
||||||
|
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||||
|
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||||
|
}) : function(o, v) {
|
||||||
|
o["default"] = v;
|
||||||
|
});
|
||||||
|
var __importStar = (this && this.__importStar) || function (mod) {
|
||||||
|
if (mod && mod.__esModule) return mod;
|
||||||
|
var result = {};
|
||||||
|
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||||
|
__setModuleDefault(result, mod);
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||||
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||||
|
return new (P || (P = Promise))(function (resolve, reject) {
|
||||||
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||||
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||||
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||||
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||||
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||||
|
const SDK = __importStar(__webpack_require__(71786));
|
||||||
|
const aws_build_runner_1 = __importDefault(__webpack_require__(29102));
|
||||||
|
const cloud_runner_logger_1 = __importDefault(__webpack_require__(17375));
|
||||||
|
const aws_job_stack_1 = __webpack_require__(85819);
|
||||||
|
class AWSBuildEnvironment {
|
||||||
|
constructor(buildParameters) {
|
||||||
|
this.baseStackName = buildParameters.awsBaseStackName;
|
||||||
|
}
|
||||||
|
cleanupSharedBuildResources(
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
buildGuid,
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
buildParameters,
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
branchName,
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
defaultSecretsArray) { }
|
||||||
|
setupSharedBuildResources(
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
buildGuid,
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
buildParameters,
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
branchName,
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
defaultSecretsArray) { }
|
||||||
|
runBuildTask(buildId, image, commands, mountdir, workingdir, environment, secrets) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
const ECS = new SDK.ECS();
|
||||||
|
const CF = new SDK.CloudFormation();
|
||||||
|
const entrypoint = ['/bin/sh'];
|
||||||
|
const t0 = Date.now();
|
||||||
|
const taskDef = yield new aws_job_stack_1.AWSJobStack(this.baseStackName).setupCloudFormations(CF, buildId, image, entrypoint, commands, mountdir, workingdir, secrets);
|
||||||
|
let t2;
|
||||||
|
try {
|
||||||
|
const t1 = Date.now();
|
||||||
|
cloud_runner_logger_1.default.log(`Setup job time: ${Math.floor((t1 - t0) / 1000)}s`);
|
||||||
|
yield aws_build_runner_1.default.runTask(taskDef, ECS, CF, environment, buildId, commands);
|
||||||
|
t2 = Date.now();
|
||||||
|
cloud_runner_logger_1.default.log(`Run job time: ${Math.floor((t2 - t1) / 1000)}s`);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
yield this.cleanupResources(CF, taskDef);
|
||||||
|
const t3 = Date.now();
|
||||||
|
if (t2 !== undefined)
|
||||||
|
cloud_runner_logger_1.default.log(`Cleanup job time: ${Math.floor((t3 - t2) / 1000)}s`);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
cleanupResources(CF, taskDef) {
|
cleanupResources(CF, taskDef) {
|
||||||
var _a;
|
var _a;
|
||||||
|
|
@ -878,6 +761,307 @@ class AWSBuildRunner {
|
||||||
exports.default = AWSBuildRunner;
|
exports.default = AWSBuildRunner;
|
||||||
|
|
||||||
|
|
||||||
|
/***/ }),
|
||||||
|
|
||||||
|
/***/ 83683:
|
||||||
|
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||||
|
if (k2 === undefined) k2 = k;
|
||||||
|
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
||||||
|
}) : (function(o, m, k, k2) {
|
||||||
|
if (k2 === undefined) k2 = k;
|
||||||
|
o[k2] = m[k];
|
||||||
|
}));
|
||||||
|
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||||
|
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||||
|
}) : function(o, v) {
|
||||||
|
o["default"] = v;
|
||||||
|
});
|
||||||
|
var __importStar = (this && this.__importStar) || function (mod) {
|
||||||
|
if (mod && mod.__esModule) return mod;
|
||||||
|
var result = {};
|
||||||
|
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||||
|
__setModuleDefault(result, mod);
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||||
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||||
|
return new (P || (P = Promise))(function (resolve, reject) {
|
||||||
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||||
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||||
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||||
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||||
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||||
|
exports.AWSError = void 0;
|
||||||
|
const cloud_runner_logger_1 = __importDefault(__webpack_require__(17375));
|
||||||
|
const core = __importStar(__webpack_require__(42186));
|
||||||
|
class AWSError {
|
||||||
|
static handleStackCreationFailure(error, CF, taskDefStackName, taskDefCloudFormation, parameters, secrets) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
cloud_runner_logger_1.default.log('aws stack parameters: ');
|
||||||
|
cloud_runner_logger_1.default.log(JSON.stringify(parameters, undefined, 4));
|
||||||
|
cloud_runner_logger_1.default.log('aws stack secrets: ');
|
||||||
|
cloud_runner_logger_1.default.log(JSON.stringify(secrets, undefined, 4));
|
||||||
|
cloud_runner_logger_1.default.log('aws stack: ');
|
||||||
|
cloud_runner_logger_1.default.log(taskDefCloudFormation);
|
||||||
|
cloud_runner_logger_1.default.log('aws error: ');
|
||||||
|
core.error(error);
|
||||||
|
cloud_runner_logger_1.default.log('Getting events and resources for task stack');
|
||||||
|
const events = (yield CF.describeStackEvents({ StackName: taskDefStackName }).promise()).StackEvents;
|
||||||
|
const resources = (yield CF.describeStackResources({ StackName: taskDefStackName }).promise()).StackResources;
|
||||||
|
cloud_runner_logger_1.default.log(JSON.stringify(events, undefined, 4));
|
||||||
|
cloud_runner_logger_1.default.log(JSON.stringify(resources, undefined, 4));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.AWSError = AWSError;
|
||||||
|
|
||||||
|
|
||||||
|
/***/ }),
|
||||||
|
|
||||||
|
/***/ 85819:
|
||||||
|
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||||
|
if (k2 === undefined) k2 = k;
|
||||||
|
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
||||||
|
}) : (function(o, m, k, k2) {
|
||||||
|
if (k2 === undefined) k2 = k;
|
||||||
|
o[k2] = m[k];
|
||||||
|
}));
|
||||||
|
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||||
|
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||||
|
}) : function(o, v) {
|
||||||
|
o["default"] = v;
|
||||||
|
});
|
||||||
|
var __importStar = (this && this.__importStar) || function (mod) {
|
||||||
|
if (mod && mod.__esModule) return mod;
|
||||||
|
var result = {};
|
||||||
|
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||||
|
__setModuleDefault(result, mod);
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||||
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||||
|
return new (P || (P = Promise))(function (resolve, reject) {
|
||||||
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||||
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||||
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||||
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||||
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||||
|
exports.AWSJobStack = void 0;
|
||||||
|
const cloud_runner_constants_1 = __importDefault(__webpack_require__(82394));
|
||||||
|
const nanoid_1 = __webpack_require__(39140);
|
||||||
|
const aws_base_stack_1 = __webpack_require__(28730);
|
||||||
|
const aws_templates_1 = __webpack_require__(6436);
|
||||||
|
const cloud_runner_logger_1 = __importDefault(__webpack_require__(17375));
|
||||||
|
const fs = __importStar(__webpack_require__(35747));
|
||||||
|
const aws_error_1 = __webpack_require__(83683);
|
||||||
|
class AWSJobStack {
|
||||||
|
constructor(baseStackName) {
|
||||||
|
this.baseStackName = baseStackName;
|
||||||
|
}
|
||||||
|
setupCloudFormations(CF, buildGuid, image, entrypoint, commands, mountdir, workingdir, secrets) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
const logGuid = nanoid_1.customAlphabet(cloud_runner_constants_1.default.alphabet, 9)();
|
||||||
|
commands[1] += `
|
||||||
|
echo "${logGuid}"
|
||||||
|
`;
|
||||||
|
yield new aws_base_stack_1.AWSBaseStack(this.baseStackName).setupBaseStack(CF);
|
||||||
|
const taskDefStackName = `${this.baseStackName}-${buildGuid}`;
|
||||||
|
let taskDefCloudFormation = aws_templates_1.AWSTemplates.readTaskCloudFormationTemplate();
|
||||||
|
const cleanupTaskDefStackName = `${taskDefStackName}-cleanup`;
|
||||||
|
const cleanupCloudFormation = fs.readFileSync(`${__dirname}/cloud-formations/cloudformation-stack-ttl.yml`, 'utf8');
|
||||||
|
for (const secret of secrets) {
|
||||||
|
if (typeof secret.ParameterValue == 'number') {
|
||||||
|
secret.ParameterValue = `${secret.ParameterValue}`;
|
||||||
|
}
|
||||||
|
if (!secret.ParameterValue || secret.ParameterValue === '') {
|
||||||
|
secrets = secrets.filter((x) => x !== secret);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
taskDefCloudFormation = aws_templates_1.AWSTemplates.insertAtTemplate(taskDefCloudFormation, 'p1 - input', aws_templates_1.AWSTemplates.getParameterTemplate(secret.ParameterKey.replace(/[^\dA-Za-z]/g, '')));
|
||||||
|
taskDefCloudFormation = aws_templates_1.AWSTemplates.insertAtTemplate(taskDefCloudFormation, 'p2 - secret', aws_templates_1.AWSTemplates.getSecretTemplate(secret.ParameterKey.replace(/[^\dA-Za-z]/g, '')));
|
||||||
|
taskDefCloudFormation = aws_templates_1.AWSTemplates.insertAtTemplate(taskDefCloudFormation, 'p3 - container def', aws_templates_1.AWSTemplates.getSecretDefinitionTemplate(secret.EnvironmentVariable, secret.ParameterKey.replace(/[^\dA-Za-z]/g, '')));
|
||||||
|
}
|
||||||
|
const secretsMappedToCloudFormationParameters = secrets.map((x) => {
|
||||||
|
return { ParameterKey: x.ParameterKey.replace(/[^\dA-Za-z]/g, ''), ParameterValue: x.ParameterValue };
|
||||||
|
});
|
||||||
|
const parameters = [
|
||||||
|
{
|
||||||
|
ParameterKey: 'EnvironmentName',
|
||||||
|
ParameterValue: this.baseStackName,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ParameterKey: 'ImageUrl',
|
||||||
|
ParameterValue: image,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ParameterKey: 'ServiceName',
|
||||||
|
ParameterValue: taskDefStackName,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ParameterKey: 'Command',
|
||||||
|
ParameterValue: 'echo "this template should be overwritten when running a task"',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ParameterKey: 'EntryPoint',
|
||||||
|
ParameterValue: entrypoint.join(','),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ParameterKey: 'WorkingDirectory',
|
||||||
|
ParameterValue: workingdir,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ParameterKey: 'EFSMountDirectory',
|
||||||
|
ParameterValue: mountdir,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ParameterKey: 'BUILDID',
|
||||||
|
ParameterValue: buildGuid,
|
||||||
|
},
|
||||||
|
...secretsMappedToCloudFormationParameters,
|
||||||
|
];
|
||||||
|
try {
|
||||||
|
yield CF.createStack({
|
||||||
|
StackName: taskDefStackName,
|
||||||
|
TemplateBody: taskDefCloudFormation,
|
||||||
|
Capabilities: ['CAPABILITY_IAM'],
|
||||||
|
Parameters: parameters,
|
||||||
|
}).promise();
|
||||||
|
cloud_runner_logger_1.default.log('Creating cloud runner job');
|
||||||
|
yield CF.createStack({
|
||||||
|
StackName: cleanupTaskDefStackName,
|
||||||
|
TemplateBody: cleanupCloudFormation,
|
||||||
|
Capabilities: ['CAPABILITY_IAM'],
|
||||||
|
Parameters: [
|
||||||
|
{
|
||||||
|
ParameterKey: 'StackName',
|
||||||
|
ParameterValue: taskDefStackName,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ParameterKey: 'DeleteStackName',
|
||||||
|
ParameterValue: cleanupTaskDefStackName,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ParameterKey: 'TTL',
|
||||||
|
ParameterValue: '100',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ParameterKey: 'BUILDID',
|
||||||
|
ParameterValue: buildGuid,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ParameterKey: 'EnvironmentName',
|
||||||
|
ParameterValue: this.baseStackName,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}).promise();
|
||||||
|
yield CF.waitFor('stackCreateComplete', { StackName: taskDefStackName }).promise();
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
yield aws_error_1.AWSError.handleStackCreationFailure(error, CF, taskDefStackName, taskDefCloudFormation, parameters, secrets);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
const taskDefResources = (yield CF.describeStackResources({
|
||||||
|
StackName: taskDefStackName,
|
||||||
|
}).promise()).StackResources;
|
||||||
|
const baseResources = (yield CF.describeStackResources({ StackName: this.baseStackName }).promise()).StackResources;
|
||||||
|
// TODO: offer a parameter to decide if you want the guaranteed shutdown or fastest startup time possible
|
||||||
|
return {
|
||||||
|
taskDefStackName,
|
||||||
|
taskDefCloudFormation,
|
||||||
|
taskDefStackNameTTL: cleanupTaskDefStackName,
|
||||||
|
ttlCloudFormation: cleanupCloudFormation,
|
||||||
|
taskDefResources,
|
||||||
|
baseResources,
|
||||||
|
logid: logGuid,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.AWSJobStack = AWSJobStack;
|
||||||
|
|
||||||
|
|
||||||
|
/***/ }),
|
||||||
|
|
||||||
|
/***/ 6436:
|
||||||
|
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||||
|
if (k2 === undefined) k2 = k;
|
||||||
|
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
||||||
|
}) : (function(o, m, k, k2) {
|
||||||
|
if (k2 === undefined) k2 = k;
|
||||||
|
o[k2] = m[k];
|
||||||
|
}));
|
||||||
|
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||||
|
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||||
|
}) : function(o, v) {
|
||||||
|
o["default"] = v;
|
||||||
|
});
|
||||||
|
var __importStar = (this && this.__importStar) || function (mod) {
|
||||||
|
if (mod && mod.__esModule) return mod;
|
||||||
|
var result = {};
|
||||||
|
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||||
|
__setModuleDefault(result, mod);
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||||
|
exports.AWSTemplates = void 0;
|
||||||
|
const fs = __importStar(__webpack_require__(35747));
|
||||||
|
class AWSTemplates {
|
||||||
|
static getParameterTemplate(p1) {
|
||||||
|
return `
|
||||||
|
${p1}:
|
||||||
|
Type: String
|
||||||
|
Default: ''
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
static getSecretTemplate(p1) {
|
||||||
|
return `
|
||||||
|
${p1}Secret:
|
||||||
|
Type: AWS::SecretsManager::Secret
|
||||||
|
Properties:
|
||||||
|
Name: !Join [ "", [ '${p1}', !Ref BUILDID ] ]
|
||||||
|
SecretString: !Ref ${p1}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
static getSecretDefinitionTemplate(p1, p2) {
|
||||||
|
return `
|
||||||
|
- Name: '${p1}'
|
||||||
|
ValueFrom: !Ref ${p2}Secret
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
static insertAtTemplate(template, insertionKey, insertion) {
|
||||||
|
const index = template.search(insertionKey) + insertionKey.length + '\n'.length;
|
||||||
|
template = [template.slice(0, index), insertion, template.slice(index)].join('');
|
||||||
|
return template;
|
||||||
|
}
|
||||||
|
static readTaskCloudFormationTemplate() {
|
||||||
|
return fs.readFileSync(`${__dirname}/cloud-formations/task-def-formation.yml`, 'utf8');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.AWSTemplates = AWSTemplates;
|
||||||
|
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
|
|
||||||
/***/ 82394:
|
/***/ 82394:
|
||||||
|
|
@ -1502,7 +1686,7 @@ class CloudRunner {
|
||||||
CloudRunner.setup(buildParameters);
|
CloudRunner.setup(buildParameters);
|
||||||
try {
|
try {
|
||||||
yield cloud_runner_state_1.CloudRunnerState.CloudRunnerProviderPlatform.setupSharedBuildResources(cloud_runner_state_1.CloudRunnerState.buildGuid, cloud_runner_state_1.CloudRunnerState.buildParams, cloud_runner_state_1.CloudRunnerState.branchName, cloud_runner_state_1.CloudRunnerState.defaultSecrets);
|
yield cloud_runner_state_1.CloudRunnerState.CloudRunnerProviderPlatform.setupSharedBuildResources(cloud_runner_state_1.CloudRunnerState.buildGuid, cloud_runner_state_1.CloudRunnerState.buildParams, cloud_runner_state_1.CloudRunnerState.branchName, cloud_runner_state_1.CloudRunnerState.defaultSecrets);
|
||||||
yield CloudRunner.runJob(baseImage);
|
yield CloudRunner.runJob(baseImage.toString());
|
||||||
yield cloud_runner_state_1.CloudRunnerState.CloudRunnerProviderPlatform.cleanupSharedBuildResources(cloud_runner_state_1.CloudRunnerState.buildGuid, cloud_runner_state_1.CloudRunnerState.buildParams, cloud_runner_state_1.CloudRunnerState.branchName, cloud_runner_state_1.CloudRunnerState.defaultSecrets);
|
yield cloud_runner_state_1.CloudRunnerState.CloudRunnerProviderPlatform.cleanupSharedBuildResources(cloud_runner_state_1.CloudRunnerState.buildGuid, cloud_runner_state_1.CloudRunnerState.buildParams, cloud_runner_state_1.CloudRunnerState.branchName, cloud_runner_state_1.CloudRunnerState.defaultSecrets);
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -1 +1,94 @@
|
||||||
export class AWSBaseStack {}
|
import CloudRunnerLogger from '../cloud-runner-services/cloud-runner-logger';
|
||||||
|
import * as core from '@actions/core';
|
||||||
|
import * as SDK from 'aws-sdk';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
const crypto = require('crypto');
|
||||||
|
|
||||||
|
export class AWSBaseStack {
|
||||||
|
constructor(baseStackName: string) {
|
||||||
|
this.baseStackName = baseStackName;
|
||||||
|
}
|
||||||
|
private baseStackName: string;
|
||||||
|
|
||||||
|
async setupBaseStack(CF: SDK.CloudFormation) {
|
||||||
|
const baseStackName = this.baseStackName;
|
||||||
|
const baseStack = fs.readFileSync(`${__dirname}/cloud-formations/base-setup.yml`, 'utf8');
|
||||||
|
|
||||||
|
// Cloud Formation Input
|
||||||
|
const describeStackInput: SDK.CloudFormation.DescribeStacksInput = {
|
||||||
|
StackName: baseStackName,
|
||||||
|
};
|
||||||
|
const parametersWithoutHash: SDK.CloudFormation.Parameter[] = [
|
||||||
|
{ ParameterKey: 'EnvironmentName', ParameterValue: baseStackName },
|
||||||
|
{ ParameterKey: 'Storage', ParameterValue: `${baseStackName}-storage` },
|
||||||
|
];
|
||||||
|
const hash = crypto
|
||||||
|
.createHash('md5')
|
||||||
|
.update(baseStack + JSON.stringify(parametersWithoutHash))
|
||||||
|
.digest('hex');
|
||||||
|
const parameters: SDK.CloudFormation.Parameter[] = [
|
||||||
|
...parametersWithoutHash,
|
||||||
|
...[{ ParameterKey: 'Version', ParameterValue: hash }],
|
||||||
|
];
|
||||||
|
const updateInput: SDK.CloudFormation.UpdateStackInput = {
|
||||||
|
StackName: baseStackName,
|
||||||
|
TemplateBody: baseStack,
|
||||||
|
Parameters: parameters,
|
||||||
|
Capabilities: ['CAPABILITY_IAM'],
|
||||||
|
};
|
||||||
|
const createStackInput: SDK.CloudFormation.CreateStackInput = {
|
||||||
|
StackName: baseStackName,
|
||||||
|
TemplateBody: baseStack,
|
||||||
|
Parameters: parameters,
|
||||||
|
Capabilities: ['CAPABILITY_IAM'],
|
||||||
|
};
|
||||||
|
|
||||||
|
const stacks = (
|
||||||
|
await CF.listStacks({ StackStatusFilter: ['UPDATE_COMPLETE', 'CREATE_COMPLETE'] }).promise()
|
||||||
|
).StackSummaries?.map((x) => x.StackName);
|
||||||
|
const stackExists: Boolean = stacks?.includes(baseStackName) || false;
|
||||||
|
const describeStack = async () => {
|
||||||
|
return await CF.describeStacks(describeStackInput).promise();
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
if (!stackExists) {
|
||||||
|
CloudRunnerLogger.log(`${baseStackName} stack does not exist (${JSON.stringify(stacks)})`);
|
||||||
|
await CF.createStack(createStackInput).promise();
|
||||||
|
CloudRunnerLogger.log(`created stack (version: ${hash})`);
|
||||||
|
}
|
||||||
|
const CFState = await describeStack();
|
||||||
|
let stack = CFState.Stacks?.[0];
|
||||||
|
if (!stack) {
|
||||||
|
throw new Error(`Base stack doesn't exist, even after creation, stackExists check: ${stackExists}`);
|
||||||
|
}
|
||||||
|
const stackVersion = stack.Parameters?.find((x) => x.ParameterKey === 'Version')?.ParameterValue;
|
||||||
|
|
||||||
|
if (stack.StackStatus === 'CREATE_IN_PROGRESS') {
|
||||||
|
await CF.waitFor('stackCreateComplete', describeStackInput).promise();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stackExists) {
|
||||||
|
CloudRunnerLogger.log(`Base stack exists (version: ${stackVersion}, local version: ${hash})`);
|
||||||
|
if (hash !== stackVersion) {
|
||||||
|
CloudRunnerLogger.log(`Updating`);
|
||||||
|
await CF.updateStack(updateInput).promise();
|
||||||
|
} else {
|
||||||
|
CloudRunnerLogger.log(`No update required`);
|
||||||
|
}
|
||||||
|
stack = (await describeStack()).Stacks?.[0];
|
||||||
|
if (!stack) {
|
||||||
|
throw new Error(
|
||||||
|
`Base stack doesn't exist, even after updating and creation, stackExists check: ${stackExists}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (stack.StackStatus === 'UPDATE_IN_PROGRESS') {
|
||||||
|
await CF.waitFor('stackUpdateComplete', describeStackInput).promise();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CloudRunnerLogger.log('base stack is ready');
|
||||||
|
} catch (error) {
|
||||||
|
core.error(JSON.stringify(await describeStack(), undefined, 4));
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,12 @@
|
||||||
import * as SDK from 'aws-sdk';
|
import * as SDK from 'aws-sdk';
|
||||||
import { customAlphabet } from 'nanoid';
|
|
||||||
import CloudRunnerSecret from '../cloud-runner-services/cloud-runner-secret';
|
import CloudRunnerSecret from '../cloud-runner-services/cloud-runner-secret';
|
||||||
import CloudRunnerEnvironmentVariable from '../cloud-runner-services/cloud-runner-environment-variable';
|
import CloudRunnerEnvironmentVariable from '../cloud-runner-services/cloud-runner-environment-variable';
|
||||||
import * as fs from 'fs';
|
|
||||||
import * as core from '@actions/core';
|
|
||||||
import CloudRunnerTaskDef from '../cloud-runner-services/cloud-runner-task-def';
|
import CloudRunnerTaskDef from '../cloud-runner-services/cloud-runner-task-def';
|
||||||
import CloudRunnerConstants from '../cloud-runner-services/cloud-runner-constants';
|
|
||||||
import AWSBuildRunner from './aws-build-runner';
|
import AWSBuildRunner from './aws-build-runner';
|
||||||
import { CloudRunnerProviderInterface } from '../cloud-runner-services/cloud-runner-provider-interface';
|
import { CloudRunnerProviderInterface } from '../cloud-runner-services/cloud-runner-provider-interface';
|
||||||
import BuildParameters from '../../build-parameters';
|
import BuildParameters from '../../build-parameters';
|
||||||
import CloudRunnerLogger from '../cloud-runner-services/cloud-runner-logger';
|
import CloudRunnerLogger from '../cloud-runner-services/cloud-runner-logger';
|
||||||
const crypto = require('crypto');
|
import { AWSJobStack } from './aws-job-stack';
|
||||||
|
|
||||||
class AWSBuildEnvironment implements CloudRunnerProviderInterface {
|
class AWSBuildEnvironment implements CloudRunnerProviderInterface {
|
||||||
private baseStackName: string;
|
private baseStackName: string;
|
||||||
|
|
@ -52,7 +48,7 @@ class AWSBuildEnvironment implements CloudRunnerProviderInterface {
|
||||||
const CF = new SDK.CloudFormation();
|
const CF = new SDK.CloudFormation();
|
||||||
const entrypoint = ['/bin/sh'];
|
const entrypoint = ['/bin/sh'];
|
||||||
const t0 = Date.now();
|
const t0 = Date.now();
|
||||||
const taskDef = await this.setupCloudFormations(
|
const taskDef = await new AWSJobStack(this.baseStackName).setupCloudFormations(
|
||||||
CF,
|
CF,
|
||||||
buildId,
|
buildId,
|
||||||
image,
|
image,
|
||||||
|
|
@ -77,294 +73,6 @@ class AWSBuildEnvironment implements CloudRunnerProviderInterface {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getParameterTemplate(p1) {
|
|
||||||
return `
|
|
||||||
${p1}:
|
|
||||||
Type: String
|
|
||||||
Default: ''
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
getSecretTemplate(p1) {
|
|
||||||
return `
|
|
||||||
${p1}Secret:
|
|
||||||
Type: AWS::SecretsManager::Secret
|
|
||||||
Properties:
|
|
||||||
Name: !Join [ "", [ '${p1}', !Ref BUILDID ] ]
|
|
||||||
SecretString: !Ref ${p1}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
getSecretDefinitionTemplate(p1, p2) {
|
|
||||||
return `
|
|
||||||
- Name: '${p1}'
|
|
||||||
ValueFrom: !Ref ${p2}Secret
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
insertAtTemplate(template, insertionKey, insertion) {
|
|
||||||
const index = template.search(insertionKey) + insertionKey.length + '\n'.length;
|
|
||||||
template = [template.slice(0, index), insertion, template.slice(index)].join('');
|
|
||||||
return template;
|
|
||||||
}
|
|
||||||
|
|
||||||
async setupCloudFormations(
|
|
||||||
CF: SDK.CloudFormation,
|
|
||||||
buildGuid: string,
|
|
||||||
image: string,
|
|
||||||
entrypoint: string[],
|
|
||||||
commands: string[],
|
|
||||||
mountdir: string,
|
|
||||||
workingdir: string,
|
|
||||||
secrets: CloudRunnerSecret[],
|
|
||||||
): Promise<CloudRunnerTaskDef> {
|
|
||||||
const logGuid = customAlphabet(CloudRunnerConstants.alphabet, 9)();
|
|
||||||
commands[1] += `
|
|
||||||
echo "${logGuid}"
|
|
||||||
`;
|
|
||||||
await this.setupBaseStack(CF);
|
|
||||||
const taskDefStackName = `${this.baseStackName}-${buildGuid}`;
|
|
||||||
let taskDefCloudFormation = this.readTaskCloudFormationTemplate();
|
|
||||||
const cleanupTaskDefStackName = `${taskDefStackName}-cleanup`;
|
|
||||||
const cleanupCloudFormation = fs.readFileSync(`${__dirname}/cloud-formations/cloudformation-stack-ttl.yml`, 'utf8');
|
|
||||||
|
|
||||||
for (const secret of secrets) {
|
|
||||||
if (typeof secret.ParameterValue == 'number') {
|
|
||||||
secret.ParameterValue = `${secret.ParameterValue}`;
|
|
||||||
}
|
|
||||||
if (!secret.ParameterValue || secret.ParameterValue === '') {
|
|
||||||
secrets = secrets.filter((x) => x !== secret);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
taskDefCloudFormation = this.insertAtTemplate(
|
|
||||||
taskDefCloudFormation,
|
|
||||||
'p1 - input',
|
|
||||||
this.getParameterTemplate(secret.ParameterKey.replace(/[^\dA-Za-z]/g, '')),
|
|
||||||
);
|
|
||||||
taskDefCloudFormation = this.insertAtTemplate(
|
|
||||||
taskDefCloudFormation,
|
|
||||||
'p2 - secret',
|
|
||||||
this.getSecretTemplate(secret.ParameterKey.replace(/[^\dA-Za-z]/g, '')),
|
|
||||||
);
|
|
||||||
taskDefCloudFormation = this.insertAtTemplate(
|
|
||||||
taskDefCloudFormation,
|
|
||||||
'p3 - container def',
|
|
||||||
this.getSecretDefinitionTemplate(secret.EnvironmentVariable, secret.ParameterKey.replace(/[^\dA-Za-z]/g, '')),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const secretsMappedToCloudFormationParameters = secrets.map((x) => {
|
|
||||||
return { ParameterKey: x.ParameterKey.replace(/[^\dA-Za-z]/g, ''), ParameterValue: x.ParameterValue };
|
|
||||||
});
|
|
||||||
const parameters = [
|
|
||||||
{
|
|
||||||
ParameterKey: 'EnvironmentName',
|
|
||||||
ParameterValue: this.baseStackName,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ParameterKey: 'ImageUrl',
|
|
||||||
ParameterValue: image,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ParameterKey: 'ServiceName',
|
|
||||||
ParameterValue: taskDefStackName,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ParameterKey: 'Command',
|
|
||||||
ParameterValue: 'echo "this template should be overwritten when running a task"',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ParameterKey: 'EntryPoint',
|
|
||||||
ParameterValue: entrypoint.join(','),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ParameterKey: 'WorkingDirectory',
|
|
||||||
ParameterValue: workingdir,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ParameterKey: 'EFSMountDirectory',
|
|
||||||
ParameterValue: mountdir,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ParameterKey: 'BUILDID',
|
|
||||||
ParameterValue: buildGuid,
|
|
||||||
},
|
|
||||||
...secretsMappedToCloudFormationParameters,
|
|
||||||
];
|
|
||||||
|
|
||||||
try {
|
|
||||||
await CF.createStack({
|
|
||||||
StackName: taskDefStackName,
|
|
||||||
TemplateBody: taskDefCloudFormation,
|
|
||||||
Capabilities: ['CAPABILITY_IAM'],
|
|
||||||
Parameters: parameters,
|
|
||||||
}).promise();
|
|
||||||
CloudRunnerLogger.log('Creating cloud runner job');
|
|
||||||
await CF.createStack({
|
|
||||||
StackName: cleanupTaskDefStackName,
|
|
||||||
TemplateBody: cleanupCloudFormation,
|
|
||||||
Capabilities: ['CAPABILITY_IAM'],
|
|
||||||
Parameters: [
|
|
||||||
{
|
|
||||||
ParameterKey: 'StackName',
|
|
||||||
ParameterValue: taskDefStackName,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ParameterKey: 'DeleteStackName',
|
|
||||||
ParameterValue: cleanupTaskDefStackName,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ParameterKey: 'TTL',
|
|
||||||
ParameterValue: '100',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ParameterKey: 'BUILDID',
|
|
||||||
ParameterValue: buildGuid,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ParameterKey: 'EnvironmentName',
|
|
||||||
ParameterValue: this.baseStackName,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}).promise();
|
|
||||||
await CF.waitFor('stackCreateComplete', { StackName: taskDefStackName }).promise();
|
|
||||||
} catch (error) {
|
|
||||||
await this.handleStackCreationFailure(error, CF, taskDefStackName, taskDefCloudFormation, parameters, secrets);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
|
|
||||||
const taskDefResources = (
|
|
||||||
await CF.describeStackResources({
|
|
||||||
StackName: taskDefStackName,
|
|
||||||
}).promise()
|
|
||||||
).StackResources;
|
|
||||||
|
|
||||||
const baseResources = (await CF.describeStackResources({ StackName: this.baseStackName }).promise()).StackResources;
|
|
||||||
|
|
||||||
// TODO: offer a parameter to decide if you want the guaranteed shutdown or fastest startup time possible
|
|
||||||
|
|
||||||
return {
|
|
||||||
taskDefStackName,
|
|
||||||
taskDefCloudFormation,
|
|
||||||
taskDefStackNameTTL: cleanupTaskDefStackName,
|
|
||||||
ttlCloudFormation: cleanupCloudFormation,
|
|
||||||
taskDefResources,
|
|
||||||
baseResources,
|
|
||||||
logid: logGuid,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
async setupBaseStack(CF: SDK.CloudFormation) {
|
|
||||||
const baseStackName = this.baseStackName;
|
|
||||||
const baseStack = fs.readFileSync(`${__dirname}/cloud-formations/base-setup.yml`, 'utf8');
|
|
||||||
|
|
||||||
// Cloud Formation Input
|
|
||||||
const describeStackInput: SDK.CloudFormation.DescribeStacksInput = {
|
|
||||||
StackName: baseStackName,
|
|
||||||
};
|
|
||||||
const parametersWithoutHash: SDK.CloudFormation.Parameter[] = [
|
|
||||||
{ ParameterKey: 'EnvironmentName', ParameterValue: baseStackName },
|
|
||||||
{ ParameterKey: 'Storage', ParameterValue: `${baseStackName}-storage` },
|
|
||||||
];
|
|
||||||
const hash = crypto
|
|
||||||
.createHash('md5')
|
|
||||||
.update(baseStack + JSON.stringify(parametersWithoutHash))
|
|
||||||
.digest('hex');
|
|
||||||
const parameters: SDK.CloudFormation.Parameter[] = [
|
|
||||||
...parametersWithoutHash,
|
|
||||||
...[{ ParameterKey: 'Version', ParameterValue: hash }],
|
|
||||||
];
|
|
||||||
const updateInput: SDK.CloudFormation.UpdateStackInput = {
|
|
||||||
StackName: baseStackName,
|
|
||||||
TemplateBody: baseStack,
|
|
||||||
Parameters: parameters,
|
|
||||||
Capabilities: ['CAPABILITY_IAM'],
|
|
||||||
};
|
|
||||||
const createStackInput: SDK.CloudFormation.CreateStackInput = {
|
|
||||||
StackName: baseStackName,
|
|
||||||
TemplateBody: baseStack,
|
|
||||||
Parameters: parameters,
|
|
||||||
Capabilities: ['CAPABILITY_IAM'],
|
|
||||||
};
|
|
||||||
|
|
||||||
const stacks = (
|
|
||||||
await CF.listStacks({ StackStatusFilter: ['UPDATE_COMPLETE', 'CREATE_COMPLETE'] }).promise()
|
|
||||||
).StackSummaries?.map((x) => x.StackName);
|
|
||||||
const stackExists: Boolean = stacks?.includes(baseStackName) || false;
|
|
||||||
const describeStack = async () => {
|
|
||||||
return await CF.describeStacks(describeStackInput).promise();
|
|
||||||
};
|
|
||||||
try {
|
|
||||||
if (!stackExists) {
|
|
||||||
CloudRunnerLogger.log(`${baseStackName} stack does not exist (${JSON.stringify(stacks)})`);
|
|
||||||
await CF.createStack(createStackInput).promise();
|
|
||||||
CloudRunnerLogger.log(`created stack (version: ${hash})`);
|
|
||||||
}
|
|
||||||
const CFState = await describeStack();
|
|
||||||
let stack = CFState.Stacks?.[0];
|
|
||||||
if (!stack) {
|
|
||||||
throw new Error(`Base stack doesn't exist, even after creation, stackExists check: ${stackExists}`);
|
|
||||||
}
|
|
||||||
const stackVersion = stack.Parameters?.find((x) => x.ParameterKey === 'Version')?.ParameterValue;
|
|
||||||
|
|
||||||
if (stack.StackStatus === 'CREATE_IN_PROGRESS') {
|
|
||||||
await CF.waitFor('stackCreateComplete', describeStackInput).promise();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stackExists) {
|
|
||||||
CloudRunnerLogger.log(`Base stack exists (version: ${stackVersion}, local version: ${hash})`);
|
|
||||||
if (hash !== stackVersion) {
|
|
||||||
CloudRunnerLogger.log(`Updating`);
|
|
||||||
await CF.updateStack(updateInput).promise();
|
|
||||||
} else {
|
|
||||||
CloudRunnerLogger.log(`No update required`);
|
|
||||||
}
|
|
||||||
stack = (await describeStack()).Stacks?.[0];
|
|
||||||
if (!stack) {
|
|
||||||
throw new Error(
|
|
||||||
`Base stack doesn't exist, even after updating and creation, stackExists check: ${stackExists}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (stack.StackStatus === 'UPDATE_IN_PROGRESS') {
|
|
||||||
await CF.waitFor('stackUpdateComplete', describeStackInput).promise();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
CloudRunnerLogger.log('base stack is ready');
|
|
||||||
} catch (error) {
|
|
||||||
core.error(JSON.stringify(await describeStack(), undefined, 4));
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async handleStackCreationFailure(
|
|
||||||
error: any,
|
|
||||||
CF: SDK.CloudFormation,
|
|
||||||
taskDefStackName: string,
|
|
||||||
taskDefCloudFormation: string,
|
|
||||||
parameters: any[],
|
|
||||||
secrets: CloudRunnerSecret[],
|
|
||||||
) {
|
|
||||||
CloudRunnerLogger.log('aws stack parameters: ');
|
|
||||||
CloudRunnerLogger.log(JSON.stringify(parameters, undefined, 4));
|
|
||||||
|
|
||||||
CloudRunnerLogger.log('aws stack secrets: ');
|
|
||||||
CloudRunnerLogger.log(JSON.stringify(secrets, undefined, 4));
|
|
||||||
|
|
||||||
CloudRunnerLogger.log('aws stack: ');
|
|
||||||
CloudRunnerLogger.log(taskDefCloudFormation);
|
|
||||||
|
|
||||||
CloudRunnerLogger.log('aws error: ');
|
|
||||||
core.error(error);
|
|
||||||
CloudRunnerLogger.log('Getting events and resources for task stack');
|
|
||||||
const events = (await CF.describeStackEvents({ StackName: taskDefStackName }).promise()).StackEvents;
|
|
||||||
const resources = (await CF.describeStackResources({ StackName: taskDefStackName }).promise()).StackResources;
|
|
||||||
CloudRunnerLogger.log(JSON.stringify(events, undefined, 4));
|
|
||||||
CloudRunnerLogger.log(JSON.stringify(resources, undefined, 4));
|
|
||||||
}
|
|
||||||
|
|
||||||
readTaskCloudFormationTemplate(): string {
|
|
||||||
return fs.readFileSync(`${__dirname}/cloud-formations/task-def-formation.yml`, 'utf8');
|
|
||||||
}
|
|
||||||
|
|
||||||
async cleanupResources(CF: SDK.CloudFormation, taskDef: CloudRunnerTaskDef) {
|
async cleanupResources(CF: SDK.CloudFormation, taskDef: CloudRunnerTaskDef) {
|
||||||
CloudRunnerLogger.log('Cleanup starting');
|
CloudRunnerLogger.log('Cleanup starting');
|
||||||
await CF.deleteStack({
|
await CF.deleteStack({
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
import CloudRunnerLogger from '../cloud-runner-services/cloud-runner-logger';
|
||||||
|
import CloudRunnerSecret from '../cloud-runner-services/cloud-runner-secret';
|
||||||
|
import * as SDK from 'aws-sdk';
|
||||||
|
import * as core from '@actions/core';
|
||||||
|
|
||||||
|
export class AWSError {
|
||||||
|
static async handleStackCreationFailure(
|
||||||
|
error: any,
|
||||||
|
CF: SDK.CloudFormation,
|
||||||
|
taskDefStackName: string,
|
||||||
|
taskDefCloudFormation: string,
|
||||||
|
parameters: any[],
|
||||||
|
secrets: CloudRunnerSecret[],
|
||||||
|
) {
|
||||||
|
CloudRunnerLogger.log('aws stack parameters: ');
|
||||||
|
CloudRunnerLogger.log(JSON.stringify(parameters, undefined, 4));
|
||||||
|
|
||||||
|
CloudRunnerLogger.log('aws stack secrets: ');
|
||||||
|
CloudRunnerLogger.log(JSON.stringify(secrets, undefined, 4));
|
||||||
|
|
||||||
|
CloudRunnerLogger.log('aws stack: ');
|
||||||
|
CloudRunnerLogger.log(taskDefCloudFormation);
|
||||||
|
|
||||||
|
CloudRunnerLogger.log('aws error: ');
|
||||||
|
core.error(error);
|
||||||
|
CloudRunnerLogger.log('Getting events and resources for task stack');
|
||||||
|
const events = (await CF.describeStackEvents({ StackName: taskDefStackName }).promise()).StackEvents;
|
||||||
|
const resources = (await CF.describeStackResources({ StackName: taskDefStackName }).promise()).StackResources;
|
||||||
|
CloudRunnerLogger.log(JSON.stringify(events, undefined, 4));
|
||||||
|
CloudRunnerLogger.log(JSON.stringify(resources, undefined, 4));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,172 @@
|
||||||
|
import * as SDK from 'aws-sdk';
|
||||||
|
import CloudRunnerTaskDef from '../cloud-runner-services/cloud-runner-task-def';
|
||||||
|
import CloudRunnerSecret from '../cloud-runner-services/cloud-runner-secret';
|
||||||
|
import CloudRunnerConstants from '../cloud-runner-services/cloud-runner-constants';
|
||||||
|
import { customAlphabet } from 'nanoid';
|
||||||
|
import { AWSBaseStack } from './aws-base-stack';
|
||||||
|
import { AWSTemplates } from './aws-templates';
|
||||||
|
import CloudRunnerLogger from '../cloud-runner-services/cloud-runner-logger';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import { AWSError } from './aws-error';
|
||||||
|
|
||||||
|
export class AWSJobStack {
|
||||||
|
private baseStackName: string;
|
||||||
|
constructor(baseStackName: string) {
|
||||||
|
this.baseStackName = baseStackName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async setupCloudFormations(
|
||||||
|
CF: SDK.CloudFormation,
|
||||||
|
buildGuid: string,
|
||||||
|
image: string,
|
||||||
|
entrypoint: string[],
|
||||||
|
commands: string[],
|
||||||
|
mountdir: string,
|
||||||
|
workingdir: string,
|
||||||
|
secrets: CloudRunnerSecret[],
|
||||||
|
): Promise<CloudRunnerTaskDef> {
|
||||||
|
const logGuid = customAlphabet(CloudRunnerConstants.alphabet, 9)();
|
||||||
|
commands[1] += `
|
||||||
|
echo "${logGuid}"
|
||||||
|
`;
|
||||||
|
await new AWSBaseStack(this.baseStackName).setupBaseStack(CF);
|
||||||
|
const taskDefStackName = `${this.baseStackName}-${buildGuid}`;
|
||||||
|
let taskDefCloudFormation = AWSTemplates.readTaskCloudFormationTemplate();
|
||||||
|
const cleanupTaskDefStackName = `${taskDefStackName}-cleanup`;
|
||||||
|
const cleanupCloudFormation = fs.readFileSync(`${__dirname}/cloud-formations/cloudformation-stack-ttl.yml`, 'utf8');
|
||||||
|
|
||||||
|
for (const secret of secrets) {
|
||||||
|
if (typeof secret.ParameterValue == 'number') {
|
||||||
|
secret.ParameterValue = `${secret.ParameterValue}`;
|
||||||
|
}
|
||||||
|
if (!secret.ParameterValue || secret.ParameterValue === '') {
|
||||||
|
secrets = secrets.filter((x) => x !== secret);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
taskDefCloudFormation = AWSTemplates.insertAtTemplate(
|
||||||
|
taskDefCloudFormation,
|
||||||
|
'p1 - input',
|
||||||
|
AWSTemplates.getParameterTemplate(secret.ParameterKey.replace(/[^\dA-Za-z]/g, '')),
|
||||||
|
);
|
||||||
|
taskDefCloudFormation = AWSTemplates.insertAtTemplate(
|
||||||
|
taskDefCloudFormation,
|
||||||
|
'p2 - secret',
|
||||||
|
AWSTemplates.getSecretTemplate(secret.ParameterKey.replace(/[^\dA-Za-z]/g, '')),
|
||||||
|
);
|
||||||
|
taskDefCloudFormation = AWSTemplates.insertAtTemplate(
|
||||||
|
taskDefCloudFormation,
|
||||||
|
'p3 - container def',
|
||||||
|
AWSTemplates.getSecretDefinitionTemplate(
|
||||||
|
secret.EnvironmentVariable,
|
||||||
|
secret.ParameterKey.replace(/[^\dA-Za-z]/g, ''),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const secretsMappedToCloudFormationParameters = secrets.map((x) => {
|
||||||
|
return { ParameterKey: x.ParameterKey.replace(/[^\dA-Za-z]/g, ''), ParameterValue: x.ParameterValue };
|
||||||
|
});
|
||||||
|
const parameters = [
|
||||||
|
{
|
||||||
|
ParameterKey: 'EnvironmentName',
|
||||||
|
ParameterValue: this.baseStackName,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ParameterKey: 'ImageUrl',
|
||||||
|
ParameterValue: image,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ParameterKey: 'ServiceName',
|
||||||
|
ParameterValue: taskDefStackName,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ParameterKey: 'Command',
|
||||||
|
ParameterValue: 'echo "this template should be overwritten when running a task"',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ParameterKey: 'EntryPoint',
|
||||||
|
ParameterValue: entrypoint.join(','),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ParameterKey: 'WorkingDirectory',
|
||||||
|
ParameterValue: workingdir,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ParameterKey: 'EFSMountDirectory',
|
||||||
|
ParameterValue: mountdir,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ParameterKey: 'BUILDID',
|
||||||
|
ParameterValue: buildGuid,
|
||||||
|
},
|
||||||
|
...secretsMappedToCloudFormationParameters,
|
||||||
|
];
|
||||||
|
|
||||||
|
try {
|
||||||
|
await CF.createStack({
|
||||||
|
StackName: taskDefStackName,
|
||||||
|
TemplateBody: taskDefCloudFormation,
|
||||||
|
Capabilities: ['CAPABILITY_IAM'],
|
||||||
|
Parameters: parameters,
|
||||||
|
}).promise();
|
||||||
|
CloudRunnerLogger.log('Creating cloud runner job');
|
||||||
|
await CF.createStack({
|
||||||
|
StackName: cleanupTaskDefStackName,
|
||||||
|
TemplateBody: cleanupCloudFormation,
|
||||||
|
Capabilities: ['CAPABILITY_IAM'],
|
||||||
|
Parameters: [
|
||||||
|
{
|
||||||
|
ParameterKey: 'StackName',
|
||||||
|
ParameterValue: taskDefStackName,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ParameterKey: 'DeleteStackName',
|
||||||
|
ParameterValue: cleanupTaskDefStackName,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ParameterKey: 'TTL',
|
||||||
|
ParameterValue: '100',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ParameterKey: 'BUILDID',
|
||||||
|
ParameterValue: buildGuid,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ParameterKey: 'EnvironmentName',
|
||||||
|
ParameterValue: this.baseStackName,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}).promise();
|
||||||
|
await CF.waitFor('stackCreateComplete', { StackName: taskDefStackName }).promise();
|
||||||
|
} catch (error) {
|
||||||
|
await AWSError.handleStackCreationFailure(
|
||||||
|
error,
|
||||||
|
CF,
|
||||||
|
taskDefStackName,
|
||||||
|
taskDefCloudFormation,
|
||||||
|
parameters,
|
||||||
|
secrets,
|
||||||
|
);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
const taskDefResources = (
|
||||||
|
await CF.describeStackResources({
|
||||||
|
StackName: taskDefStackName,
|
||||||
|
}).promise()
|
||||||
|
).StackResources;
|
||||||
|
|
||||||
|
const baseResources = (await CF.describeStackResources({ StackName: this.baseStackName }).promise()).StackResources;
|
||||||
|
|
||||||
|
// TODO: offer a parameter to decide if you want the guaranteed shutdown or fastest startup time possible
|
||||||
|
|
||||||
|
return {
|
||||||
|
taskDefStackName,
|
||||||
|
taskDefCloudFormation,
|
||||||
|
taskDefStackNameTTL: cleanupTaskDefStackName,
|
||||||
|
ttlCloudFormation: cleanupCloudFormation,
|
||||||
|
taskDefResources,
|
||||||
|
baseResources,
|
||||||
|
logid: logGuid,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
import * as fs from 'fs';
|
||||||
|
|
||||||
|
export class AWSTemplates {
|
||||||
|
public static getParameterTemplate(p1) {
|
||||||
|
return `
|
||||||
|
${p1}:
|
||||||
|
Type: String
|
||||||
|
Default: ''
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getSecretTemplate(p1) {
|
||||||
|
return `
|
||||||
|
${p1}Secret:
|
||||||
|
Type: AWS::SecretsManager::Secret
|
||||||
|
Properties:
|
||||||
|
Name: !Join [ "", [ '${p1}', !Ref BUILDID ] ]
|
||||||
|
SecretString: !Ref ${p1}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getSecretDefinitionTemplate(p1, p2) {
|
||||||
|
return `
|
||||||
|
- Name: '${p1}'
|
||||||
|
ValueFrom: !Ref ${p2}Secret
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static insertAtTemplate(template, insertionKey, insertion) {
|
||||||
|
const index = template.search(insertionKey) + insertionKey.length + '\n'.length;
|
||||||
|
template = [template.slice(0, index), insertion, template.slice(index)].join('');
|
||||||
|
return template;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readTaskCloudFormationTemplate(): string {
|
||||||
|
return fs.readFileSync(`${__dirname}/cloud-formations/task-def-formation.yml`, 'utf8');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -49,7 +49,7 @@ class CloudRunner {
|
||||||
CloudRunnerState.branchName,
|
CloudRunnerState.branchName,
|
||||||
CloudRunnerState.defaultSecrets,
|
CloudRunnerState.defaultSecrets,
|
||||||
);
|
);
|
||||||
await CloudRunner.runJob(baseImage);
|
await CloudRunner.runJob(baseImage.toString());
|
||||||
await CloudRunnerState.CloudRunnerProviderPlatform.cleanupSharedBuildResources(
|
await CloudRunnerState.CloudRunnerProviderPlatform.cleanupSharedBuildResources(
|
||||||
CloudRunnerState.buildGuid,
|
CloudRunnerState.buildGuid,
|
||||||
CloudRunnerState.buildParams,
|
CloudRunnerState.buildParams,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue