Last active
March 13, 2020 06:39
-
-
Save joona/925920873419fe276fd8022ba2484946 to your computer and use it in GitHub Desktop.
Simplisting YAML syntax processor for CF
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use strict"; | |
const config = global.dasBootConfig; | |
const path = require('path'); | |
const fs = require('fs'); | |
const jsonjs = require('jsonjs'); | |
const handlebars = require('handlebars'); | |
const bucket = config.basePath; | |
const BASE_PATH = config.basePath; | |
class YamlProcessorException extends Error { | |
constructor(message) { | |
super(message); | |
} | |
} | |
class YamlProcessorPartial { | |
constructor(templateLines) { | |
this.templateLines = templateLines; | |
} | |
render() { | |
return this.templateLines; | |
} | |
} | |
var argumentsToObject = function(string) { | |
var parts = string.split(' '); | |
return parts.reduce((obj, x) => { | |
var y = x.split('='); | |
obj[y[0]] = y[1]; | |
return obj; | |
}, {}); | |
}; | |
const TAGS = { | |
dasPartial: { | |
tag: '!DasPartial', | |
minMatches: 1, | |
regexp: new RegExp("!DasPartial (\\S+)(\\W+(.*))?$"), | |
process(matches, ctx, tag) { | |
//console.log('!DasPartial matches:', matches); | |
var templatePath = matches[1]; | |
var stringArguments = matches[3] || ''; | |
var args = argumentsToObject(stringArguments); | |
var templateContext = Object.assign({ _: args, _args: stringArguments }, ctx.object()); | |
var template = fs.readFileSync(path.join(BASE_PATH, templatePath)); | |
var compiled = handlebars.compile(template.toString()); | |
var processed = compiled(templateContext); | |
//console.log('Processed partial template:', processed); | |
var processor = new YamlProcessor(processed, templateContext); | |
processor.process(); | |
var processedLines = processor.processedLines; | |
processedLines.unshift(`# ${matches[0]}`); | |
return new YamlProcessorPartial(processedLines); | |
} | |
}, | |
dasEnv: { | |
tag: '!DasEnv', | |
minMatches: 1, | |
regexp: new RegExp("!DasEnv ([a-zA-Z0-9._]+)"), | |
process(matches, ctx, tag) { | |
var key = matches[1]; | |
var keyParts = key.split('.'); | |
keyParts.unshift('environment'); | |
var value = ctx.get(keyParts); | |
return value; | |
} | |
}, | |
dasContext: { | |
tag: '!DasContext', | |
minMatches: 1, | |
regexp: new RegExp("!DasContext ([a-zA-Z0-9._]+)(\\W+(.*))?$"), | |
process(matches, ctx, tag) { | |
var key = matches[1]; | |
var keyParts = key.split('.'); | |
keyParts.unshift('context'); | |
var value = ctx.get(keyParts); | |
if(!value) return; | |
// string value modifies, like Limit | |
if(matches[3]) { | |
var args = argumentsToObject(matches[3]); | |
if(args.Limit) { | |
value = value.substr(0, args.Limit); | |
} | |
if(args.Lower) { | |
value = value.toLowerCase(); | |
} | |
} | |
return value; | |
} | |
}, | |
/* | |
dasExportNamespace: { | |
tag: '!InbotExportNamespace', | |
minMatches: 2, | |
regexp: new RegExp("!DasExportNamespace ([a-zA-Z0-9._]+) ([a-zA-Z0-9._]+)"), | |
process(matches, ctx, tag) { | |
var scope = matches[1]; | |
var suffix = matches[2]; | |
if(!scope || !suffix) { | |
throw new Error('Validation error, check parameters!'); | |
} | |
switch(scope) { | |
case 'Environment': | |
var parts = ctx.get('context', 'environmentName').split('-'); | |
var environmentBase = parts[1]; | |
var environmentName = parts[0]; | |
if(ctx.get('context', 'stackType') == 'environment') { | |
environmentBase = ctx.get('context', 'environmentName'); | |
environmentName = ctx.get('context', 'stackName'); | |
} | |
if(!environmentBase) { | |
throw new Error(`DasExportNamespace: Unable to figure out base environment from: ${parts}`); | |
} | |
if(!environmentName) { | |
throw new Error(`DasExportNamespace: Unable to figure out environment name from: ${parts}`); | |
} | |
return `${environmentBase}${environmentName}${suffix}`; | |
} | |
return; | |
} | |
},*/ | |
dasImportValue: { | |
tag: '!DasImportValue', | |
minMatches: 1, | |
regexp: new RegExp("!DasImportValue ([a-zA-Z0-9._]+)"), | |
process(matches, ctx, tag) { | |
var key = matches[1]; | |
if(!key) { | |
throw new Error('ValidationError, check parameters!'); | |
} | |
var outputs = ctx.getOrCreateDecoratedObject('upstreamOutputs'); | |
return outputs.get(key); | |
} | |
} | |
}; | |
class YamlProcessor { | |
constructor(template, context) { | |
this.template = template; | |
this.context = jsonjs.decorate(context); | |
this.processedLines = []; | |
this.tagKeys = Object.keys(TAGS); | |
} | |
toString() { | |
return this.processedLines.join("\n"); | |
} | |
process() { | |
var templateLines = this.getLines(); | |
for(var i = 0; i < templateLines.length; i++) { | |
var lines = this.processLine(templateLines[i], i+1); | |
if(lines && lines.length > 0) { | |
for(var ii = 0; ii < lines.length; ii++) { | |
this.processedLines.push(lines[ii]); | |
} | |
} else { | |
this.processedLines.push(templateLines[i]); | |
} | |
} | |
return this.toString(); | |
} | |
processLine(line, ln) { | |
var results = []; | |
for(var i = 0; i < this.tagKeys.length; i++) { | |
const tag = TAGS[this.tagKeys[i]]; | |
if(line.indexOf(tag.tag) < 0) { | |
continue; | |
} | |
var matches = tag.regexp.exec(line); | |
if(matches && matches.length > 0) { | |
if((matches.length - 1) < tag.minMatches) { | |
throw new YamlProcessorException(`Found tag ${tag.tag} on line ${ln} expecting ${tag.minMatches} values, but found only ${matches.length-1}`); | |
} | |
var value; | |
try { | |
value = tag.process(matches, this.context, tag); | |
} catch(err) { | |
throw new YamlProcessorException(`Problem while processing tag ${tag.tag} on line ${ln}. Processor error: ${err.message}`); | |
} | |
var pre = line.substr(0, matches.index); | |
var whitespace = ''; | |
for(var wi = 0; line.charAt(wi) == ' '; wi++) { | |
whitespace += ' '; | |
} | |
if(Array.isArray(value)) { | |
results.push(`${pre} # (${matches[0]})`); | |
for(var ii = 0; ii < value.length; ii++) { | |
results.push(`${whitespace} - ${value[ii]}`); | |
} | |
} | |
else if(value instanceof YamlProcessorPartial) { | |
var arr = value.render(); | |
for(var yi = 0; yi < arr.length; yi++) { | |
results.push(`${whitespace}${arr[yi]}`); | |
} | |
} | |
else if(typeof value === "string") { | |
if(value.charAt(0) === '!') { | |
results.push(`${pre}${value} # (${matches[0]})`); | |
} else { | |
results.push(`${pre}"${value}" # (${matches[0]})`); | |
} | |
} | |
else { | |
console.error('Return value:', value); | |
throw new Error(`Found tag ${tag.tag} on line ${ln}, but got invalid return value from processor "${typeof value}"`); | |
} | |
} else { | |
throw new Error(`Found tag ${tag.tag} on line ${ln}, but line ${line} it's not matching regexp provided [${tag.regexp.toString()}]`) | |
} | |
} | |
return results; | |
} | |
getLines() { | |
return this.template.split("\n"); | |
} | |
} | |
module.exports.create = function(template, context) { | |
return new YamlProcessor(template, context); | |
}; | |
// smoketest | |
if(!module.parent) { | |
var defaultUpstream = { | |
EnvironmentSecurityGroupId: 'environmentSG', | |
BaseEnvironmentSecurityGroupId: 'baseSG', | |
VpnBaseSecurityGroupId: 'vpnSG', | |
VpcId: 'foo-vpc' | |
}; | |
var template = require('fs').readFileSync('../../templates/das-api.yml'); | |
var processor = new YamlProcessor(template.toString(), { | |
environment: { | |
VPCZoneIdentifiers: ['a', 'b', 'c'], | |
AvailabilityZones: ['a', 'b', 'c'] | |
}, | |
upstreamOutputs: defaultUpstream, | |
context: { | |
stackName: 'Service-das-api-FooBar-FooEuWest' | |
} | |
}); | |
var stack = processor.process(); | |
template = require('fs').readFileSync('../../templates/new-testing-environment.yml'); | |
processor = new YamlProcessor(template.toString(), { | |
environment: { | |
VPCZoneIdentifiers: ['a', 'b', 'c'], | |
AvailabilityZones: ['a', 'b', 'c'] | |
}, | |
context: { | |
stackName: 'Environment-FooBar-FooEuWest' | |
} | |
}); | |
var environment = processor.process(); | |
console.log('====> Environment:'); | |
console.log(environment); | |
console.log('---'); | |
console.log('====> Stack:'); | |
console.log(stack); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment