Repository: Kong/kubernetes-ingress-controller
Pull request #7378 (2025-04-25 14:26:59)

chore(deps): bump actions/download-artifact from 4.2.1 to 4.3.0
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4.2.1 to 4.3.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/actions/download-artifact/releases">actions/download-artifact's releases</a>.</em></p>
<blockquote>
<h2>v4.3.0</h2>
<h2>What's Changed</h2>
<ul>
<li>feat: implement new <code>artifact-ids</code> input by <a href="https://github.com/GrantBirki"><code>@​GrantBirki</code></a> in <a href="https://redirect.github.com/actions/download-artifact/pull/401">actions/download-artifact#401</a></li>
<li>Fix workflow example for downloading by artifact ID by <a href="https://github.com/joshmgross"><code>@​joshmgross</code></a> in <a href="https://redirect.github.com/actions/download-artifact/pull/402">actions/download-artifact#402</a></li>
<li>Prep for v4.3.0 release by <a href="https://github.com/robherley"><code>@​robherley</code></a> in <a href="https://redirect.github.com/actions/download-artifact/pull/404">actions/download-artifact#404</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/GrantBirki"><code>@​GrantBirki</code></a> made their first contribution in <a href="https://redirect.github.com/actions/download-artifact/pull/401">actions/download-artifact#401</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a href="https://github.com/actions/download-artifact/compare/v4.2.1...v4.3.0">https://github.com/actions/download-artifact/compare/v4.2.1...v4.3.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/actions/download-artifact/commit/d3f86a106a0bac45b974a628896c90dbdf5c8093"><code>d3f86a1</code></a> Merge pull request <a href="https://redirect.github.com/actions/download-artifact/issues/404">#404</a> from actions/robherley/v4.3.0</li>
<li><a href="https://github.com/actions/download-artifact/commit/fc02353415da80201a0da48ab47022efd7725d11"><code>fc02353</code></a> prep for v4.3.0 release</li>
<li><a href="https://github.com/actions/download-artifact/commit/77454371a433f370a16d329ef7db197f700a7a8f"><code>7745437</code></a> Merge pull request <a href="https://redirect.github.com/actions/download-artifact/issues/402">#402</a> from actions/joshmgross/download-by-id-example</li>
<li><a href="https://github.com/actions/download-artifact/commit/84fc7a0a358aabc7f97f7f590cbfc25f57e26c6a"><code>84fc7a0</code></a> Remove path filters from Check dist workflow</li>
<li><a href="https://github.com/actions/download-artifact/commit/67f2bc382f6ba5ba75812a05909e8c25a366b5fb"><code>67f2bc3</code></a> Fix workflow example for downloading by artifact ID</li>
<li><a href="https://github.com/actions/download-artifact/commit/8ea3c2c174f79a56792e9fdd9baad75d27c5d369"><code>8ea3c2c</code></a> Merge pull request <a href="https://redirect.github.com/actions/download-artifact/issues/401">#401</a> from actions/download-by-id</li>
<li><a href="https://github.com/actions/download-artifact/commit/d219c630f65d8bd14366a9e2f731cbf854f62258"><code>d219c63</code></a> add supporting unit tests for artifact downloads with ids</li>
<li><a href="https://github.com/actions/download-artifact/commit/54124fbd881f8ce794405a06896c93c49c17463e"><code>54124fb</code></a> revert <code>getArtifact()</code> changes - for now we have to list and filter by artifa...</li>
<li><a href="https://github.com/actions/download-artifact/commit/b83057b90d3e218abf5c7b1906579eb6c598ae85"><code>b83057b</code></a> bundle</li>
<li><a href="https://github.com/actions/download-artifact/commit/171183c7dce98c3cf8a1fc842429d0a38ed21d33"><code>171183c</code></a> use the same <code>artifactClient.getArtifact</code> structure as seen above in `isSingl...</li>
<li>Additional commits viewable in <a href="https://github.com/actions/download-artifact/compare/95815c38cf2ff2164869cbab79da8d1f422bc89e...d3f86a106a0bac45b974a628896c90dbdf5c8093">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/download-artifact&package-manager=github_actions&previous-version=4.2.1&new-version=4.3.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/Kong/kubernetes-ingress-controller/pull/7378?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Kong) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 77.1%. Comparing base [(`f04cd66`)](https://app.codecov.io/gh/Kong/kubernetes-ingress-controller/commit/f04cd667f13733e667278124e16527f877e0f4bb?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Kong) to head [(`50106f7`)](https://app.codecov.io/gh/Kong/kubernetes-ingress-controller/commit/50106f74c5f0ed102f8fa2804256708c7b46708f?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Kong).

<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##            main   #7378     +/-   ##
=======================================
- Coverage   77.3%   77.1%   -0.2%     
=======================================
  Files        220     220             
  Lines      25309   25309             
=======================================
- Hits       19578   19534     -44     
- Misses      4744    4787     +43     
- Partials     987     988      +1     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/Kong/kubernetes-ingress-controller/pull/7378?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Kong).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Kong).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>

Patch:
diff --git a/.github/workflows/performance_nightly.yaml b/.github/workflows/performance_nightly.yaml
index 6f8abe963..2df76cdf6 100644
--- a/.github/workflows/performance_nightly.yaml
+++ b/.github/workflows/performance_nightly.yaml
@@ -47,7 +47,7 @@ jobs:
           gem install youplot
 
       - name: download performance test results
-        uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1
+        uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
         with:
           pattern: performance-tests-results-*
           path: perf-results
diff --git a/.github/workflows/performance_targeted.yaml b/.github/workflows/performance_targeted.yaml
index d7be8919e..49151ca74 100644
--- a/.github/workflows/performance_targeted.yaml
+++ b/.github/workflows/performance_targeted.yaml
@@ -110,7 +110,7 @@ jobs:
           gem install youplot
 
       - name: download performance test results
-        uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1
+        uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
         with:
           pattern: performance-tests-results-*
           path: perf-results
diff --git a/.tools_versions.yaml b/.tools_versions.yaml
index 1ae40e0f0..38ff5c928 100644
--- a/.tools_versions.yaml
+++ b/.tools_versions.yaml
@@ -5,7 +5,7 @@ controller-tools: "0.17.3"
 # renovate: datasource=github-releases depName=kubernetes-sigs/kustomize
 kustomize: "5.6.0"
 # renovate: datasource=github-releases depName=golangci/golangci-lint
-golangci-lint: "2.1.2"
+golangci-lint: "2.1.5"
 # renovate: datasource=github-releases depName=GoogleContainerTools/skaffold
 skaffold: "2.15.0"
 # renovate: datasource=github-releases depName=kubernetes-sigs/controller-runtime
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 75f157e59..bbb6684c9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -104,6 +104,14 @@ Adding a new version? You'll need three changes:
  - [0.0.5](#005)
  - [0.0.4 and prior](#004-and-prior)
 
+## Unreleased
+
+### Fixed
+
+- In validation webhooks for `Ingress`es and `HTTPRoute`s, override the translated
+  Kong route by annotations before sending to Kong gateways.
+  [#7346](https://github.com/Kong/kubernetes-ingress-controller/pull/7346)
+
 ## [3.4.4]
 
 > Release date: 2025-04-08
diff --git a/internal/admission/validation/gateway/httproute.go b/internal/admission/validation/gateway/httproute.go
index a29495fce..2e261bb5c 100644
--- a/internal/admission/validation/gateway/httproute.go
+++ b/internal/admission/validation/gateway/httproute.go
@@ -212,6 +212,7 @@ func validateWithKongGateway(
 	}
 	for _, service := range translationResult.ServiceNameToKongstateService {
 		for _, route := range service.Routes {
+			route.Override(logr.Discard())
 			ok, msg, err := routesValidator.Validate(ctx, &route.Route)
 			if err != nil {
 				return false, fmt.Sprintf("Unable to validate HTTPRoute schema: %s", err.Error())
diff --git a/internal/admission/validation/ingress/ingress.go b/internal/admission/validation/ingress/ingress.go
index 0a2b43232..361d03e2a 100644
--- a/internal/admission/validation/ingress/ingress.go
+++ b/internal/admission/validation/ingress/ingress.go
@@ -39,7 +39,7 @@ func ValidateIngress(
 		return false, fmt.Sprintf("Ingress has invalid Kong annotations: %s", err), nil
 	}
 
-	for _, kg := range ingressToKongRoutesForValidation(translatorFeatures, ingress, failuresCollector, storer) {
+	for _, kg := range ingressToKongRoutesForValidation(translatorFeatures, ingress, failuresCollector, logger, storer) {
 		// Validate by using feature of Kong Gateway.
 		ok, msg, err := routesValidator.Validate(ctx, &kg)
 		if err != nil {
@@ -65,6 +65,7 @@ func ingressToKongRoutesForValidation(
 	translatorFeatures translator.FeatureFlags,
 	ingress *netv1.Ingress,
 	failuresCollector subtranslator.FailuresCollector,
+	logger logr.Logger,
 	storer store.Storer,
 ) []kong.Route {
 	kongServices := subtranslator.TranslateIngresses(
@@ -82,6 +83,7 @@ func ingressToKongRoutesForValidation(
 	var kongRoutes []kong.Route
 	for _, svc := range kongServices {
 		for _, route := range svc.Routes {
+			route.Override(logger)
 			kongRoutes = append(kongRoutes, route.Route)
 		}
 	}
diff --git a/internal/dataplane/kongstate/kongstate.go b/internal/dataplane/kongstate/kongstate.go
index 1e751e3c5..d22d51618 100644
--- a/internal/dataplane/kongstate/kongstate.go
+++ b/internal/dataplane/kongstate/kongstate.go
@@ -267,7 +267,7 @@ func (ks *KongState) FillOverrides(
 			// down into the kongstate.Route copy looked a bit annoying. Protocol validation for routes instead lives in the
 			// HTTPRoute and Ingress translators (these may override to ws/wss, whereas the others are expected to derive
 			// their protcol from the resource type alone).
-			ks.Services[i].Routes[j].override(logger)
+			ks.Services[i].Routes[j].Override(logger)
 		}
 	}
 
diff --git a/internal/dataplane/kongstate/route.go b/internal/dataplane/kongstate/route.go
index bddc0b5a1..719317edb 100644
--- a/internal/dataplane/kongstate/route.go
+++ b/internal/dataplane/kongstate/route.go
@@ -245,8 +245,8 @@ func (r *Route) overrideByAnnotation(logger logr.Logger) {
 	}
 }
 
-// override sets Route fields by KongIngress first, then by annotation.
-func (r *Route) override(logger logr.Logger) {
+// Override sets Route fields by KongIngress first, then by annotation.
+func (r *Route) Override(logger logr.Logger) {
 	if r == nil {
 		return
 	}


Test patch:
diff --git a/.github/workflows/_e2e_tests.yaml b/.github/workflows/_e2e_tests.yaml
index d6a6d85f2..8c49f2740 100644
--- a/.github/workflows/_e2e_tests.yaml
+++ b/.github/workflows/_e2e_tests.yaml
@@ -109,7 +109,7 @@ jobs:
     steps:
       - name: Download built image artifact
         if: ${{ inputs.load-local-image }}
-        uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1
+        uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
         with:
           name: kic-image
           path: /tmp
@@ -341,7 +341,7 @@ jobs:
     steps:
       - name: Download built image artifact
         if: ${{ inputs.load-local-image }}
-        uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1
+        uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
         with:
           name: kic-image
           path: /tmp
diff --git a/.github/workflows/_performance_tests.yaml b/.github/workflows/_performance_tests.yaml
index 795d0c6a2..c820054f6 100644
--- a/.github/workflows/_performance_tests.yaml
+++ b/.github/workflows/_performance_tests.yaml
@@ -38,7 +38,7 @@ jobs:
     steps:
       - name: Download built image artifact
         if: ${{ inputs.load-local-image }}
-        uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1
+        uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
         with:
           name: kic-image
           path: /tmp
@@ -131,7 +131,7 @@ jobs:
     steps:
       - name: Download built image artifact
         if: ${{ inputs.load-local-image }}
-        uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1
+        uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
         with:
           name: kic-image
           path: /tmp
diff --git a/.github/workflows/_test_reports.yaml b/.github/workflows/_test_reports.yaml
index 70c64f725..11fb507a9 100644
--- a/.github/workflows/_test_reports.yaml
+++ b/.github/workflows/_test_reports.yaml
@@ -30,7 +30,7 @@ jobs:
 
       - name: collect test coverage artifacts
         id: download-coverage
-        uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1
+        uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
         with:
           pattern: coverage-*
           path: coverage
@@ -55,7 +55,7 @@ jobs:
 
       - name: download tests report
         id: download-coverage
-        uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1
+        uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
         with:
           pattern: tests-report-*
           path: report
diff --git a/internal/admission/validation/ingress/ingress_test.go b/internal/admission/validation/ingress/ingress_test.go
index 7b170420f..53f519649 100644
--- a/internal/admission/validation/ingress/ingress_test.go
+++ b/internal/admission/validation/ingress/ingress_test.go
@@ -6,6 +6,7 @@ import (
 
 	"github.com/go-logr/zapr"
 	"github.com/kong/go-kong/kong"
+	"github.com/samber/lo"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 	"go.uber.org/zap"
@@ -40,6 +41,43 @@ func TestValidateIngress(t *testing.T) {
 			valid:         false,
 			validationMsg: "Ingress has invalid Kong annotations: invalid konghq.com/protocols value: ohno",
 		},
+		{
+			msg: "invalid protocol combination: mutally exclusive protocols",
+			ingress: &netv1.Ingress{
+				ObjectMeta: metav1.ObjectMeta{
+					Namespace: corev1.NamespaceDefault,
+					Name:      "testing",
+					Annotations: map[string]string{
+						annotations.AnnotationPrefix + annotations.ProtocolsKey: "http, tcp",
+					},
+				},
+				Spec: netv1.IngressSpec{
+					Rules: []netv1.IngressRule{
+						{
+							Host: "example.com",
+							IngressRuleValue: netv1.IngressRuleValue{
+								HTTP: &netv1.HTTPIngressRuleValue{
+									Paths: []netv1.HTTPIngressPath{
+										{
+											Path:     "/",
+											PathType: lo.ToPtr(netv1.PathTypePrefix),
+											Backend: netv1.IngressBackend{
+												Service: &netv1.IngressServiceBackend{
+													Name: "httpbin",
+													Port: netv1.ServiceBackendPort{Number: int32(80)},
+												},
+											},
+										},
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+			valid:         false,
+			validationMsg: "Ingress failed schema validation: Invalid protocols: http and tcp are mutally exclusive",
+		},
 	} {
 		t.Run(tt.msg, func(t *testing.T) {
 			logger := zapr.NewLogger(zap.NewNop())
@@ -66,6 +104,10 @@ func TestValidateIngress(t *testing.T) {
 
 type mockRoutesValidator struct{}
 
-func (mockRoutesValidator) Validate(_ context.Context, _ *kong.Route) (bool, string, error) {
+func (mockRoutesValidator) Validate(_ context.Context, r *kong.Route) (bool, string, error) {
+	protocols := lo.FromSlicePtr(r.Protocols)
+	if lo.Contains(protocols, "http") && lo.Contains(protocols, "tcp") {
+		return false, "Invalid protocols: http and tcp are mutally exclusive", nil
+	}
 	return true, "", nil
 }
diff --git a/internal/dataplane/kongstate/route_test.go b/internal/dataplane/kongstate/route_test.go
index 9adbdf668..1d5f0f583 100644
--- a/internal/dataplane/kongstate/route_test.go
+++ b/internal/dataplane/kongstate/route_test.go
@@ -220,7 +220,7 @@ func TestOverrideRoute(t *testing.T) {
 func TestNilRouteOverrideDoesntPanic(t *testing.T) {
 	require.NotPanics(t, func() {
 		var nilRoute *Route
-		nilRoute.override(zapr.NewLogger(zap.NewNop()))
+		nilRoute.Override(zapr.NewLogger(zap.NewNop()))
 	})
 }
 
@@ -305,7 +305,7 @@ func TestOverrideExpressionRoute(t *testing.T) {
 
 	for i, tc := range testCases {
 		t.Run(strconv.Itoa(i)+"-"+tc.name, func(t *testing.T) {
-			tc.inRoute.override(zapr.NewLogger(zap.NewNop()))
+			tc.inRoute.Override(zapr.NewLogger(zap.NewNop()))
 			assert.Equal(t, tc.outRoute, tc.inRoute, "should be the same as expected after overriding")
 		})
 	}