diff --git a/api/adc/types.go b/api/adc/types.go
index 124094c5..472a12a1 100644
--- a/api/adc/types.go
+++ b/api/adc/types.go
@@ -448,18 +448,28 @@ func (n *UpstreamNodes) UnmarshalJSON(p []byte) error {
return nil
}
-// MarshalJSON implements the json.Marshaler interface for UpstreamNodes.
-// By default, Go serializes a nil slice as JSON null. However, for compatibility
-// with APISIX semantics, we want a nil UpstreamNodes to be encoded as an empty
-// array ([]) instead of null. Non-nil slices are marshaled as usual.
-//
-// See APISIX upstream nodes schema definition for details:
-// https://github.com/apache/apisix/blob/77dacda31277a31d6014b4970e36bae2a5c30907/apisix/schema_def.lua#L295-L338
-func (n UpstreamNodes) MarshalJSON() ([]byte, error) {
- if n == nil {
- return []byte("[]"), nil
+func (n Upstream) MarshalJSON() ([]byte, error) {
+ type Alias Upstream
+ // APISIX does not allow discovery_type and nodes to exist at the same time.
+ // https://github.com/apache/apisix/blob/01b4b49eb2ba642b337f7a1fbe1894a77942910b/apisix/schema_def.lua#L501-L504
+ if n.DiscoveryType != "" {
+ aux := struct {
+ Alias
+ Nodes UpstreamNodes `json:"nodes,omitempty" yaml:"nodes,omitempty"`
+ }{
+ Alias: (Alias)(n),
+ }
+ aux.Nodes = nil
+ return json.Marshal(&aux)
+ }
+
+ // By default Go serializes a nil slice as JSON null.
+ // For APISIX compatibility, nil UpstreamNodes should be encoded as [] instead.
+ // https://github.com/apache/apisix/blob/77dacda31277a31d6014b4970e36bae2a5c30907/apisix/schema_def.lua#L295-L338
+ if n.Nodes == nil {
+ n.Nodes = UpstreamNodes{}
}
- return json.Marshal([]UpstreamNode(n))
+ return json.Marshal((Alias)(n))
}
// ComposeRouteName uses namespace, name and rule name to compose
@@ -571,8 +581,7 @@ func NewDefaultUpstream() *Upstream {
"managed-by": "apisix-ingress-controller",
},
},
- Nodes: make(UpstreamNodes, 0),
- Type: Roundrobin,
+ Type: Roundrobin,
}
}
diff --git a/api/v2/apisixupstream_types.go b/api/v2/apisixupstream_types.go
index a1562618..73a925be 100644
--- a/api/v2/apisixupstream_types.go
+++ b/api/v2/apisixupstream_types.go
@@ -150,7 +150,6 @@ type ApisixUpstreamConfig struct {
UpstreamHost string `json:"upstreamHost,omitempty" yaml:"upstreamHost,omitempty"`
// Discovery configures service discovery for the upstream.
- // Deprecated: no longer supported in standalone mode.
// +kubebuilder:validation:Optional
Discovery *Discovery `json:"discovery,omitempty" yaml:"discovery,omitempty"`
}
diff --git a/config/crd/bases/apisix.apache.org_apisixupstreams.yaml b/config/crd/bases/apisix.apache.org_apisixupstreams.yaml
index 2dcb2b63..fdded278 100644
--- a/config/crd/bases/apisix.apache.org_apisixupstreams.yaml
+++ b/config/crd/bases/apisix.apache.org_apisixupstreams.yaml
@@ -42,9 +42,7 @@ spec:
description: ApisixUpstreamSpec defines the upstream configuration.
properties:
discovery:
- description: |-
- Discovery configures service discovery for the upstream.
- Deprecated: no longer supported in standalone mode.
+ description: Discovery configures service discovery for the upstream.
properties:
args:
additionalProperties:
@@ -337,9 +335,8 @@ spec:
them if they are set on the port level.
properties:
discovery:
- description: |-
- Discovery configures service discovery for the upstream.
- Deprecated: no longer supported in standalone mode.
+ description: Discovery configures service discovery for the
+ upstream.
properties:
args:
additionalProperties:
diff --git a/docs/en/latest/reference/api-reference.md b/docs/en/latest/reference/api-reference.md
index 06203d6f..7bfeb239 100644
--- a/docs/en/latest/reference/api-reference.md
+++ b/docs/en/latest/reference/api-reference.md
@@ -1325,7 +1325,7 @@ ApisixUpstreamConfig defines configuration for upstream services.
| `subsets` _[ApisixUpstreamSubset](#apisixupstreamsubset) array_ | Subsets defines labeled subsets of service endpoints, typically used for service versioning or canary deployments. |
| `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`:
• `pass`: preserve the original Host header
• `node`: use the upstream node’s host
• `rewrite`: set to a custom host via upstreamHost |
| `upstreamHost` _string_ | UpstreamHost sets a custom Host header when passHost is set to `rewrite`. |
-| `discovery` _[Discovery](#discovery)_ | Discovery configures service discovery for the upstream. Deprecated: no longer supported in standalone mode. |
+| `discovery` _[Discovery](#discovery)_ | Discovery configures service discovery for the upstream. |
_Appears in:_
@@ -1385,7 +1385,7 @@ definitions and custom configuration.
| `subsets` _[ApisixUpstreamSubset](#apisixupstreamsubset) array_ | Subsets defines labeled subsets of service endpoints, typically used for service versioning or canary deployments. |
| `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`:
• `pass`: preserve the original Host header
• `node`: use the upstream node’s host
• `rewrite`: set to a custom host via upstreamHost |
| `upstreamHost` _string_ | UpstreamHost sets a custom Host header when passHost is set to `rewrite`. |
-| `discovery` _[Discovery](#discovery)_ | Discovery configures service discovery for the upstream. Deprecated: no longer supported in standalone mode. |
+| `discovery` _[Discovery](#discovery)_ | Discovery configures service discovery for the upstream. |
| `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. |
@@ -1555,7 +1555,7 @@ them if they are set on the port level.
| `subsets` _[ApisixUpstreamSubset](#apisixupstreamsubset) array_ | Subsets defines labeled subsets of service endpoints, typically used for service versioning or canary deployments. |
| `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`:
• `pass`: preserve the original Host header
• `node`: use the upstream node’s host
• `rewrite`: set to a custom host via upstreamHost |
| `upstreamHost` _string_ | UpstreamHost sets a custom Host header when passHost is set to `rewrite`. |
-| `discovery` _[Discovery](#discovery)_ | Discovery configures service discovery for the upstream. Deprecated: no longer supported in standalone mode. |
+| `discovery` _[Discovery](#discovery)_ | Discovery configures service discovery for the upstream. |
| `port` _integer_ | Port is a Kubernetes Service port. |
diff --git a/docs/en/latest/upgrade-guide.md b/docs/en/latest/upgrade-guide.md
index a8b3a6d2..7d738edf 100644
--- a/docs/en/latest/upgrade-guide.md
+++ b/docs/en/latest/upgrade-guide.md
@@ -138,14 +138,6 @@ spec:
### API Changes
-#### `ApisixUpstream`
-
-Due to current limitations in the [ADC](https://github.com/api7/adc) component, the following fields are not yet supported:
-
-* `spec.discovery`: Service Discovery
-
-More details: [ADC Backend Differences](https://github.com/api7/adc/blob/2449ca81e3c61169f8c1e59efb4c1173a766bce2/libs/backend-apisix-standalone/README.md#differences-in-upstream)
-
#### `ApisixClusterConfig`
The `ApisixClusterConfig` CRD has been removed in 2.0.0. global rules and configurations should now be managed through the `ApisixGlobalRule` CRDs.
diff --git a/internal/adc/translator/apisixroute.go b/internal/adc/translator/apisixroute.go
index 87cbe3ee..505ba35f 100644
--- a/internal/adc/translator/apisixroute.go
+++ b/internal/adc/translator/apisixroute.go
@@ -271,7 +271,7 @@ func (t *Translator) buildUpstream(tctx *provider.TranslateContext, service *adc
}
// no valid upstream
- if len(upstreams) == 0 || len(upstreams[0].Nodes) == 0 {
+ if len(upstreams) == 0 {
return
}
diff --git a/internal/adc/translator/apisixupstream.go b/internal/adc/translator/apisixupstream.go
index 5a76025e..b56791dc 100644
--- a/internal/adc/translator/apisixupstream.go
+++ b/internal/adc/translator/apisixupstream.go
@@ -21,7 +21,9 @@ import (
"cmp"
"fmt"
+ "github.com/api7/gopkg/pkg/log"
"github.com/pkg/errors"
+ "go.uber.org/zap"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
@@ -40,6 +42,7 @@ func (t *Translator) translateApisixUpstream(tctx *provider.TranslateContext, au
translateApisixUpstreamRetriesAndTimeout,
translateApisixUpstreamPassHost,
translateUpstreamHealthCheck,
+ translateUpstreamDiscovery,
} {
if err = f(au, ups); err != nil {
return
@@ -54,6 +57,8 @@ func (t *Translator) translateApisixUpstream(tctx *provider.TranslateContext, au
}
}
+ log.Debugw("translated ApisixUpstream", zap.Any("upstream", ups),
+ zap.String("namespace", au.Namespace), zap.String("name", au.Name))
return
}
@@ -340,3 +345,14 @@ func translateUpstreamPassiveHealthCheck(config *apiv2.PassiveHealthCheck) *adc.
}
return &passive
}
+
+func translateUpstreamDiscovery(au *apiv2.ApisixUpstream, ups *adc.Upstream) error {
+ discovery := au.Spec.Discovery
+ if discovery == nil {
+ return nil
+ }
+ ups.ServiceName = discovery.ServiceName
+ ups.DiscoveryType = discovery.Type
+ ups.DiscoveryArgs = discovery.Args
+ return nil
+}
diff --git a/test/e2e/crds/v2/upstream.go b/test/e2e/crds/v2/upstream.go
index 7d7d9a61..62392344 100644
--- a/test/e2e/crds/v2/upstream.go
+++ b/test/e2e/crds/v2/upstream.go
@@ -135,4 +135,54 @@ spec:
}
})
})
+
+ Context("external service discovery", func() {
+ ar := `
+apiVersion: apisix.apache.org/v2
+kind: ApisixRoute
+metadata:
+ name: httpbin-route
+spec:
+ ingressClassName: %s
+ http:
+ - name: rule1
+ match:
+ hosts:
+ - httpbin.org
+ paths:
+ - /*
+ upstreams:
+ - name: httpbin-dns
+`
+
+ au := `
+apiVersion: apisix.apache.org/v2
+kind: ApisixUpstream
+metadata:
+ name: httpbin-dns
+spec:
+ ingressClassName: %s
+ discovery:
+ type: dns
+ serviceName: %s
+`
+
+ It("should be able to access through service discovery", func() {
+ if framework.ProviderType == framework.ProviderTypeAPI7EE {
+ Skip("api7ee does not support DNS service discovery")
+ }
+ svcName := fmt.Sprintf("httpbin-service-e2e-test.%s.svc.cluster.local", s.Namespace())
+ applier.MustApplyAPIv2(types.NamespacedName{Namespace: s.Namespace(), Name: "httpbin-dns"},
+ &apiv2.ApisixUpstream{}, fmt.Sprintf(au, s.Namespace(), svcName))
+ applier.MustApplyAPIv2(types.NamespacedName{Namespace: s.Namespace(), Name: "httpbin-route"},
+ &apiv2.ApisixRoute{}, fmt.Sprintf(ar, s.Namespace()))
+
+ s.RequestAssert(&scaffold.RequestAssert{
+ Method: "GET",
+ Path: "/ip",
+ Host: "httpbin.org",
+ Check: scaffold.WithExpectedStatus(200),
+ })
+ })
+ })
})
diff --git a/test/e2e/framework/manifests/apisix-standalone.yaml b/test/e2e/framework/manifests/apisix-standalone.yaml
index 8c0a91fd..4b7adfe9 100644
--- a/test/e2e/framework/manifests/apisix-standalone.yaml
+++ b/test/e2e/framework/manifests/apisix-standalone.yaml
@@ -42,6 +42,11 @@ data:
- 9100
udp: # UDP proxy port list
- 9200
+ discovery:
+ dns:
+ servers:
+ - "10.96.0.10:53" # use the real address of your dns server.
+ # currently we use KIND as the standard test environment, so here we can hard-code the default DNS address first.
---
apiVersion: apps/v1
kind: Deployment
diff --git a/test/e2e/framework/manifests/apisix.yaml b/test/e2e/framework/manifests/apisix.yaml
index 88b83637..ae8a1396 100644
--- a/test/e2e/framework/manifests/apisix.yaml
+++ b/test/e2e/framework/manifests/apisix.yaml
@@ -49,6 +49,11 @@ data:
- 9100
udp: # UDP proxy port list
- 9200
+ discovery:
+ dns:
+ servers:
+ - "10.96.0.10:53" # use the real address of your dns server.
+ # currently we use KIND as the standard test environment, so here we can hard-code the default DNS address first.
---
apiVersion: apps/v1
kind: Deployment
diff --git a/test/e2e/scaffold/httpbin.go b/test/e2e/scaffold/httpbin.go
index 884e5ac3..03a81007 100644
--- a/test/e2e/scaffold/httpbin.go
+++ b/test/e2e/scaffold/httpbin.go
@@ -28,6 +28,8 @@ import (
)
var (
+ HTTPBinServiceName = "httpbin-service-e2e-test"
+
_httpbinDeploymentTemplate = `
apiVersion: apps/v1
kind: Deployment