Skip to content

Commit

Permalink
feat(lint): Add no-missing-gateway-class rule
Browse files Browse the repository at this point in the history
  • Loading branch information
tommy351 committed Mar 18, 2024
1 parent dda0cdd commit fe88dcc
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 3 deletions.
83 changes: 83 additions & 0 deletions plugins/lint/src/rules/no-missing-gateway-class.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/// <reference types="jest-extended" />
import { Gateway } from "@kubernetes-models/gateway-api/gateway.networking.k8s.io/v1/Gateway";
import { GatewayClass } from "@kubernetes-models/gateway-api/gateway.networking.k8s.io/v1/GatewayClass";
import { createManifest, validateAll } from "../test-utils";
import rule from "./no-missing-gateway-class";

test("should pass when data is undefined", () => {
const manifest = createManifest(undefined);
expect(validateAll(rule, undefined, [manifest])).toBeEmpty();
});

test("should pass when data is an empty object", () => {
const manifest = createManifest({});
expect(validateAll(rule, undefined, [manifest])).toBeEmpty();
});

test("should pass when gatewayClassName is empty", () => {
const manifest = createManifest(
new Gateway({
metadata: { name: "foo" },
spec: {
gatewayClassName: "",
listeners: []
}
})
);
expect(validateAll(rule, undefined, [manifest])).toBeEmpty();
});

test("should pass when gateway class exists", () => {
const gatewayClass = createManifest(
new GatewayClass({
metadata: { name: "bar" },
spec: {
controllerName: ""
}
})
);
const gateway = createManifest(
new Gateway({
metadata: { name: "foo" },

spec: {
gatewayClassName: "bar",
listeners: []
}
})
);
expect(validateAll(rule, undefined, [gatewayClass, gateway])).toBeEmpty();
});

test("should pass when gateway class is allowed", () => {
const gateway = createManifest(
new Gateway({
metadata: { name: "foo" },

spec: {
gatewayClassName: "bar",
listeners: []
}
})
);
expect(validateAll(rule, { allow: ["bar"] }, [gateway])).toBeEmpty();
});

test("should report when gateway class does not exist", () => {
const gateway = createManifest(
new Gateway({
metadata: { name: "foo" },

spec: {
gatewayClassName: "bar",
listeners: []
}
})
);
expect(validateAll(rule, undefined, [gateway])).toEqual([
{
manifest: gateway,
message: `GatewayClass "bar" does not exist.`
}
]);
});
45 changes: 45 additions & 0 deletions plugins/lint/src/rules/no-missing-gateway-class.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { array, object, optional, string } from "superstruct";
import { createRule } from "./types";
import { compilePattern, matchAny } from "../utils/pattern";
import {
GATEWAY_GROUP,
buildMissingResourceMessage,
isGateway
} from "../utils/manifest";

export default createRule({
config: object({
allow: optional(array(string()))
}),
factory(ctx) {
const isAllowed = matchAny((ctx.config?.allow ?? []).map(compilePattern));

return {
validateAll(manifests) {
manifests.forEach((manifest) => {
if (!isGateway(manifest)) return;

const name = manifest.data.spec?.gatewayClassName;
if (!name) return;

if (isAllowed(name)) return;

if (
manifests.find({
apiGroup: GATEWAY_GROUP,
kind: "GatewayClass",
name
})
) {
return;
}

ctx.report(
manifest,
buildMissingResourceMessage("GatewayClass", { name })
);
});
}
};
}
});
4 changes: 2 additions & 2 deletions plugins/lint/src/rules/no-missing-gateway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import {
buildMissingResourceMessage,
compileNamespacedNamePattern,
namespacedNameArraySchema,
isGatewayRoute
isGatewayRoute,
GATEWAY_GROUP
} from "../utils/manifest";

const GATEWAY_GROUP = "gateway.networking.k8s.io";
const GATEWAY_KIND = "Gateway";

export default createRule({
Expand Down
2 changes: 2 additions & 0 deletions plugins/lint/src/rules/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import banImage from "./ban-image";
import banServiceType from "./ban-service-type";
import noMissingConfigMap from "./no-missing-config-map";
import noMissingGateway from "./no-missing-gateway";
import noMissingGatewayClass from "./no-missing-gateway-class";
import noMissingNamespace from "./no-missing-namespace";
import noMissingPodVolumeMount from "./no-missing-pod-volume-mount";
import noMissingPv from "./no-missing-pv";
Expand Down Expand Up @@ -34,6 +35,7 @@ export const rules = {
"ban-image": banImage,
"ban-service-type": banServiceType,
"no-missing-config-map": noMissingConfigMap,
"no-missing-gateway-class": noMissingGatewayClass,
"no-missing-gateway": noMissingGateway,
"no-missing-namespace": noMissingNamespace,
"no-missing-pod-volume-mount": noMissingPodVolumeMount,
Expand Down
6 changes: 5 additions & 1 deletion plugins/lint/src/utils/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import type { IGRPCRoute } from "@kubernetes-models/gateway-api/gateway.networki
import type { ITCPRoute } from "@kubernetes-models/gateway-api/gateway.networking.k8s.io/v1alpha2/TCPRoute";
import type { ITLSRoute } from "@kubernetes-models/gateway-api/gateway.networking.k8s.io/v1alpha2/TLSRoute";
import type { IUDPRoute } from "@kubernetes-models/gateway-api/gateway.networking.k8s.io/v1alpha2/UDPRoute";
import type { IGateway } from "@kubernetes-models/gateway-api/gateway.networking.k8s.io/v1/Gateway";
import { Manifest } from "../rules/types";
import { Matcher, compilePattern } from "./pattern";

Expand All @@ -33,6 +34,8 @@ export type GatewayRoute =
| ITLSRoute
| IUDPRoute;

export const GATEWAY_GROUP = "gateway.networking.k8s.io";

const GATEWAY_ROUTE_KINDS = new Set([
"HTTPRoute",
"GRPCRoute",
Expand Down Expand Up @@ -138,6 +141,7 @@ export const isCronJob = groupKindPredicate<ICronJob>("batch", "CronJob");
export const isHPA = groupKindPredicate<
IHPAV1 | IHPAV2Beta1 | IHPAV2Beta2 | IHPAV2
>("autoscaling", "HorizontalPodAutoscaler");
export const isGateway = groupKindPredicate<IGateway>(GATEWAY_GROUP, "Gateway");

export function isGatewayRoute(
manifest: Manifest
Expand All @@ -146,7 +150,7 @@ export function isGatewayRoute(

return (
typeof meta?.apiVersion === "string" &&
apiVersionToGroup(meta.apiVersion) === "gateway.networking.k8s.io" &&
apiVersionToGroup(meta.apiVersion) === GATEWAY_GROUP &&
GATEWAY_ROUTE_KINDS.has(meta.kind)
);
}

0 comments on commit fe88dcc

Please sign in to comment.