Skip to content

Commit d22aff0

Browse files
committed
feat(apisixupstream): support discovery (#2577)
1 parent 3019898 commit d22aff0

File tree

11 files changed

+104
-32
lines changed

11 files changed

+104
-32
lines changed

api/adc/types.go

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -448,18 +448,28 @@ func (n *UpstreamNodes) UnmarshalJSON(p []byte) error {
448448
return nil
449449
}
450450

451-
// MarshalJSON implements the json.Marshaler interface for UpstreamNodes.
452-
// By default, Go serializes a nil slice as JSON null. However, for compatibility
453-
// with APISIX semantics, we want a nil UpstreamNodes to be encoded as an empty
454-
// array ([]) instead of null. Non-nil slices are marshaled as usual.
455-
//
456-
// See APISIX upstream nodes schema definition for details:
457-
// https://github.com/apache/apisix/blob/77dacda31277a31d6014b4970e36bae2a5c30907/apisix/schema_def.lua#L295-L338
458-
func (n UpstreamNodes) MarshalJSON() ([]byte, error) {
459-
if n == nil {
460-
return []byte("[]"), nil
451+
func (n Upstream) MarshalJSON() ([]byte, error) {
452+
type Alias Upstream
453+
// APISIX does not allow discovery_type and nodes to exist at the same time.
454+
// https://github.com/apache/apisix/blob/01b4b49eb2ba642b337f7a1fbe1894a77942910b/apisix/schema_def.lua#L501-L504
455+
if n.DiscoveryType != "" {
456+
aux := struct {
457+
Alias
458+
Nodes UpstreamNodes `json:"nodes,omitempty" yaml:"nodes,omitempty"`
459+
}{
460+
Alias: (Alias)(n),
461+
}
462+
aux.Nodes = nil
463+
return json.Marshal(&aux)
464+
}
465+
466+
// By default Go serializes a nil slice as JSON null.
467+
// For APISIX compatibility, nil UpstreamNodes should be encoded as [] instead.
468+
// https://github.com/apache/apisix/blob/77dacda31277a31d6014b4970e36bae2a5c30907/apisix/schema_def.lua#L295-L338
469+
if n.Nodes == nil {
470+
n.Nodes = UpstreamNodes{}
461471
}
462-
return json.Marshal([]UpstreamNode(n))
472+
return json.Marshal((Alias)(n))
463473
}
464474

465475
// ComposeRouteName uses namespace, name and rule name to compose
@@ -571,8 +581,7 @@ func NewDefaultUpstream() *Upstream {
571581
"managed-by": "apisix-ingress-controller",
572582
},
573583
},
574-
Nodes: make(UpstreamNodes, 0),
575-
Type: Roundrobin,
584+
Type: Roundrobin,
576585
}
577586
}
578587

api/v2/apisixupstream_types.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,6 @@ type ApisixUpstreamConfig struct {
150150
UpstreamHost string `json:"upstreamHost,omitempty" yaml:"upstreamHost,omitempty"`
151151

152152
// Discovery configures service discovery for the upstream.
153-
// Deprecated: no longer supported in standalone mode.
154153
// +kubebuilder:validation:Optional
155154
Discovery *Discovery `json:"discovery,omitempty" yaml:"discovery,omitempty"`
156155
}

config/crd/bases/apisix.apache.org_apisixupstreams.yaml

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,7 @@ spec:
4242
description: ApisixUpstreamSpec defines the upstream configuration.
4343
properties:
4444
discovery:
45-
description: |-
46-
Discovery configures service discovery for the upstream.
47-
Deprecated: no longer supported in standalone mode.
45+
description: Discovery configures service discovery for the upstream.
4846
properties:
4947
args:
5048
additionalProperties:
@@ -337,9 +335,8 @@ spec:
337335
them if they are set on the port level.
338336
properties:
339337
discovery:
340-
description: |-
341-
Discovery configures service discovery for the upstream.
342-
Deprecated: no longer supported in standalone mode.
338+
description: Discovery configures service discovery for the
339+
upstream.
343340
properties:
344341
args:
345342
additionalProperties:

