diff --git a/internal/module/openapi.go b/internal/module/openapi.go index dc6d6c84..da7e1bff 100644 --- a/internal/module/openapi.go +++ b/internal/module/openapi.go @@ -65,6 +65,10 @@ func helmFormatModuleImages(m *Module, rawValues map[string]any) (chartutil.Valu caps := &capsCopy vers := append([]string{}, caps.APIVersions...) vers = append(vers, "autoscaling.k8s.io/v1/VerticalPodAutoscaler", "cert-manager.io/v1") + vers = appendIfMissing(vers, "gateway.networking.k8s.io/v1/Gateway") + vers = appendIfMissing(vers, "gateway.networking.k8s.io/v1/HTTPRoute") + vers = appendIfMissing(vers, "gateway.networking.k8s.io/v1/ListenerSet") + caps.APIVersions = vers digests := map[string]any{ @@ -86,6 +90,17 @@ func helmFormatModuleImages(m *Module, rawValues map[string]any) (chartutil.Valu } applyDigests(m.GetName(), digests, rawValues) + _ = mergo.Merge(&rawValues, map[string]any{ + "global": map[string]any{ + "discovery": map[string]any{ + "gatewayAPIDefaultGateway": map[string]any{ + "name": "default", + "namespace": "d8-alb", + }, + }, + }, + }, mergo.WithOverride) + top := map[string]any{ "Chart": m.GetMetadata(), "Capabilities": caps, @@ -103,6 +118,16 @@ func helmFormatModuleImages(m *Module, rawValues map[string]any) (chartutil.Valu return top, nil } +func appendIfMissing(values []string, value string) []string { + for _, item := range values { + if item == value { + return values + } + } + + return append(values, value) +} + func ComposeValuesFromSchemas(m *Module, globalSchema *spec.Schema) (chartutil.Values, error) { return ComposeValuesFromSchemasForValuesFile(m, globalSchema, "values.yaml") } diff --git a/pkg/config.go b/pkg/config.go index 7a28986b..d5e9872a 100644 --- a/pkg/config.go +++ b/pkg/config.go @@ -140,6 +140,7 @@ type TemplatesLinterRules struct { ServicePortRule RuleConfig ClusterDomainRule RuleConfig RegistryRule RuleConfig + HTTPRouteRule RuleConfig EnabledModulesRule RuleConfig } @@ -156,6 +157,7 @@ type TemplatesExcludeRules struct { ServicePort ServicePortExcludeList KubeRBACProxy StringRuleExcludeList Ingress KindRuleExcludeList + HTTPRoute KindRuleExcludeList EnabledModules EnabledModulesExcludeRule } diff --git a/pkg/linters/hooks/hooks.go b/pkg/linters/hooks/hooks.go index 8a2bccff..1817a201 100644 --- a/pkg/linters/hooks/hooks.go +++ b/pkg/linters/hooks/hooks.go @@ -50,7 +50,7 @@ func (h *Hooks) Run(m *module.Module) { r := rules.NewHookRule(h.cfg) for _, object := range m.GetStorage() { - r.CheckIngressCopyCustomCertificateRule(m, object, errorList) + r.CheckCopyCustomCertificateRule(m, object, errorList) } } diff --git a/pkg/linters/hooks/rules/rules.go b/pkg/linters/hooks/rules/rules.go index 4f75e2cc..f0f5b390 100644 --- a/pkg/linters/hooks/rules/rules.go +++ b/pkg/linters/hooks/rules/rules.go @@ -48,7 +48,7 @@ func NewHookRule(cfg *pkg.HooksLinterConfig) *HookRule { } } -func (l *HookRule) CheckIngressCopyCustomCertificateRule(m *module.Module, object storage.StoreObject, errorList *errors.LintRuleErrorsList) { +func (l *HookRule) CheckCopyCustomCertificateRule(m *module.Module, object storage.StoreObject, errorList *errors.LintRuleErrorsList) { errorList = errorList.WithRule(l.GetName()).WithFilePath(object.GetPath()) const ( @@ -59,7 +59,7 @@ func (l *HookRule) CheckIngressCopyCustomCertificateRule(m *module.Module, objec errorList = errorList.WithMaxLevel(ptr.To(pkg.Ignored)) } - if object.Unstructured.GetKind() != "Ingress" { + if object.Unstructured.GetKind() != "Ingress" && object.Unstructured.GetKind() != "HTTPRoute" { return } @@ -82,7 +82,7 @@ func (l *HookRule) CheckIngressCopyCustomCertificateRule(m *module.Module, objec } if _, ok := imports[copyCustomCertificateImport]; !ok { - errorList.Error("Ingress resource exists but module does not have copy_custom_certificate hook") + errorList.Errorf("%s resource exists but module does not have copy_custom_certificate hook", object.Unstructured.GetKind()) } } diff --git a/pkg/linters/templates/README.md b/pkg/linters/templates/README.md index 2d804420..62b0aabe 100644 --- a/pkg/linters/templates/README.md +++ b/pkg/linters/templates/README.md @@ -15,6 +15,7 @@ Proper template validation prevents runtime issues, ensures applications are pro | [kube-rbac-proxy](#kube-rbac-proxy) | Validates kube-rbac-proxy CA certificates in namespaces | ✅ | enabled | | [service-port](#service-port) | Validates services use named target ports | ✅ | enabled | | [ingress-rules](#ingress-rules) | Validates Ingress configuration snippets | ✅ | enabled | +| [httproute-rules](#httproute-rules) | Validates that every Ingress has a companion HTTPRoute backed by a ListenerSet | ✅ | enabled | | [prometheus-rules](#prometheus-rules) | Validates Prometheus rules with promtool and proper templates | ✅ | enabled | | [grafana-dashboards](#grafana-dashboards) | Validates Grafana dashboard templates | ✅ | enabled | | [cluster-domain](#cluster-domain) | Validates cluster domain configuration is dynamic | ❌ | enabled | @@ -1009,6 +1010,213 @@ linters-settings: --- +### httproute-rules + +**Purpose:** Ensures that every module using Ingress-Nginx also ships a Gateway API equivalent — an `HTTPRoute` with a matching `app` label and a `ListenerSet` referenced by that route's `parentRefs`. This is the linter enforcement of DKP's transition to Gateway API. + +**Background:** + +DKP is moving from Ingress-Nginx toward the Kubernetes Gateway API. The goal is to claim full Gateway API readiness: every module that exposes HTTP traffic must work through both stacks simultaneously during the transition period. When Ingress-Nginx is eventually retired, modules that followed this rule will require no additional changes. + +The rule enforces the migration contract: + +> *If a module ships an Ingress, it must also ship a functionally equivalent HTTPRoute and a ListenerSet. An Ingress without a Gateway API counterpart is incomplete.* + +**What it checks:** + +For each `Ingress` object in the module templates: + +1. The Ingress has an `app` label. +2. An `HTTPRoute` with the same `app` label value exists in the module templates. +3. The HTTPRoute's `spec.parentRefs` references at least one `ListenerSet` that is also present in the module templates. + +**Why it matters:** + +- Modules that expose services only through Ingress-Nginx will stop working the moment that controller is removed from a cluster. +- Shipping both resources in parallel keeps the module functional on Ingress-Nginx clusters today and on Gateway-API-only clusters tomorrow — with zero additional effort at migration time. +- The ListenerSet requirement guarantees the HTTPRoute is actually connected to a gateway and not left dangling. + +**Examples:** + +❌ **Incorrect** — Ingress without a matching HTTPRoute: + +```yaml +# templates/ingress.yaml +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: dashboard + namespace: d8-my-module + labels: + app: dashboard # ← app label is present, but no HTTPRoute exists +spec: + rules: + - host: dashboard.example.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: dashboard + port: + number: 80 +``` + +**Error:** +``` +Error: Ingress "dashboard" requires a matching HTTPRoute with the same app label, but none was found +``` + +❌ **Incorrect** — HTTPRoute exists but its `parentRefs` do not reference any `ListenerSet` in the module: + +```yaml +# templates/ingress.yaml +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: dashboard + namespace: d8-my-module + labels: + app: dashboard +spec: + rules: + - host: dashboard.example.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: dashboard + port: + number: 80 +--- +# templates/httproute.yaml +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: dashboard + namespace: d8-my-module + labels: + app: dashboard +spec: + parentRefs: + - name: some-external-gateway # ❌ Not a ListenerSet defined in this module + rules: + - matches: + - path: + type: PathPrefix + value: / + backendRefs: + - name: dashboard + port: 80 +``` + +**Error:** +``` +Error: HTTPRoute "dashboard" is invalid for Ingress migration: + spec.parentRefs does not reference any ListenerSet found in module templates +``` + +❌ **Incorrect** — HTTPRoute has empty `parentRefs`: + +```yaml +# templates/httproute.yaml +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: dashboard + labels: + app: dashboard +spec: + parentRefs: [] # ❌ Empty + rules: + - ... +``` + +**Error:** +``` +Error: HTTPRoute "dashboard" is invalid for Ingress migration: + spec.parentRefs must reference an existing ListenerSet +``` + +✅ **Correct** — Ingress, HTTPRoute and ListenerSet all present and properly connected: + +```yaml +# templates/ingress.yaml +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: dashboard + namespace: d8-my-module + labels: + app: dashboard +spec: + rules: + - host: dashboard.example.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: dashboard + port: + number: 80 +--- +# templates/httproute.yaml +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: dashboard + namespace: d8-my-module + labels: + app: dashboard # ✅ Matches Ingress app label +spec: + parentRefs: + - name: d8-alb-listener # ✅ References a ListenerSet defined below + rules: + - matches: + - path: + type: PathPrefix + value: / + backendRefs: + - name: dashboard + port: 80 +--- +# templates/listenerset.yaml +apiVersion: gateway.networking.k8s.io/v1 +kind: ListenerSet +metadata: + name: d8-alb-listener # ✅ Name matches HTTPRoute parentRef + namespace: d8-my-module +spec: + parentRef: + name: default + namespace: d8-alb + listeners: + - name: http + protocol: HTTP + port: 80 +``` + +**Configuration:** + +```yaml +# .dmt.yaml +linters-settings: + templates: + exclude-rules: + httproute: + - kind: Ingress + name: legacy-webhook # Ingress that cannot yet be migrated + - kind: Ingress + name: internal-only +``` + +--- + ### prometheus-rules **Purpose:** Validates Prometheus alerting and recording rules using promtool and ensures proper Helm template structure. This catches syntax errors, invalid queries, and ensures monitoring rules are properly packaged. @@ -1680,6 +1888,8 @@ linters-settings: impact: error ingress: impact: warning + httproute: + impact: error prometheus-rules: impact: info grafana-dashboards: @@ -1725,7 +1935,14 @@ linters-settings: name: dashboard - kind: Ingress name: legacy-api - + + # HTTPRoute exclusions — Ingresses that are not yet required to have a Gateway API companion + httproute: + - kind: Ingress + name: legacy-webhook + - kind: Ingress + name: internal-only + # Service port exclusions (by service name and port name) service-port: - name: d8-control-plane-apiserver @@ -2073,6 +2290,118 @@ Error: Ingress annotation "nginx.ingress.kubernetes.io/configuration-snippet" do name: dashboard ``` +### Issue: Ingress requires a matching HTTPRoute + +**Symptom:** +``` +Error: Ingress "my-app" requires a matching HTTPRoute with the same app label, but none was found +``` + +**Cause:** A module ships an Ingress but no `HTTPRoute` with the same `app` label exists in the templates. + +**Solutions:** + +1. **Add an HTTPRoute and a ListenerSet** (preferred — full Gateway API compliance): + + ```yaml + # templates/httproute.yaml + apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + name: my-app + namespace: d8-my-module + labels: + app: my-app # Must match the Ingress app label + spec: + parentRefs: + - name: d8-alb-listener + rules: + - matches: + - path: + type: PathPrefix + value: / + backendRefs: + - name: my-app + port: 80 + --- + # templates/listenerset.yaml + apiVersion: gateway.networking.k8s.io/v1 + kind: ListenerSet + metadata: + name: d8-alb-listener + namespace: d8-my-module + spec: + parentRef: + name: default + namespace: d8-alb + listeners: + - name: http + protocol: HTTP + port: 80 + ``` + +2. **Exclude the Ingress temporarily** (only when migration is genuinely blocked): + + ```yaml + # .dmt.yaml + linters-settings: + templates: + exclude-rules: + httproute: + - kind: Ingress + name: my-app + ``` + +### Issue: HTTPRoute parentRefs do not reference any ListenerSet + +**Symptom:** +``` +Error: HTTPRoute "my-app" is invalid for Ingress migration: + spec.parentRefs does not reference any ListenerSet found in module templates +``` + +**Cause:** The HTTPRoute exists and matches the Ingress `app` label, but its `spec.parentRefs` either is empty or names a gateway that is not defined as a `ListenerSet` in the module. + +**Solutions:** + +1. **Add a ListenerSet to the module and reference it**: + + ```yaml + # templates/listenerset.yaml + apiVersion: gateway.networking.k8s.io/v1 + kind: ListenerSet + metadata: + name: d8-alb-listener # ← this name must appear in parentRefs + namespace: d8-my-module + spec: + parentRef: + name: default + namespace: d8-alb + listeners: + - name: http + protocol: HTTP + port: 80 + ``` + + ```yaml + # templates/httproute.yaml (excerpt) + spec: + parentRefs: + - name: d8-alb-listener # ← matches ListenerSet above + ``` + +2. **Exclude the Ingress** if a ListenerSet cannot be provided yet: + + ```yaml + # .dmt.yaml + linters-settings: + templates: + exclude-rules: + httproute: + - kind: Ingress + name: my-app + ``` + ### Issue: Invalid Prometheus rules **Symptom:** diff --git a/pkg/linters/templates/rules/httproute.go b/pkg/linters/templates/rules/httproute.go new file mode 100644 index 00000000..a50d8585 --- /dev/null +++ b/pkg/linters/templates/rules/httproute.go @@ -0,0 +1,157 @@ +/* +Copyright 2026 Flant JSC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package rules + +import ( + "fmt" + + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + + "github.com/deckhouse/dmt/internal/storage" + "github.com/deckhouse/dmt/pkg" + "github.com/deckhouse/dmt/pkg/errors" +) + +const ( + HTTPRouteRuleName = "httproute-rules" + IngressKind = "Ingress" + HTTPRouteKind = "HTTPRoute" + ListenerSetKind = "ListenerSet" + AppLabelKey = "app" + + // networkTeamHint is appended to every finding so module authors know who + // can help with the Ingress -> Gateway API migration and review the change. + networkTeamHint = "If you need help with the Gateway API migration or a review, reach out to the network team in #dev-network." +) + +type HTTPRouteRule struct { + pkg.RuleMeta + pkg.KindRule +} + +func NewHTTPRouteRule(excludeRules []pkg.KindRuleExclude) *HTTPRouteRule { + return &HTTPRouteRule{ + RuleMeta: pkg.RuleMeta{ + Name: HTTPRouteRuleName, + }, + KindRule: pkg.KindRule{ + ExcludeRules: excludeRules, + }, + } +} + +func (r *HTTPRouteRule) ModuleMustHaveGatewayResources(md pkg.Module, errorList *errors.LintRuleErrorsList) { + errorList = errorList.WithRule(r.GetName()) + + httpRoutes := collectStoreObjectsByKind(md, HTTPRouteKind) + listenerSets := collectStoreObjectsByKind(md, ListenerSetKind) + + for _, object := range md.GetStorage() { + if object.Unstructured.GetKind() != IngressKind { + continue + } + + name := object.Unstructured.GetName() + if !r.Enabled(IngressKind, name) { + continue + } + + errorListObj := errorList.WithObjectID(object.Identity()).WithFilePath(object.GetPath()) + + route, ok := findHTTPRouteByLabels(object, httpRoutes) + if !ok { + errorListObj.Errorf( + "Ingress %q ships in this module, but the corresponding Gateway API resources are missing: "+ + "no HTTPRoute with a matching %q=%q label was found. "+ + "Every Ingress must be accompanied by a Gateway API HTTPRoute (referencing a ListenerSet) so the module is ready for the Ingress -> Gateway API migration. %s", + name, AppLabelKey, object.Unstructured.GetLabels()[AppLabelKey], networkTeamHint, + ) + + continue + } + + if err := validateHTTPRouteParentRefs(route, listenerSets); err != nil { + errorList.WithObjectID(route.Identity()). + WithFilePath(route.GetPath()). + Errorf( + "HTTPRoute %q does not yet provide a valid Gateway API replacement for Ingress %q: %v. %s", + route.Unstructured.GetName(), name, err, networkTeamHint, + ) + } + } +} + +func collectStoreObjectsByKind(md pkg.Module, kind string) []storage.StoreObject { + var objects []storage.StoreObject + + for _, object := range md.GetStorage() { + if object.Unstructured.GetKind() == kind { + objects = append(objects, object) + } + } + + return objects +} + +func findHTTPRouteByLabels(ingress storage.StoreObject, routes []storage.StoreObject) (storage.StoreObject, bool) { + ingressAppLabel := ingress.Unstructured.GetLabels()[AppLabelKey] + if ingressAppLabel == "" { + return storage.StoreObject{}, false + } + + for _, route := range routes { + if route.Unstructured.GetLabels()[AppLabelKey] == ingressAppLabel { + return route, true + } + } + + return storage.StoreObject{}, false +} + +func validateHTTPRouteParentRefs( + route storage.StoreObject, + listenerSets []storage.StoreObject, +) error { + parentRefs, found, err := unstructured.NestedSlice(route.Unstructured.Object, "spec", "parentRefs") + if err != nil { + return fmt.Errorf("cannot read spec.parentRefs: %w", err) + } + + if !found || len(parentRefs) == 0 { + return fmt.Errorf("spec.parentRefs is empty, so it does not reference a ListenerSet defined in the module templates") + } + + for _, parent := range parentRefs { + parentMap, ok := parent.(map[string]any) + if !ok { + continue + } + + name, ok := parentMap["name"].(string) + if !ok || name == "" { + continue + } + + for _, listenerSet := range listenerSets { + if listenerSet.Unstructured.GetName() == name { + return nil + } + } + } + + return fmt.Errorf("none of the spec.parentRefs reference a ListenerSet defined in the module templates") +} diff --git a/pkg/linters/templates/templates.go b/pkg/linters/templates/templates.go index 3ed5261b..d4d4363b 100644 --- a/pkg/linters/templates/templates.go +++ b/pkg/linters/templates/templates.go @@ -59,7 +59,8 @@ func (l *Templates) Run(m *module.Module) { rules.NewPDBRule(l.cfg.ExcludeRules.PDBAbsent.Get()).ControllerMustHavePDB(m, errorList.WithMaxLevel(l.cfg.Rules.PDBRule.GetLevel())) // Ingress ingressRule := rules.NewIngressRule(l.cfg.ExcludeRules.Ingress.Get()) - + // HttpRoute + httpRouteRule := rules.NewHTTPRouteRule(l.cfg.ExcludeRules.HTTPRoute.Get()) // monitoring prometheusRule := rules.NewPrometheusRule(l.cfg) grafanaRule := rules.NewGrafanaRule(l.cfg) @@ -82,6 +83,8 @@ func (l *Templates) Run(m *module.Module) { ingressRule.CheckSnippetsRule(object, errorList.WithMaxLevel(l.cfg.Rules.IngressRule.GetLevel())) } + httpRouteRule.ModuleMustHaveGatewayResources(m, errorList.WithMaxLevel(l.cfg.Rules.HTTPRouteRule.GetLevel())) + // Cluster domain rule clusterDomainRule := rules.NewClusterDomainRule() clusterDomainRule.ValidateClusterDomainInTemplates(m, errorList.WithMaxLevel(l.cfg.Rules.ClusterDomainRule.GetLevel())) diff --git a/test/e2e/testdata/templates/httproute-ingress-missing/expected.yaml b/test/e2e/testdata/templates/httproute-ingress-missing/expected.yaml new file mode 100644 index 00000000..011717d5 --- /dev/null +++ b/test/e2e/testdata/templates/httproute-ingress-missing/expected.yaml @@ -0,0 +1,9 @@ +description: > + A module that ships an Ingress with an app label but no matching HTTPRoute + must be flagged by the httproute-rules rule. +module: module +expect: + - linter: templates + rule: httproute-rules + level: error + textContains: "the corresponding Gateway API resources are missing" diff --git a/test/e2e/testdata/templates/httproute-ingress-missing/module/module.yaml b/test/e2e/testdata/templates/httproute-ingress-missing/module/module.yaml new file mode 100644 index 00000000..fc14f6e0 --- /dev/null +++ b/test/e2e/testdata/templates/httproute-ingress-missing/module/module.yaml @@ -0,0 +1,2 @@ +name: e2e-httproute-ingress-missing +namespace: e2e-httproute-ingress-missing diff --git a/test/e2e/testdata/templates/httproute-ingress-missing/module/openapi/config-values.yaml b/test/e2e/testdata/templates/httproute-ingress-missing/module/openapi/config-values.yaml new file mode 100644 index 00000000..03b0d8bf --- /dev/null +++ b/test/e2e/testdata/templates/httproute-ingress-missing/module/openapi/config-values.yaml @@ -0,0 +1,2 @@ +type: object +properties: {} diff --git a/test/e2e/testdata/templates/httproute-ingress-missing/module/openapi/values.yaml b/test/e2e/testdata/templates/httproute-ingress-missing/module/openapi/values.yaml new file mode 100644 index 00000000..47180da5 --- /dev/null +++ b/test/e2e/testdata/templates/httproute-ingress-missing/module/openapi/values.yaml @@ -0,0 +1,4 @@ +x-extend: + schema: config-values.yaml +type: object +properties: {} diff --git a/test/e2e/testdata/templates/httproute-ingress-missing/module/templates/ingress.yaml b/test/e2e/testdata/templates/httproute-ingress-missing/module/templates/ingress.yaml new file mode 100644 index 00000000..fd882781 --- /dev/null +++ b/test/e2e/testdata/templates/httproute-ingress-missing/module/templates/ingress.yaml @@ -0,0 +1,19 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: e2e-app + namespace: e2e-httproute-ingress-missing + labels: + app: e2e-app +spec: + rules: + - host: e2e.example.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: e2e-svc + port: + number: 80 diff --git a/test/e2e/testdata/templates/httproute-no-listenerset/expected.yaml b/test/e2e/testdata/templates/httproute-no-listenerset/expected.yaml new file mode 100644 index 00000000..1192d7fd --- /dev/null +++ b/test/e2e/testdata/templates/httproute-no-listenerset/expected.yaml @@ -0,0 +1,10 @@ +description: > + A module that ships an Ingress and a matching HTTPRoute (same app label), + but whose HTTPRoute parentRefs do not reference any ListenerSet present in + the module templates, must be flagged by the httproute-rules rule. +module: module +expect: + - linter: templates + rule: httproute-rules + level: error + textContains: "does not yet provide a valid Gateway API replacement for Ingress" diff --git a/test/e2e/testdata/templates/httproute-no-listenerset/module/module.yaml b/test/e2e/testdata/templates/httproute-no-listenerset/module/module.yaml new file mode 100644 index 00000000..b622dc99 --- /dev/null +++ b/test/e2e/testdata/templates/httproute-no-listenerset/module/module.yaml @@ -0,0 +1,2 @@ +name: e2e-httproute-no-listenerset +namespace: e2e-httproute-no-listenerset diff --git a/test/e2e/testdata/templates/httproute-no-listenerset/module/openapi/config-values.yaml b/test/e2e/testdata/templates/httproute-no-listenerset/module/openapi/config-values.yaml new file mode 100644 index 00000000..03b0d8bf --- /dev/null +++ b/test/e2e/testdata/templates/httproute-no-listenerset/module/openapi/config-values.yaml @@ -0,0 +1,2 @@ +type: object +properties: {} diff --git a/test/e2e/testdata/templates/httproute-no-listenerset/module/openapi/values.yaml b/test/e2e/testdata/templates/httproute-no-listenerset/module/openapi/values.yaml new file mode 100644 index 00000000..47180da5 --- /dev/null +++ b/test/e2e/testdata/templates/httproute-no-listenerset/module/openapi/values.yaml @@ -0,0 +1,4 @@ +x-extend: + schema: config-values.yaml +type: object +properties: {} diff --git a/test/e2e/testdata/templates/httproute-no-listenerset/module/templates/httproute.yaml b/test/e2e/testdata/templates/httproute-no-listenerset/module/templates/httproute.yaml new file mode 100644 index 00000000..92d3c222 --- /dev/null +++ b/test/e2e/testdata/templates/httproute-no-listenerset/module/templates/httproute.yaml @@ -0,0 +1,18 @@ +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: e2e-app + namespace: e2e-httproute-no-listenerset + labels: + app: e2e-app +spec: + parentRefs: + - name: nonexistent-listener-set + rules: + - matches: + - path: + type: PathPrefix + value: / + backendRefs: + - name: e2e-svc + port: 80 diff --git a/test/e2e/testdata/templates/httproute-no-listenerset/module/templates/ingress.yaml b/test/e2e/testdata/templates/httproute-no-listenerset/module/templates/ingress.yaml new file mode 100644 index 00000000..c9ba97fc --- /dev/null +++ b/test/e2e/testdata/templates/httproute-no-listenerset/module/templates/ingress.yaml @@ -0,0 +1,19 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: e2e-app + namespace: e2e-httproute-no-listenerset + labels: + app: e2e-app +spec: + rules: + - host: e2e.example.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: e2e-svc + port: + number: 80 diff --git a/test/e2e/testdata/templates/httproute-valid/expected.yaml b/test/e2e/testdata/templates/httproute-valid/expected.yaml new file mode 100644 index 00000000..d0a734f3 --- /dev/null +++ b/test/e2e/testdata/templates/httproute-valid/expected.yaml @@ -0,0 +1,8 @@ +description: > + A module with an Ingress, a matching HTTPRoute (same app label), and a + ListenerSet referenced by the HTTPRoute parentRefs must produce no + httproute-rules findings. +module: module +expectAbsent: + - linter: templates + rule: httproute-rules diff --git a/test/e2e/testdata/templates/httproute-valid/module/module.yaml b/test/e2e/testdata/templates/httproute-valid/module/module.yaml new file mode 100644 index 00000000..ac628d1e --- /dev/null +++ b/test/e2e/testdata/templates/httproute-valid/module/module.yaml @@ -0,0 +1,2 @@ +name: e2e-httproute-valid +namespace: e2e-httproute-valid diff --git a/test/e2e/testdata/templates/httproute-valid/module/openapi/config-values.yaml b/test/e2e/testdata/templates/httproute-valid/module/openapi/config-values.yaml new file mode 100644 index 00000000..03b0d8bf --- /dev/null +++ b/test/e2e/testdata/templates/httproute-valid/module/openapi/config-values.yaml @@ -0,0 +1,2 @@ +type: object +properties: {} diff --git a/test/e2e/testdata/templates/httproute-valid/module/openapi/values.yaml b/test/e2e/testdata/templates/httproute-valid/module/openapi/values.yaml new file mode 100644 index 00000000..47180da5 --- /dev/null +++ b/test/e2e/testdata/templates/httproute-valid/module/openapi/values.yaml @@ -0,0 +1,4 @@ +x-extend: + schema: config-values.yaml +type: object +properties: {} diff --git a/test/e2e/testdata/templates/httproute-valid/module/templates/httproute.yaml b/test/e2e/testdata/templates/httproute-valid/module/templates/httproute.yaml new file mode 100644 index 00000000..f6de80c6 --- /dev/null +++ b/test/e2e/testdata/templates/httproute-valid/module/templates/httproute.yaml @@ -0,0 +1,18 @@ +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: e2e-app + namespace: e2e-httproute-valid + labels: + app: e2e-app +spec: + parentRefs: + - name: e2e-listener-set + rules: + - matches: + - path: + type: PathPrefix + value: / + backendRefs: + - name: e2e-svc + port: 80 diff --git a/test/e2e/testdata/templates/httproute-valid/module/templates/ingress.yaml b/test/e2e/testdata/templates/httproute-valid/module/templates/ingress.yaml new file mode 100644 index 00000000..2a9b6f6b --- /dev/null +++ b/test/e2e/testdata/templates/httproute-valid/module/templates/ingress.yaml @@ -0,0 +1,19 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: e2e-app + namespace: e2e-httproute-valid + labels: + app: e2e-app +spec: + rules: + - host: e2e.example.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: e2e-svc + port: + number: 80 diff --git a/test/e2e/testdata/templates/httproute-valid/module/templates/listenerset.yaml b/test/e2e/testdata/templates/httproute-valid/module/templates/listenerset.yaml new file mode 100644 index 00000000..b8ef5e86 --- /dev/null +++ b/test/e2e/testdata/templates/httproute-valid/module/templates/listenerset.yaml @@ -0,0 +1,13 @@ +apiVersion: gateway.networking.k8s.io/v1 +kind: ListenerSet +metadata: + name: e2e-listener-set + namespace: e2e-httproute-valid +spec: + parentRef: + name: default + namespace: d8-alb + listeners: + - name: http + protocol: HTTP + port: 80