Skip to content

Commit 9baf472

Browse files
authored
feat: support cross-namespace access service for ingress annotations (#2634)
1 parent 9be64b6 commit 9baf472

File tree

9 files changed

+133
-6
lines changed

9 files changed

+133
-6
lines changed

internal/adc/translator/annotations.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"github.com/apache/apisix-ingress-controller/internal/adc/translator/annotations"
2626
"github.com/apache/apisix-ingress-controller/internal/adc/translator/annotations/pluginconfig"
2727
"github.com/apache/apisix-ingress-controller/internal/adc/translator/annotations/plugins"
28+
"github.com/apache/apisix-ingress-controller/internal/adc/translator/annotations/servicenamespace"
2829
"github.com/apache/apisix-ingress-controller/internal/adc/translator/annotations/upstream"
2930
"github.com/apache/apisix-ingress-controller/internal/adc/translator/annotations/websocket"
3031
)
@@ -34,6 +35,7 @@ type IngressConfig struct {
3435
Upstream upstream.Upstream
3536
Plugins adctypes.Plugins
3637
EnableWebsocket bool
38+
ServiceNamespace string
3739
PluginConfigName string
3840
}
3941

@@ -42,6 +44,7 @@ var ingressAnnotationParsers = map[string]annotations.IngressAnnotationsParser{
4244
"plugins": plugins.NewParser(),
4345
"EnableWebsocket": websocket.NewParser(),
4446
"PluginConfigName": pluginconfig.NewParser(),
47+
"ServiceNamespace": servicenamespace.NewParser(),
4548
}
4649

4750
func (t *Translator) TranslateIngressAnnotations(anno map[string]string) *IngressConfig {
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one or more
2+
// contributor license agreements. See the NOTICE file distributed with
3+
// this work for additional information regarding copyright ownership.
4+
// The ASF licenses this file to You under the Apache License, Version 2.0
5+
// (the "License"); you may not use this file except in compliance with
6+
// the License. You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
package servicenamespace
17+
18+
import (
19+
"github.com/apache/apisix-ingress-controller/internal/adc/translator/annotations"
20+
)
21+
22+
type servicenamespace struct{}
23+
24+
func NewParser() annotations.IngressAnnotationsParser {
25+
return &servicenamespace{}
26+
}
27+
28+
func (w *servicenamespace) Parse(e annotations.Extractor) (any, error) {
29+
return e.GetStringAnnotation(annotations.AnnotationsSvcNamespace), nil
30+
}

internal/adc/translator/annotations_test.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,15 @@ func TestTranslateIngressAnnotations(t *testing.T) {
279279
},
280280
},
281281
},
282+
{
283+
name: "service namespace",
284+
anno: map[string]string{
285+
annotations.AnnotationsSvcNamespace: "custom-namespace",
286+
},
287+
expected: &IngressConfig{
288+
ServiceNamespace: "custom-namespace",
289+
},
290+
},
282291
}
283292

