feat: run schema, do everything but select nodes
This commit is contained in:
parent
e8173d4be4
commit
29aaa93f17
|
@ -1,4 +1,5 @@
|
|||
import commonjs from 'rollup-plugin-commonjs';
|
||||
import json from 'rollup-plugin-json';
|
||||
import replace from 'rollup-plugin-replace';
|
||||
import resolve from 'rollup-plugin-node-resolve';
|
||||
import typescript from 'rollup-plugin-typescript2';
|
||||
|
@ -17,6 +18,7 @@ export default {
|
|||
sourcemap: true,
|
||||
},
|
||||
plugins: [
|
||||
json(),
|
||||
replace({
|
||||
delimiters: ['{{ ', ' }}'],
|
||||
values: {
|
||||
|
@ -33,7 +35,7 @@ export default {
|
|||
}),
|
||||
commonjs({
|
||||
namedExports: {
|
||||
'node_modules/lodash/lodash.js': ['isNil', 'isString'],
|
||||
'node_modules/lodash/lodash.js': ['intersection', 'isNil', 'isString'],
|
||||
'node_modules/noicejs/out/main-bundle.js': ['BaseError'],
|
||||
'node_modules/js-yaml/index.js': ['DEFAULT_SAFE_SCHEMA', 'SAFE_SCHEMA', 'safeLoad', 'Schema', 'Type'],
|
||||
'node_modules/yargs-parser/index.js': ['detailed'],
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
"@types/yargs-parser": "^13.0.0",
|
||||
"rollup": "^1.15.5",
|
||||
"rollup-plugin-commonjs": "^10.0.0",
|
||||
"rollup-plugin-json": "^4.0.0",
|
||||
"rollup-plugin-node-resolve": "^5.0.2",
|
||||
"rollup-plugin-replace": "^2.2.0",
|
||||
"rollup-plugin-typescript2": "^0.21.1",
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
metadata:
|
||||
name: example
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: test
|
||||
# missing resources
|
|
@ -0,0 +1,40 @@
|
|||
rules:
|
||||
- name: require-resources
|
||||
level: info
|
||||
tags:
|
||||
- cluster-health
|
||||
- important
|
||||
|
||||
nodes:
|
||||
filter: ''
|
||||
select: '$.spec.template.spec.containers[*]'
|
||||
|
||||
schema:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
required: [resources]
|
||||
properties:
|
||||
resources:
|
||||
type: object
|
||||
required: [limits, requests]
|
||||
properties:
|
||||
limits:
|
||||
type: object
|
||||
required: [cpu, memory]
|
||||
properties:
|
||||
cpu:
|
||||
type: string
|
||||
default: 100m
|
||||
memory:
|
||||
type: string
|
||||
default: 256Mi
|
||||
requests:
|
||||
type: object
|
||||
required: [cpu, memory]
|
||||
properties:
|
||||
cpu:
|
||||
type: string
|
||||
default: 100m
|
||||
memory:
|
||||
type: string
|
||||
default: 256Mi
|
|
@ -9,7 +9,7 @@ import { includeSchema, includeType } from 'src/config/type/Include';
|
|||
import { regexpType } from 'src/config/type/Regexp';
|
||||
import { NotFoundError } from 'src/error/NotFoundError';
|
||||
|
||||
export const CONFIG_ENV = 'ISOLEX_HOME';
|
||||
export const CONFIG_ENV = 'SALTY_HOME';
|
||||
export const CONFIG_SCHEMA = Schema.create([DEFAULT_SAFE_SCHEMA], [
|
||||
envType,
|
||||
includeType,
|
||||
|
@ -23,7 +23,7 @@ const readFileSync = promisify(readFile);
|
|||
/**
|
||||
* With the given name, generate all potential config paths in their complete, absolute form.
|
||||
*
|
||||
* This will include the value of `ISOLEX_HOME`, `HOME`, the current working directory, and any extra paths
|
||||
* This will include the value of `SALTY_HOME`, `HOME`, the current working directory, and any extra paths
|
||||
* passed as the final arguments.
|
||||
*/
|
||||
export function completePaths(name: string, extras: Array<string>): Array<string> {
|
||||
|
|
51
src/index.ts
51
src/index.ts
|
@ -1,20 +1,46 @@
|
|||
import { createLogger } from 'bunyan';
|
||||
import { detailed, Options } from 'yargs-parser';
|
||||
|
||||
import { loadConfig } from 'src/config';
|
||||
import { loadConfig, CONFIG_SCHEMA } from 'src/config';
|
||||
import { loadRules, resolveRules, checkRule } from 'src/rule';
|
||||
import { loadSource } from 'src/source';
|
||||
import { VERSION_INFO } from 'src/version';
|
||||
import { safeLoad } from 'js-yaml';
|
||||
|
||||
const CONFIG_ARGS_NAME = 'config-name';
|
||||
const CONFIG_ARGS_PATH = 'config-path';
|
||||
|
||||
const MAIN_ARGS: Options = {
|
||||
array: [CONFIG_ARGS_PATH],
|
||||
alias: {
|
||||
'includeTag': ['t', 'tag'],
|
||||
'mode': ['m'],
|
||||
},
|
||||
array: [
|
||||
CONFIG_ARGS_PATH,
|
||||
'excludeLevel',
|
||||
'excludeName',
|
||||
'excludeTag',
|
||||
'includeLevel',
|
||||
'includeName',
|
||||
'includeTag',
|
||||
'rules',
|
||||
],
|
||||
count: ['v'],
|
||||
default: {
|
||||
[CONFIG_ARGS_NAME]: `.${VERSION_INFO.app.name}.yml`,
|
||||
[CONFIG_ARGS_PATH]: [],
|
||||
'excludeLevel': [],
|
||||
'excludeName': [],
|
||||
'excludeTag': [],
|
||||
'includeLevel': [],
|
||||
'includeName': [],
|
||||
'includeTag': [],
|
||||
'mode': 'check',
|
||||
'rules': [],
|
||||
'source': '-',
|
||||
},
|
||||
envPrefix: VERSION_INFO.app.name,
|
||||
string: ['mode'],
|
||||
};
|
||||
|
||||
const STATUS_SUCCESS = 0;
|
||||
|
@ -35,12 +61,25 @@ export async function main(argv: Array<string>): Promise<number> {
|
|||
return STATUS_ERROR;
|
||||
}
|
||||
|
||||
if (args.argv.test) {
|
||||
logger.info('config is valid');
|
||||
return STATUS_SUCCESS;
|
||||
const rules = await loadRules(args.argv.rules);
|
||||
const source = await loadSource(args.argv.source);
|
||||
const data = safeLoad(source, {
|
||||
schema: CONFIG_SCHEMA,
|
||||
});
|
||||
const activeRules = await resolveRules(rules, args.argv as any);
|
||||
|
||||
// run rules
|
||||
let status = STATUS_SUCCESS;
|
||||
for (const rule of activeRules) {
|
||||
if (checkRule(rule, data)) {
|
||||
logger.info({ rule }, 'passed rule');
|
||||
} else {
|
||||
logger.warn({ rule }, 'failed rule');
|
||||
status = STATUS_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
return status;
|
||||
}
|
||||
|
||||
main(process.argv).then((status) => process.exit(status)).catch((err) => {
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
import * as ajv from 'ajv';
|
||||
import { readFile } from 'fs';
|
||||
import { intersection } from 'lodash';
|
||||
import { LogLevel } from 'noicejs';
|
||||
import { promisify } from 'util';
|
||||
import { safeLoad } from 'js-yaml';
|
||||
import { CONFIG_SCHEMA } from './config';
|
||||
|
||||
const readFileSync = promisify(readFile);
|
||||
|
||||
export interface Rule {
|
||||
level: LogLevel;
|
||||
name: string;
|
||||
nodes: {
|
||||
filter: string;
|
||||
select: string;
|
||||
};
|
||||
schema: any;
|
||||
tags: Array<string>;
|
||||
}
|
||||
|
||||
export interface RuleSelector {
|
||||
excludeLevel: Array<LogLevel>;
|
||||
excludeName: Array<string>;
|
||||
excludeTag: Array<string>;
|
||||
includeLevel: Array<LogLevel>;
|
||||
includeName: Array<string>;
|
||||
includeTag: Array<string>;
|
||||
}
|
||||
|
||||
export async function loadRules(paths: Array<string>): Promise<Array<Rule>> {
|
||||
const rules = [];
|
||||
|
||||
for (const path of paths) {
|
||||
const contents = await readFileSync(path, {
|
||||
encoding: 'utf-8',
|
||||
});
|
||||
|
||||
const data = safeLoad(contents, {
|
||||
schema: CONFIG_SCHEMA,
|
||||
});
|
||||
|
||||
rules.push(...data.rules);
|
||||
}
|
||||
|
||||
return rules;
|
||||
}
|
||||
|
||||
export async function resolveRules(rules: Array<Rule>, selector: RuleSelector): Promise<Array<Rule>> {
|
||||
const activeRules = new Set<Rule>();
|
||||
|
||||
for (const r of rules) {
|
||||
if (selector.excludeLevel.includes(r.level)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (selector.excludeName.includes(r.name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const excludedTags = intersection(selector.excludeTag, r.tags);
|
||||
if (excludedTags.length > 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (selector.includeLevel.includes(r.level)) {
|
||||
activeRules.add(r);
|
||||
}
|
||||
|
||||
if (selector.includeName.includes(r.name)) {
|
||||
activeRules.add(r);
|
||||
}
|
||||
|
||||
const includedTags = intersection(selector.includeTag, r.tags);
|
||||
if (includedTags.length > 0) {
|
||||
activeRules.add(r);
|
||||
}
|
||||
}
|
||||
|
||||
return Array.from(activeRules);
|
||||
}
|
||||
|
||||
export function checkRule(rule: Rule, data: any): boolean {
|
||||
const schema = new ((ajv as any).default)().compile(rule.schema);
|
||||
const valid = schema(data);
|
||||
console.log(data, valid);
|
||||
return !!valid;
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
import { promisify } from 'util';
|
||||
import { readFile } from 'fs';
|
||||
|
||||
const readFileSync = promisify(readFile);
|
||||
|
||||
export async function loadSource(path: string): Promise<string> {
|
||||
if (path === '-') {
|
||||
return readFileSync(0, 'utf-8');
|
||||
} else {
|
||||
return readFileSync(path, 'utf-8');
|
||||
}
|
||||
}
|
|
@ -798,6 +798,13 @@ rollup-plugin-commonjs@^10.0.0:
|
|||
resolve "^1.10.1"
|
||||
rollup-pluginutils "^2.7.0"
|
||||
|
||||
rollup-plugin-json@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/rollup-plugin-json/-/rollup-plugin-json-4.0.0.tgz#a18da0a4b30bf5ca1ee76ddb1422afbb84ae2b9e"
|
||||
integrity sha512-hgb8N7Cgfw5SZAkb3jf0QXii6QX/FOkiIq2M7BAQIEydjHvTyxXHQiIzZaTFgx1GK0cRCHOCBHIyEkkLdWKxow==
|
||||
dependencies:
|
||||
rollup-pluginutils "^2.5.0"
|
||||
|
||||
rollup-plugin-node-resolve@^5.0.2:
|
||||
version "5.0.2"
|
||||
resolved "https://registry.yarnpkg.com/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-5.0.2.tgz#6475dc689934a2f85bb5c5867d03d8bd1bf859c1"
|
||||
|
@ -835,7 +842,7 @@ rollup-pluginutils@2.6.0:
|
|||
estree-walker "^0.6.0"
|
||||
micromatch "^3.1.10"
|
||||
|
||||
rollup-pluginutils@^2.6.0, rollup-pluginutils@^2.7.0, rollup-pluginutils@^2.8.0:
|
||||
rollup-pluginutils@^2.5.0, rollup-pluginutils@^2.6.0, rollup-pluginutils@^2.7.0, rollup-pluginutils@^2.8.0:
|
||||
version "2.8.1"
|
||||
resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.8.1.tgz#8fa6dd0697344938ef26c2c09d2488ce9e33ce97"
|
||||
integrity sha512-J5oAoysWar6GuZo0s+3bZ6sVZAC0pfqKz68De7ZgDi5z63jOVZn1uJL/+z1jeKHNbGII8kAyHF5q8LnxSX5lQg==
|
||||
|
|
Loading…
Reference in New Issue