UNPKG

dce-dev-wizard

Version:

Wizard for managing development apps at Harvard DCE.

258 lines (235 loc) 8.67 kB
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;