fix: allow passing plugin config name for route with no backends (#1578)

Co-authored-by: Katlinsky, Ilya <i.katlinsky@itransition.com>
diff --git a/pkg/providers/apisix/translation/apisix_route.go b/pkg/providers/apisix/translation/apisix_route.go
index 1d7f404..2706f0f 100644
--- a/pkg/providers/apisix/translation/apisix_route.go
+++ b/pkg/providers/apisix/translation/apisix_route.go
@@ -325,6 +325,9 @@
 		route.EnableWebsocket = part.Websocket
 		route.Plugins = pluginMap
 		route.Timeout = timeout
+		if part.PluginConfigName != "" {
+			route.PluginConfigId = id.GenID(apisixv1.ComposePluginConfigName(ar.Namespace, part.PluginConfigName))
+		}
 		for k, v := range ar.ObjectMeta.Labels {
 			route.Metadata.Labels[k] = v
 		}
@@ -351,9 +354,6 @@
 
 			upstreamName := apisixv1.ComposeUpstreamName(ar.Namespace, backend.ServiceName, backend.Subset, svcPort, backend.ResolveGranularity)
 			route.UpstreamId = id.GenID(upstreamName)
-			if part.PluginConfigName != "" {
-				route.PluginConfigId = id.GenID(apisixv1.ComposePluginConfigName(ar.Namespace, part.PluginConfigName))
-			}
 
 			if len(backends) > 0 {
 				weight := translation.DefaultWeight
diff --git a/pkg/providers/apisix/translation/apisix_route_test.go b/pkg/providers/apisix/translation/apisix_route_test.go
index e95e6a4..ab7ebab 100644
--- a/pkg/providers/apisix/translation/apisix_route_test.go
+++ b/pkg/providers/apisix/translation/apisix_route_test.go
@@ -741,3 +741,42 @@
 		TLS:     nil,
 	}, ups)
 }
+
+func TestTranslateApisixRouteV2WithUpstreamNoBackendsAndPluginConfigName(t *testing.T) {
+	tr, processCh := mockTranslatorV2(t)
+	<-processCh
+	<-processCh
+
+	ar := &configv2.ApisixRoute{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      "ar",
+			Namespace: "test",
+		},
+		Spec: configv2.ApisixRouteSpec{
+			HTTP: []configv2.ApisixRouteHTTP{
+				{
+					Name: "rule1",
+					Match: configv2.ApisixRouteHTTPMatch{
+						Paths: []string{
+							"/*",
+						},
+					},
+					Upstreams: []configv2.ApisixRouteUpstreamReference{
+						{
+							Name:   "au",
+							Weight: ptrOf(1),
+						},
+					},
+					PluginConfigName: "test-PluginConfigName-1",
+				},
+			},
+		},
+	}
+
+	tctx, err := tr.TranslateRouteV2(ar)
+	assert.NoError(t, err)
+	assert.Len(t, tctx.PluginConfigs, 0)
+	assert.Len(t, tctx.Routes, 1)
+	expectedPluginId := id.GenID(apisixv1.ComposePluginConfigName(ar.Namespace, ar.Spec.HTTP[0].PluginConfigName))
+	assert.Equal(t, expectedPluginId, tctx.Routes[0].PluginConfigId)
+}
diff --git a/test/e2e/suite-plugins/suite-plugins-other/plugin_config.go b/test/e2e/suite-plugins/suite-plugins-other/plugin_config.go
index 3daa07f..d1cfbb4 100644
--- a/test/e2e/suite-plugins/suite-plugins-other/plugin_config.go
+++ b/test/e2e/suite-plugins/suite-plugins-other/plugin_config.go
@@ -539,5 +539,68 @@
 	})
 	ginkgo.Describe("suite-plugins-other: scaffold v2", func() {
 		suites(scaffold.NewDefaultV2Scaffold)
+
+		s := scaffold.NewDefaultV2Scaffold()
+		ginkgo.It("applies plugin config for route with upstream", func() {
+			apc := fmt.Sprintf(`
+apiVersion: apisix.apache.org/v2
+kind: ApisixPluginConfig
+metadata:
+ name: httpbin-plugins
+spec:
+ plugins:
+ - name: proxy-rewrite
+   enable: true
+   config:
+     regex_uri:
+     - ^/httpbin/(.*)
+     - /$1			
+`)
+			assert.Nil(ginkgo.GinkgoT(), s.CreateVersionedApisixResource(apc))
+
+			au := fmt.Sprintf(`
+apiVersion: apisix.apache.org/v2
+kind: ApisixUpstream
+metadata:
+ name: httpbin-upstream
+spec:
+ externalNodes:
+ - type: Domain
+   name: httpbin.org
+`)
+			assert.Nil(ginkgo.GinkgoT(), s.CreateVersionedApisixResource(au))
+
+			ar := fmt.Sprintf(`
+apiVersion: apisix.apache.org/v2
+kind: ApisixRoute
+metadata:
+ name: httpbin-route
+spec:
+ http:
+ - name: httpbin-route-rule
+   match:
+    hosts:
+    - httpbin.org
+    paths:
+    - /httpbin/*
+    methods:
+    - GET
+   upstreams:
+   - name: httpbin-upstream
+   plugin_config_name: httpbin-plugins
+`)
+
+			assert.Nil(ginkgo.GinkgoT(), s.CreateVersionedApisixResource(ar))
+
+			err := s.EnsureNumApisixUpstreamsCreated(1)
+			assert.Nil(ginkgo.GinkgoT(), err, "Checking number of upstreams")
+			err = s.EnsureNumApisixPluginConfigCreated(1)
+			assert.Nil(ginkgo.GinkgoT(), err, "Checking number of pluginConfigs")
+			err = s.EnsureNumApisixRoutesCreated(1)
+			assert.Nil(ginkgo.GinkgoT(), err, "Checking number of routes")
+
+			resp := s.NewAPISIXClient().GET("/httpbin/ip").WithHeader("Host", "httpbin.org").Expect()
+			resp.Status(http.StatusOK)
+		})
 	})
 })