Skip to content

Commit 229cd37

Browse files
committed
feat: Enhance upstream and traffic-split handling in translator and tests
- Refactor `NewDefaultUpstream` initialization for streamlined field ordering. - Update e2e framework's `GetServiceEndpoints` to accept `types.NamespacedName`. - Incorporate upstream weights into traffic-split logic, including default handling and labels. - Add comprehensive e2e tests for mixed backend and upstream scenarios.
1 parent 1d2de25 commit 229cd37

File tree

4 files changed

+117
-25
lines changed

4 files changed

+117
-25
lines changed

api/adc/types.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -533,15 +533,14 @@ func NewDefaultService() *Service {
533533

534534
func NewDefaultUpstream() *Upstream {
535535
return &Upstream{
536-
Type: Roundrobin,
537-
Nodes: make(UpstreamNodes, 0),
538-
Scheme: SchemeHTTP,
539536
Metadata: Metadata{
540-
Desc: "Created by apisix-ingress-controller, DO NOT modify it manually",
541537
Labels: map[string]string{
542538
"managed-by": "apisix-ingress-controller",
543539
},
544540
},
541+
Nodes: make(UpstreamNodes, 0),
542+
Scheme: SchemeHTTP,
543+
Type: Roundrobin,
545544
}
546545
}
547546

internal/provider/adc/translator/apisixroute.go

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,9 @@ func (t *Translator) TranslateApisixRoute(tctx *provider.TranslateContext, ar *a
166166
t.Log.Error(err, "failed to translate ApisixUpstream", "ApisixUpstream", utils.NamespacedName(au))
167167
continue
168168
}
169+
if upstreamRef.Weight != nil {
170+
upstream.Labels["meta_weight"] = strconv.FormatInt(int64(*upstreamRef.Weight), 10)
171+
}
169172

170173
upstreams = append(upstreams, upstream)
171174
}
@@ -178,6 +181,17 @@ func (t *Translator) TranslateApisixRoute(tctx *provider.TranslateContext, ar *a
178181
}
179182

