UNPKG

@mapbox/cloudfriend

Version:

Helper functions for assembling CloudFormation templates in JavaScript

132 lines (112 loc) 4.27 kB
'use strict'; const assert = require('assert'); /** * Merges two or more templates together into one. * * @static * @ignore * @memberof cloudfriend * @name merge * @param {...object} template - a CloudFormation template to merge with * @returns {object} a CloudFormation template including all the Metadata, * Parameters, Rules, Mappings, Conditions, Resources, and Outputs from the * input templates * @throws errors when there is overlap in logical resource names between * templates */ module.exports = function() { // This should capture all the top-level keys described in // https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-anatomy.html const template = { AWSTemplateFormatVersion: '2010-09-09', Metadata: {}, Parameters: {}, Rules: {}, Mappings: {}, Conditions: {}, Resources: {}, Outputs: {}, // An empty array is rejected by CFN validation. Transform: undefined }; const names = { Metadata: new Set(), Parameters: new Set(), Rules: new Set(), Mappings: new Set(), Conditions: new Set(), Resources: new Set(), Outputs: new Set(), Transform: new Set() }; for (const arg of arguments) { if (arg.Metadata) Object.keys(arg.Metadata).forEach((key) => { if (names.Metadata.has(key)) { try { assert.deepEqual(template.Metadata[key], arg.Metadata[key]); } catch (err) { throw new Error('Metadata name used more than once: ' + key); } } template.Metadata[key] = arg.Metadata[key]; names.Metadata.add(key); }); if (arg.Parameters) Object.keys(arg.Parameters).forEach((key) => { if (names.Parameters.has(key)) { try { assert.deepEqual(template.Parameters[key], arg.Parameters[key]); } catch (err) { throw new Error('Parameters name used more than once: ' + key); } } template.Parameters[key] = arg.Parameters[key]; names.Parameters.add(key); }); if (arg.Rules) Object.keys(arg.Rules).forEach((key) => { if (names.Rules.has(key)) { try { assert.deepEqual(template.Rules[key], arg.Rules[key]); } catch (err) { throw new Error('Rules name used more than once: ' + key); } } template.Rules[key] = arg.Rules[key]; names.Rules.add(key); }); if (arg.Mappings) Object.keys(arg.Mappings).forEach((key) => { if (names.Mappings.has(key)) { try { assert.deepEqual(template.Mappings[key], arg.Mappings[key]); } catch (err) { throw new Error('Mappings name used more than once: ' + key); } } template.Mappings[key] = arg.Mappings[key]; names.Mappings.add(key); }); if (arg.Conditions) Object.keys(arg.Conditions).forEach((key) => { if (names.Conditions.has(key)) { try { assert.deepEqual(template.Conditions[key], arg.Conditions[key]); } catch (err) { throw new Error('Conditions name used more than once: ' + key); } } template.Conditions[key] = arg.Conditions[key]; names.Conditions.add(key); }); if (arg.Resources) Object.keys(arg.Resources).forEach((key) => { if (names.Resources.has(key)) { try { assert.deepEqual(template.Resources[key], arg.Resources[key]); } catch (err) { throw new Error('Resources name used more than once: ' + key); } } template.Resources[key] = arg.Resources[key]; names.Resources.add(key); }); if (arg.Outputs) Object.keys(arg.Outputs).forEach((key) => { if (names.Outputs.has(key)) { try { assert.deepEqual(template.Outputs[key], arg.Outputs[key]); } catch (err) { throw new Error('Outputs name used more than once: ' + key); } } template.Outputs[key] = arg.Outputs[key]; names.Outputs.add(key); }); if (arg.Transform) { if (!template.Transform) template.Transform = []; const transforms = typeof arg.Transform === 'string' ? [arg.Transform] : arg.Transform; for (const transform of transforms) { if (names.Transform.has(transform)) { throw new Error('Transform macro used more than once: ' + transform); } template.Transform.push(transform); names.Transform.add(transform); } } } return template; };