UNPKG

template-configuration

Version:

CloudFormation + CodePipeline Template Configuration For CLI Tools

249 lines (221 loc) 6.4 kB
const path = require('path') const params = '--params' const parameters = '--parameters' const parameterOverrides = '--parameter-overrides' const identity = v => v const keyValue = { formatParameter: (key, value) => `${key}=${value}`, formatParameters: identity, formatTag: (key, value) => `${key}=${value}`, formatTags: identity } const keyNameValueName = { formatParameter: (key, value) => `ParameterKey=${key},ParameterValue=${value}`, formatParameters: identity, formatTag: (key, value) => `Key=${key},Value=${value}`, formatTags: identity } const keyNameValueNameNoTags = { formatParameter: (key, value) => `ParameterKey=${key},ParameterValue=${value}`, formatParameters: identity, formatTag: () => null, formatTags: () => [] } const keyNameValueNameJoinedCommand = { formatParameter: (key, value) => `${key}=${value}`, formatParameters: parameters => [parameters.join(',')], formatTag: (key, value) => `${key}=${value}`, formatTags: tags => [tags.join(',')] } const keyNameValueNameKeyValueTags = { formatParameter: (key, value) => `ParameterKey=${key},ParameterValue=${value}`, formatParameters: identity, formatTag: (key, value) => `${key}=${value}`, formatTags: identity } const nameValue = { formatParameter: (key, value) => `Name=${key},Value=${value}`, formatParameters: identity, formatTag: (key, value) => `Key=${key},Value=${value}`, formatTags: tags => [tags.join(',')] } const helpCommands = { aws: ['help'], sam: ['--help'], rain: ['--help', '-h'] } const parameterTypes = { aws: { cloudformation: { deploy: [parameterOverrides, keyValue], 'create-change-set': [parameters, keyNameValueName], 'create-stack': [parameters, keyNameValueName], 'create-stack-instances': [parameterOverrides, keyNameValueNameNoTags], 'create-stack-set': [parameters, keyNameValueName], 'update-stack': [parameters, keyNameValueName], 'update-stack-instances': [parameterOverrides, keyNameValueNameNoTags], 'update-stack-set': [parameters, keyNameValueName] }, serverlessrepo: { 'create-cloud-formation-change-set': [parameterOverrides, nameValue] } }, sam: { deploy: [parameterOverrides, keyNameValueNameKeyValueTags] }, rain: { deploy: [params, keyNameValueNameJoinedCommand] } } class TCCommand { constructor () { this.logger = console.error this.loggingEnabled = false this.dryRun = false this.ownCommand = null this.command = null this.commandName = null this.arguments = [] this.configurationFile = path.resolve('template-configuration', 'default.json') this.readConfigurationFunction = f => { throw new Error('No configuration read function defined') } this.existsConfigurationFunction = f => { throw new Error('No configuration exists function defined') } this.writeConfigurationFunction = f => { throw new Error('No configuration write function defined') } } isDryRun () { return this.dryRun } setLogger (logger) { this.logger = logger } log (str) { if (!this.loggingEnabled) { return } this.logger(str) } readConfiguration (f) { this.readConfigurationFunction = f } existsConfiguration (f) { this.existsConfigurationFunction = f } writeConfiguration (f) { this.writeConfigurationFunction = f } pushArguments (args) { args.forEach(arg => this.pushArgument(arg)) } pushArgument (arg) { if (this.arguments.length === 0) { if (arg === '--dryrun') { this.dryRun = true this.loggingEnabled = true return } else if (arg === '--debug') { this.loggingEnabled = true return } else if (['-c', '--config'].indexOf(arg) > -1) { this.nextArgumentConfig = true return } else if (this.nextArgumentConfig) { this.configurationFile = path.resolve(arg) delete this.nextArgumentConfig return } else if (arg === 'init') { this.ownCommand = 'init' return } } this.arguments.push(arg) } isHelp () { const cmd = this.arguments[0] const [help] = this.arguments.slice(-1) return helpCommands[cmd].indexOf(help) !== -1 } getParameterType () { let next = parameterTypes for (const arg of this.arguments) { const parameterType = next[arg] if (Array.isArray(parameterType)) { return this.isHelp(this.arguments) ? null : parameterType } if (!parameterType) { return } next = parameterType } } getParameterArguments ({ formatParameter, formatParameters }, parameters) { const keys = Object.keys(parameters) return formatParameters( keys.reduce( (acc, key) => [...acc, formatParameter(key, parameters[key])], [] ) ) } getTagArguments ({ formatTag, formatTags }, tags) { const keys = Object.keys(tags) return formatTags( keys.reduce( (acc, key) => [...acc, formatTag(key, tags[key])], [] ) ) } getArguments () { const { Parameters, Tags } = JSON.parse(this.readConfigurationFunction(this.configurationFile)) const parameterType = this.getParameterType() // TODO empty Parameters and Empty Tags const tagArguments = parameterType ? this.getTagArguments(parameterType[1], Tags) : [] const args = [ ...this.arguments, ...( parameterType && Object.keys(Parameters).length ? [parameterType[0], ...this.getParameterArguments(parameterType[1], Parameters)] : [] ), ...( tagArguments.length ? ['--tags', ...tagArguments] : [] ) ] this.log(args.join(' ')) return args } isOwnCommand () { return Boolean(this.ownCommand) } init () { this.loggingEnabled = true // implicit if (this.existsConfigurationFunction(this.configurationFile)) { throw new Error(`${this.configurationFile} already exists - not overwriting`) } this.writeConfigurationFunction(this.configurationFile, JSON.stringify({ Parameters: {}, Tags: {}, StackPolicy: { Statement: [] } }, null, 2)) this.log(`written ${this.configurationFile}`) } run () { this.init() } } module.exports = { TCCommand }