180183
var weightedUpstreams []adc.TrafficSplitConfigRuleWeightedUpstream
184+
185+
// set the default upstream's weight in traffic-split
186+
weight, err := strconv.Atoi(upstream.Labels["meta_weight"])
187+
if err != nil {
188+
weight = apiv2.DefaultWeight
189+
}
190+
weightedUpstreams = append(weightedUpstreams, adc.TrafficSplitConfigRuleWeightedUpstream{
191+
Weight: weight,
192+
})
193+
194+
// set others upstreams in traffic-split
181195
for _, item := range upstreams {
182196
weight, err := strconv.Atoi(item.Labels["meta_weight"])
183197
if err != nil {
@@ -189,7 +203,7 @@ func (t *Translator) TranslateApisixRoute(tctx *provider.TranslateContext, ar *a
189203
})
190204
}
191205
if len(weightedUpstreams) > 0 {
192-
route.Plugins["traffic-split"] = &adc.TrafficSplitConfig{
206+
service.Plugins["traffic-split"] = &adc.TrafficSplitConfig{
193207
Rules: []adc.TrafficSplitConfigRule{
194208
{
195209
WeightedUpstreams: weightedUpstreams,
@@ -207,9 +221,6 @@ func (t *Translator) TranslateApisixRoute(tctx *provider.TranslateContext, ar *a
207221
service.Routes = []*adc.Route{route}
208222

209223
if backendErr != nil && len(upstream.Nodes) == 0 {
210-
if service.Plugins == nil {
211-
service.Plugins = make(map[string]any)
212-
}
213224
service.Plugins["fault-injection"] = map[string]any{
214225
"abort": map[string]any{
215226
"http_status": 500,

test/e2e/apisix/route.go

Lines changed: 95 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -35,20 +35,20 @@ var _ = Describe("Test ApisixRoute", func() {
3535
applier = framework.NewApplier(s.GinkgoT, s.K8sClient, s.CreateResourceFromString)
3636
)
3737

38-
Context("Test ApisixRoute", func() {
39-
BeforeEach(func() {
40-
By("create GatewayProxy")
41-
gatewayProxy := fmt.Sprintf(gatewayProxyYaml, s.Deployer.GetAdminEndpoint(), s.AdminKey())
42-
err := s.CreateResourceFromStringWithNamespace(gatewayProxy, "default")
43-
Expect(err).NotTo(HaveOccurred(), "creating GatewayProxy")
44-
time.Sleep(5 * time.Second)
45-
46-
By("create IngressClass")
47-
err = s.CreateResourceFromStringWithNamespace(ingressClassYaml, "")
48-
Expect(err).NotTo(HaveOccurred(), "creating IngressClass")
49-
time.Sleep(5 * time.Second)
50-
})
38+
BeforeEach(func() {
39+
By("create GatewayProxy")
40+
gatewayProxy := fmt.Sprintf(gatewayProxyYaml, s.Deployer.GetAdminEndpoint(), s.AdminKey())
41+
err := s.CreateResourceFromStringWithNamespace(gatewayProxy, "default")
42+
Expect(err).NotTo(HaveOccurred(), "creating GatewayProxy")
43+
time.Sleep(5 * time.Second)
44+
45+
By("create IngressClass")
46+
err = s.CreateResourceFromStringWithNamespace(ingressClassYaml, "")
47+
Expect(err).NotTo(HaveOccurred(), "creating IngressClass")
48+
time.Sleep(5 * time.Second)
49+
})
5150

51+
Context("Test ApisixRoute", func() {
5252
It("Basic tests", func() {
5353
const apisixRouteSpec = `
5454
apiVersion: apisix.apache.org/v2
@@ -295,8 +295,11 @@ spec:
295295
// ApisixUpstream is not implemented yet.
296296
// So the case is pending for now
297297
})
298+
})
298299

299-
It("Test ApisixRoute reference ApisixUpstream", func() {
300+
Context("Test ApisixRoute reference ApisixUpstream", func() {
301+
302+
It("Test reference ApisixUpstream", func() {
300303
const apisixRouteSpec = `
301304
apiVersion: apisix.apache.org/v2
302305
kind: ApisixRoute
@@ -310,7 +313,7 @@ spec:
310313
paths:
311314
- /*
312315
upstreams:
313-
- name: default-upstream
316+
- name: default-upstream
314317
`
315318
const apisixUpstreamSpec0 = `
316319
apiVersion: apisix.apache.org/v2
@@ -362,7 +365,84 @@ spec:
362365
err = s.CreateResourceFromString(apisixUpstreamSpec1)
363366
Expect(err).ShouldNot(HaveOccurred(), "update apisixUpstream")
364367
Eventually(request).WithArguments("/get").WithTimeout(8 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusOK))
368+
})
369+
370+
It("Test a Mix of Backends and Upstreams", func() {
371+
// apisixUpstreamSpec is an ApisixUpstream reference to the Service httpbin-service-e2e-test
372+
const apisixUpstreamSpec = `
373+
apiVersion: apisix.apache.org/v2
374+
kind: ApisixUpstream
375+
metadata:
376+
name: default-upstream
377+
spec:
378+
ingressClassName: apisix
379+
externalNodes:
380+
- type: Domain
381+
name: httpbin-service-e2e-test
382+
passHost: node
383+
`
384+
// apisixRouteSpec is an ApisixUpstream uses a backend and reference an upstream.
385+
// It contains a plugin response-rewrite that lets us know what upstream the gateway forwards the request to.
386+
const apisixRouteSpec = `
387+
apiVersion: apisix.apache.org/v2
388+
kind: ApisixRoute
389+
metadata:
390+
name: default
391+
spec:
392+
ingressClassName: apisix
393+
http:
394+
- name: rule0
395+
match:
396+
paths:
397+
- /*
398+
backends:
399+
- serviceName: httpbin-service-e2e-test
400+
servicePort: 80
401+
upstreams:
402+
- name: default-upstream
403+
plugins:
404+
- name: response-rewrite
405+
enable: true
406+
config:
407+
headers:
408+
set:
409+
"X-Upstream-Host": "$upstream_addr"
410+
`
411+
By("apply ApisixUpstream")
412+
err := s.CreateResourceFromString(apisixUpstreamSpec)
413+
Expect(err).ShouldNot(HaveOccurred(), "apply ApisixUpstream")
414+
415+
By("apply ApisixRoute")
416+
var apisixRoute apiv2.ApisixRoute
417+
applier.MustApplyAPIv2(types.NamespacedName{Namespace: s.Namespace(), Name: "default"}, &apisixRoute, apisixRouteSpec)
418+
419+
By("verify ApisixRoute works")
420+
request := func(path string) int {
421+
return s.NewAPISIXClient().GET(path).Expect().Raw().StatusCode
422+
}
423+
Eventually(request).WithArguments("/get").WithTimeout(8 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusOK))
424+
425+
By("verify the backends and the upstreams work commonly")
426+
// .backends -> service httpbin-service-e2e-test -> endpoints httpbin-service-e2e-test, so we will get the $upstream_addr as endpoint IP
427+
// .upstreams -> service alias-httpbin-service-e2e-test -> service httpbin-service-e2e-test, so we will get the $upstream_addr as the service's ClusterIP
428+
var upstreamAddrs = make(map[string]struct{})
429+
for range 10 {
430+
upstreamAddr := s.NewAPISIXClient().GET("/get").Expect().Raw().Header.Get("X-Upstream-Host")
431+
upstreamAddrs[upstreamAddr] = struct{}{}
432+
}
433+
434+
endpoints, err := s.GetServiceEndpoints(types.NamespacedName{Namespace: s.Namespace(), Name: "httpbin-service-e2e-test"})
435+
Expect(err).ShouldNot(HaveOccurred(), "get endpoints")
436+
Expect(endpoints).Should(HaveLen(1))
437+
endpoint := net.JoinHostPort(endpoints[0], "80")
438+
439+
service, err := s.GetServiceByName("httpbin-service-e2e-test")
440+
Expect(err).ShouldNot(HaveOccurred(), "get service")
441+
clusterIP := net.JoinHostPort(service.Spec.ClusterIP, "80")
365442

443+
Expect(upstreamAddrs).Should(HaveLen(2))
444+
Eventually(upstreamAddrs).Should(HaveKey(endpoint))
445+
Eventually(upstreamAddrs).Should(HaveKey(clusterIP))
366446
})
367447
})
368448
})

test/e2e/framework/k8s.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ package framework
1515
import (
1616
"bufio"
1717
"bytes"
18+
"cmp"
1819
"context"
1920
"encoding/json"
2021
"fmt"
@@ -33,6 +34,7 @@ import (
3334
corev1 "k8s.io/api/core/v1"
3435
"k8s.io/apimachinery/pkg/api/errors"
3536
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
37+
"k8s.io/apimachinery/pkg/types"
3638
"k8s.io/apimachinery/pkg/util/wait"
3739
applycorev1 "k8s.io/client-go/applyconfigurations/core/v1"
3840
applymetav1 "k8s.io/client-go/applyconfigurations/meta/v1"
@@ -108,8 +110,8 @@ func (f *Framework) ensureServiceWithTimeout(name, namespace string, desiredEndp
108110
return nil
109111
}
110112

111-
func (f *Framework) GetServiceEndpoints(name string) ([]string, error) {
112-
ep, err := f.clientset.CoreV1().Endpoints(_namespace).Get(f.Context, name, metav1.GetOptions{})
113+
func (f *Framework) GetServiceEndpoints(nn types.NamespacedName) ([]string, error) {
114+
ep, err := f.clientset.CoreV1().Endpoints(cmp.Or(nn.Namespace, _namespace)).Get(f.Context, nn.Name, metav1.GetOptions{})
113115
if err != nil {
114116
return nil, err
115117
}

0 commit comments

Comments
 (0)