Repository: Kong/kubernetes-ingress-controller
Pull request #6832 (2024-12-13 04:45:49)

chore(deps): update module github.com/kong/kubernetes-configuration to v1
This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [github.com/kong/kubernetes-configuration](https://redirect.github.com/kong/kubernetes-configuration) | `v0.0.46` -> `v1.0.0` | [![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fkong%2fkubernetes-configuration/v1.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/go/github.com%2fkong%2fkubernetes-configuration/v1.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/go/github.com%2fkong%2fkubernetes-configuration/v0.0.46/v1.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fkong%2fkubernetes-configuration/v0.0.46/v1.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the warning logs for more information.

---

### Release Notes

<details>
<summary>kong/kubernetes-configuration (github.com/kong/kubernetes-configuration)</summary>

### [`v1.0.0`](https://redirect.github.com/kong/kubernetes-configuration/blob/HEAD/CHANGELOG.md#v100)

[Compare Source](https://redirect.github.com/kong/kubernetes-configuration/compare/v0.0.46...v1.0.0)

[v1.0.0]: https://redirect.github.com/kong/kubernetes-configuration/compare/ecf9b7bd62bfb92327a6ddd9aeaec9f73fc13a72...v1.0.0

> Release date: 2024-12-12

This is an initial stable release of the Kong Kubernetes configuration module.
It includes CRDs for the Kong Ingress Controller and the Kong Gateway Operator
in the `ingress-controller`, `ingress-controller-incubator` and `gateway-operator`
channels.

Go bindings for the CRDs are available in the [`api/`][api] directory and the
[`pkg/clientset/`][clientset] directory contains the clientset for interacting
with the CRDs.

[api]: ./api/

[clientset]: ./pkg/clientset/

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 5am every weekday" (UTC), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/Kong/kubernetes-ingress-controller).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS41OC4xIiwidXBkYXRlZEluVmVyIjoiMzkuNTguMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiZGVwZW5kZW5jaWVzIiwicmVub3ZhdGUvYXV0by1yZWdlbmVyYXRlIl19-->


renovate[bot]: ### ℹ Artifact update notice

##### File name: go.mod

In order to perform the update(s) described in the table above, Renovate ran the `go get` command, which resulted in the following additional change(s):


- 1 additional dependency was updated


Details:


| **Package**               | **Change**             |
| :------------------------ | :--------------------- |
| `github.com/kong/go-kong` | `v0.60.0` -> `v0.61.0` |
renovate[bot]: ### Edited/Blocked Notification

Renovate will not automatically rebase this PR, because it does not recognize the last commit author and assumes somebody else may have edited the PR.

You can manually request rebase by checking the rebase/retry box above.

 ⚠️ **Warning**: custom changes will be lost.
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/Kong/kubernetes-ingress-controller/pull/6832?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 79.2%. Comparing base [(`5375909`)](https://app.codecov.io/gh/Kong/kubernetes-ingress-controller/commit/537590971f66036bfd7f9d0c7191a3d6350c73f8?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Kong) to head [(`2df0afb`)](https://app.codecov.io/gh/Kong/kubernetes-ingress-controller/commit/2df0afb8803af22fe9174a4ab193010404be0fc1?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Kong).
> Report is 1 commits behind head on main.

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


```diff
@@           Coverage Diff           @@
##            main   #6832     +/-   ##
=======================================
+ Coverage   77.3%   79.2%   +1.9%     
=======================================
  Files        209     209             
  Lines      25153   25153             
=======================================
+ Hits       19450   19930    +480     
+ Misses      4715    4194    -521     
- Partials     988    1029     +41     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/Kong/kubernetes-ingress-controller/pull/6832?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).

renovate[bot]: ### Edited/Blocked Notification

Renovate will not automatically rebase this PR, because it does not recognize the last commit author and assumes somebody else may have edited the PR.

You can manually request rebase by checking the rebase/retry box above.

 ⚠️ **Warning**: custom changes will be lost.

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6b8d8de6b..223d52a1b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -133,10 +133,11 @@ Adding a new version? You'll need three changes:
   any needed information is reported in the status of the affected object or
   with Kubernetes events.
   [#6790](https://github.com/Kong/kubernetes-ingress-controller/pull/6790)
-- Do not log `ERROR` when referenced `KongPlugin` or `KongClusterPlugin` does not exist,
-  instead generate a Kubernetes Event - `KongConfigurationTranslationFailed` for object
-  that references it.
+- Do not log `ERROR` when referenced `KongPlugin` or `KongClusterPlugin` does not exist
+  or there is no grant that allows referencing it, instead generate a Kubernetes Event
+  `KongConfigurationTranslationFailed` for object that references it.
   [#6814](https://github.com/Kong/kubernetes-ingress-controller/pull/6814)
+  [#6841](https://github.com/Kong/kubernetes-ingress-controller/pull/6841)
 - From now on, upstreams produced by KIC from `Service`s that are configured as
   upstream services (either by `ingress.kubernetes.io/service-upstream` annotation
   or through `IngressClassNamespacedParameters`'s `serviceUpstream` field), will use
diff --git a/config/crd/incubator/kustomization.yaml b/config/crd/incubator/kustomization.yaml
index 42fd40aee..d9792346c 100644
--- a/config/crd/incubator/kustomization.yaml
+++ b/config/crd/incubator/kustomization.yaml
@@ -2,4 +2,4 @@
 apiVersion: kustomize.config.k8s.io/v1beta1
 kind: Kustomization
 resources:
-  - https://github.com/kong/kubernetes-configuration/config/crd/ingress-controller-incubator?ref=v0.0.46 # Version is auto-updated by the generating script.
+  - https://github.com/kong/kubernetes-configuration/config/crd/ingress-controller-incubator?ref=v1.0.0 # Version is auto-updated by the generating script.
diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml
index 636c0409a..4acf6919a 100644
--- a/config/crd/kustomization.yaml
+++ b/config/crd/kustomization.yaml
@@ -2,4 +2,4 @@
 apiVersion: kustomize.config.k8s.io/v1beta1
 kind: Kustomization
 resources:
-  - https://github.com/kong/kubernetes-configuration/config/crd/ingress-controller?ref=v0.0.46 # Version is auto-updated by the generating script.
+  - https://github.com/kong/kubernetes-configuration/config/crd/ingress-controller?ref=v1.0.0 # Version is auto-updated by the generating script.
diff --git a/docs/api-reference.md b/docs/api-reference.md
index e114d10f9..b4f7d42df 100644
--- a/docs/api-reference.md
+++ b/docs/api-reference.md
@@ -518,10 +518,10 @@ ObjectReference defines reference of a kubernetes object.
 
 | Field | Description |
 | --- | --- |
-| `group` _string_ |  |
-| `kind` _string_ |  |
+| `group` _string_ | Group defines the API group of the referred object. |
+| `kind` _string_ | Kind defines the kind of the referred object. |
 | `namespace` _string_ | Empty namespace means the same namespace of the owning object. |
-| `name` _string_ |  |
+| `name` _string_ | Name defines the name of the referred object. |
 
 
 _Appears in:_
diff --git a/go.mod b/go.mod
index beb035d9a..79bce8bab 100644
--- a/go.mod
+++ b/go.mod
@@ -18,6 +18,9 @@ go 1.23.2
 //
 // toolchain go1.21.0
 
+// NOTICE: Issue https://github.com/Kong/kubernetes-ingress-controller/issues/6823 has to be resolved.
+replace github.com/kong/go-kong => github.com/kong/go-kong v0.60.0
+
 require (
 	cloud.google.com/go/container v1.42.0
 	github.com/Masterminds/sprig/v3 v3.3.0
@@ -31,8 +34,8 @@ require (
 	github.com/google/uuid v1.6.0
 	github.com/jpillora/backoff v1.0.0
 	github.com/kong/go-database-reconciler v1.16.1
-	github.com/kong/go-kong v0.60.0
-	github.com/kong/kubernetes-configuration v0.0.46
+	github.com/kong/go-kong v0.61.0
+	github.com/kong/kubernetes-configuration v1.0.0
 	github.com/kong/kubernetes-telemetry v0.1.7
 	github.com/kong/kubernetes-testing-framework v0.47.2
 	github.com/lithammer/dedent v1.1.0
diff --git a/go.sum b/go.sum
index b6d34665a..fc5f11c1f 100644
--- a/go.sum
+++ b/go.sum
@@ -220,8 +220,8 @@ github.com/kong/go-database-reconciler v1.16.1 h1:qcQzEuMGfpNjx3UgulBOKulKA+upmH
 github.com/kong/go-database-reconciler v1.16.1/go.mod h1:7CGvStUvUOmUnodUFsWcW3PX2bJgnaKClJ/yhNGEVIE=
 github.com/kong/go-kong v0.60.0 h1:CVrLXRLVE+Gl4IZ3tdvpO7xNDz3c9YLTmra/HvT4oM8=
 github.com/kong/go-kong v0.60.0/go.mod h1:t1eMY8GRS6778uQNzxgzRgnA3YKBXSZOEvYbNocH/aA=
-github.com/kong/kubernetes-configuration v0.0.46 h1:dIxVu9dOtGi9aY2prTlQ1CkiSu8Fk/0oal9m9iiUdSk=
-github.com/kong/kubernetes-configuration v0.0.46/go.mod h1:Bk0H032d+aPgVYakc7C9Zo5nLwiXXm9thKUWF8vvisA=
+github.com/kong/kubernetes-configuration v1.0.0 h1:f/OaX6oq8/MPur45KzcrIFGJ9Kl6OyTjlXqTJH+ij4s=
+github.com/kong/kubernetes-configuration v1.0.0/go.mod h1:lq/MxAtpz35I5I9MeTe4CdDG1iSd67aV0Ft9wih2UUs=
 github.com/kong/kubernetes-telemetry v0.1.7 h1:R4NUpvbF5uZ+5kgSQsIcf/oulRBGQCHsffFRDE4wxV4=
 github.com/kong/kubernetes-telemetry v0.1.7/go.mod h1:USy5pcD1+Mm9NtKuz3Pb/rSx71VN76gHCFhdbAB4/lg=
 github.com/kong/kubernetes-testing-framework v0.47.2 h1:+2Z9anTpbV/hwNeN+NFQz53BMU+g3QJydkweBp3tULo=
diff --git a/internal/dataplane/kongstate/customentity.go b/internal/dataplane/kongstate/customentity.go
index 179ec2fdf..5536ea673 100644
--- a/internal/dataplane/kongstate/customentity.go
+++ b/internal/dataplane/kongstate/customentity.go
@@ -178,7 +178,7 @@ func (ks *KongState) FillCustomEntities(
 	}
 	// Fetch relations between plugins and services/routes/consumers and store the pointer to translated Kong entities.
 	// Used for fetching entity referred by a custom entity and fill the ID of referred entity.
-	pluginRels := ks.getPluginRelatedEntitiesRef(s, logger)
+	pluginRels := ks.getPluginRelatedEntitiesRef(s, logger, failuresCollector)
 
 	for _, entity := range entities {
 		// reject the custom entity if its type is in "known" entity types that are already processed.
@@ -333,7 +333,7 @@ func findCustomEntityRelatedPlugin(logger logr.Logger, cacheStore store.Storer,
 			Namespace: parentRefNamespace,
 			Name:      parentRef.Name,
 		}
-		paretRefNamespace, err := extractReferredPluginNamespace(logger, cacheStore, k8sEntity, nn)
+		paretRefNamespace, err := extractReferredPluginNamespaceIfAllowed(logger, cacheStore, k8sEntity, nn)
 		if err != nil {
 			return "", false, err
 		}
diff --git a/internal/dataplane/kongstate/kongstate.go b/internal/dataplane/kongstate/kongstate.go
index 370c48179..addc3c64b 100644
--- a/internal/dataplane/kongstate/kongstate.go
+++ b/internal/dataplane/kongstate/kongstate.go
@@ -3,6 +3,7 @@ package kongstate
 import (
 	"bytes"
 	"crypto/sha256"
+	"errors"
 	"fmt"
 	"strconv"
 	"strings"
@@ -378,7 +379,9 @@ type NamespacedKongPlugin struct {
 	Name      string
 }
 
-func (ks *KongState) getPluginRelations(cacheStore store.Storer, log logr.Logger) map[string]util.ForeignRelations {
+func (ks *KongState) getPluginRelations(
+	cacheStore store.Storer, log logr.Logger, failuresCollector *failures.ResourceFailuresCollector,
+) map[string]util.ForeignRelations {
 	// KongPlugin key (KongPlugin's name:namespace) to corresponding associations
 	pluginRels := map[string]util.ForeignRelations{}
 
@@ -400,9 +403,13 @@ func (ks *KongState) getPluginRelations(cacheStore store.Storer, log logr.Logger
 		//
 		// Code in buildPlugins() will combine plugin associations into
 		// multi-entity plugins within the local namespace
-		namespace, err := extractReferredPluginNamespace(log, cacheStore, referrer, plugin)
+		namespace, err := extractReferredPluginNamespaceIfAllowed(log, cacheStore, referrer, plugin)
 		if err != nil {
-			log.Error(err, "could not bind requested plugin", "plugin", plugin.Name, "namespace", plugin.Namespace)
+			if errors.As(err, &referredPluginNotAllowedError{}) {
+				failuresCollector.PushResourceFailure(err.Error(), referrer)
+			} else {
+				log.Error(err, "could not bind requested plugin", "plugin", plugin.Name, "namespace", plugin.Namespace)
+			}
 			return
 		}
 
@@ -489,58 +496,6 @@ type pluginReference struct {
 	Name      string
 }
 
-func isRemotePluginReferenceAllowed(log logr.Logger, s store.Storer, r pluginReference) error {
-	// remote plugin. considered part of this namespace if a suitable ReferenceGrant exists
-	grants, err := s.ListReferenceGrants()
-	if err != nil {
-		return fmt.Errorf("could not retrieve ReferenceGrants from store when building plugin relations map: %w", err)
-	}
-	allowed := gatewayapi.GetPermittedForReferenceGrantFrom(
-		log,
-		gatewayapi.ReferenceGrantFrom{
-			Group:     gatewayapi.Group(r.Referrer.GetObjectKind().GroupVersionKind().Group),
-			Kind:      gatewayapi.Kind(r.Referrer.GetObjectKind().GroupVersionKind().Kind),
-			Namespace: gatewayapi.Namespace(r.Referrer.GetNamespace()),
-		},
-		grants,
-	)
-
-	// TODO https://github.com/Kong/kubernetes-ingress-controller/issues/6000
-	// Our reference checking code wasn't originally designed to handle multiple object types and relationships and
-	// wasn't designed with much guidance for future usage. It expects something akin to the BackendRef section of an
-	// HTTPRoute or similar, since that's what it was originally designed for. A future simplified model should probably
-	// work with source and target client.Object resources derived from the particular resources' reference specs, as
-	// those reference formats aren't necessarily consistent. We should possibly push for a standard upstream utility as
-	// part of https://github.com/kubernetes/enhancements/issues/3766 if it proceeds further, as this is can be difficult
-	// to wrap your head around when deep in the code that's checking an individual use case. A standard model for "these
-	// are the inputs and outputs for a reference grant check, derive them from your spec" should help avoid inconsistency
-	// in check implementation.
-
-	// we don't have a full plugin resource here for the grant checker, so we build a fake one with the correct
-	// name and namespace.
-	pluginReference := gatewayapi.PluginLabelReference{
-		Namespace: &r.Namespace,
-		Name:      r.Name,
-	}
-
-	// Because we should check whether it is allowed to refer FROM the referrer TO the plugin here,
-	// we should put the referrer on the "target" and the plugin on the "backendRef".
-
-	log.V(logging.TraceLevel).Info("requested grant to plugins",
-		"from-namespace", r.Referrer.GetNamespace(),
-		"from-group", r.Referrer.GetObjectKind().GroupVersionKind().Group,
-		"from-kind", r.Referrer.GetObjectKind().GroupVersionKind().Kind,
-		"to-namespace", r.Namespace,
-		"to-name", r.Name,
-	)
-
-	if !gatewayapi.NewRefCheckerForKongPlugin(log, r.Referrer, pluginReference).IsRefAllowedByGrant(allowed) {
-		return fmt.Errorf("no grant found for %s in %s to plugin %s in %s requested remote KongPlugin bind",
-			r.Referrer.GetObjectKind().GroupVersionKind().Kind, r.Referrer.GetNamespace(), r.Name, r.Namespace)
-	}
-	return nil
-}
-
 func buildPlugins(
 	logger logr.Logger,
 	s store.Storer,
@@ -691,7 +646,7 @@ func (ks *KongState) FillPlugins(
 	s store.Storer,
 	failuresCollector *failures.ResourceFailuresCollector,
 ) {
-	ks.Plugins = buildPlugins(log, s, failuresCollector, ks.getPluginRelations(s, log))
+	ks.Plugins = buildPlugins(log, s, failuresCollector, ks.getPluginRelations(s, log, failuresCollector))
 }
 
 // FillIDs iterates over the KongState and fills in the ID field for each entity
@@ -819,15 +774,21 @@ func getServiceIDFromPluginRels(
 //
 // TODO: refactor the building of plugin related entities and share the result between here and building plugins:
 // https://github.com/Kong/kubernetes-ingress-controller/issues/6115
-func (ks *KongState) getPluginRelatedEntitiesRef(cacheStore store.Storer, log logr.Logger) PluginRelatedEntitiesRefs {
+func (ks *KongState) getPluginRelatedEntitiesRef(
+	cacheStore store.Storer, log logr.Logger, failuresCollector *failures.ResourceFailuresCollector,
+) PluginRelatedEntitiesRefs {
 	pluginRels := PluginRelatedEntitiesRefs{
 		RelatedEntities:      map[string]RelatedEntitiesRef{},
 		RouteAttachedService: map[string]*Service{},
 	}
 	addRelation := func(referrer client.Object, plugin k8stypes.NamespacedName, entity any) {
-		namespace, err := extractReferredPluginNamespace(log, cacheStore, referrer, plugin)
+		namespace, err := extractReferredPluginNamespaceIfAllowed(log, cacheStore, referrer, plugin)
 		if err != nil {
-			log.Error(err, "could not bind requested plugin", "plugin", plugin.Name, "namespace", plugin.Namespace)
+			if errors.As(err, &referredPluginNotAllowedError{}) {
+				failuresCollector.PushResourceFailure(err.Error(), referrer)
+			} else {
+				log.Error(err, "could not bind requested plugin", "plugin", plugin.Name, "namespace", plugin.Namespace)
+			}
 			return
 		}
 		pluginKey := namespace + ":" + plugin.Name
@@ -882,7 +843,15 @@ func (ks *KongState) getPluginRelatedEntitiesRef(cacheStore store.Storer, log lo
 	return pluginRels
 }
 
-func extractReferredPluginNamespace(
+type referredPluginNotAllowedError struct {
+	pluginReference gatewayapi.PluginLabelReference
+}
+
+func (e referredPluginNotAllowedError) Error() string {
+	return fmt.Sprintf("no grant found to referenced %q plugin in the requested remote KongPlugin bind", e.pluginReference)
+}
+
+func extractReferredPluginNamespaceIfAllowed(
 	log logr.Logger, cacheStore store.Storer, referrer client.Object, plugin k8stypes.NamespacedName,
 ) (string, error) {
 	// There are 2 types of KongPlugin references: local and remote.
@@ -911,3 +880,56 @@ func extractReferredPluginNamespace(
 	}
 	return plugin.Namespace, nil
 }
+
+func isRemotePluginReferenceAllowed(log logr.Logger, s store.Storer, r pluginReference) error {
+	// remote plugin. considered part of this namespace if a suitable ReferenceGrant exists
+	grants, err := s.ListReferenceGrants()
+	if err != nil {
+		return fmt.Errorf("could not retrieve ReferenceGrants from store when building plugin relations map: %w", err)
+	}
+	allowed := gatewayapi.GetPermittedForReferenceGrantFrom(
+		log,
+		gatewayapi.ReferenceGrantFrom{
+			Group:     gatewayapi.Group(r.Referrer.GetObjectKind().GroupVersionKind().Group),
+			Kind:      gatewayapi.Kind(r.Referrer.GetObjectKind().GroupVersionKind().Kind),
+			Namespace: gatewayapi.Namespace(r.Referrer.GetNamespace()),
+		},
+		grants,
+	)
+
+	// TODO https://github.com/Kong/kubernetes-ingress-controller/issues/6000
+	// Our reference checking code wasn't originally designed to handle multiple object types and relationships and
+	// wasn't designed with much guidance for future usage. It expects something akin to the BackendRef section of an
+	// HTTPRoute or similar, since that's what it was originally designed for. A future simplified model should probably
+	// work with source and target client.Object resources derived from the particular resources' reference specs, as
+	// those reference formats aren't necessarily consistent. We should possibly push for a standard upstream utility as
+	// part of https://github.com/kubernetes/enhancements/issues/3766 if it proceeds further, as this is can be difficult
+	// to wrap your head around when deep in the code that's checking an individual use case. A standard model for "these
+	// are the inputs and outputs for a reference grant check, derive them from your spec" should help avoid inconsistency
+	// in check implementation.
+
+	// we don't have a full plugin resource here for the grant checker, so we build a fake one with the correct
+	// name and namespace.
+	pluginReference := gatewayapi.PluginLabelReference{
+		Namespace: &r.Namespace,
+		Name:      r.Name,
+	}
+
+	// Because we should check whether it is allowed to refer FROM the referrer TO the plugin here,
+	// we should put the referrer on the "target" and the plugin on the "backendRef".
+
+	log.V(logging.TraceLevel).Info("requested grant to plugins",
+		"from-namespace", r.Referrer.GetNamespace(),
+		"from-group", r.Referrer.GetObjectKind().GroupVersionKind().Group,
+		"from-kind", r.Referrer.GetObjectKind().GroupVersionKind().Kind,
+		"to-namespace", r.Namespace,
+		"to-name", r.Name,
+	)
+
+	if !gatewayapi.NewRefCheckerForKongPlugin(log, r.Referrer, pluginReference).IsRefAllowedByGrant(allowed) {
+		return referredPluginNotAllowedError{
+			pluginReference: pluginReference,
+		}
+	}
+	return nil
+}
diff --git a/internal/gatewayapi/contraints.go b/internal/gatewayapi/contraints.go
index c52e0f3ed..6008b5f8c 100644
--- a/internal/gatewayapi/contraints.go
+++ b/internal/gatewayapi/contraints.go
@@ -1,6 +1,10 @@
 package gatewayapi
 
-import "sigs.k8s.io/controller-runtime/pkg/client"
+import (
+	"fmt"
+
+	"sigs.k8s.io/controller-runtime/pkg/client"
+)
 
 type HostnameT interface {
 	Hostname | string
@@ -33,3 +37,10 @@ type PluginLabelReference struct {
 	Namespace *string
 	Name      string
 }
+
+func (plr PluginLabelReference) String() string {
+	if plr.Namespace == nil {
+		return plr.Name
+	}
+	return fmt.Sprintf("%s:%s", *plr.Namespace, plr.Name)
+}


Test patch:
diff --git a/internal/dataplane/kongstate/kongstate_test.go b/internal/dataplane/kongstate/kongstate_test.go
index a4a620953..f7b099121 100644
--- a/internal/dataplane/kongstate/kongstate_test.go
+++ b/internal/dataplane/kongstate/kongstate_test.go
@@ -677,7 +677,7 @@ func TestGetPluginRelations(t *testing.T) {
 		t.Run(tt.name, func(t *testing.T) {
 			store, err := store.NewFakeStore(store.FakeObjects{})
 			require.NoError(t, err)
-			computedPluginRelations := tt.data.inputState.getPluginRelations(store, logr.Discard())
+			computedPluginRelations := tt.data.inputState.getPluginRelations(store, logr.Discard(), nil)
 			if diff := cmp.Diff(tt.data.expectedPluginRelations, computedPluginRelations); diff != "" {
 				t.Fatal("expected value differs from actual, see the human-readable diff:", diff)
 			}
@@ -816,10 +816,12 @@ func BenchmarkGetPluginRelations(b *testing.B) {
 
 	store, err := store.NewFakeStore(store.FakeObjects{})
 	require.NoError(b, err)
+	logger := logr.Discard()
+	failuresCollector := failures.NewResourceFailuresCollector(logger)
 	b.ResetTimer()
 
 	for i := 0; i < b.N; i++ {
-		fr := ks.getPluginRelations(store, logr.Discard())
+		fr := ks.getPluginRelations(store, logger, failuresCollector)
 		_ = fr
 	}
 }
diff --git a/test/e2e/manifests/all-in-one-dbless-k4k8s-enterprise.yaml b/test/e2e/manifests/all-in-one-dbless-k4k8s-enterprise.yaml
index 2252b3b80..698e048bc 100644
--- a/test/e2e/manifests/all-in-one-dbless-k4k8s-enterprise.yaml
+++ b/test/e2e/manifests/all-in-one-dbless-k4k8s-enterprise.yaml
@@ -960,10 +960,16 @@ spec:
                   to the entity(service/route/consumer) where the plugin is attached.
                 properties:
                   group:
+                    description: Group defines the API group of the referred object.
                     type: string
                   kind:
+                    description: Kind defines the kind of the referred object.
+                    enum:
+                    - KongPlugin
+                    - KongClusterPlugin
                     type: string
                   name:
+                    description: Name defines the name of the referred object.
                     type: string
                   namespace:
                     description: Empty namespace means the same namespace of the owning
@@ -981,6 +987,9 @@ spec:
             - fields
             - type
             type: object
+            x-kubernetes-validations:
+            - message: The type field cannot be one of the known Kong entity types
+              rule: '!(self.type in [''services'',''routes'',''upstreams'',''targets'',''plugins'',''consumers'',''consumer_groups''])'
           status:
             description: Status stores the reconciling status of the resource.
             properties:
@@ -1065,8 +1074,6 @@ spec:
         x-kubernetes-validations:
         - message: The spec.type field is immutable
           rule: self.spec.type == oldSelf.spec.type
-        - message: The spec.type field cannot be known Kong entity types
-          rule: '!(self.spec.type in [''services'',''routes'',''upstreams'',''targets'',''plugins'',''consumers'',''consumer_groups''])'
     served: true
     storage: true
     subresources:
@@ -3253,6 +3260,8 @@ spec:
                             Ports is a list of records of service ports
                             If used, every port defined in the service should have an entry in it
                           items:
+                            description: PortStatus represents the error condition
+                              of a service port
                             properties:
                               error:
                                 description: |-
@@ -3424,6 +3433,8 @@ spec:
                             Ports is a list of records of service ports
                             If used, every port defined in the service should have an entry in it
                           items:
+                            description: PortStatus represents the error condition
+                              of a service port
                             properties:
                               error:
                                 description: |-
diff --git a/test/e2e/manifests/all-in-one-dbless-konnect-enterprise.yaml b/test/e2e/manifests/all-in-one-dbless-konnect-enterprise.yaml
index f92a21a85..e09fbd01b 100644
--- a/test/e2e/manifests/all-in-one-dbless-konnect-enterprise.yaml
+++ b/test/e2e/manifests/all-in-one-dbless-konnect-enterprise.yaml
@@ -960,10 +960,16 @@ spec:
                   to the entity(service/route/consumer) where the plugin is attached.
                 properties:
                   group:
+                    description: Group defines the API group of the referred object.
                     type: string
                   kind:
+                    description: Kind defines the kind of the referred object.
+                    enum:
+                    - KongPlugin
+                    - KongClusterPlugin
                     type: string
                   name:
+                    description: Name defines the name of the referred object.
                     type: string
                   namespace:
                     description: Empty namespace means the same namespace of the owning
@@ -981,6 +987,9 @@ spec:
             - fields
             - type
             type: object
+            x-kubernetes-validations:
+            - message: The type field cannot be one of the known Kong entity types
+              rule: '!(self.type in [''services'',''routes'',''upstreams'',''targets'',''plugins'',''consumers'',''consumer_groups''])'
           status:
             description: Status stores the reconciling status of the resource.
             properties:
@@ -1065,8 +1074,6 @@ spec:
         x-kubernetes-validations:
         - message: The spec.type field is immutable
           rule: self.spec.type == oldSelf.spec.type
-        - message: The spec.type field cannot be known Kong entity types
-          rule: '!(self.spec.type in [''services'',''routes'',''upstreams'',''targets'',''plugins'',''consumers'',''consumer_groups''])'
     served: true
     storage: true
     subresources:
@@ -3253,6 +3260,8 @@ spec:
                             Ports is a list of records of service ports
                             If used, every port defined in the service should have an entry in it
                           items:
+                            description: PortStatus represents the error condition
+                              of a service port
                             properties:
                               error:
                                 description: |-
@@ -3424,6 +3433,8 @@ spec:
                             Ports is a list of records of service ports
                             If used, every port defined in the service should have an entry in it
                           items:
+                            description: PortStatus represents the error condition
+                              of a service port
                             properties:
                               error:
                                 description: |-
diff --git a/test/e2e/manifests/all-in-one-dbless-konnect.yaml b/test/e2e/manifests/all-in-one-dbless-konnect.yaml
index c02e3b0bb..cefa4657b 100644
--- a/test/e2e/manifests/all-in-one-dbless-konnect.yaml
+++ b/test/e2e/manifests/all-in-one-dbless-konnect.yaml
@@ -960,10 +960,16 @@ spec:
                   to the entity(service/route/consumer) where the plugin is attached.
                 properties:
                   group:
+                    description: Group defines the API group of the referred object.
                     type: string
                   kind:
+                    description: Kind defines the kind of the referred object.
+                    enum:
+                    - KongPlugin
+                    - KongClusterPlugin
                     type: string
                   name:
+                    description: Name defines the name of the referred object.
                     type: string
                   namespace:
                     description: Empty namespace means the same namespace of the owning
@@ -981,6 +987,9 @@ spec:
             - fields
             - type
             type: object
+            x-kubernetes-validations:
+            - message: The type field cannot be one of the known Kong entity types
+              rule: '!(self.type in [''services'',''routes'',''upstreams'',''targets'',''plugins'',''consumers'',''consumer_groups''])'
           status:
             description: Status stores the reconciling status of the resource.
             properties:
@@ -1065,8 +1074,6 @@ spec:
         x-kubernetes-validations:
         - message: The spec.type field is immutable
           rule: self.spec.type == oldSelf.spec.type
-        - message: The spec.type field cannot be known Kong entity types
-          rule: '!(self.spec.type in [''services'',''routes'',''upstreams'',''targets'',''plugins'',''consumers'',''consumer_groups''])'
     served: true
     storage: true
     subresources:
@@ -3253,6 +3260,8 @@ spec:
                             Ports is a list of records of service ports
                             If used, every port defined in the service should have an entry in it
                           items:
+                            description: PortStatus represents the error condition
+                              of a service port
                             properties:
                               error:
                                 description: |-
@@ -3424,6 +3433,8 @@ spec:
                             Ports is a list of records of service ports
                             If used, every port defined in the service should have an entry in it
                           items:
+                            description: PortStatus represents the error condition
+                              of a service port
                             properties:
                               error:
                                 description: |-
diff --git a/test/e2e/manifests/all-in-one-dbless.yaml b/test/e2e/manifests/all-in-one-dbless.yaml
index 01814bc83..ef1ac9117 100644
--- a/test/e2e/manifests/all-in-one-dbless.yaml
+++ b/test/e2e/manifests/all-in-one-dbless.yaml
@@ -960,10 +960,16 @@ spec:
                   to the entity(service/route/consumer) where the plugin is attached.
                 properties:
                   group:
+                    description: Group defines the API group of the referred object.
                     type: string
                   kind:
+                    description: Kind defines the kind of the referred object.
+                    enum:
+                    - KongPlugin
+                    - KongClusterPlugin
                     type: string
                   name:
+                    description: Name defines the name of the referred object.
                     type: string
                   namespace:
                     description: Empty namespace means the same namespace of the owning
@@ -981,6 +987,9 @@ spec:
             - fields
             - type
             type: object
+            x-kubernetes-validations:
+            - message: The type field cannot be one of the known Kong entity types
+              rule: '!(self.type in [''services'',''routes'',''upstreams'',''targets'',''plugins'',''consumers'',''consumer_groups''])'
           status:
             description: Status stores the reconciling status of the resource.
             properties:
@@ -1065,8 +1074,6 @@ spec:
         x-kubernetes-validations:
         - message: The spec.type field is immutable
           rule: self.spec.type == oldSelf.spec.type
-        - message: The spec.type field cannot be known Kong entity types
-          rule: '!(self.spec.type in [''services'',''routes'',''upstreams'',''targets'',''plugins'',''consumers'',''consumer_groups''])'
     served: true
     storage: true
     subresources:
@@ -3253,6 +3260,8 @@ spec:
                             Ports is a list of records of service ports
                             If used, every port defined in the service should have an entry in it
                           items:
+                            description: PortStatus represents the error condition
+                              of a service port
                             properties:
                               error:
                                 description: |-
@@ -3424,6 +3433,8 @@ spec:
                             Ports is a list of records of service ports
                             If used, every port defined in the service should have an entry in it
                           items:
+                            description: PortStatus represents the error condition
+                              of a service port
                             properties:
                               error:
                                 description: |-
diff --git a/test/e2e/manifests/all-in-one-postgres-enterprise.yaml b/test/e2e/manifests/all-in-one-postgres-enterprise.yaml
index 7aeae5227..02a834baf 100644
--- a/test/e2e/manifests/all-in-one-postgres-enterprise.yaml
+++ b/test/e2e/manifests/all-in-one-postgres-enterprise.yaml
@@ -960,10 +960,16 @@ spec:
                   to the entity(service/route/consumer) where the plugin is attached.
                 properties:
                   group:
+                    description: Group defines the API group of the referred object.
                     type: string
                   kind:
+                    description: Kind defines the kind of the referred object.
+                    enum:
+                    - KongPlugin
+                    - KongClusterPlugin
                     type: string
                   name:
+                    description: Name defines the name of the referred object.
                     type: string
                   namespace:
                     description: Empty namespace means the same namespace of the owning
@@ -981,6 +987,9 @@ spec:
             - fields
             - type
             type: object
+            x-kubernetes-validations:
+            - message: The type field cannot be one of the known Kong entity types
+              rule: '!(self.type in [''services'',''routes'',''upstreams'',''targets'',''plugins'',''consumers'',''consumer_groups''])'
           status:
             description: Status stores the reconciling status of the resource.
             properties:
@@ -1065,8 +1074,6 @@ spec:
         x-kubernetes-validations:
         - message: The spec.type field is immutable
           rule: self.spec.type == oldSelf.spec.type
-        - message: The spec.type field cannot be known Kong entity types
-          rule: '!(self.spec.type in [''services'',''routes'',''upstreams'',''targets'',''plugins'',''consumers'',''consumer_groups''])'
     served: true
     storage: true
     subresources:
@@ -3253,6 +3260,8 @@ spec:
                             Ports is a list of records of service ports
                             If used, every port defined in the service should have an entry in it
                           items:
+                            description: PortStatus represents the error condition
+                              of a service port
                             properties:
                               error:
                                 description: |-
@@ -3424,6 +3433,8 @@ spec:
                             Ports is a list of records of service ports
                             If used, every port defined in the service should have an entry in it
                           items:
+                            description: PortStatus represents the error condition
+                              of a service port
                             properties:
                               error:
                                 description: |-
diff --git a/test/e2e/manifests/all-in-one-postgres-multiple-gateways.yaml b/test/e2e/manifests/all-in-one-postgres-multiple-gateways.yaml
index 3211da4ad..d68237988 100644
--- a/test/e2e/manifests/all-in-one-postgres-multiple-gateways.yaml
+++ b/test/e2e/manifests/all-in-one-postgres-multiple-gateways.yaml
@@ -960,10 +960,16 @@ spec:
                   to the entity(service/route/consumer) where the plugin is attached.
                 properties:
                   group:
+                    description: Group defines the API group of the referred object.
                     type: string
                   kind:
+                    description: Kind defines the kind of the referred object.
+                    enum:
+                    - KongPlugin
+                    - KongClusterPlugin
                     type: string
                   name:
+                    description: Name defines the name of the referred object.
                     type: string
                   namespace:
                     description: Empty namespace means the same namespace of the owning
@@ -981,6 +987,9 @@ spec:
             - fields
             - type
             type: object
+            x-kubernetes-validations:
+            - message: The type field cannot be one of the known Kong entity types
+              rule: '!(self.type in [''services'',''routes'',''upstreams'',''targets'',''plugins'',''consumers'',''consumer_groups''])'
           status:
             description: Status stores the reconciling status of the resource.
             properties:
@@ -1065,8 +1074,6 @@ spec:
         x-kubernetes-validations:
         - message: The spec.type field is immutable
           rule: self.spec.type == oldSelf.spec.type
-        - message: The spec.type field cannot be known Kong entity types
-          rule: '!(self.spec.type in [''services'',''routes'',''upstreams'',''targets'',''plugins'',''consumers'',''consumer_groups''])'
     served: true
     storage: true
     subresources:
@@ -3253,6 +3260,8 @@ spec:
                             Ports is a list of records of service ports
                             If used, every port defined in the service should have an entry in it
                           items:
+                            description: PortStatus represents the error condition
+                              of a service port
                             properties:
                               error:
                                 description: |-
@@ -3424,6 +3433,8 @@ spec:
                             Ports is a list of records of service ports
                             If used, every port defined in the service should have an entry in it
                           items:
+                            description: PortStatus represents the error condition
+                              of a service port
                             properties:
                               error:
                                 description: |-
diff --git a/test/e2e/manifests/all-in-one-postgres.yaml b/test/e2e/manifests/all-in-one-postgres.yaml
index 3b69aeaab..f158a2c36 100644
--- a/test/e2e/manifests/all-in-one-postgres.yaml
+++ b/test/e2e/manifests/all-in-one-postgres.yaml
@@ -960,10 +960,16 @@ spec:
                   to the entity(service/route/consumer) where the plugin is attached.
                 properties:
                   group:
+                    description: Group defines the API group of the referred object.
                     type: string
                   kind:
+                    description: Kind defines the kind of the referred object.
+                    enum:
+                    - KongPlugin
+                    - KongClusterPlugin
                     type: string
                   name:
+                    description: Name defines the name of the referred object.
                     type: string
                   namespace:
                     description: Empty namespace means the same namespace of the owning
@@ -981,6 +987,9 @@ spec:
             - fields
             - type
             type: object
+            x-kubernetes-validations:
+            - message: The type field cannot be one of the known Kong entity types
+              rule: '!(self.type in [''services'',''routes'',''upstreams'',''targets'',''plugins'',''consumers'',''consumer_groups''])'
           status:
             description: Status stores the reconciling status of the resource.
             properties:
@@ -1065,8 +1074,6 @@ spec:
         x-kubernetes-validations:
         - message: The spec.type field is immutable
           rule: self.spec.type == oldSelf.spec.type
-        - message: The spec.type field cannot be known Kong entity types
-          rule: '!(self.spec.type in [''services'',''routes'',''upstreams'',''targets'',''plugins'',''consumers'',''consumer_groups''])'
     served: true
     storage: true
     subresources:
@@ -3253,6 +3260,8 @@ spec:
                             Ports is a list of records of service ports
                             If used, every port defined in the service should have an entry in it
                           items:
+                            description: PortStatus represents the error condition
+                              of a service port
                             properties:
                               error:
                                 description: |-
@@ -3424,6 +3433,8 @@ spec:
                             Ports is a list of records of service ports
                             If used, every port defined in the service should have an entry in it
                           items:
+                            description: PortStatus represents the error condition
+                              of a service port
                             properties:
                               error:
                                 description: |-
diff --git a/test/envtest/configerrorevent_envtest_test.go b/test/envtest/configerrorevent_envtest_test.go
index 7342e30fa..fa513be7d 100644
--- a/test/envtest/configerrorevent_envtest_test.go
+++ b/test/envtest/configerrorevent_envtest_test.go
@@ -66,7 +66,7 @@ func TestConfigErrorEventGenerationInMemoryMode(t *testing.T) {
 		"konghq.com/protocol": "tcp",
 		"konghq.com/path":     "/aitmatov",
 		// Referencing non-existent KongPlugins.
-		"konghq.com/plugins": "foo,bar",
+		"konghq.com/plugins": "foo,bar,n1:p1",
 	}
 	service.Namespace = ns.Name
 	require.NoError(t, ctrlClient.Create(ctx, service))
@@ -102,7 +102,7 @@ func TestConfigErrorEventGenerationInMemoryMode(t *testing.T) {
 		}
 		t.Logf("got %d events", len(events.Items))
 
-		const numberOfExpectedEvents = 7
+		const numberOfExpectedEvents = 8
 		matches := make([]bool, numberOfExpectedEvents)
 		matches[0] = lo.ContainsBy(events.Items, func(e corev1.Event) bool {
 			return e.Type == corev1.EventTypeWarning &&
@@ -154,6 +154,13 @@ func TestConfigErrorEventGenerationInMemoryMode(t *testing.T) {
 				e.InvolvedObject.Name == ingress.Name &&
 				e.Message == `referenced KongPlugin or KongClusterPlugin "baz" does not exist`
 		})
+		matches[7] = lo.ContainsBy(events.Items, func(e corev1.Event) bool {
+			return e.Type == corev1.EventTypeWarning &&
+				e.Reason == dataplane.KongConfigurationTranslationFailedEventReason &&
+				e.InvolvedObject.Kind == "Service" &&
+				e.InvolvedObject.Name == service.Name &&
+				e.Message == `no grant found to referenced "n1:p1" plugin in the requested remote KongPlugin bind`
+		})
 		if lo.Count(matches, true) != numberOfExpectedEvents {
 			t.Logf("not all events matched: %+v", matches)
 			return false
@@ -311,7 +318,7 @@ func TestConfigErrorEventGenerationDBMode(t *testing.T) {
 			Annotations: map[string]string{
 				annotations.IngressClassKey: ingressClassName,
 				// Referencing non-existent KongPlugin.
-				"konghq.com/plugins": "foo",
+				"konghq.com/plugins": "foo, n1:p1",
 			},
 		},
 		Username: "donenbai",
@@ -342,7 +349,7 @@ func TestConfigErrorEventGenerationDBMode(t *testing.T) {
 		}
 		t.Logf("got %d events", len(events.Items))
 
-		const numberOfExpectedEvents = 2
+		const numberOfExpectedEvents = 3
 		matches := make([]bool, numberOfExpectedEvents)
 		matches[0] = lo.ContainsBy(events.Items, func(e corev1.Event) bool {
 			return e.Type == corev1.EventTypeWarning &&
@@ -358,6 +365,13 @@ func TestConfigErrorEventGenerationDBMode(t *testing.T) {
 				e.InvolvedObject.Name == consumer.Name &&
 				e.Message == `referenced KongPlugin or KongClusterPlugin "foo" does not exist`
 		})
+		matches[2] = lo.ContainsBy(events.Items, func(e corev1.Event) bool {
+			return e.Type == corev1.EventTypeWarning &&
+				e.Reason == dataplane.KongConfigurationTranslationFailedEventReason &&
+				e.InvolvedObject.Kind == "KongConsumer" &&
+				e.InvolvedObject.Name == consumer.Name &&
+				e.Message == `no grant found to referenced "n1:p1" plugin in the requested remote KongPlugin bind`
+		})
 		if lo.Count(matches, true) != numberOfExpectedEvents {
 			t.Logf("not all events matched: %+v", matches)
 			return false