From 2bd60c8f6cc806ce781eb6af63939637d0725e98 Mon Sep 17 00:00:00 2001 From: ssube Date: Mon, 24 Jun 2019 22:50:46 -0500 Subject: [PATCH] feat: support multiple documents per source (#11) --- README.md | 2 +- config/rollup.js | 1 + examples/kubernetes-resources-some.yml | 41 ++++++++++++++++++++++++++ src/config/index.ts | 5 ++-- src/index.ts | 36 +++++++++++----------- src/parser/YamlParser.ts | 10 ++++--- src/parser/index.ts | 4 +-- src/rule.ts | 18 ++++++----- 8 files changed, 83 insertions(+), 34 deletions(-) create mode 100644 examples/kubernetes-resources-some.yml diff --git a/README.md b/README.md index 5f395f0..e8f5245 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ To download, validate, and apply a Kubernetes resource: ... {"name":"salty-dog","hostname":"cerberus","pid":7860,"level":30,"msg":"all rules passed","time":"2019-06-16T02:04:37.797Z","v":0} -ingress.extensions/gitlab created (dry run +ingress.extensions/gitlab created (dry run) ``` ### Docker diff --git a/config/rollup.js b/config/rollup.js index 24a02b9..b560d4a 100644 --- a/config/rollup.js +++ b/config/rollup.js @@ -51,6 +51,7 @@ export default { 'SAFE_SCHEMA', 'safeDump', 'safeLoad', + 'safeLoadAll', 'Schema', 'Type', ], diff --git a/examples/kubernetes-resources-some.yml b/examples/kubernetes-resources-some.yml new file mode 100644 index 0000000..e824944 --- /dev/null +++ b/examples/kubernetes-resources-some.yml @@ -0,0 +1,41 @@ +metadata: + name: example +spec: + template: + spec: + containers: + - name: test + +--- + +metadata: + name: example +spec: + template: + spec: + containers: + - name: test + resources: + limits: + memory: 5Mi + requests: + cpu: 1m + memory: 2Mi + +--- + +metadata: + name: example +spec: + template: + spec: + containers: + - name: test + resources: + limits: + cpu: 4000m + memory: 5Gi + requests: + cpu: 4000m + memory: 5Gi + diff --git a/src/config/index.ts b/src/config/index.ts index 07ce883..3ffbbe3 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -40,13 +40,14 @@ export function completePaths(name: string, extras: Array): Array): Promise { - const parser = new YamlParser(); const paths = completePaths(name, extras); for (const p of paths) { const data = await readConfig(p); if (!isNil(data)) { - return parser.parse(data); + const parser = new YamlParser(); + const [head] = parser.parse(data); + return head; } } diff --git a/src/index.ts b/src/index.ts index 0c16f80..06e3893 100644 --- a/src/index.ts +++ b/src/index.ts @@ -111,29 +111,31 @@ export async function main(argv: Array): Promise { const parser = new YamlParser(); const source = await loadSource(args.source); - let data = parser.parse(source); + let docs = parser.parse(source); const rules = await loadRules(args.rules, ctx.ajv); const activeRules = await resolveRules(rules, args as any); - for (const rule of activeRules) { - const items = await rule.pick(ctx, data); - for (const item of items) { - const itemCopy = cloneDeep(item); - const itemResult = await rule.visit(ctx, itemCopy); + for (const data of docs) { + for (const rule of activeRules) { + const items = await rule.pick(ctx, data); + for (const item of items) { + const itemCopy = cloneDeep(item); + const itemResult = await rule.visit(ctx, itemCopy); - if (itemResult.errors.length > 0) { - logger.warn({ count: itemResult.errors.length, rule }, 'rule failed'); + if (itemResult.errors.length > 0) { + logger.warn({ count: itemResult.errors.length, rule }, 'rule failed'); - ctx.mergeResult(itemResult); - } else { - const itemDiff = diff(item, itemCopy); - if (Array.isArray(itemDiff) && itemDiff.length > 0) { - logger.info({ diff: itemDiff, item, rule }, 'rule passed with modifications'); - - applyDiff(item, itemCopy); + ctx.mergeResult(itemResult); } else { - logger.info({ rule }, 'rule passed'); + const itemDiff = diff(item, itemCopy); + if (Array.isArray(itemDiff) && itemDiff.length > 0) { + logger.info({ diff: itemDiff, item, rule }, 'rule passed with modifications'); + + applyDiff(item, itemCopy); + } else { + logger.info({ rule }, 'rule passed'); + } } } } @@ -148,7 +150,7 @@ export async function main(argv: Array): Promise { } } else { logger.info('all rules passed'); - const output = parser.dump(data); + const output = parser.dump(...docs); await writeSource(args.dest, output); return STATUS_SUCCESS; } diff --git a/src/parser/YamlParser.ts b/src/parser/YamlParser.ts index 366e118..883359c 100644 --- a/src/parser/YamlParser.ts +++ b/src/parser/YamlParser.ts @@ -1,18 +1,20 @@ -import { safeDump, safeLoad } from 'js-yaml'; +import { safeDump, safeLoadAll } from 'js-yaml'; import { CONFIG_SCHEMA } from 'src/config/schema'; import { Parser } from 'src/parser'; export class YamlParser implements Parser { - dump(data: any): string { + dump(...data: Array): string { return safeDump(data, { schema: CONFIG_SCHEMA, }); } - parse(body: string): any { - return safeLoad(body, { + parse(body: string): Array { + const docs: Array = []; + safeLoadAll(body, (doc: any) => docs.push(doc), { schema: CONFIG_SCHEMA, }); + return docs; } } \ No newline at end of file diff --git a/src/parser/index.ts b/src/parser/index.ts index 4f8f58f..f0a2bea 100644 --- a/src/parser/index.ts +++ b/src/parser/index.ts @@ -1,4 +1,4 @@ export interface Parser { - dump(data: any): string; - parse(body: string): any; + dump(...data: Array): string; + parse(body: string): Array; } \ No newline at end of file diff --git a/src/rule.ts b/src/rule.ts index 37ff39b..7ac8f71 100644 --- a/src/rule.ts +++ b/src/rule.ts @@ -44,16 +44,18 @@ export async function loadRules(paths: Array, ajv: any): Promise; - if (!isNil(data.definitions)) { - ajv.addSchema({ - '$id': data.name, - definitions: data.definitions, - }); + for (const data of docs) { + if (!isNil(data.definitions)) { + ajv.addSchema({ + '$id': data.name, + definitions: data.definitions, + }); + } + + rules.push(...data.rules.map((data: any) => new Rule(data))); } - - rules.push(...data.rules.map((data: any) => new Rule(data))); } return rules;