UNPKG

mira

Version:

NearForm Accelerator for Cloud Native Serverless AWS

246 lines 9.45 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.quickDeploy = exports.removeAssetDirectories = exports.getTemplateFiles = exports.getSiteBuckets = exports.getS3Buckets = exports.getS3 = exports.getEnvironment = exports.getBucketResources = exports.getBucketRefs = exports.getBucketObjects = exports.getAssetPrefix = exports.getAssetFiles = void 0; /** * Deploys Custom::CDKBucketDeployment resources without using the CDK * toolchain. */ const aws_sdk_1 = __importDefault(require("aws-sdk")); const assert_1 = __importDefault(require("assert")); const fs_1 = __importDefault(require("fs")); const safe_1 = __importDefault(require("colors/safe")); const child_process_1 = __importDefault(require("child_process")); const config_1 = __importDefault(require("config")); const assume_role_1 = require("../assume-role"); const cloudformation_1 = require("../sdk/cloudformation"); const glob_1 = __importDefault(require("glob")); const app_1 = require("./app"); const mira_config_1 = require("../config/mira-config"); let cdkFiles = fs_1.default.existsSync('cdk.out') ? fs_1.default.readdirSync('cdk.out') : []; let miraS3; /** * Gets the files within an asset folder. */ exports.getAssetFiles = async (id) => { assert_1.default(fs_1.default.existsSync(exports.getAssetPrefix(id)) && fs_1.default.statSync(exports.getAssetPrefix(id)).isDirectory(), 'A provided asset ID' + ' either did not exist or was not a directory. Was this intended?'); return new Promise((resolve, reject) => { glob_1.default(`${exports.getAssetPrefix(id)}/**/*`, (err, matches) => { if (err) { reject(err); } resolve(matches.map((match) => match.substr(exports.getAssetPrefix(id).length + 1))); }); }); }; /** * Gets the asset prefix given some ID. */ exports.getAssetPrefix = (id) => `cdk.out/asset.${id}`; /** * Gets the objects from a bucket. */ exports.getBucketObjects = async (Bucket) => { const s3 = await exports.getS3(); return s3.listObjects({ Bucket }).promise(); }; /** * Gets references for bucket. */ exports.getBucketRefs = async () => { const files = exports.getTemplateFiles(); const bucketsBySite = await exports.getSiteBuckets(); for (const file in files) { const template = files[file]; if (!template.Resources) { continue; } for (const name in template.Resources) { if (!template.Resources[name]) { continue; } const { Type, Properties } = template.Resources[name]; if (!Type) { continue; } if (Type !== 'Custom::CDKBucketDeployment') { continue; } if (!bucketsBySite[Properties.DestinationBucketName.Ref]) { // TODO: Throw an error or provide warning? console.warn('Something unexpected happened. Found a ' + 'Custom::CDKBucketDeployment with a DestinationBucketName' + ' that is unknown.', Properties.DestinationBucketName.Ref); continue; } bucketsBySite[Properties.DestinationBucketName.Ref].assets = Properties.SourceBucketNames.map(({ Ref }) => { return Ref.split(/AssetParameters/g)[1].split(/S3Bucket/g)[0]; }); } } return bucketsBySite; }; /** * Given some template JSON, grabs all resource objects that are of type * AWS::S3::Bucket. */ exports.getBucketResources = () => { const files = exports.getTemplateFiles(); const bucketsByFile = {}; for (const file in files) { const template = files[file]; if (!template.Resources) { continue; } for (const name in template.Resources) { const { Type } = template.Resources[name]; if (!Type) { continue; } if (Type !== 'AWS::S3::Bucket') { continue; } if (!bucketsByFile[file]) { bucketsByFile[file] = {}; } bucketsByFile[file][name] = template.Resources[name]; } } return bucketsByFile; }; /** * Gets the environment for Mira. */ exports.getEnvironment = () => { const env = mira_config_1.MiraConfig.getEnvironment(); return env; }; /** * Gets the S3 object. */ exports.getS3 = async () => { if (miraS3) { return miraS3; } const role = assume_role_1.getRoleArn(config_1.default.get(`accounts.${exports.getEnvironment().name}.profile`)); const awsConfig = await assume_role_1.assumeRole(role); aws_sdk_1.default.config = awsConfig; miraS3 = new aws_sdk_1.default.S3({ apiVersion: '2006-03-01' }); return miraS3; }; /** * Gets S3 buckets beginning with a prefix. * @param {String} prefix * @param {String} siteName */ exports.getS3Buckets = async (prefix, siteName) => { const s3 = await exports.getS3(); const response = await s3.listBuckets().promise(); if (!response || !response.Buckets) { throw new Error('Failed to retrieve buckets.'); } prefix = prefix.toLowerCase().slice(0, 30); siteName = siteName.toLowerCase(); const bucketPrefix = `${prefix}-${siteName}`; const targetBuckets = response.Buckets.filter(({ Name }) => { return Name.startsWith(bucketPrefix); }); return targetBuckets; }; /** * For a given template file, gets all site buckets. */ exports.getSiteBuckets = async () => { const files = exports.getTemplateFiles(); const siteBuckets = {}; const bucketsByFile = exports.getBucketResources(); for (const file in files) { if (!bucketsByFile[file]) { continue; } for (const name in bucketsByFile[file]) { const { Properties } = bucketsByFile[file][name]; const { Value: stackName } = Properties.Tags.find(({ Key }) => Key === 'StackName'); const s3Buckets = await exports.getS3Buckets(stackName, name); siteBuckets[name] = { s3: s3Buckets.map(({ Name }) => Name) }; } } return siteBuckets; }; /** * Gets the template files for the given CWD. */ exports.getTemplateFiles = () => { const templateFiles = {}; cdkFiles = cdkFiles.filter((file) => file.endsWith('.template.json')); for (const file of cdkFiles) { templateFiles[file] = JSON.parse(fs_1.default.readFileSync(`cdk.out/${file}`, 'utf8')); } return templateFiles; }; /** * Removes assets directories. */ exports.removeAssetDirectories = () => { for (const dir of cdkFiles) { if (fs_1.default.statSync(`cdk.out/${dir}`).isDirectory()) { child_process_1.default.execSync(`rm -rf ${dir}`, { cwd: `${process.cwd()}/cdk.out` }); } } }; /** * Quickly deploys an asset bundle generated by CDK to an intended S3 bucket * as defined by a CDK generated Cfn template. */ exports.quickDeploy = async () => { const sites = await exports.getBucketRefs(); const s3 = await exports.getS3(); for (const site in sites) { const { s3: buckets, assets } = sites[site]; for (const Bucket of buckets) { console.info(safe_1.default.yellow('Updating Bucket'), Bucket); for (const id of assets) { const files = await exports.getAssetFiles(id); for (const file of files) { const obj = { ACL: 'public-read', Body: fs_1.default.readFileSync(`${exports.getAssetPrefix(id)}/${file}`, 'utf8'), Bucket, ContentType: require('mime-types').lookup(file), Key: file }; if (app_1.MiraApp.isVerbose()) { console.info(`Putting object: ${JSON.stringify(obj, null, 2)}`); } else { console.info(`\n${safe_1.default.yellow('Putting object:')}\n${file}`); } const result = await s3.putObject(obj).promise(); if (app_1.MiraApp.isVerbose()) { console.info(`Put object: ${JSON.stringify(result, null, 2)}`); } console.info(`${safe_1.default.cyan('File Available at')}: https://${Bucket}.s3-${exports.getEnvironment().env.region}.amazonaws.com/${file}`); } console.info(safe_1.default.green('Done Updating Bucket')); const stackResources = await cloudformation_1.getInstanceResourcesByType(); for (const stackName in stackResources) { if (Object.keys(stackResources[stackName]).indexOf('AWS::CloudFront::Distribution') >= 0) { console.warn(safe_1.default.yellow('Deployment Notice'), 'Your stack includes a CloudFront. You may need to refresh your' + ' distribution\'s cache.'); break; } } } } } }; //# sourceMappingURL=deploy-buckets.js.map