284293
for _, tt := range tests {

internal/adc/translator/ingress.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,11 @@ func (t *Translator) resolveIngressUpstream(
181181
backendService *networkingv1.IngressServiceBackend,
182182
upstream *adctypes.Upstream,
183183
) string {
184-
backendRef := convertBackendRef(obj.Namespace, backendService.Name, internaltypes.KindService)
184+
ns := obj.Namespace
185+
if config != nil && config.ServiceNamespace != "" {
186+
ns = config.ServiceNamespace
187+
}
188+
backendRef := convertBackendRef(ns, backendService.Name, internaltypes.KindService)
185189
t.AttachBackendTrafficPolicyToUpstream(backendRef, tctx.BackendTrafficPolicies, upstream)
186190
if config != nil {
187191
upConfig := config.Upstream
@@ -209,7 +213,7 @@ func (t *Translator) resolveIngressUpstream(
209213
}
210214

211215
getService := tctx.Services[types.NamespacedName{
212-
Namespace: obj.Namespace,
216+
Namespace: ns,
213217
Name: backendService.Name,
214218
}]
215219
if getService == nil {
@@ -238,7 +242,7 @@ func (t *Translator) resolveIngressUpstream(
238242
}
239243

240244
endpointSlices := tctx.EndpointSlices[types.NamespacedName{
241-
Namespace: obj.Namespace,
245+
Namespace: ns,
242246
Name: backendService.Name,
243247
}]
244248
if len(endpointSlices) > 0 {

internal/controller/indexer/indexer.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,10 @@ func IngressServiceIndexFunc(rawObj client.Object) []string {
472472
ingress := rawObj.(*networkingv1.Ingress)
473473
var services []string
474474

475+
ns := ingress.Namespace
476+
if svcNs := ingress.Annotations[annotations.AnnotationsSvcNamespace]; svcNs != "" {
477+
ns = svcNs
478+
}
475479
for _, rule := range ingress.Spec.Rules {
476480
if rule.HTTP == nil {
477481
continue
@@ -481,7 +485,7 @@ func IngressServiceIndexFunc(rawObj client.Object) []string {
481485
if path.Backend.Service == nil {
482486
continue
483487
}
484-
key := GenIndexKey(ingress.Namespace, path.Backend.Service.Name)
488+
key := GenIndexKey(ns, path.Backend.Service.Name)
485489
services = append(services, key)
486490
}
487491
}

internal/controller/ingress_controller.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,7 @@ func (r *IngressReconciler) listIngressForBackendTrafficPolicy(ctx context.Conte
410410
r.Log.Error(fmt.Errorf("unexpected object type"), "failed to convert object to BackendTrafficPolicy")
411411
return nil
412412
}
413+
413414
var namespacedNameMap = make(map[types.NamespacedName]struct{})
414415
ingresses := []networkingv1.Ingress{}
415416
for _, ref := range v.Spec.TargetRefs {
@@ -532,7 +533,11 @@ func (r *IngressReconciler) processBackends(tctx *provider.TranslateContext, ing
532533
continue
533534
}
534535
service := path.Backend.Service
535-
if err := r.processBackendService(tctx, ingress.Namespace, service); err != nil {
536+
ns := ingress.Namespace
537+
if svcNs := ingress.Annotations[annotations.AnnotationsSvcNamespace]; svcNs != "" {
538+
ns = ingress.Annotations[annotations.AnnotationsSvcNamespace]
539+
}
540+
if err := r.processBackendService(tctx, ns, service); err != nil {
536541
terr = err
537542
}
538543
}

internal/webhook/v1/ingress_webhook.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ var unsupportedAnnotations = []string{
4848
"k8s.apisix.apache.org/allowlist-source-range",
4949
"k8s.apisix.apache.org/blocklist-source-range",
5050
"k8s.apisix.apache.org/auth-type",
51-
"k8s.apisix.apache.org/svc-namespace",
5251
}
5352

5453
// checkUnsupportedAnnotations checks if the Ingress contains any unsupported annotations

test/e2e/framework/k8s.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,16 @@ func (f *Framework) CreateNamespaceWithTestService(name string) {
175175
}
176176
}
177177

178+
func (f *Framework) CreateNamespace(name string) {
179+
_, err := f.clientset.CoreV1().
180+
Namespaces().
181+
Create(f.Context, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: name}}, metav1.CreateOptions{})
182+
if err != nil && !errors.IsAlreadyExists(err) {
183+
f.GomegaT.Expect(err).ShouldNot(HaveOccurred(), "create namespace")
184+
return
185+
}
186+
}
187+
178188
func (f *Framework) DeleteNamespace(name string) {
179189
err := f.clientset.CoreV1().Namespaces().Delete(f.Context, name, metav1.DeleteOptions{})
180190
if err == nil || errors.IsNotFound(err) {

test/e2e/ingress/annotations.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -952,4 +952,67 @@ spec:
952952
Expect(rewriteConfig["body_base64"]).To(BeTrue(), "checking body_base64")
953953
})
954954
})
955+
956+
Context("Service Namespace", func() {
957+
var (
958+
ns string
959+
svc = `
960+
apiVersion: v1
961+
kind: Service
962+
metadata:
963+
name: httpbin-external-domain
964+
spec:
965+
type: ExternalName
966+
externalName: httpbin-service-e2e-test.%s.svc
967+
`
968+
ingressSvcNamespace = `
969+
apiVersion: networking.k8s.io/v1
970+
kind: Ingress
971+
metadata:
972+
name: retries
973+
annotations:
974+
k8s.apisix.apache.org/svc-namespace: %s
975+
spec:
976+
ingressClassName: %s
977+
rules:
978+
- host: httpbin.example
979+
http:
980+
paths:
981+
- path: /get
982+
pathType: Exact
983+
backend:
984+
service:
985+
name: httpbin-external-domain
986+
port:
987+
number: 80
988+
`
989+
)
990+
BeforeEach(func() {
991+
ns = s.Namespace() + "-v2"
992+
s.CreateNamespace(ns)
993+
err := s.CreateResourceFromStringWithNamespace(fmt.Sprintf(svc, s.Namespace()), ns)
994+
Expect(err).NotTo(HaveOccurred(), "creating Service in custom namespace")
995+
996+
By("create GatewayProxy")
997+
Expect(s.CreateResourceFromString(s.GetGatewayProxySpec())).NotTo(HaveOccurred(), "creating GatewayProxy")
998+
999+
By("create IngressClass")
1000+
err = s.CreateResourceFromStringWithNamespace(s.GetIngressClassYaml(), "")
1001+
Expect(err).NotTo(HaveOccurred(), "creating IngressClass")
1002+
time.Sleep(5 * time.Second)
1003+
})
1004+
AfterEach(func() {
1005+
s.DeleteNamespace(ns)
1006+
})
1007+
It("svc-namespace", func() {
1008+
Expect(s.CreateResourceFromString(fmt.Sprintf(ingressSvcNamespace, ns, s.Namespace()))).ShouldNot(HaveOccurred(), "creating Ingress")
1009+
1010+
s.RequestAssert(&scaffold.RequestAssert{
1011+
Method: "GET",
1012+
Path: "/get",
1013+
Host: "httpbin.example",
1014+
Check: scaffold.WithExpectedStatus(http.StatusOK),
1015+
})
1016+
})
1017+
})
9551018
})

0 commit comments

Comments
 (0)