This is an automated email from the ASF dual-hosted git repository.
AlinsRan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix-ingress-controller.git
The following commit(s) were added to refs/heads/master by this push:
new b0fa3e54 feat: support consumer labels from metadata labels (#2760)
b0fa3e54 is described below
commit b0fa3e5459e26b98a2c409a34d70af438ab79353
Author: AlinsRan <[email protected]>
AuthorDate: Tue May 12 11:48:59 2026 +0800
feat: support consumer labels from metadata labels (#2760)
---
internal/adc/translator/apisixconsumer.go | 2 +-
internal/adc/translator/apisixconsumer_test.go | 71 ++++++++++++++++++++++++
internal/adc/translator/apisixroute.go | 5 +-
internal/adc/translator/apisixroute_test.go | 39 +++++++++++++
internal/adc/translator/consumer.go | 2 +-
internal/adc/translator/consumer_test.go | 61 +++++++++++++++++++++
internal/controller/label/label.go | 13 ++++-
internal/controller/label/label_test.go | 76 ++++++++++++++++++++++++++
8 files changed, 262 insertions(+), 7 deletions(-)
diff --git a/internal/adc/translator/apisixconsumer.go
b/internal/adc/translator/apisixconsumer.go
index 406f1c2c..823d65bb 100644
--- a/internal/adc/translator/apisixconsumer.go
+++ b/internal/adc/translator/apisixconsumer.go
@@ -98,7 +98,7 @@ func (t *Translator) TranslateApisixConsumer(tctx
*provider.TranslateContext, ac
Username: username,
}
consumer.Plugins = plugins
- consumer.Labels = label.GenLabel(ac)
+ consumer.Labels = label.GenLabelWithObjectLabels(ac)
result.Consumers = append(result.Consumers, consumer)
return result, nil
}
diff --git a/internal/adc/translator/apisixconsumer_test.go
b/internal/adc/translator/apisixconsumer_test.go
new file mode 100644
index 00000000..7dbe131c
--- /dev/null
+++ b/internal/adc/translator/apisixconsumer_test.go
@@ -0,0 +1,71 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you 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 translator
+
+import (
+ "context"
+ "testing"
+
+ "github.com/go-logr/logr"
+ "github.com/stretchr/testify/require"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+ apiv2 "github.com/apache/apisix-ingress-controller/api/v2"
+ "github.com/apache/apisix-ingress-controller/internal/controller/label"
+ "github.com/apache/apisix-ingress-controller/internal/provider"
+)
+
+func
TestTranslateApisixConsumer_UsesMetadataLabelsWithoutOverwritingControllerLabels(t
*testing.T) {
+ translator := NewTranslator(logr.Discard())
+ tctx := provider.NewDefaultTranslateContext(context.Background())
+
+ consumer := &apiv2.ApisixConsumer{
+ TypeMeta: metav1.TypeMeta{
+ Kind: "ApisixConsumer",
+ APIVersion: apiv2.GroupVersion.String(),
+ },
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "demo",
+ Namespace: "default",
+ Labels: map[string]string{
+ "team": "payments",
+ label.LabelName: "user-value",
+ label.LabelManagedBy: "user-manager",
+ },
+ },
+ Spec: apiv2.ApisixConsumerSpec{
+ AuthParameter: apiv2.ApisixConsumerAuthParameter{
+ BasicAuth: &apiv2.ApisixConsumerBasicAuth{
+ Value:
&apiv2.ApisixConsumerBasicAuthValue{
+ Username: "demo",
+ Password: "secret",
+ },
+ },
+ },
+ },
+ }
+
+ result, err := translator.TranslateApisixConsumer(tctx, consumer)
+ require.NoError(t, err)
+ require.Len(t, result.Consumers, 1)
+
+ translated := result.Consumers[0]
+ require.Equal(t, "payments", translated.Labels["team"])
+ require.Equal(t, consumer.Name, translated.Labels[label.LabelName])
+ require.Equal(t, "apisix-ingress-controller",
translated.Labels[label.LabelManagedBy])
+}
diff --git a/internal/adc/translator/apisixroute.go
b/internal/adc/translator/apisixroute.go
index 76b32970..b002d6f9 100644
--- a/internal/adc/translator/apisixroute.go
+++ b/internal/adc/translator/apisixroute.go
@@ -184,7 +184,7 @@ func (t *Translator) buildRoute(ar *apiv2.ApisixRoute,
service *adc.Service, rul
route.Name = adc.ComposeRouteName(ar.Namespace, ar.Name, rule.Name)
route.ID = id.GenID(route.Name)
route.Desc = "Created by apisix-ingress-controller, DO NOT modify it
manually"
- route.Labels = label.GenLabel(ar)
+ route.Labels = label.GenLabelWithObjectLabels(ar)
route.EnableWebsocket = rule.Websocket
if route.EnableWebsocket == nil && *enableWebsocket != nil {
route.EnableWebsocket = *enableWebsocket
@@ -197,9 +197,6 @@ func (t *Translator) buildRoute(ar *apiv2.ApisixRoute,
service *adc.Service, rul
route.Timeout = timeout
route.Uris = rule.Match.Paths
route.Vars = vars
- for key, value := range ar.GetObjectMeta().GetLabels() {
- route.Labels[key] = value
- }
service.Routes = []*adc.Route{route}
}
diff --git a/internal/adc/translator/apisixroute_test.go
b/internal/adc/translator/apisixroute_test.go
index 6ea15aec..9feda221 100644
--- a/internal/adc/translator/apisixroute_test.go
+++ b/internal/adc/translator/apisixroute_test.go
@@ -82,3 +82,42 @@ func TestBuildService_HostsSet(t *testing.T) {
// service.Hosts SHOULD be set — this is the canonical location for
hosts.
assert.Equal(t, []string{"example.com", "foo.com"}, service.Hosts)
}
+
+func TestBuildRoute_MetadataLabelsDoNotOverwriteControllerLabels(t *testing.T)
{
+ translator := NewTranslator(logr.Discard())
+
+ ar := &apiv2.ApisixRoute{
+ TypeMeta: metav1.TypeMeta{
+ Kind: "ApisixRoute",
+ APIVersion: apiv2.GroupVersion.String(),
+ },
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "test-route",
+ Namespace: "default",
+ Labels: map[string]string{
+ "team": "payments",
+ "k8s/name": "user-value",
+ "manager-by": "user-manager",
+ "k8s/namespace": "user-namespace",
+ },
+ },
+ }
+
+ service := &adc.Service{}
+ rule := apiv2.ApisixRouteHTTP{
+ Name: "rule1",
+ Match: apiv2.ApisixRouteHTTPMatch{
+ Paths: []string{"/api/*"},
+ },
+ }
+
+ var enableWebsocket *bool
+ translator.buildRoute(ar, service, rule, nil, nil, nil,
&enableWebsocket)
+
+ assert.Len(t, service.Routes, 1)
+ route := service.Routes[0]
+ assert.Equal(t, "payments", route.Labels["team"])
+ assert.Equal(t, ar.Name, route.Labels["k8s/name"])
+ assert.Equal(t, ar.Namespace, route.Labels["k8s/namespace"])
+ assert.Equal(t, "apisix-ingress-controller", route.Labels["manager-by"])
+}
diff --git a/internal/adc/translator/consumer.go
b/internal/adc/translator/consumer.go
index 70328765..93601fc3 100644
--- a/internal/adc/translator/consumer.go
+++ b/internal/adc/translator/consumer.go
@@ -71,7 +71,7 @@ func (t *Translator) TranslateConsumerV1alpha1(tctx
*provider.TranslateContext,
credentials = append(credentials, credential)
}
consumer.Credentials = credentials
- consumer.Labels = label.GenLabel(consumerV)
+ consumer.Labels = label.GenLabelWithObjectLabels(consumerV)
plugins := adctypes.Plugins{}
for _, plugin := range consumerV.Spec.Plugins {
pluginName := plugin.Name
diff --git a/internal/adc/translator/consumer_test.go
b/internal/adc/translator/consumer_test.go
new file mode 100644
index 00000000..cd572d87
--- /dev/null
+++ b/internal/adc/translator/consumer_test.go
@@ -0,0 +1,61 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you 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 translator
+
+import (
+ "context"
+ "testing"
+
+ "github.com/go-logr/logr"
+ "github.com/stretchr/testify/require"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+ "github.com/apache/apisix-ingress-controller/api/v1alpha1"
+ "github.com/apache/apisix-ingress-controller/internal/controller/label"
+ "github.com/apache/apisix-ingress-controller/internal/provider"
+)
+
+func
TestTranslateConsumerV1alpha1_UsesMetadataLabelsWithoutOverwritingControllerLabels(t
*testing.T) {
+ translator := NewTranslator(logr.Discard())
+ tctx := provider.NewDefaultTranslateContext(context.Background())
+
+ consumer := &v1alpha1.Consumer{
+ TypeMeta: metav1.TypeMeta{
+ Kind: "Consumer",
+ APIVersion: v1alpha1.GroupVersion.String(),
+ },
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "demo",
+ Namespace: "default",
+ Labels: map[string]string{
+ "team": "payments",
+ label.LabelName: "user-value",
+ label.LabelManagedBy: "user-manager",
+ },
+ },
+ }
+
+ result, err := translator.TranslateConsumerV1alpha1(tctx, consumer)
+ require.NoError(t, err)
+ require.Len(t, result.Consumers, 1)
+
+ translated := result.Consumers[0]
+ require.Equal(t, "payments", translated.Labels["team"])
+ require.Equal(t, consumer.Name, translated.Labels[label.LabelName])
+ require.Equal(t, "apisix-ingress-controller",
translated.Labels[label.LabelManagedBy])
+}
diff --git a/internal/controller/label/label.go
b/internal/controller/label/label.go
index 694e262b..0c636f72 100644
--- a/internal/controller/label/label.go
+++ b/internal/controller/label/label.go
@@ -40,8 +40,19 @@ func GenLabel(client client.Object, args ...string) Label {
label[LabelName] = client.GetName()
label[LabelControllerName] = config.ControllerConfig.ControllerName
label[LabelManagedBy] = "apisix-ingress-controller"
- for i := 0; i < len(args); i += 2 {
+ for i := 0; i+1 < len(args); i += 2 {
label[args[i]] = args[i+1]
}
return label
}
+
+func GenLabelWithObjectLabels(obj client.Object, args ...string) Label {
+ label := make(Label)
+ for key, value := range obj.GetLabels() {
+ label[key] = value
+ }
+ for key, value := range GenLabel(obj, args...) {
+ label[key] = value
+ }
+ return label
+}
diff --git a/internal/controller/label/label_test.go
b/internal/controller/label/label_test.go
new file mode 100644
index 00000000..81403447
--- /dev/null
+++ b/internal/controller/label/label_test.go
@@ -0,0 +1,76 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you 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 label
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+ apiv2 "github.com/apache/apisix-ingress-controller/api/v2"
+ "github.com/apache/apisix-ingress-controller/internal/controller/config"
+)
+
+func TestGenLabelWithObjectLabels(t *testing.T) {
+ consumer := &apiv2.ApisixConsumer{
+ TypeMeta: metav1.TypeMeta{
+ Kind: "ApisixConsumer",
+ APIVersion: apiv2.GroupVersion.String(),
+ },
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "demo",
+ Namespace: "default",
+ Labels: map[string]string{
+ "team": "payments",
+ LabelName: "user-value",
+ LabelManagedBy: "user-manager",
+ LabelNamespace: "user-namespace",
+ LabelControllerName: "user-controller",
+ LabelKind: "user-kind",
+ },
+ },
+ }
+
+ labels := GenLabelWithObjectLabels(consumer)
+
+ require.Equal(t, "payments", labels["team"])
+ require.Equal(t, consumer.Name, labels[LabelName])
+ require.Equal(t, consumer.Namespace, labels[LabelNamespace])
+ require.Equal(t, "ApisixConsumer", labels[LabelKind])
+ require.Equal(t, config.ControllerConfig.ControllerName,
labels[LabelControllerName])
+ require.Equal(t, "apisix-ingress-controller", labels[LabelManagedBy])
+}
+
+func TestGenLabel_IgnoresDanglingKeyArg(t *testing.T) {
+ consumer := &apiv2.ApisixConsumer{
+ TypeMeta: metav1.TypeMeta{
+ Kind: "ApisixConsumer",
+ APIVersion: apiv2.GroupVersion.String(),
+ },
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "demo",
+ Namespace: "default",
+ },
+ }
+
+ labels := GenLabel(consumer, "team", "payments", "dangling")
+
+ require.Equal(t, "payments", labels["team"])
+ require.NotContains(t, labels, "dangling")
+}