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 commonjs from 'rollup-plugin-commonjs';
|
||||||
|
import json from 'rollup-plugin-json';
|
||||||
import replace from 'rollup-plugin-replace';
|
import replace from 'rollup-plugin-replace';
|
||||||
import resolve from 'rollup-plugin-node-resolve';
|
import resolve from 'rollup-plugin-node-resolve';
|
||||||
import typescript from 'rollup-plugin-typescript2';
|
import typescript from 'rollup-plugin-typescript2';
|
||||||
|
@ -17,6 +18,7 @@ export default {
|
||||||
sourcemap: true,
|
sourcemap: true,
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
json(),
|
||||||
replace({
|
replace({
|
||||||
delimiters: ['{{ ', ' }}'],
|
delimiters: ['{{ ', ' }}'],
|
||||||
values: {
|
values: {
|
||||||
|
@ -33,7 +35,7 @@ export default {
|
||||||
}),
|
}),
|
||||||
commonjs({
|
commonjs({
|
||||||
namedExports: {
|
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/noicejs/out/main-bundle.js': ['BaseError'],
|
||||||
'node_modules/js-yaml/index.js': ['DEFAULT_SAFE_SCHEMA', 'SAFE_SCHEMA', 'safeLoad', 'Schema', 'Type'],
|
'node_modules/js-yaml/index.js': ['DEFAULT_SAFE_SCHEMA', 'SAFE_SCHEMA', 'safeLoad', 'Schema', 'Type'],
|
||||||
'node_modules/yargs-parser/index.js': ['detailed'],
|
'node_modules/yargs-parser/index.js': ['detailed'],
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
"@types/yargs-parser": "^13.0.0",
|
"@types/yargs-parser": "^13.0.0",
|
||||||
"rollup": "^1.15.5",
|
"rollup": "^1.15.5",
|
||||||
"rollup-plugin-commonjs": "^10.0.0",
|
"rollup-plugin-commonjs": "^10.0.0",
|
||||||
|
"rollup-plugin-json": "^4.0.0",
|
||||||
"rollup-plugin-node-resolve": "^5.0.2",
|
"rollup-plugin-node-resolve": "^5.0.2",
|
||||||
"rollup-plugin-replace": "^2.2.0",
|
"rollup-plugin-replace": "^2.2.0",
|
||||||
"rollup-plugin-typescript2": "^0.21.1",
|
"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 { regexpType } from 'src/config/type/Regexp';
|
||||||
import { NotFoundError } from 'src/error/NotFoundError';
|
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], [
|
export const CONFIG_SCHEMA = Schema.create([DEFAULT_SAFE_SCHEMA], [
|
||||||
envType,
|
envType,
|
||||||
includeType,
|
includeType,
|
||||||
|
@ -23,7 +23,7 @@ const readFileSync = promisify(readFile);
|
||||||
/**
|
/**
|
||||||
* With the given name, generate all potential config paths in their complete, absolute form.
|
* 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.
|
* passed as the final arguments.
|
||||||
*/
|
*/
|
||||||
export function completePaths(name: string, extras: Array<string>): Array<string> {
|
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 { createLogger } from 'bunyan';
|
||||||
import { detailed, Options } from 'yargs-parser';
|
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 { VERSION_INFO } from 'src/version';
|
||||||
|
import { safeLoad } from 'js-yaml';
|
||||||
|
|
||||||
const CONFIG_ARGS_NAME = 'config-name';
|
const CONFIG_ARGS_NAME = 'config-name';
|
||||||
const CONFIG_ARGS_PATH = 'config-path';
|
const CONFIG_ARGS_PATH = 'config-path';
|
||||||
|
|
||||||
const MAIN_ARGS: Options = {
|
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'],
|
count: ['v'],
|
||||||
default: {
|
default: {
|
||||||
[CONFIG_ARGS_NAME]: `.${VERSION_INFO.app.name}.yml`,
|
[CONFIG_ARGS_NAME]: `.${VERSION_INFO.app.name}.yml`,
|
||||||
[CONFIG_ARGS_PATH]: [],
|
[CONFIG_ARGS_PATH]: [],
|
||||||
|
'excludeLevel': [],
|
||||||
|
'excludeName': [],
|
||||||
|
'excludeTag': [],
|
||||||
|
'includeLevel': [],
|
||||||
|
'includeName': [],
|
||||||
|
'includeTag': [],
|
||||||
|
'mode': 'check',
|
||||||
|
'rules': [],
|
||||||
|
'source': '-',
|
||||||
},
|
},
|
||||||
envPrefix: VERSION_INFO.app.name,
|
envPrefix: VERSION_INFO.app.name,
|
||||||
|
string: ['mode'],
|
||||||
};
|
};
|
||||||
|
|
||||||
const STATUS_SUCCESS = 0;
|
const STATUS_SUCCESS = 0;
|
||||||
|
@ -35,12 +61,25 @@ export async function main(argv: Array<string>): Promise<number> {
|
||||||
return STATUS_ERROR;
|
return STATUS_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args.argv.test) {
|
const rules = await loadRules(args.argv.rules);
|
||||||
logger.info('config is valid');
|
const source = await loadSource(args.argv.source);
|
||||||
return STATUS_SUCCESS;
|
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) => {
|
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"
|
resolve "^1.10.1"
|
||||||
rollup-pluginutils "^2.7.0"
|
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:
|
rollup-plugin-node-resolve@^5.0.2:
|
||||||
version "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"
|
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"
|
estree-walker "^0.6.0"
|
||||||
micromatch "^3.1.10"
|
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"
|
version "2.8.1"
|
||||||
resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.8.1.tgz#8fa6dd0697344938ef26c2c09d2488ce9e33ce97"
|
resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.8.1.tgz#8fa6dd0697344938ef26c2c09d2488ce9e33ce97"
|
||||||
integrity sha512-J5oAoysWar6GuZo0s+3bZ6sVZAC0pfqKz68De7ZgDi5z63jOVZn1uJL/+z1jeKHNbGII8kAyHF5q8LnxSX5lQg==
|
integrity sha512-J5oAoysWar6GuZo0s+3bZ6sVZAC0pfqKz68De7ZgDi5z63jOVZn1uJL/+z1jeKHNbGII8kAyHF5q8LnxSX5lQg==
|
||||||
|
|
Loading…
Reference in New Issue