feat: support multiple documents per source (#11)
This commit is contained in:
parent
146bcc7268
commit
2bd60c8f6c
|
@ -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}
|
{"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
|
### Docker
|
||||||
|
|
|
@ -51,6 +51,7 @@ export default {
|
||||||
'SAFE_SCHEMA',
|
'SAFE_SCHEMA',
|
||||||
'safeDump',
|
'safeDump',
|
||||||
'safeLoad',
|
'safeLoad',
|
||||||
|
'safeLoadAll',
|
||||||
'Schema',
|
'Schema',
|
||||||
'Type',
|
'Type',
|
||||||
],
|
],
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -40,13 +40,14 @@ export function completePaths(name: string, extras: Array<string>): Array<string
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function loadConfig(name: string, ...extras: Array<string>): Promise<any> {
|
export async function loadConfig(name: string, ...extras: Array<string>): Promise<any> {
|
||||||
const parser = new YamlParser();
|
|
||||||
const paths = completePaths(name, extras);
|
const paths = completePaths(name, extras);
|
||||||
|
|
||||||
for (const p of paths) {
|
for (const p of paths) {
|
||||||
const data = await readConfig(p);
|
const data = await readConfig(p);
|
||||||
if (!isNil(data)) {
|
if (!isNil(data)) {
|
||||||
return parser.parse(data);
|
const parser = new YamlParser();
|
||||||
|
const [head] = parser.parse(data);
|
||||||
|
return head;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
36
src/index.ts
36
src/index.ts
|
@ -111,29 +111,31 @@ export async function main(argv: Array<string>): Promise<number> {
|
||||||
|
|
||||||
const parser = new YamlParser();
|
const parser = new YamlParser();
|
||||||
const source = await loadSource(args.source);
|
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 rules = await loadRules(args.rules, ctx.ajv);
|
||||||
const activeRules = await resolveRules(rules, args as any);
|
const activeRules = await resolveRules(rules, args as any);
|
||||||
|
|
||||||
for (const rule of activeRules) {
|
for (const data of docs) {
|
||||||
const items = await rule.pick(ctx, data);
|
for (const rule of activeRules) {
|
||||||
for (const item of items) {
|
const items = await rule.pick(ctx, data);
|
||||||
const itemCopy = cloneDeep(item);
|
for (const item of items) {
|
||||||
const itemResult = await rule.visit(ctx, itemCopy);
|
const itemCopy = cloneDeep(item);
|
||||||
|
const itemResult = await rule.visit(ctx, itemCopy);
|
||||||
|
|
||||||
if (itemResult.errors.length > 0) {
|
if (itemResult.errors.length > 0) {
|
||||||
logger.warn({ count: itemResult.errors.length, rule }, 'rule failed');
|
logger.warn({ count: itemResult.errors.length, rule }, 'rule failed');
|
||||||
|
|
||||||
ctx.mergeResult(itemResult);
|
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);
|
|
||||||
} else {
|
} 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<string>): Promise<number> {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.info('all rules passed');
|
logger.info('all rules passed');
|
||||||
const output = parser.dump(data);
|
const output = parser.dump(...docs);
|
||||||
await writeSource(args.dest, output);
|
await writeSource(args.dest, output);
|
||||||
return STATUS_SUCCESS;
|
return STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,20 @@
|
||||||
import { safeDump, safeLoad } from 'js-yaml';
|
import { safeDump, safeLoadAll } from 'js-yaml';
|
||||||
|
|
||||||
import { CONFIG_SCHEMA } from 'src/config/schema';
|
import { CONFIG_SCHEMA } from 'src/config/schema';
|
||||||
import { Parser } from 'src/parser';
|
import { Parser } from 'src/parser';
|
||||||
|
|
||||||
export class YamlParser implements Parser {
|
export class YamlParser implements Parser {
|
||||||
dump(data: any): string {
|
dump(...data: Array<any>): string {
|
||||||
return safeDump(data, {
|
return safeDump(data, {
|
||||||
schema: CONFIG_SCHEMA,
|
schema: CONFIG_SCHEMA,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
parse(body: string): any {
|
parse(body: string): Array<any> {
|
||||||
return safeLoad(body, {
|
const docs: Array<any> = [];
|
||||||
|
safeLoadAll(body, (doc: any) => docs.push(doc), {
|
||||||
schema: CONFIG_SCHEMA,
|
schema: CONFIG_SCHEMA,
|
||||||
});
|
});
|
||||||
|
return docs;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
export interface Parser {
|
export interface Parser {
|
||||||
dump(data: any): string;
|
dump(...data: Array<any>): string;
|
||||||
parse(body: string): any;
|
parse(body: string): Array<any>;
|
||||||
}
|
}
|
18
src/rule.ts
18
src/rule.ts
|
@ -44,16 +44,18 @@ export async function loadRules(paths: Array<string>, ajv: any): Promise<Array<R
|
||||||
encoding: 'utf-8',
|
encoding: 'utf-8',
|
||||||
});
|
});
|
||||||
|
|
||||||
const data = parser.parse(contents) as RuleSource;
|
const docs = parser.parse(contents) as Array<RuleSource>;
|
||||||
|
|
||||||
if (!isNil(data.definitions)) {
|
for (const data of docs) {
|
||||||
ajv.addSchema({
|
if (!isNil(data.definitions)) {
|
||||||
'$id': data.name,
|
ajv.addSchema({
|
||||||
definitions: data.definitions,
|
'$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;
|
return rules;
|
||||||
|
|
Loading…
Reference in New Issue