docs/en/latest/reference/api-reference.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1325,7 +1325,7 @@ ApisixUpstreamConfig defines configuration for upstream services.
13251325
| `subsets` _[ApisixUpstreamSubset](#apisixupstreamsubset) array_ | Subsets defines labeled subsets of service endpoints, typically used for service versioning or canary deployments. |
13261326
| `passHost` _string_ | PassHost configures how the host header should be determined when a request is forwarded to the upstream. Default is `pass`. Can be `pass`, `node` or `rewrite`:<br /> • `pass`: preserve the original Host header<br /> • `node`: use the upstream node’s host<br /> • `rewrite`: set to a custom host via upstreamHost |
13271327
| `upstreamHost` _string_ | UpstreamHost sets a custom Host header when passHost is set to `rewrite`. |
1328-
| `discovery` _[Discovery](#discovery)_ | Discovery configures service discovery for the upstream. Deprecated: no longer supported in standalone mode. |
1328+
| `discovery` _[Discovery](#discovery)_ | Discovery configures service discovery for the upstream. |
13291329

13301330

13311331
_Appears in:_
@@ -1385,7 +1385,7 @@ definitions and custom configuration.
13851385
| `subsets` _[ApisixUpstreamSubset](#apisixupstreamsubset) array_ | Subsets defines labeled subsets of service endpoints, typically used for service versioning or canary deployments. |
13861386
| `passHost` _string_ | PassHost configures how the host header should be determined when a request is forwarded to the upstream. Default is `pass`. Can be `pass`, `node` or `rewrite`:<br /> • `pass`: preserve the original Host header<br /> • `node`: use the upstream node’s host<br /> • `rewrite`: set to a custom host via upstreamHost |
13871387
| `upstreamHost` _string_ | UpstreamHost sets a custom Host header when passHost is set to `rewrite`. |
1388-
| `discovery` _[Discovery](#discovery)_ | Discovery configures service discovery for the upstream. Deprecated: no longer supported in standalone mode. |
1388+
| `discovery` _[Discovery](#discovery)_ | Discovery configures service discovery for the upstream. |
13891389
| `portLevelSettings` _[PortLevelSettings](#portlevelsettings) array_ | PortLevelSettings allows fine-grained upstream configuration for specific ports, useful when a backend service exposes multiple ports with different behaviors or protocols. |
13901390

13911391

@@ -1555,7 +1555,7 @@ them if they are set on the port level.
15551555
| `subsets` _[ApisixUpstreamSubset](#apisixupstreamsubset) array_ | Subsets defines labeled subsets of service endpoints, typically used for service versioning or canary deployments. |
15561556
| `passHost` _string_ | PassHost configures how the host header should be determined when a request is forwarded to the upstream. Default is `pass`. Can be `pass`, `node` or `rewrite`:<br /> • `pass`: preserve the original Host header<br /> • `node`: use the upstream node’s host<br /> • `rewrite`: set to a custom host via upstreamHost |
15571557
| `upstreamHost` _string_ | UpstreamHost sets a custom Host header when passHost is set to `rewrite`. |
1558-
| `discovery` _[Discovery](#discovery)_ | Discovery configures service discovery for the upstream. Deprecated: no longer supported in standalone mode. |
1558+
| `discovery` _[Discovery](#discovery)_ | Discovery configures service discovery for the upstream. |
15591559
| `port` _integer_ | Port is a Kubernetes Service port. |
15601560

15611561

docs/en/latest/upgrade-guide.md

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -138,14 +138,6 @@ spec:
138138

139139
### API Changes
140140

141-
#### `ApisixUpstream`
142-
143-
Due to current limitations in the [ADC](https://github.com/api7/adc) component, the following fields are not yet supported:
144-
145-
* `spec.discovery`: Service Discovery
146-
147-
More details: [ADC Backend Differences](https://github.com/api7/adc/blob/2449ca81e3c61169f8c1e59efb4c1173a766bce2/libs/backend-apisix-standalone/README.md#differences-in-upstream)
148-
149141
#### `ApisixClusterConfig`
150142

151143
The `ApisixClusterConfig` CRD has been removed in 2.0.0. global rules and configurations should now be managed through the `ApisixGlobalRule` CRDs.

internal/adc/translator/apisixroute.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ func (t *Translator) buildUpstream(tctx *provider.TranslateContext, service *adc
271271
}
272272

273273
// no valid upstream
274-
if len(upstreams) == 0 || len(upstreams[0].Nodes) == 0 {
274+
if len(upstreams) == 0 {
275275
return
276276
}
277277

internal/adc/translator/apisixupstream.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ import (
2121
"cmp"
2222
"fmt"
2323

24+
"github.com/api7/gopkg/pkg/log"
2425
"github.com/pkg/errors"
26+
"go.uber.org/zap"
2527
corev1 "k8s.io/api/core/v1"
2628
"k8s.io/apimachinery/pkg/types"
2729

@@ -40,6 +42,7 @@ func (t *Translator) translateApisixUpstream(tctx *provider.TranslateContext, au
4042
translateApisixUpstreamRetriesAndTimeout,
4143
translateApisixUpstreamPassHost,
4244
translateUpstreamHealthCheck,
45+
translateUpstreamDiscovery,
4346
} {
4447
if err = f(au, ups); err != nil {
4548
return
@@ -54,6 +57,8 @@ func (t *Translator) translateApisixUpstream(tctx *provider.TranslateContext, au
5457
}
5558
}
5659

60+
log.Debugw("translated ApisixUpstream", zap.Any("upstream", ups),
61+
zap.String("namespace", au.Namespace), zap.String("name", au.Name))
5762
return
5863
}
5964

@@ -340,3 +345,14 @@ func translateUpstreamPassiveHealthCheck(config *apiv2.PassiveHealthCheck) *adc.
340345
}
341346
return &passive
342347
}
348+
349+
func translateUpstreamDiscovery(au *apiv2.ApisixUpstream, ups *adc.Upstream) error {
350+
discovery := au.Spec.Discovery
351+
if discovery == nil {
352+
return nil
353+
}
354+
ups.ServiceName = discovery.ServiceName
355+
ups.DiscoveryType = discovery.Type
356+
ups.DiscoveryArgs = discovery.Args
357+
return nil
358+
}

test/e2e/crds/v2/upstream.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,4 +135,51 @@ spec:
135135
}
136136
})
137137
})
138+
139+
Context("external service discovery", func() {
140+
ar := `
141+
apiVersion: apisix.apache.org/v2
142+
kind: ApisixRoute
143+
metadata:
144+
name: httpbin-route
145+
spec:
146+
ingressClassName: %s
147+
http:
148+
- name: rule1
149+
match:
150+
hosts:
151+
- httpbin.org
152+
paths:
153+
- /*
154+
upstreams:
155+
- name: httpbin-dns
156+
`
157+
158+
au := `
159+
apiVersion: apisix.apache.org/v2
160+
kind: ApisixUpstream
161+
metadata:
162+
name: httpbin-dns
163+
spec:
164+
ingressClassName: %s
165+
discovery:
166+
type: dns
167+
serviceName: %s
168+
`
169+
170+
It("should be able to access through service discovery", func() {
171+
svcName := fmt.Sprintf("httpbin-service-e2e-test.%s.svc.cluster.local", s.Namespace())
172+
applier.MustApplyAPIv2(types.NamespacedName{Namespace: s.Namespace(), Name: "httpbin-dns"},
173+
&apiv2.ApisixUpstream{}, fmt.Sprintf(au, s.Namespace(), svcName))
174+
applier.MustApplyAPIv2(types.NamespacedName{Namespace: s.Namespace(), Name: "httpbin-route"},
175+
&apiv2.ApisixRoute{}, fmt.Sprintf(ar, s.Namespace()))
176+
177+
s.RequestAssert(&scaffold.RequestAssert{
178+
Method: "GET",
179+
Path: "/ip",
180+
Host: "httpbin.org",
181+
Check: scaffold.WithExpectedStatus(200),
182+
})
183+
})
184+
})
138185
})

test/e2e/framework/manifests/apisix-standalone.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ data:
4242
- 9100
4343
udp: # UDP proxy port list
4444
- 9200
45+
discovery:
46+
dns:
47+
servers:
48+
- "10.96.0.10:53" # use the real address of your dns server.
49+
# currently we use KIND as the standard test environment, so here we can hard-code the default DNS address first.
4550
---
4651
apiVersion: apps/v1
4752
kind: Deployment

test/e2e/framework/manifests/apisix.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ data:
4949
- 9100
5050
udp: # UDP proxy port list
5151
- 9200
52+
discovery:
53+
dns:
54+
servers:
55+
- "10.96.0.10:53" # use the real address of your dns server.
56+
# currently we use KIND as the standard test environment, so here we can hard-code the default DNS address first.
5257
---
5358
apiVersion: apps/v1
5459
kind: Deployment

0 commit comments

Comments
 (0)