From deee5c0cbde83b44279a7382b2cf8537d95d77cc Mon Sep 17 00:00:00 2001 From: ssube Date: Sat, 15 Jun 2019 19:25:47 -0500 Subject: [PATCH] rules(kubernetes): add example of minimum CPU limit --- rules/examples/kubernetes-resources-high.yml | 14 +++++ rules/examples/kubernetes-resources-low.yml | 14 +++++ ...-pass.yml => kubernetes-resources-med.yml} | 0 ...fail.yml => kubernetes-resources-none.yml} | 0 rules/kubernetes.yml | 38 ++++++++++++-- src/rule.ts | 51 ++++++++++++------- 6 files changed, 95 insertions(+), 22 deletions(-) create mode 100644 rules/examples/kubernetes-resources-high.yml create mode 100644 rules/examples/kubernetes-resources-low.yml rename rules/examples/{kubernetes-require-resources-pass.yml => kubernetes-resources-med.yml} (100%) rename rules/examples/{kubernetes-require-resources-fail.yml => kubernetes-resources-none.yml} (100%) diff --git a/rules/examples/kubernetes-resources-high.yml b/rules/examples/kubernetes-resources-high.yml new file mode 100644 index 0000000..ed520f4 --- /dev/null +++ b/rules/examples/kubernetes-resources-high.yml @@ -0,0 +1,14 @@ +metadata: + name: example +spec: + template: + spec: + containers: + - name: test + resources: + limits: + cpu: 4000m + memory: 5Gi + requests: + cpu: 4000m + memory: 5Gi diff --git a/rules/examples/kubernetes-resources-low.yml b/rules/examples/kubernetes-resources-low.yml new file mode 100644 index 0000000..41ebf8e --- /dev/null +++ b/rules/examples/kubernetes-resources-low.yml @@ -0,0 +1,14 @@ +metadata: + name: example +spec: + template: + spec: + containers: + - name: test + resources: + limits: + cpu: 2m + memory: 5Mi + requests: + cpu: 1m + memory: 2Mi \ No newline at end of file diff --git a/rules/examples/kubernetes-require-resources-pass.yml b/rules/examples/kubernetes-resources-med.yml similarity index 100% rename from rules/examples/kubernetes-require-resources-pass.yml rename to rules/examples/kubernetes-resources-med.yml diff --git a/rules/examples/kubernetes-require-resources-fail.yml b/rules/examples/kubernetes-resources-none.yml similarity index 100% rename from rules/examples/kubernetes-require-resources-fail.yml rename to rules/examples/kubernetes-resources-none.yml diff --git a/rules/kubernetes.yml b/rules/kubernetes.yml index c931cbc..0cb1ae4 100644 --- a/rules/kubernetes.yml +++ b/rules/kubernetes.yml @@ -1,14 +1,15 @@ rules: - - name: kubernetes-require-resources + - name: kubernetes-resources level: info tags: - cluster-health - important - nodes: - select: '$.spec.template.spec.containers[*]' + select: '$.spec.template.spec.containers[*]' + filter: + type: object - schema: + check: type: object additionalProperties: true required: [resources] @@ -32,4 +33,31 @@ rules: cpu: type: string memory: - type: string \ No newline at end of file + type: string + + - name: kubernetes-resources-minimum-cpu + desc: resource limits are too low + level: debug + tags: + - optional + + select: '$.spec.template.spec.containers[*].resources' + filter: + type: object + properties: + limits: + type: object + properties: + cpu: + type: string + pattern: "[0-9]{1,3}m" + + check: + type: object + properties: + limits: + type: object + properties: + cpu: + type: string + pattern: "[0-9]{3}m" diff --git a/src/rule.ts b/src/rule.ts index 78b1966..7ee10df 100644 --- a/src/rule.ts +++ b/src/rule.ts @@ -1,23 +1,25 @@ import * as Ajv from 'ajv'; import { readFile } from 'fs'; -import { JSONPath } from 'jsonpath-plus'; -import { intersection } from 'lodash'; -import { LogLevel, Logger } from 'noicejs'; -import { promisify } from 'util'; import { safeLoad } from 'js-yaml'; +import { JSONPath } from 'jsonpath-plus'; +import { intersection, isNil } from 'lodash'; +import { Logger, LogLevel } from 'noicejs'; +import { promisify } from 'util'; + import { CONFIG_SCHEMA } from './config'; const readFileSync = promisify(readFile); export interface Rule { + // metadata + desc: string; level: LogLevel; name: string; - nodes: { - filter: string; - select: string; - }; - schema: any; tags: Array; + // data + check: any; + filter: any; + select: string; } export interface RuleSelector { @@ -83,19 +85,34 @@ export async function resolveRules(rules: Array, selector: RuleSelector): export function checkRule(rule: Rule, data: any, logger: Logger): boolean { const ajv = new ((Ajv as any).default)() - const schema = ajv.compile(rule.schema); + const check = ajv.compile(rule.check); + const filter = ajv.compile(rule.filter); const scopes = JSONPath({ json: data, - path: rule.nodes.select, + path: rule.select, }); - for (const scope of scopes) { - const valid = schema(scope); - if (!valid) { - logger.warn({ errors: schema.errors, item: scope }, 'rule failed on item'); - return false; + if (isNil(scopes) || scopes.length === 0) { + logger.debug('no data selected'); + return true; + } + + for (const item of scopes) { + logger.debug({ item }, 'filtering item'); + if (filter(item)) { + logger.debug({ item }, 'checking item') + if (!check(item)) { + logger.warn({ + desc: rule.desc, + errors: check.errors, + item, + }, 'rule failed on item'); + return false; + } + } else { + logger.debug({ errors: filter.errors, item }, 'skipping item'); } } - + return true; } \ No newline at end of file