@intentius/chant-lexicon-helm 0.0.18 → 0.0.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/integrity.json +5 -5
- package/dist/manifest.json +1 -1
- package/dist/skills/chant-helm.md +447 -0
- package/package.json +21 -3
- package/src/codegen/docs.test.ts +14 -0
- package/src/codegen/generate.test.ts +71 -0
- package/src/codegen/package.test.ts +36 -0
- package/src/composites/composites.test.ts +116 -110
- package/src/composites/helm-batch-job.ts +33 -19
- package/src/composites/helm-crd-lifecycle.ts +37 -24
- package/src/composites/helm-cron-job.ts +25 -13
- package/src/composites/helm-daemon-set.ts +26 -14
- package/src/composites/helm-external-secret.ts +21 -12
- package/src/composites/helm-library.ts +21 -7
- package/src/composites/helm-microservice.ts +46 -29
- package/src/composites/helm-monitored-service.ts +75 -51
- package/src/composites/helm-namespace-env.ts +80 -52
- package/src/composites/helm-secure-ingress.ts +66 -50
- package/src/composites/helm-stateful-service.ts +29 -16
- package/src/composites/helm-web-app.ts +37 -22
- package/src/composites/helm-worker.ts +34 -20
- package/src/import/roundtrip.test.ts +144 -0
- package/src/lint/post-synth/whm101.test.ts +69 -0
- package/src/lint/post-synth/whm102.test.ts +57 -0
- package/src/lint/post-synth/whm103.test.ts +58 -0
- package/src/lint/post-synth/whm104.test.ts +57 -0
- package/src/lint/post-synth/whm105.test.ts +41 -0
- package/src/lint/post-synth/whm201.test.ts +59 -0
- package/src/lint/post-synth/whm202.test.ts +62 -0
- package/src/lint/post-synth/whm203.test.ts +58 -0
- package/src/lint/post-synth/whm204.test.ts +56 -0
- package/src/lint/post-synth/whm301.test.ts +49 -0
- package/src/lint/post-synth/whm302.test.ts +58 -0
- package/src/lint/post-synth/whm401.test.ts +59 -0
- package/src/lint/post-synth/whm402.test.ts +58 -0
- package/src/lint/post-synth/whm403.test.ts +50 -0
- package/src/lint/post-synth/whm404.test.ts +50 -0
- package/src/lint/post-synth/whm405.test.ts +60 -0
- package/src/lint/post-synth/whm406.test.ts +43 -0
- package/src/lint/post-synth/whm407.test.ts +60 -0
- package/src/lint/post-synth/whm501.test.ts +70 -0
- package/src/lint/post-synth/whm502.test.ts +72 -0
- package/src/lint/rules/chart-metadata.test.ts +45 -0
- package/src/lint/rules/no-hardcoded-image.test.ts +41 -0
- package/src/lint/rules/values-no-secrets.test.ts +48 -0
- package/src/plugin.test.ts +3 -3
- package/src/plugin.ts +190 -19
- package/src/resources.ts +29 -0
- package/src/skills/chant-helm.md +447 -0
- package/dist/skills/chant-helm-create-chart.md +0 -211
- package/src/skills/create-chart.md +0 -211
- /package/dist/skills/{chant-helm-chart-patterns.md → chant-helm-patterns.md} +0 -0
- /package/dist/skills/{chant-helm-chart-security-patterns.md → chant-helm-security.md} +0 -0
- /package/src/skills/{chart-patterns.md → chant-helm-patterns.md} +0 -0
- /package/src/skills/{chart-security-patterns.md → chant-helm-security.md} +0 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test";
|
|
2
|
+
import type { PostSynthContext } from "@intentius/chant/lint/post-synth";
|
|
3
|
+
import type { SerializerResult } from "@intentius/chant/serializer";
|
|
4
|
+
import { whm301 } from "./whm301";
|
|
5
|
+
|
|
6
|
+
function makeCtx(files: Record<string, string>): PostSynthContext {
|
|
7
|
+
const result: SerializerResult = { primary: files["Chart.yaml"] ?? "", files };
|
|
8
|
+
const outputs = new Map<string, string | SerializerResult>();
|
|
9
|
+
outputs.set("helm", result);
|
|
10
|
+
return {
|
|
11
|
+
outputs,
|
|
12
|
+
entities: new Map(),
|
|
13
|
+
buildResult: {
|
|
14
|
+
outputs,
|
|
15
|
+
entities: new Map(),
|
|
16
|
+
warnings: [],
|
|
17
|
+
errors: [],
|
|
18
|
+
sourceFileCount: 1,
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
describe("WHM301: Helm tests", () => {
|
|
24
|
+
test("info when no tests defined for application chart", () => {
|
|
25
|
+
const ctx = makeCtx({
|
|
26
|
+
"Chart.yaml": "apiVersion: v2\nname: test\nversion: 0.1.0\ntype: application\n",
|
|
27
|
+
"templates/deploy.yaml": "kind: Deployment\n",
|
|
28
|
+
});
|
|
29
|
+
const diags = whm301.check(ctx);
|
|
30
|
+
expect(diags).toHaveLength(1);
|
|
31
|
+
expect(diags[0].checkId).toBe("WHM301");
|
|
32
|
+
expect(diags[0].severity).toBe("info");
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("passes when test exists", () => {
|
|
36
|
+
const ctx = makeCtx({
|
|
37
|
+
"Chart.yaml": "apiVersion: v2\nname: test\nversion: 0.1.0\ntype: application\n",
|
|
38
|
+
"templates/tests/test-connection.yaml": "helm.sh/hook: test\n",
|
|
39
|
+
});
|
|
40
|
+
expect(whm301.check(ctx)).toHaveLength(0);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("skips library charts", () => {
|
|
44
|
+
const ctx = makeCtx({
|
|
45
|
+
"Chart.yaml": "apiVersion: v2\nname: test\nversion: 0.1.0\ntype: library\n",
|
|
46
|
+
});
|
|
47
|
+
expect(whm301.check(ctx)).toHaveLength(0);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test";
|
|
2
|
+
import type { PostSynthContext } from "@intentius/chant/lint/post-synth";
|
|
3
|
+
import type { SerializerResult } from "@intentius/chant/serializer";
|
|
4
|
+
import { whm302 } from "./whm302";
|
|
5
|
+
|
|
6
|
+
function makeCtx(files: Record<string, string>): PostSynthContext {
|
|
7
|
+
const result: SerializerResult = { primary: files["Chart.yaml"] ?? "", files };
|
|
8
|
+
const outputs = new Map<string, string | SerializerResult>();
|
|
9
|
+
outputs.set("helm", result);
|
|
10
|
+
return {
|
|
11
|
+
outputs,
|
|
12
|
+
entities: new Map(),
|
|
13
|
+
buildResult: {
|
|
14
|
+
outputs,
|
|
15
|
+
entities: new Map(),
|
|
16
|
+
warnings: [],
|
|
17
|
+
errors: [],
|
|
18
|
+
sourceFileCount: 1,
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
describe("WHM302: resource limits", () => {
|
|
24
|
+
test("info when containers lack resources", () => {
|
|
25
|
+
const ctx = makeCtx({
|
|
26
|
+
"Chart.yaml": "apiVersion: v2\nname: test\nversion: 0.1.0\n",
|
|
27
|
+
"templates/deploy.yaml": "kind: Deployment\nspec:\n containers:\n - name: app\n",
|
|
28
|
+
});
|
|
29
|
+
const diags = whm302.check(ctx);
|
|
30
|
+
expect(diags).toHaveLength(1);
|
|
31
|
+
expect(diags[0].checkId).toBe("WHM302");
|
|
32
|
+
expect(diags[0].severity).toBe("info");
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("passes when resources are set", () => {
|
|
36
|
+
const ctx = makeCtx({
|
|
37
|
+
"Chart.yaml": "apiVersion: v2\nname: test\nversion: 0.1.0\n",
|
|
38
|
+
"templates/deploy.yaml": "kind: Deployment\nspec:\n containers:\n - name: app\n resources:\n limits:\n cpu: 100m\n",
|
|
39
|
+
});
|
|
40
|
+
expect(whm302.check(ctx)).toHaveLength(0);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("passes when resources use values reference", () => {
|
|
44
|
+
const ctx = makeCtx({
|
|
45
|
+
"Chart.yaml": "apiVersion: v2\nname: test\nversion: 0.1.0\n",
|
|
46
|
+
"templates/deploy.yaml": "kind: Deployment\nspec:\n containers:\n - name: app\n resources: {{ toYaml .Values.resources }}\n",
|
|
47
|
+
});
|
|
48
|
+
expect(whm302.check(ctx)).toHaveLength(0);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("skips test templates", () => {
|
|
52
|
+
const ctx = makeCtx({
|
|
53
|
+
"Chart.yaml": "apiVersion: v2\nname: test\nversion: 0.1.0\n",
|
|
54
|
+
"templates/tests/test.yaml": "containers:\n - name: test\n",
|
|
55
|
+
});
|
|
56
|
+
expect(whm302.check(ctx)).toHaveLength(0);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test";
|
|
2
|
+
import type { PostSynthContext } from "@intentius/chant/lint/post-synth";
|
|
3
|
+
import type { SerializerResult } from "@intentius/chant/serializer";
|
|
4
|
+
import { whm401 } from "./whm401";
|
|
5
|
+
|
|
6
|
+
function makeCtx(files: Record<string, string>): PostSynthContext {
|
|
7
|
+
const result: SerializerResult = { primary: files["Chart.yaml"] ?? "", files };
|
|
8
|
+
const outputs = new Map<string, string | SerializerResult>();
|
|
9
|
+
outputs.set("helm", result);
|
|
10
|
+
return {
|
|
11
|
+
outputs,
|
|
12
|
+
entities: new Map(),
|
|
13
|
+
buildResult: {
|
|
14
|
+
outputs,
|
|
15
|
+
entities: new Map(),
|
|
16
|
+
warnings: [],
|
|
17
|
+
errors: [],
|
|
18
|
+
sourceFileCount: 1,
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
describe("WHM401: image tag", () => {
|
|
24
|
+
test("warns on :latest tag", () => {
|
|
25
|
+
const ctx = makeCtx({
|
|
26
|
+
"Chart.yaml": "apiVersion: v2\nname: test\nversion: 0.1.0\n",
|
|
27
|
+
"templates/deploy.yaml": "kind: Deployment\nspec:\n containers:\n - name: app\n image: nginx:latest\n",
|
|
28
|
+
});
|
|
29
|
+
const diags = whm401.check(ctx);
|
|
30
|
+
expect(diags).toHaveLength(1);
|
|
31
|
+
expect(diags[0].checkId).toBe("WHM401");
|
|
32
|
+
expect(diags[0].severity).toBe("warning");
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("warns on untagged image", () => {
|
|
36
|
+
const ctx = makeCtx({
|
|
37
|
+
"Chart.yaml": "apiVersion: v2\nname: test\nversion: 0.1.0\n",
|
|
38
|
+
"templates/deploy.yaml": "kind: Deployment\nspec:\n containers:\n - name: app\n image: nginx\n",
|
|
39
|
+
});
|
|
40
|
+
const diags = whm401.check(ctx);
|
|
41
|
+
expect(diags).toHaveLength(1);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test("passes with pinned tag", () => {
|
|
45
|
+
const ctx = makeCtx({
|
|
46
|
+
"Chart.yaml": "apiVersion: v2\nname: test\nversion: 0.1.0\n",
|
|
47
|
+
"templates/deploy.yaml": "kind: Deployment\nspec:\n containers:\n - name: app\n image: nginx:1.25.0\n",
|
|
48
|
+
});
|
|
49
|
+
expect(whm401.check(ctx)).toHaveLength(0);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("passes with .Values reference", () => {
|
|
53
|
+
const ctx = makeCtx({
|
|
54
|
+
"Chart.yaml": "apiVersion: v2\nname: test\nversion: 0.1.0\n",
|
|
55
|
+
"templates/deploy.yaml": "kind: Deployment\nspec:\n containers:\n - name: app\n image: {{ .Values.image.repository }}:{{ .Values.image.tag }}\n",
|
|
56
|
+
});
|
|
57
|
+
expect(whm401.check(ctx)).toHaveLength(0);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test";
|
|
2
|
+
import type { PostSynthContext } from "@intentius/chant/lint/post-synth";
|
|
3
|
+
import type { SerializerResult } from "@intentius/chant/serializer";
|
|
4
|
+
import { whm402 } from "./whm402";
|
|
5
|
+
|
|
6
|
+
function makeCtx(files: Record<string, string>): PostSynthContext {
|
|
7
|
+
const result: SerializerResult = { primary: files["Chart.yaml"] ?? "", files };
|
|
8
|
+
const outputs = new Map<string, string | SerializerResult>();
|
|
9
|
+
outputs.set("helm", result);
|
|
10
|
+
return {
|
|
11
|
+
outputs,
|
|
12
|
+
entities: new Map(),
|
|
13
|
+
buildResult: {
|
|
14
|
+
outputs,
|
|
15
|
+
entities: new Map(),
|
|
16
|
+
warnings: [],
|
|
17
|
+
errors: [],
|
|
18
|
+
sourceFileCount: 1,
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
describe("WHM402: runAsNonRoot", () => {
|
|
24
|
+
test("warns when containers lack runAsNonRoot", () => {
|
|
25
|
+
const ctx = makeCtx({
|
|
26
|
+
"Chart.yaml": "apiVersion: v2\nname: test\nversion: 0.1.0\n",
|
|
27
|
+
"templates/deploy.yaml": "kind: Deployment\nspec:\n containers:\n - name: app\n",
|
|
28
|
+
});
|
|
29
|
+
const diags = whm402.check(ctx);
|
|
30
|
+
expect(diags).toHaveLength(1);
|
|
31
|
+
expect(diags[0].checkId).toBe("WHM402");
|
|
32
|
+
expect(diags[0].severity).toBe("warning");
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("passes with runAsNonRoot: true", () => {
|
|
36
|
+
const ctx = makeCtx({
|
|
37
|
+
"Chart.yaml": "apiVersion: v2\nname: test\nversion: 0.1.0\n",
|
|
38
|
+
"templates/deploy.yaml": "kind: Deployment\nspec:\n containers:\n - name: app\n securityContext:\n runAsNonRoot: true\n",
|
|
39
|
+
});
|
|
40
|
+
expect(whm402.check(ctx)).toHaveLength(0);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("passes with .Values.securityContext ref", () => {
|
|
44
|
+
const ctx = makeCtx({
|
|
45
|
+
"Chart.yaml": "apiVersion: v2\nname: test\nversion: 0.1.0\n",
|
|
46
|
+
"templates/deploy.yaml": "kind: Deployment\nspec:\n containers:\n - name: app\n securityContext: {{ toYaml .Values.securityContext }}\n",
|
|
47
|
+
});
|
|
48
|
+
expect(whm402.check(ctx)).toHaveLength(0);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("passes with .Values.podSecurityContext ref", () => {
|
|
52
|
+
const ctx = makeCtx({
|
|
53
|
+
"Chart.yaml": "apiVersion: v2\nname: test\nversion: 0.1.0\n",
|
|
54
|
+
"templates/deploy.yaml": "kind: Deployment\nspec:\n containers:\n - name: app\n securityContext: {{ toYaml .Values.podSecurityContext }}\n",
|
|
55
|
+
});
|
|
56
|
+
expect(whm402.check(ctx)).toHaveLength(0);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test";
|
|
2
|
+
import type { PostSynthContext } from "@intentius/chant/lint/post-synth";
|
|
3
|
+
import type { SerializerResult } from "@intentius/chant/serializer";
|
|
4
|
+
import { whm403 } from "./whm403";
|
|
5
|
+
|
|
6
|
+
function makeCtx(files: Record<string, string>): PostSynthContext {
|
|
7
|
+
const result: SerializerResult = { primary: files["Chart.yaml"] ?? "", files };
|
|
8
|
+
const outputs = new Map<string, string | SerializerResult>();
|
|
9
|
+
outputs.set("helm", result);
|
|
10
|
+
return {
|
|
11
|
+
outputs,
|
|
12
|
+
entities: new Map(),
|
|
13
|
+
buildResult: {
|
|
14
|
+
outputs,
|
|
15
|
+
entities: new Map(),
|
|
16
|
+
warnings: [],
|
|
17
|
+
errors: [],
|
|
18
|
+
sourceFileCount: 1,
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
describe("WHM403: readOnlyRootFilesystem", () => {
|
|
24
|
+
test("info when containers lack readOnlyRootFilesystem", () => {
|
|
25
|
+
const ctx = makeCtx({
|
|
26
|
+
"Chart.yaml": "apiVersion: v2\nname: test\nversion: 0.1.0\n",
|
|
27
|
+
"templates/deploy.yaml": "kind: Deployment\nspec:\n containers:\n - name: app\n",
|
|
28
|
+
});
|
|
29
|
+
const diags = whm403.check(ctx);
|
|
30
|
+
expect(diags).toHaveLength(1);
|
|
31
|
+
expect(diags[0].checkId).toBe("WHM403");
|
|
32
|
+
expect(diags[0].severity).toBe("info");
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("passes with readOnlyRootFilesystem: true", () => {
|
|
36
|
+
const ctx = makeCtx({
|
|
37
|
+
"Chart.yaml": "apiVersion: v2\nname: test\nversion: 0.1.0\n",
|
|
38
|
+
"templates/deploy.yaml": "kind: Deployment\nspec:\n containers:\n - name: app\n securityContext:\n readOnlyRootFilesystem: true\n",
|
|
39
|
+
});
|
|
40
|
+
expect(whm403.check(ctx)).toHaveLength(0);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("passes with .Values.securityContext ref", () => {
|
|
44
|
+
const ctx = makeCtx({
|
|
45
|
+
"Chart.yaml": "apiVersion: v2\nname: test\nversion: 0.1.0\n",
|
|
46
|
+
"templates/deploy.yaml": "kind: Deployment\nspec:\n containers:\n - name: app\n securityContext: {{ toYaml .Values.securityContext }}\n",
|
|
47
|
+
});
|
|
48
|
+
expect(whm403.check(ctx)).toHaveLength(0);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test";
|
|
2
|
+
import type { PostSynthContext } from "@intentius/chant/lint/post-synth";
|
|
3
|
+
import type { SerializerResult } from "@intentius/chant/serializer";
|
|
4
|
+
import { whm404 } from "./whm404";
|
|
5
|
+
|
|
6
|
+
function makeCtx(files: Record<string, string>): PostSynthContext {
|
|
7
|
+
const result: SerializerResult = { primary: files["Chart.yaml"] ?? "", files };
|
|
8
|
+
const outputs = new Map<string, string | SerializerResult>();
|
|
9
|
+
outputs.set("helm", result);
|
|
10
|
+
return {
|
|
11
|
+
outputs,
|
|
12
|
+
entities: new Map(),
|
|
13
|
+
buildResult: {
|
|
14
|
+
outputs,
|
|
15
|
+
entities: new Map(),
|
|
16
|
+
warnings: [],
|
|
17
|
+
errors: [],
|
|
18
|
+
sourceFileCount: 1,
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
describe("WHM404: privileged mode", () => {
|
|
24
|
+
test("error on privileged: true", () => {
|
|
25
|
+
const ctx = makeCtx({
|
|
26
|
+
"Chart.yaml": "apiVersion: v2\nname: test\nversion: 0.1.0\n",
|
|
27
|
+
"templates/deploy.yaml": "kind: Deployment\nspec:\n containers:\n - name: app\n securityContext:\n privileged: true\n",
|
|
28
|
+
});
|
|
29
|
+
const diags = whm404.check(ctx);
|
|
30
|
+
expect(diags).toHaveLength(1);
|
|
31
|
+
expect(diags[0].checkId).toBe("WHM404");
|
|
32
|
+
expect(diags[0].severity).toBe("error");
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("passes without privileged mode", () => {
|
|
36
|
+
const ctx = makeCtx({
|
|
37
|
+
"Chart.yaml": "apiVersion: v2\nname: test\nversion: 0.1.0\n",
|
|
38
|
+
"templates/deploy.yaml": "kind: Deployment\nspec:\n containers:\n - name: app\n",
|
|
39
|
+
});
|
|
40
|
+
expect(whm404.check(ctx)).toHaveLength(0);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("passes with privileged: false", () => {
|
|
44
|
+
const ctx = makeCtx({
|
|
45
|
+
"Chart.yaml": "apiVersion: v2\nname: test\nversion: 0.1.0\n",
|
|
46
|
+
"templates/deploy.yaml": "kind: Deployment\nspec:\n containers:\n - name: app\n securityContext:\n privileged: false\n",
|
|
47
|
+
});
|
|
48
|
+
expect(whm404.check(ctx)).toHaveLength(0);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test";
|
|
2
|
+
import type { PostSynthContext } from "@intentius/chant/lint/post-synth";
|
|
3
|
+
import type { SerializerResult } from "@intentius/chant/serializer";
|
|
4
|
+
import { whm405 } from "./whm405";
|
|
5
|
+
|
|
6
|
+
function makeCtx(files: Record<string, string>): PostSynthContext {
|
|
7
|
+
const result: SerializerResult = { primary: files["Chart.yaml"] ?? "", files };
|
|
8
|
+
const outputs = new Map<string, string | SerializerResult>();
|
|
9
|
+
outputs.set("helm", result);
|
|
10
|
+
return {
|
|
11
|
+
outputs,
|
|
12
|
+
entities: new Map(),
|
|
13
|
+
buildResult: {
|
|
14
|
+
outputs,
|
|
15
|
+
entities: new Map(),
|
|
16
|
+
warnings: [],
|
|
17
|
+
errors: [],
|
|
18
|
+
sourceFileCount: 1,
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
describe("WHM405: resource spec detail", () => {
|
|
24
|
+
test("warns when resources lack cpu", () => {
|
|
25
|
+
const ctx = makeCtx({
|
|
26
|
+
"Chart.yaml": "apiVersion: v2\nname: test\nversion: 0.1.0\n",
|
|
27
|
+
"templates/deploy.yaml": "kind: Deployment\nspec:\n containers:\n - name: app\n resources:\n limits:\n memory: 256Mi\n",
|
|
28
|
+
});
|
|
29
|
+
const diags = whm405.check(ctx);
|
|
30
|
+
expect(diags).toHaveLength(1);
|
|
31
|
+
expect(diags[0].checkId).toBe("WHM405");
|
|
32
|
+
expect(diags[0].message).toContain("cpu");
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("passes with both cpu and memory", () => {
|
|
36
|
+
const ctx = makeCtx({
|
|
37
|
+
"Chart.yaml": "apiVersion: v2\nname: test\nversion: 0.1.0\n",
|
|
38
|
+
"templates/deploy.yaml": "kind: Deployment\nspec:\n containers:\n - name: app\n resources:\n limits:\n cpu: 100m\n memory: 256Mi\n",
|
|
39
|
+
});
|
|
40
|
+
expect(whm405.check(ctx)).toHaveLength(0);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("passes when resources use .Values", () => {
|
|
44
|
+
const ctx = makeCtx({
|
|
45
|
+
"Chart.yaml": "apiVersion: v2\nname: test\nversion: 0.1.0\n",
|
|
46
|
+
"templates/deploy.yaml": "kind: Deployment\nspec:\n containers:\n - name: app\n resources: {{ toYaml .Values.resources }}\n",
|
|
47
|
+
});
|
|
48
|
+
expect(whm405.check(ctx)).toHaveLength(0);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("warns when resources lack memory", () => {
|
|
52
|
+
const ctx = makeCtx({
|
|
53
|
+
"Chart.yaml": "apiVersion: v2\nname: test\nversion: 0.1.0\n",
|
|
54
|
+
"templates/deploy.yaml": "kind: Deployment\nspec:\n containers:\n - name: app\n resources:\n limits:\n cpu: 100m\n",
|
|
55
|
+
});
|
|
56
|
+
const diags = whm405.check(ctx);
|
|
57
|
+
expect(diags).toHaveLength(1);
|
|
58
|
+
expect(diags[0].message).toContain("memory");
|
|
59
|
+
});
|
|
60
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test";
|
|
2
|
+
import type { PostSynthContext } from "@intentius/chant/lint/post-synth";
|
|
3
|
+
import type { SerializerResult } from "@intentius/chant/serializer";
|
|
4
|
+
import { whm406 } from "./whm406";
|
|
5
|
+
|
|
6
|
+
function makeCtx(files: Record<string, string>): PostSynthContext {
|
|
7
|
+
const result: SerializerResult = { primary: files["Chart.yaml"] ?? "", files };
|
|
8
|
+
const outputs = new Map<string, string | SerializerResult>();
|
|
9
|
+
outputs.set("helm", result);
|
|
10
|
+
return {
|
|
11
|
+
outputs,
|
|
12
|
+
entities: new Map(),
|
|
13
|
+
buildResult: {
|
|
14
|
+
outputs,
|
|
15
|
+
entities: new Map(),
|
|
16
|
+
warnings: [],
|
|
17
|
+
errors: [],
|
|
18
|
+
sourceFileCount: 1,
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
describe("WHM406: CRD lifecycle", () => {
|
|
24
|
+
test("info when crds/ directory exists", () => {
|
|
25
|
+
const ctx = makeCtx({
|
|
26
|
+
"Chart.yaml": "apiVersion: v2\nname: test\nversion: 0.1.0\n",
|
|
27
|
+
"crds/my-crd.yaml": "apiVersion: apiextensions.k8s.io/v1\nkind: CustomResourceDefinition\n",
|
|
28
|
+
});
|
|
29
|
+
const diags = whm406.check(ctx);
|
|
30
|
+
expect(diags).toHaveLength(1);
|
|
31
|
+
expect(diags[0].checkId).toBe("WHM406");
|
|
32
|
+
expect(diags[0].severity).toBe("info");
|
|
33
|
+
expect(diags[0].message).toContain("CRD");
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("passes without crds/ directory", () => {
|
|
37
|
+
const ctx = makeCtx({
|
|
38
|
+
"Chart.yaml": "apiVersion: v2\nname: test\nversion: 0.1.0\n",
|
|
39
|
+
"templates/deploy.yaml": "kind: Deployment\n",
|
|
40
|
+
});
|
|
41
|
+
expect(whm406.check(ctx)).toHaveLength(0);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test";
|
|
2
|
+
import type { PostSynthContext } from "@intentius/chant/lint/post-synth";
|
|
3
|
+
import type { SerializerResult } from "@intentius/chant/serializer";
|
|
4
|
+
import { whm407 } from "./whm407";
|
|
5
|
+
|
|
6
|
+
function makeCtx(files: Record<string, string>): PostSynthContext {
|
|
7
|
+
const result: SerializerResult = { primary: files["Chart.yaml"] ?? "", files };
|
|
8
|
+
const outputs = new Map<string, string | SerializerResult>();
|
|
9
|
+
outputs.set("helm", result);
|
|
10
|
+
return {
|
|
11
|
+
outputs,
|
|
12
|
+
entities: new Map(),
|
|
13
|
+
buildResult: {
|
|
14
|
+
outputs,
|
|
15
|
+
entities: new Map(),
|
|
16
|
+
warnings: [],
|
|
17
|
+
errors: [],
|
|
18
|
+
sourceFileCount: 1,
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
describe("WHM407: inline secrets", () => {
|
|
24
|
+
test("warns on Secret with inline data", () => {
|
|
25
|
+
const ctx = makeCtx({
|
|
26
|
+
"Chart.yaml": "apiVersion: v2\nname: test\nversion: 0.1.0\n",
|
|
27
|
+
"templates/secret.yaml": "apiVersion: v1\nkind: Secret\nmetadata:\n name: my-secret\ndata:\n password: c2VjcmV0\n",
|
|
28
|
+
});
|
|
29
|
+
const diags = whm407.check(ctx);
|
|
30
|
+
expect(diags).toHaveLength(1);
|
|
31
|
+
expect(diags[0].checkId).toBe("WHM407");
|
|
32
|
+
expect(diags[0].severity).toBe("warning");
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("passes when ExternalSecret is used in chart", () => {
|
|
36
|
+
const ctx = makeCtx({
|
|
37
|
+
"Chart.yaml": "apiVersion: v2\nname: test\nversion: 0.1.0\n",
|
|
38
|
+
"templates/secret.yaml": "apiVersion: v1\nkind: Secret\nmetadata:\n name: my-secret\ndata:\n password: c2VjcmV0\n",
|
|
39
|
+
"templates/external-secret.yaml": "apiVersion: external-secrets.io/v1beta1\nkind: ExternalSecret\n",
|
|
40
|
+
});
|
|
41
|
+
expect(whm407.check(ctx)).toHaveLength(0);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test("passes when data uses template values", () => {
|
|
45
|
+
const ctx = makeCtx({
|
|
46
|
+
"Chart.yaml": "apiVersion: v2\nname: test\nversion: 0.1.0\n",
|
|
47
|
+
"templates/secret.yaml": "apiVersion: v1\nkind: Secret\nmetadata:\n name: my-secret\ndata:\n password: {{ .Values.secret.password }}\n",
|
|
48
|
+
});
|
|
49
|
+
expect(whm407.check(ctx)).toHaveLength(0);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("passes when SealedSecret is present", () => {
|
|
53
|
+
const ctx = makeCtx({
|
|
54
|
+
"Chart.yaml": "apiVersion: v2\nname: test\nversion: 0.1.0\n",
|
|
55
|
+
"templates/secret.yaml": "apiVersion: v1\nkind: Secret\nmetadata:\n name: my-secret\ndata:\n password: c2VjcmV0\n",
|
|
56
|
+
"templates/sealed.yaml": "kind: SealedSecret\n",
|
|
57
|
+
});
|
|
58
|
+
expect(whm407.check(ctx)).toHaveLength(0);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test";
|
|
2
|
+
import type { PostSynthContext } from "@intentius/chant/lint/post-synth";
|
|
3
|
+
import type { SerializerResult } from "@intentius/chant/serializer";
|
|
4
|
+
import { whm501 } from "./whm501";
|
|
5
|
+
|
|
6
|
+
function makeCtx(files: Record<string, string>): PostSynthContext {
|
|
7
|
+
const result: SerializerResult = { primary: files["Chart.yaml"] ?? "", files };
|
|
8
|
+
const outputs = new Map<string, string | SerializerResult>();
|
|
9
|
+
outputs.set("helm", result);
|
|
10
|
+
return {
|
|
11
|
+
outputs,
|
|
12
|
+
entities: new Map(),
|
|
13
|
+
buildResult: {
|
|
14
|
+
outputs,
|
|
15
|
+
entities: new Map(),
|
|
16
|
+
warnings: [],
|
|
17
|
+
errors: [],
|
|
18
|
+
sourceFileCount: 1,
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
describe("WHM501: unused values", () => {
|
|
24
|
+
test("info on values key not referenced in templates", () => {
|
|
25
|
+
const ctx = makeCtx({
|
|
26
|
+
"Chart.yaml": "apiVersion: v2\nname: test\nversion: 0.1.0\n",
|
|
27
|
+
"values.yaml": "replicaCount: 1\nunusedKey: hello\n",
|
|
28
|
+
"templates/deploy.yaml": "replicas: {{ .Values.replicaCount }}\n",
|
|
29
|
+
});
|
|
30
|
+
const diags = whm501.check(ctx);
|
|
31
|
+
expect(diags.some((d) => d.message.includes("unusedKey"))).toBe(true);
|
|
32
|
+
expect(diags[0].checkId).toBe("WHM501");
|
|
33
|
+
expect(diags[0].severity).toBe("info");
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("passes when all values are referenced", () => {
|
|
37
|
+
const ctx = makeCtx({
|
|
38
|
+
"Chart.yaml": "apiVersion: v2\nname: test\nversion: 0.1.0\n",
|
|
39
|
+
"values.yaml": "replicaCount: 1\n",
|
|
40
|
+
"templates/deploy.yaml": "replicas: {{ .Values.replicaCount }}\n",
|
|
41
|
+
});
|
|
42
|
+
expect(whm501.check(ctx)).toHaveLength(0);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test("excludes nameOverride and fullnameOverride", () => {
|
|
46
|
+
const ctx = makeCtx({
|
|
47
|
+
"Chart.yaml": "apiVersion: v2\nname: test\nversion: 0.1.0\n",
|
|
48
|
+
"values.yaml": 'nameOverride: ""\nfullnameOverride: ""\n',
|
|
49
|
+
"templates/deploy.yaml": "kind: Deployment\n",
|
|
50
|
+
});
|
|
51
|
+
expect(whm501.check(ctx)).toHaveLength(0);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test("parent key is not unused when child is referenced", () => {
|
|
55
|
+
const ctx = makeCtx({
|
|
56
|
+
"Chart.yaml": "apiVersion: v2\nname: test\nversion: 0.1.0\n",
|
|
57
|
+
"values.yaml": "image:\n repository: nginx\n tag: latest\n",
|
|
58
|
+
"templates/deploy.yaml": "image: {{ .Values.image.repository }}:{{ .Values.image.tag }}\n",
|
|
59
|
+
});
|
|
60
|
+
expect(whm501.check(ctx)).toHaveLength(0);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("passes with empty values", () => {
|
|
64
|
+
const ctx = makeCtx({
|
|
65
|
+
"Chart.yaml": "apiVersion: v2\nname: test\nversion: 0.1.0\n",
|
|
66
|
+
"values.yaml": "{}\n",
|
|
67
|
+
});
|
|
68
|
+
expect(whm501.check(ctx)).toHaveLength(0);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test";
|
|
2
|
+
import type { PostSynthContext } from "@intentius/chant/lint/post-synth";
|
|
3
|
+
import type { SerializerResult } from "@intentius/chant/serializer";
|
|
4
|
+
import { whm502 } from "./whm502";
|
|
5
|
+
|
|
6
|
+
function makeCtx(files: Record<string, string>): PostSynthContext {
|
|
7
|
+
const result: SerializerResult = { primary: files["Chart.yaml"] ?? "", files };
|
|
8
|
+
const outputs = new Map<string, string | SerializerResult>();
|
|
9
|
+
outputs.set("helm", result);
|
|
10
|
+
return {
|
|
11
|
+
outputs,
|
|
12
|
+
entities: new Map(),
|
|
13
|
+
buildResult: {
|
|
14
|
+
outputs,
|
|
15
|
+
entities: new Map(),
|
|
16
|
+
warnings: [],
|
|
17
|
+
errors: [],
|
|
18
|
+
sourceFileCount: 1,
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
describe("WHM502: deprecated API versions", () => {
|
|
24
|
+
test("warns on extensions/v1beta1 Ingress", () => {
|
|
25
|
+
const ctx = makeCtx({
|
|
26
|
+
"Chart.yaml": "apiVersion: v2\nname: test\nversion: 0.1.0\n",
|
|
27
|
+
"templates/ingress.yaml": "apiVersion: extensions/v1beta1\nkind: Ingress\n",
|
|
28
|
+
});
|
|
29
|
+
const diags = whm502.check(ctx);
|
|
30
|
+
expect(diags).toHaveLength(1);
|
|
31
|
+
expect(diags[0].checkId).toBe("WHM502");
|
|
32
|
+
expect(diags[0].severity).toBe("warning");
|
|
33
|
+
expect(diags[0].message).toContain("networking.k8s.io/v1");
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("warns on apps/v1beta2", () => {
|
|
37
|
+
const ctx = makeCtx({
|
|
38
|
+
"Chart.yaml": "apiVersion: v2\nname: test\nversion: 0.1.0\n",
|
|
39
|
+
"templates/deploy.yaml": "apiVersion: apps/v1beta2\nkind: Deployment\n",
|
|
40
|
+
});
|
|
41
|
+
const diags = whm502.check(ctx);
|
|
42
|
+
expect(diags).toHaveLength(1);
|
|
43
|
+
expect(diags[0].message).toContain("apps/v1");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("passes with current API versions", () => {
|
|
47
|
+
const ctx = makeCtx({
|
|
48
|
+
"Chart.yaml": "apiVersion: v2\nname: test\nversion: 0.1.0\n",
|
|
49
|
+
"templates/deploy.yaml": "apiVersion: apps/v1\nkind: Deployment\n",
|
|
50
|
+
"templates/ingress.yaml": "apiVersion: networking.k8s.io/v1\nkind: Ingress\n",
|
|
51
|
+
});
|
|
52
|
+
expect(whm502.check(ctx)).toHaveLength(0);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test("skips template expression apiVersions", () => {
|
|
56
|
+
const ctx = makeCtx({
|
|
57
|
+
"Chart.yaml": "apiVersion: v2\nname: test\nversion: 0.1.0\n",
|
|
58
|
+
"templates/deploy.yaml": "apiVersion: {{ .Capabilities.APIVersions }}\nkind: Deployment\n",
|
|
59
|
+
});
|
|
60
|
+
expect(whm502.check(ctx)).toHaveLength(0);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("warns on batch/v1beta1 CronJob", () => {
|
|
64
|
+
const ctx = makeCtx({
|
|
65
|
+
"Chart.yaml": "apiVersion: v2\nname: test\nversion: 0.1.0\n",
|
|
66
|
+
"templates/cron.yaml": "apiVersion: batch/v1beta1\nkind: CronJob\n",
|
|
67
|
+
});
|
|
68
|
+
const diags = whm502.check(ctx);
|
|
69
|
+
expect(diags).toHaveLength(1);
|
|
70
|
+
expect(diags[0].message).toContain("batch/v1");
|
|
71
|
+
});
|
|
72
|
+
});
|