diff --git a/CHANGELOG b/CHANGELOG index 37574a4..5dfdfd2 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -466,3 +466,6 @@ 0.14.17 2019-02-01 * Add 'meta.redact' to replicated-supportbundle schema + +0.15.0 2019-03-07 + * Allow passing multidoc yaml to linter, though only the first document will be linted diff --git a/package.json b/package.json index c22958a..218b255 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "replicated-lint", "author": "Replicated, Inc.", - "version": "0.14.17", + "version": "0.15.0", "engines": { "node": ">=4.3.2" }, @@ -46,7 +46,7 @@ "bin": "bin/replicated-lint", "devDependencies": { "@types/chai": "^3.4.34", - "@types/js-yaml": "^3.5.31", + "@types/js-yaml": "^3.12.0", "@types/lodash": "^4.14.52", "@types/node": "^7.0.0", "chai": "^3.5.0", @@ -70,7 +70,7 @@ "@types/json-schema": "^6.0.1", "@types/tv4": "^1.2.28", "chalk": "^1.1.3", - "js-yaml": "^3.8.4", + "js-yaml": "^3.12.2", "junit-report-builder": "^1.3.1", "line-column": "^1.0.2", "lodash": "^4.17.5", diff --git a/src/engine.ts b/src/engine.ts index 3ed17ae..98d09bd 100644 --- a/src/engine.ts +++ b/src/engine.ts @@ -22,6 +22,7 @@ import { GTE, InvalidURL, IsEmpty, + IsNative, IsNotBytesCount, IsNotKubernetesQuantity, IsNotUint, @@ -70,6 +71,7 @@ const defaultPredicates: PredicateRegistry = { Falsey, FalseyIfPresent, IsEmpty, + IsNative, IsNotBytesCount, IsNotKubernetesQuantity, IsNotUint, diff --git a/src/lint.ts b/src/lint.ts index 70714c4..ca63ed1 100644 --- a/src/lint.ts +++ b/src/lint.ts @@ -72,6 +72,7 @@ export interface Test { IsEmpty?: { path: string; }; + IsNative?: {}; IsNotBytesCount?: { path: string; }; @@ -230,34 +231,18 @@ const DOC_SEPARATOR_LENGTH = DOC_SEPARATOR.length; */ export function hackLintMultidoc(inYaml: string, maybeOpts?: MultidocLintOpts): RuleTrigger[] { const targetIndex = maybeOpts ? maybeOpts.multidocIndex : 0; - // It's just one doc, do the normal thing, but keep signature same - if (inYaml.indexOf(DOC_SEPARATOR) === -1) { - return lint(inYaml, maybeOpts); - } - - // It's many docs, split it on --- and lint each one, - // tracking offset for accurate global line/column computation const opts = maybeOpts || {}; - const docs = inYaml.split(DOC_SEPARATOR).slice(1); - let offset = inYaml.indexOf(DOC_SEPARATOR) + DOC_SEPARATOR_LENGTH; - const lineColumnFinder = lineColumn(inYaml); - - let index = 0; - for (const doc of docs) { - if (index === targetIndex) { - return new Linter(doc, { - rules: opts.rules, - registry: opts.registry, - lineColumnFinder, - offset, - }).lint(); - } - offset += doc.length + DOC_SEPARATOR_LENGTH; - index += 1; - } - return [Linter.noDocError()]; + + return new Linter(inYaml, { + rules: opts.rules, + schema: opts.schema, + registry: opts.registry, + multidocIndex: targetIndex, + }).lint(); } +export let docCount: number; + export class Linter { public static withDefaults(inYaml: string): Linter { @@ -278,8 +263,8 @@ export class Linter { private readonly schema?: any; private readonly registry: Registry; - private readonly lineColumnFinder: any; - private readonly offset: number; + private lineColumnFinder: any; + private offset: number; private readonly multidocIndex: number; @@ -299,9 +284,30 @@ export class Linter { if (!this.inYaml) { return [Linter.noDocError()]; } - let root; + + let docs = this.inYaml.split(DOC_SEPARATOR); + if (docs.length > 1) { + docs = docs.slice(1); + this.offset += this.inYaml.indexOf(DOC_SEPARATOR) + DOC_SEPARATOR_LENGTH; + } + + docCount = docs.length; + if (docs.length < this.multidocIndex) { + return [Linter.noDocError()]; + } + + let index = 0; + for (const doc of docs) { + if (index === this.multidocIndex) { + break; + } + this.offset += doc.length + DOC_SEPARATOR_LENGTH; + index += 1; + } + + let root: any; try { - root = yaml.safeLoad(this.inYaml); + root = yaml.safeLoad(docs[this.multidocIndex]); } catch (err) { return [this.loadYamlError(err)]; } diff --git a/src/predicates.ts b/src/predicates.ts index 23b61eb..f7cb208 100644 --- a/src/predicates.ts +++ b/src/predicates.ts @@ -1,7 +1,7 @@ import * as _ from "lodash"; import * as semver from "semver"; import * as urlParse from "url-parse"; -import { Predicate, RuleMatchedAt } from "./lint"; +import { Predicate, RuleMatchedAt, docCount } from "./lint"; import { FoundValue, TraverseSearcher, ValueSearcher, ValueTraverser } from "./traverse"; import { Component, ConfigChildItem, ConfigOption, ConfigSection, Container } from "./replicated"; import { Registry } from "./engine"; @@ -1336,3 +1336,21 @@ export class Not implements Predicate { } } } + +export class IsNative implements Predicate { + public static fromJson(): IsNative { + return new IsNative(); + } + + public test(root: any): RuleMatchedAt { + if (!root) { + return {matched: false}; + } + + if (docCount === 1) { + return {matched: true, paths: []}; + } else { + return {matched: false}; + } + } +} diff --git a/src/rules/monitors.ts b/src/rules/monitors.ts index ba8b60c..c28da5d 100644 --- a/src/rules/monitors.ts +++ b/src/rules/monitors.ts @@ -3,12 +3,12 @@ import { YAMLRule } from "../lint"; export const cpuMonitorContainerExists: YAMLRule = { name: "prop-monitors-cpuacct-container-exists", type: "error", - message: "Entries in `monitors.cpuacct` must have matching component+container or the scheduler must be swarm", + message: "Entries in `monitors.cpuacct` must have matching component+container if the scheduler is Native", test: { And: { preds: [ { MonitorContainerMissing: { monitorPath: "monitors.cpuacct"} }, - { Not: { pred: { Exists: { path: "swarm"} } } }, + { IsNative: { } }, ], }, }, @@ -61,14 +61,19 @@ monitors: `, }, { - description: "All `cpuacct` monitors are valid if the scheduler is swarm", + description: "All `cpuacct` monitors are valid if the scheduler is not native", yaml: ` --- monitors: cpuacct: - swarmstash -swarm: - minimum_node_count: "1" +--- +# kind: scheduler-swarm + +version: '3.3' +services: + mysql: + image: mysql `, }, { @@ -86,12 +91,12 @@ monitors: export const memMonitorContainerExists: YAMLRule = { name: "prop-monitors-memory-container-exists", type: "error", - message: "Entries in `monitors.memory` must have matching component+container or the scheduler must be swarm", + message: "Entries in `monitors.memory` must have matching component+container if the scheduler is Native", test: { And: { preds: [ { MonitorContainerMissing: { monitorPath: "monitors.memory"} }, - { Not: { pred: { Exists: { path: "swarm"} } } }, + { IsNative: { } }, ], }, }, @@ -151,8 +156,18 @@ monitors: monitors: memory: - swarmstash -swarm: - minimum_node_count: "1" +--- +# kind: scheduler-swarm + +version: '3.3' +services: + mysql: + image: mysql +--- +# kind: scheduler-swarm + +#this is not valid yaml +{ `, }, ], diff --git a/yarn.lock b/yarn.lock index d058386..4082c47 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7,10 +7,10 @@ resolved "https://registry.yarnpkg.com/@types/chai/-/chai-3.4.34.tgz#d5335792823bb09cddd5e38c3d211b709183854d" integrity sha1-1TNXkoI7sJzd1eOMPSEbcJGDhU0= -"@types/js-yaml@^3.5.31": - version "3.5.31" - resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-3.5.31.tgz#54aeb8bcaaf94a7b1a64311bc318dbfe601a593a" - integrity sha512-tDsBKuC7nlShdRbR+rCe6qrs9Fqodi7WUxyeysCwKG0kWFWsisyZ8FhmYhCF6+lt3XhIwSExaD1MxidYtRW15w== +"@types/js-yaml@^3.12.0": + version "3.12.0" + resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-3.12.0.tgz#3494ce97358e2675e24e97a747ec23478eeaf8b6" + integrity sha512-UGEe/6RsNAxgWdknhzFZbCxuYc5I7b/YEKlfKbo+76SM8CJzGs7XKCj7zyugXViRbKYpXhSXhCYVQZL5tmDbpQ== "@types/json-schema@^6.0.1": version "6.0.1" @@ -1562,7 +1562,7 @@ js-yaml@3.6.1: argparse "^1.0.7" esprima "^2.6.0" -js-yaml@3.x, js-yaml@^3.8.4: +js-yaml@3.x: version "3.8.4" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.8.4.tgz#520b4564f86573ba96662af85a8cafa7b4b5a6f6" integrity sha1-UgtFZPhlc7qWZir4Woyvp7S1pvY= @@ -1578,6 +1578,14 @@ js-yaml@^3.12.0: argparse "^1.0.7" esprima "^4.0.0" +js-yaml@^3.12.2: + version "3.12.2" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.2.tgz#ef1d067c5a9d9cb65bd72f285b5d8105c77f14fc" + integrity sha512-QHn/Lh/7HhZ/Twc7vJYQTkjuCa0kaCcDcjK5Zlk2rvnUpy7DxMJ23+Jc2dcyvltwQVg1nygAVlB2oRDFHoRS5Q== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"