diff --git a/bin/renderlocal b/bin/renderlocal new file mode 100755 index 00000000..d1b039f5 --- /dev/null +++ b/bin/renderlocal @@ -0,0 +1,56 @@ +#!/usr/bin/env node + +const nopt = require('nopt'); +const path = require('path'); + +const { FetchEnvs, MockController } = require('@razee/razeedeploy-core'); +const IOUtils = require('../src/IOUtils'); +const LocalMustacheTemplateController = require('../src/LocalMustacheTemplateController'); + +const args = nopt( + { + mtp: path, + out: path, + env: [path, Array] + }, + {}, process.argv, 2 +); + +if (args.mtp === undefined) { + console.error("exactly one --mtp is required!"); + process.exit(1); +} + +if (args.env === undefined || args.env.length < 1) { + console.error("at least one --env is required!"); + process.exit(1); +} + +async function renderLocal() { + const mtp = await IOUtils.readYamlFile(args.mtp); + const kubeData = await IOUtils.kubeDataFromYamlFiles(...args.env); + + const eventData = { + type: 'ADDED', + object: mtp[0] + } + + const fetchEnvs = new FetchEnvs(new MockController(eventData, kubeData)); + const view = await fetchEnvs.get('spec'); + + const mtpController = new LocalMustacheTemplateController({eventData: eventData, kubeData: kubeData}); + let templates = mtpController.concatTemplates(); + await mtpController.processTemplate(templates, view); + + if (templates.length > 1) { + throw Error("can only handle one template!"); + } + + if (args.out === undefined) { + await IOUtils.printYaml(templates[0]); + } else { + await IOUtils.writeYamlFile(templates[0], args.out) + } +}; + +renderLocal(); diff --git a/package.json b/package.json index 1782a85a..30f37d22 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,10 @@ "version": "0.0.0-dev", "description": "Razee: component to use the Mustache template processor on kubernetes resource configurations.", "main": "./src/index.js", - "bin": "./bin/mustachetemplate", + "bin": { + "mustachetemplate": "./bin/mustachetemplate", + "renderlocal": "./bin/renderlocal" + }, "keywords": [ "kubernetes", "razee", @@ -45,6 +48,7 @@ "handlebars": "^4.7.7", "js-yaml": "^4.1.0", "mustache": "^4.2.0", + "nopt": "^5.0.0", "object-path": "^0.11.8", "pino": "^7.11.0" }, diff --git a/src/IOUtils.js b/src/IOUtils.js new file mode 100644 index 00000000..87736daa --- /dev/null +++ b/src/IOUtils.js @@ -0,0 +1,52 @@ +const fs = require('fs'); +const yaml = require('js-yaml'); + +async function kubeDataFromYamlFiles(...filePaths) { + let kubeData = {}; + for (const filePath of filePaths) { + let data = await readYamlFile(filePath); + for (const element of data) { + const kind = element.kind; + if (!(kind in kubeData)) { + kubeData[kind] = []; + } + kubeData[kind].push(element); + } + } + return kubeData; +} + +async function objToYaml(asObj) { + return await yaml.dump(asObj); +} + +async function printYaml(asObj) { + process.stdout.write(await objToYaml(asObj)); +} + +async function readFile(filePath) { + return await fs.promises.readFile(filePath); +} + +async function readYamlFile(filePath) { + return await yamlToObj(await readFile(filePath)); +} + +async function writeFile(contents, filePath) { + fs.writeFile(filePath, contents, (err) => { console.error(err); }); +} + +async function writeYamlFile(asObj, filePath) { + await writeFile(await objToYaml(asObj), filePath); +} + +async function yamlToObj(asYaml) { + return await yaml.loadAll(asYaml); +} + +module.exports = { + kubeDataFromYamlFiles, + printYaml, + readYamlFile, + writeYamlFile +}; diff --git a/src/LocalMustacheTemplateController.js b/src/LocalMustacheTemplateController.js new file mode 100644 index 00000000..6755df4c --- /dev/null +++ b/src/LocalMustacheTemplateController.js @@ -0,0 +1,19 @@ +const { MockKubeResourceMeta } = require('@razee/razeedeploy-core'); +const MustacheTemplateController = require('../src/MustacheTemplateController'); + +module.exports = class LocalMustacheTemplateController extends MustacheTemplateController { + constructor(params) { + let kubeData = {}; + if (params.kubeData !== undefined) { + kubeData = params.kubeData; + delete params.kubeData; + } + params.kubeResourceMeta = new MockKubeResourceMeta( + 'deploy.razee.io/v1alpha2', 'MustacheTemplate', kubeData + ); + params.logger = { + info: () => {} + }; + super(params); + } +}; diff --git a/test/mustache-template-tests.js b/test/mustache-template-tests.js index 59571066..108da502 100644 --- a/test/mustache-template-tests.js +++ b/test/mustache-template-tests.js @@ -1,5 +1,5 @@ var assert = require('chai').assert; -var Controller = require('../src/MustacheTemplateController'); +var Controller = require('../src/LocalMustacheTemplateController'); describe('#processTemplates', async function () { const eventData = { @@ -26,57 +26,50 @@ describe('#processTemplates', async function () { } } }; - const noop = () => {}; - const kubeResourceMeta = { - uri: noop, - }; - const logger = { - info: noop, - }; it('should evaulate a mustache template correctly', async function () { - const controller = new Controller({eventData, kubeResourceMeta, logger}); + const controller = new Controller({eventData}); const res = await controller.processTemplate(eventData.object.spec.strTemplates, { CRN_REGION: 'us-east'}); assert.deepEqual(res[0].data, {'us-east': 'true'}); }); it('should evaulate a handlebars template correctly', async function () { eventData.object.spec['templateEngine'] = 'handlebars'; - eventData.object.spec.strTemplates = [ + eventData.object.spec.strTemplates = [ 'apiVersion: v1\nkind: ConfigMap\nmetadata:\n name: seed-config\ndata:\n {{#if CRN_REGION}}\n "region": {{ CRN_REGION }}\n {{/if}}' ]; - const controller = new Controller({eventData, kubeResourceMeta, logger}); + const controller = new Controller({eventData}); const res = await controller.processTemplate(eventData.object.spec.strTemplates, { CRN_REGION: 'us-east'}); assert.equal(res[0].data.region, 'us-east'); }); it('should evaulate a handlebars template correctly with an equality register helper', async function () { eventData.object.spec['templateEngine'] = 'handlebars'; - eventData.object.spec.strTemplates = [ + eventData.object.spec.strTemplates = [ 'apiVersion: v1\nkind: ConfigMap\nmetadata:\n name: seed-config\ndata:\n {{#if (eq CRN_REGION "us-west") }}\n "region": {{ CRN_REGION }}\n {{/if}}' ]; - const controller = new Controller({eventData, kubeResourceMeta, logger}); - + const controller = new Controller({eventData}); + const res = await controller.processTemplate(eventData.object.spec.strTemplates, { CRN_REGION: 'us-west'}); assert.equal(res[0].data.region, 'us-west'); }); it('should be able to assign a var in handlebars with the "assign" helper', async function () { eventData.object.spec['templateEngine'] = 'handlebars'; - eventData.object.spec.strTemplates = [ + eventData.object.spec.strTemplates = [ 'apiVersion: v1\nkind: ConfigMap\nmetadata:\n name: seed-config\ndata:\n {{ assign "GEO" "us-west" }}\n "region": {{ GEO }}' ]; - const controller = new Controller({eventData, kubeResourceMeta, logger}); - + const controller = new Controller({eventData}); + const res = await controller.processTemplate(eventData.object.spec.strTemplates, { CRN_REGION: 'us-west'}); assert.equal(res[0].data.region, 'us-west'); }); it('should throw succeed without any view data', async function () { eventData.object.spec['templateEngine'] = 'handlebars'; - eventData.object.spec.strTemplates = [ + eventData.object.spec.strTemplates = [ 'apiVersion: v1\nkind: ConfigMap\nmetadata:\n name: seed-config\ndata:\n {{ assign "GEO" "us-west" }}\n "region": {{ GEO }}' ]; - const controller = new Controller({eventData, kubeResourceMeta, logger}); + const controller = new Controller({eventData}); const res = await controller.processTemplate(eventData.object.spec.strTemplates, {}); assert.equal(res[0].data.region, 'us-west'); });