389 lines
14 KiB
JavaScript
389 lines
14 KiB
JavaScript
const { RDSClient, StartDBClusterCommand, StartDBInstanceCommand, StopDBInstanceCommand, StopDBClusterCommand } = require("@aws-sdk/client-rds");
|
|
const { EC2Client, DescribeInstancesCommand, StopInstancesCommand, StartInstancesCommand } = require("@aws-sdk/client-ec2");
|
|
const { IAMClient, CreateAccessKeyCommand, ListAccessKeysCommand, DeleteAccessKeyCommand } = require("@aws-sdk/client-iam");
|
|
const { RedshiftClient, PauseClusterCommand, ResumeClusterCommand } = require("@aws-sdk/client-redshift");
|
|
|
|
const db = require("./mongo");
|
|
const utils = require("./utils");
|
|
|
|
function _getRgName(provision) {
|
|
let rgName = provision.scenario.toUpperCase();
|
|
rgName = rgName.replace(/AZQMI/g, 'QMI');
|
|
rgName = rgName + "-" + provision._id.toString();
|
|
return rgName;
|
|
}
|
|
|
|
function _getRegion(provision) {
|
|
let region = "eu-west-1";
|
|
if ( provision.deployOpts.location === 'East US') {
|
|
region = "us-east-1";
|
|
} else if ( provision.deployOpts.location === 'Southeast Asia' ) {
|
|
region = "ap-southeast-1";
|
|
}
|
|
return region;
|
|
}
|
|
|
|
function _getDBType(provision) {
|
|
let type = "instance";
|
|
if (provision.options && provision.options.db && provision.options.db.selected && provision.options.db.selected.value) {
|
|
let selectedLower = provision.options.db.selected.value.toLowerCase();
|
|
if (selectedLower.indexOf("aurora") !== -1) {
|
|
type = "cluster";
|
|
} else {
|
|
type = "instance";
|
|
}
|
|
}
|
|
return type;
|
|
}
|
|
|
|
function _getDbIndentifier(provision) {
|
|
var out;
|
|
if ( provision.outputs['db_instance_id'] ) {
|
|
out = provision.outputs['db_instance_id'];
|
|
} else if (provision.outputs['db_instance_endpoint']) {
|
|
out = provision.outputs['db_instance_endpoint'].split(".")[0];
|
|
}
|
|
return out;
|
|
}
|
|
|
|
async function rotateAccessKey(provision) {
|
|
|
|
if (!provision || !provision.outputs ) {
|
|
console.log(`AWSCLI# rotateAccessKey - Provision not found or Provision (${provision._id}) does not contain Outputs`);
|
|
return null;
|
|
}
|
|
|
|
var username;
|
|
var prefix = "";
|
|
if (provision.outputs['iam_user_name']){
|
|
username = provision.outputs['iam_user_name'];
|
|
} else if ( provision.outputs['S3_iam_user_name'] ) {
|
|
prefix = "S3_";
|
|
username = provision.outputs['S3_iam_user_name'];
|
|
} else if ( provision.outputs['s3_iam_user_name'] ) {
|
|
prefix = "s3_";
|
|
username = provision.outputs['s3_iam_user_name'];
|
|
} else {
|
|
console.log(`AWSCLI# rotateAccessKey - Provision (${provision._id}) does not contain an IAM user`);
|
|
return null;
|
|
}
|
|
|
|
|
|
try {
|
|
const client = new IAMClient({ region: _getRegion(provision) });
|
|
// Step 1: Create a new access key
|
|
const createKeyCommand = new CreateAccessKeyCommand({ UserName: username });
|
|
const newKey = await client.send(createKeyCommand);
|
|
|
|
console.log(`AWSCLI# rotateAccessKey (${username}) - New Access Key Created:`, newKey.AccessKey);
|
|
|
|
// You should now update your application with newKey.AccessKeyId and newKey.SecretAccessKey
|
|
|
|
// Step 2: List existing access keys
|
|
const listKeysCommand = new ListAccessKeysCommand({ UserName: username });
|
|
const keys = await client.send(listKeysCommand);
|
|
|
|
// Find the old access key (assuming we only need to keep the latest one)
|
|
for (const key of keys.AccessKeyMetadata) {
|
|
if (key.AccessKeyId !== newKey.AccessKey.AccessKeyId) {
|
|
// Step 3: Delete the old access key
|
|
console.log(`AWSCLI# rotateAccessKey (${username}): Deleting old access key: ${key.AccessKeyId}`);
|
|
const deleteKeyCommand = new DeleteAccessKeyCommand({
|
|
UserName: username,
|
|
AccessKeyId: key.AccessKeyId,
|
|
});
|
|
await client.send(deleteKeyCommand);
|
|
}
|
|
}
|
|
|
|
console.log(`AWSCLI# rotateAccessKey (${username}): Old Access Key Deleted Successfully.`);
|
|
let outputs = provision.outputs;
|
|
outputs[prefix+'iam_user_access_key'] = newKey.AccessKey.AccessKeyId;
|
|
outputs[prefix+'iam_user_access_secret'] = newKey.AccessKey.SecretAccessKey;
|
|
|
|
return await db.provision.update(provision._id, {"outputs": outputs});
|
|
|
|
} catch (error) {
|
|
console.log("AWSCLI# rotateAccessKey Error rotating access key:", error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
async function checkAccessKeys(provision) {
|
|
|
|
if (!provision || !provision.outputs ) {
|
|
console.log(`AWSCLI# checkAccessKeys - Provision not found or Provision (${provision._id}) does not contain Outputs`);
|
|
return null;
|
|
}
|
|
|
|
var username;
|
|
if (provision.outputs['iam_user_name']){
|
|
username = provision.outputs['iam_user_name'];
|
|
} else if ( provision.outputs['S3_iam_user_name'] ) {
|
|
username = provision.outputs['S3_iam_user_name'];
|
|
} else if ( provision.outputs['s3_iam_user_name'] ) {
|
|
username = provision.outputs['s3_iam_user_name'];
|
|
} else {
|
|
console.log(`AWSCLI# checkAccessKeys - Provision (${provision._id}) does not contain an IAM user`);
|
|
return null;
|
|
}
|
|
|
|
try {
|
|
const client = new IAMClient({ region: _getRegion(provision) });
|
|
// Step 1: List all access keys for the user
|
|
const listKeysCommand = new ListAccessKeysCommand({ UserName: username });
|
|
const response = await client.send(listKeysCommand);
|
|
|
|
console.log(`AWSCLI# checkAccessKeys (${username})`);
|
|
var out = [];
|
|
response.AccessKeyMetadata.forEach((key) => {
|
|
|
|
// Step 2: Check if the key is older than 90 days
|
|
const createdDate = new Date(key.CreateDate);
|
|
const now = new Date();
|
|
const ageInDays = (now - createdDate) / (1000 * 60 * 60 * 24);
|
|
|
|
key.ageInDays = ageInDays;
|
|
key.is90dExpired = ageInDays > 90;
|
|
out.push(key);
|
|
|
|
});
|
|
|
|
if (out.length === 0) {
|
|
out.push({
|
|
is90dExpired: true
|
|
});
|
|
}
|
|
|
|
return out;
|
|
|
|
} catch (error) {
|
|
console.error("Error checking access keys:", error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
async function stopDbInstance(provision, triggerUserId, isSendEmailAfter) {
|
|
if (provision.statusVms === 'Stopped' || provision.statusVms === 'Stopping'){
|
|
return;
|
|
}
|
|
var identifier = _getDbIndentifier(provision);
|
|
|
|
if ( !identifier ) {
|
|
return;
|
|
}
|
|
|
|
await db.provision.update(provision._id.toString(), {"statusVms": "Stopping" });
|
|
db.event.add({ user: triggerUserId, provision: provision._id, type: `db.stopping` });
|
|
|
|
let region = _getRegion(provision);
|
|
let type = _getDBType(provision);
|
|
|
|
console.log(`AWSCLI# DB (${type})-(${identifier}) in region (${region}) stopping...`);
|
|
|
|
try {
|
|
const client = new RDSClient({ region: region });
|
|
|
|
var params, command;
|
|
if (type === "instance") {
|
|
params = {
|
|
DBInstanceIdentifier: identifier
|
|
};
|
|
command = new StopDBInstanceCommand(params);
|
|
} else {
|
|
params = {
|
|
DBClusterIdentifier: identifier
|
|
};
|
|
command = new StopDBClusterCommand(params);
|
|
}
|
|
|
|
const response = await client.send(command);
|
|
console.log(`AWSCLI# DB (${type})-(${identifier}) stopped!`);
|
|
//console.log(`AWSCLI# DB`, response);
|
|
return response;
|
|
} catch (error) {
|
|
console.log("AWSCLI# ERROR stopping DB: "+identifier, error);
|
|
await db.provision.update(provision._id.toString(), {"statusVms": "Running" });
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
async function startDbInstance(provision, triggerUserId) {
|
|
if (provision.statusVms === 'Running' || provision.statusVms === 'Starting'){
|
|
return;
|
|
}
|
|
|
|
var identifier = _getDbIndentifier(provision);
|
|
|
|
if ( !identifier ) {
|
|
return;
|
|
}
|
|
|
|
await db.provision.update(provision._id.toString(), {"statusVms": "Starting" });
|
|
db.event.add({ user: triggerUserId, provision: provision._id, type: `db.starting` });
|
|
|
|
let region = _getRegion(provision);
|
|
let type = _getDBType(provision);
|
|
|
|
console.log(`AWSCLI# DB (${type})-(${identifier}) in region (${region}) starting...`);
|
|
|
|
try {
|
|
const client = new RDSClient({ region: region });
|
|
var params, command;
|
|
if (type === "instance") {
|
|
params = {
|
|
DBInstanceIdentifier: identifier
|
|
};
|
|
command = new StartDBInstanceCommand(params);
|
|
} else {
|
|
params = {
|
|
DBClusterIdentifier: identifier
|
|
};
|
|
command = new StartDBClusterCommand(params);
|
|
}
|
|
const response = await client.send(command);
|
|
console.log(`AWSCLI# DB (${identifier}) started!`);
|
|
await utils.afterStartVms( provision, triggerUserId, "db" );
|
|
//console.log(`AWSCLI# DB`, response);
|
|
return response;
|
|
} catch (error) {
|
|
console.log("AWSCLI# ERROR starting DB: "+identifier, error);
|
|
await db.provision.update(provision._id.toString(), {"statusVms": "Stopped" });
|
|
return;
|
|
}
|
|
}
|
|
|
|
async function deallocate(provision, triggerUserId, isSendEmailAfter) {
|
|
|
|
let rgName = _getRgName(provision);
|
|
let region = _getRegion(provision);
|
|
|
|
console.log("AWSCLI# Stopping EC2s for resource group: "+rgName);
|
|
|
|
var client = new EC2Client({region: region});
|
|
|
|
var params = {
|
|
DryRun: false,
|
|
Filters: [
|
|
{
|
|
Name: 'tag:Name',
|
|
Values: [ `fort-${provision._id}` ]
|
|
},
|
|
]
|
|
};
|
|
|
|
try {
|
|
var command = new DescribeInstancesCommand(params);
|
|
const data = await client.send(command);
|
|
if (data && data.Reservations && data.Reservations.length) {
|
|
var ec2Ids = data.Reservations[0].Instances.map(ec2=> ec2.InstanceId);
|
|
console.log("AWSCLI# Stopping Ec2s ids...", ec2Ids);
|
|
params = {
|
|
DryRun: false,
|
|
InstanceIds: ec2Ids
|
|
};
|
|
command = new StopInstancesCommand(params);
|
|
await client.send(command);
|
|
await utils.afterStopVms( provision, triggerUserId, isSendEmailAfter );
|
|
}
|
|
} catch (error) {
|
|
console.log("AWSCLI# ERROR describing/stopping EC2s: "+rgName, error);
|
|
await db.provision.update(provision._id.toString(), {"statusVms": "Running" });
|
|
}
|
|
|
|
}
|
|
|
|
async function start(provision, triggerUserId) {
|
|
|
|
let rgName = _getRgName(provision);
|
|
let region = _getRegion(provision);
|
|
|
|
console.log("AWSCLI# Stopping EC2s for resource group: "+rgName);
|
|
|
|
var client = new EC2Client({region: region});
|
|
|
|
var params = {
|
|
DryRun: false,
|
|
Filters: [
|
|
{
|
|
Name: 'tag:Name',
|
|
Values: [ `fort-${provision._id}` ]
|
|
},
|
|
]
|
|
};
|
|
|
|
try {
|
|
var command = new DescribeInstancesCommand(params);
|
|
const data = await client.send(command);
|
|
if (data && data.Reservations && data.Reservations.length) {
|
|
var ec2Ids = data.Reservations[0].Instances.map(ec2=> ec2.InstanceId);
|
|
console.log("AWSCLI# Starting Ec2s ids...", ec2Ids);
|
|
params = {
|
|
DryRun: false,
|
|
InstanceIds: ec2Ids
|
|
};
|
|
command = new StartInstancesCommand(params);
|
|
await client.send(command);
|
|
console.log("AWSCLI# Ec2s started!");
|
|
await utils.afterStartVms( provision, triggerUserId );
|
|
}
|
|
} catch (error) {
|
|
console.log("AWSCLI# ERROR describing/starting EC2s: "+rgName, error);
|
|
await db.provision.update(provision._id.toString(), {"statusVms": "Stopped" });
|
|
}
|
|
|
|
}
|
|
|
|
// Stop Redshift cluster
|
|
async function stopRedshiftCluster(provision, triggerUserId) {
|
|
/*if (!provision || !provision.outputs || !provision.outputs['redshift_cluster_id']) {
|
|
console.log('AWSCLI# stopRedshiftCluster - Missing Redshift cluster ID in provision');
|
|
return null;
|
|
}*/
|
|
const clusterId = "qmi-"+provision._id.toString();
|
|
const region = _getRegion(provision);
|
|
try {
|
|
const client = new RedshiftClient({ region });
|
|
const command = new PauseClusterCommand({ ClusterIdentifier: clusterId });
|
|
const response = await client.send(command);
|
|
console.log(`AWSCLI# Redshift cluster (${clusterId}) paused.`);
|
|
await utils.afterStopVms( provision, triggerUserId, false, "redshift-cluster" );
|
|
return response;
|
|
} catch (error) {
|
|
console.log(`AWSCLI# ERROR pausing Redshift cluster: ${clusterId}`, error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// Start Redshift cluster
|
|
async function startRedshiftCluster(provision, triggerUserId) {
|
|
/*if (!provision || !provision.outputs || !provision.outputs['redshift_cluster_id']) {
|
|
console.log('AWSCLI# startRedshiftCluster - Missing Redshift cluster ID in provision');
|
|
return null;
|
|
}*/
|
|
const clusterId = "qmi-"+provision._id.toString();
|
|
const region = _getRegion(provision);
|
|
try {
|
|
const client = new RedshiftClient({ region });
|
|
const command = new ResumeClusterCommand({ ClusterIdentifier: clusterId });
|
|
const response = await client.send(command);
|
|
console.log(`AWSCLI# Redshift cluster (${clusterId}) resumed.`);
|
|
await utils.afterStartVms( provision, triggerUserId, "redshift-cluster" );
|
|
return response;
|
|
} catch (error) {
|
|
console.log(`AWSCLI# ERROR resuming Redshift cluster: ${clusterId}`, error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
|
|
module.exports.deallocate = deallocate;
|
|
module.exports.start = start;
|
|
module.exports.stopDbInstance = stopDbInstance;
|
|
module.exports.startDbInstance = startDbInstance;
|
|
module.exports.rotateAccessKey = rotateAccessKey;
|
|
module.exports.checkAccessKeys = checkAccessKeys;
|
|
module.exports.stopRedshiftCluster = stopRedshiftCluster;
|
|
module.exports.startRedshiftCluster = startRedshiftCluster;
|