dce-dev-wizard
Version:
Wizard for managing development apps at Harvard DCE.
258 lines (235 loc) • 8.67 kB
text/typescript
import os from 'os';
import clear from 'clear';
// Import helpers
import showChooser from '../helpers/showChooser';
import print from '../helpers/print';
import exec from '../helpers/exec';
import config from '../helpers/config';
import prompt from '../helpers/prompt';
// Import shared types
import Deployment from '../types/Deployment';
// Get home directory
const homeDir = os.homedir();
/**
* Connect/tunnel into a db
* @author Gabe Abrams
* @param deployment the currently selected deployment
*/
const connectToDatabase = async (deployment: Deployment) => {
// Get dbName
const { dbName } = config;
// Make sure the deployment has a db
if (!dbName) {
clear();
print.title('No Database!');
console.log('');
console.log('This deployment does not have a database.');
console.log('');
print.enterToContinue();
return;
}
// Destructure
const {
name,
app,
profile,
} = deployment;
// Kill apps
clear();
print.title('Connect to database');
console.log('\nEnter sudo to cleanup existing sessions:');
exec(
'sudo lsof -ti tcp:27018 | xargs kill',
true,
);
clear();
print.title('Connect to database');
console.log('');
// Run the connection script
const stackName = `CacclDeploy-${app}`;
// > Allow for alternate stack names
const bastionInstanceId = exec(
`aws cloudformation describe-stacks --stack-name ${stackName} --query "Stacks[].Outputs[?ExportName=='${stackName}-bastion-host-id'].OutputValue" --profile ${profile} --output text`,
false,
).trim();
console.log(`Bastion Instance Id: ${bastionInstanceId}`);
// > Endpoint/hostname of the db
const dbEndpoint = exec(
`aws cloudformation describe-stacks --stack-name ${stackName} --query "Stacks[].Outputs[?ExportName=='${stackName}-db-cluster-endpoint'].OutputValue" --profile ${profile} --output text`,
false,
).trim();
console.log(`DB Endpoint: ${dbEndpoint}`);
// > Arn of the secretsmanager secret where the db master password is stored
const dbPasswordSecret = exec(
`aws cloudformation describe-stacks --stack-name ${stackName} --query "Stacks[].Outputs[?ExportName=='${stackName}-db-password-secret-arn'].OutputValue" --profile ${profile} --output text`,
false,
).trim();
console.log(`DB Password Secret: ${dbPasswordSecret}`);
// > Get DB password
const dbPassword = exec(
`aws secretsmanager get-secret-value --secret-id ${dbPasswordSecret} --query 'SecretString' --profile ${profile} --output text`,
false,
).trim();
console.log(`DB Password: ${dbPassword}`);
// > Arn of the secretsmanager secret where the db availability zone is stored
const bastionAz = exec(
`aws cloudformation describe-stacks --stack-name ${stackName} --query "Stacks[].Outputs[?ExportName=='${stackName}-bastion-host-az'].OutputValue" --profile ${profile} --output text`,
false,
).trim();
console.log(`Bastion AZ: ${bastionAz}`);
// > Arn of the secretsmanager secret where the db IP is stored
const bastionIP = exec(
`aws cloudformation describe-stacks --stack-name ${stackName} --query "Stacks[].Outputs[?ExportName=='${stackName}-bastion-host-ip'].OutputValue" --profile ${profile} --output text`,
false,
).trim();
console.log(`Bastion IP: ${bastionIP}`);
// > Add public key to AWS for 60s
exec(
`aws ec2-instance-connect send-ssh-public-key --instance-id ${bastionInstanceId} --instance-os-user ec2-user --availability-zone ${bastionAz} --ssh-public-key file://${homeDir}/.ssh/id_rsa.pub --profile ${profile}`,
false,
);
console.log('Public key added to AWS');
// Start tunneling
console.log('Tunneling into AWS...');
exec(
`ssh -C -o StrictHostKeyChecking=no -f -L 27018:${dbEndpoint} ec2-user@${bastionIP} sleep 330`,
true,
);
// Finished
console.log('\nFinished! Scroll up for errors or more details.');
// Choose an operation
const { tag } = showChooser({
question: 'Operation:',
options: [
{
description: 'Robo 3T Setup Instructions',
tag: 'R',
},
{
description: 'Studio 3T Setup Instructions',
tag: 'S',
},
{
description: 'Export Collection',
tag: 'E',
},
{
description: 'Insert Items in Collection',
tag: 'I',
},
{
description: 'Upsert Items in Collection',
tag: 'U',
},
{
description: 'Back to Main Menu',
tag: 'B',
},
],
title: 'Choose an operation:',
});
// Robo 3T
if (tag === 'R') {
clear();
print.title('Robo 3T Setup Instructions');
console.log(
[
'1. Open Robo 3T, create a new connection',
'2. Fill out the "Connection" tab:',
` Name = ${name}`,
` Address = localhost`,
' Port = 27018',
'3. Fill out the "Authentication" tab:',
' Check the "Perform authentication" box',
` Database = ${dbName}`,
` Username = root`,
` Password = ${dbPassword}`,
'4. Fill out the "SSL" tab:',
' Check the "Use SSL protocol" box',
' Authentication Method = Self-signed certificate',
' Check the "Advanced Options" box',
' Invalid Hostnames = Allowed',
'5. Click "Test"',
'6. If the test is successful, click "Save"',
].join('\n'),
);
console.log('');
print.enterToContinue();
}
// Studio 3T
if (tag === 'S') {
clear();
print.title('Studio 3T Setup Instructions');
console.log(
[
'1. Open Studio 3T, create a new connection',
'2. Choose "Manually configure my connection settings", then click "Next"',
`3. Set the "Connection name" to ${name}`,
'4. Fill out the "Server" tab:',
' Connection Type = Standalone',
' Address = localhost',
' Port = 27018',
'5. Fill out the "Authentication" tab:',
' Authentication Mode = Legacy',
` Username = root`,
` Password = ${dbPassword}`,
` Authentication DB = ${dbName}`,
'6. Fill out the "SSL" tab:',
' Check the "Use SSL protocol to connect" box',
' Check the "Allow invalid hostnames" box',
'5. Click "Test Connection"',
'6. If the test is successful, click "Save"',
].join('\n'),
);
console.log('');
await print.enterToContinue();
}
// Export Collection
if (tag === 'E') {
clear();
print.title('Export Collection');
// Get the collection name
const collectionName = prompt('Collection Name: ');
const outFilename = `${collectionName}-${new Date().toISOString()}.json`;
console.log(`\nExporting collection to ${outFilename}\n`);
exec(
`mongoexport --collection=${collectionName} --host="localhost:27018" --db=${dbName} --out="${outFilename}" --username=root --password=${dbPassword} --ssl --sslAllowInvalidHostnames --sslAllowInvalidCertificates`,
true,
);
console.log(`\nExported to ${outFilename}\n`);
await print.enterToContinue();
}
// Insert Items in Collection
if (tag === 'I') {
clear();
print.title('Insert Items in Collection');
// Get the collection name
const collectionName = prompt('Collection Name: ');
console.log('\nCreate a JSON file (one entry per line), drop the file here, then press enter.');
const jsonPath = prompt('Path: ').trim();
console.log(`\nInserting items into collection "${collectionName}"\n`);
exec(
`mongoimport --collection=${collectionName} --host="localhost:27018" --db=${dbName} --username=root --password=${dbPassword} --ssl --sslAllowInvalidHostnames --sslAllowInvalidCertificates --file="${jsonPath}"`,
true,
);
print.title('Inserted Successfully');
await print.enterToContinue();
}
// Upsert Items in Collection
if (tag === 'U') {
clear();
print.title('Upsert Items in Collection');
// Get the collection name
const collectionName = prompt('Collection Name: ');
console.log('\nCreate a JSON file (one entry per line), drop the file here, then press enter.');
const jsonPath = prompt('Path: ').trim();
console.log(`\nUpserting items into collection "${collectionName}"\n`);
exec(
`mongoimport --upsert --collection=${collectionName} --host="localhost:27018" --db=${dbName} --username=root --password=${dbPassword} --ssl --sslAllowInvalidHostnames --sslAllowInvalidCertificates --file="${jsonPath}"`,
true,
);
print.title('Upsert Successful');
await print.enterToContinue();
}
};
export default connectToDatabase;