From e36608b379b292c777c301992a73c6d01e26be89 Mon Sep 17 00:00:00 2001 From: AlinsRan Date: Thu, 25 Sep 2025 15:31:57 +0800 Subject: [PATCH] feat(apisixupstream): support healthcheck (#2574) --- api/adc/types.go | 2 +- api/v2/apisixupstream_types.go | 23 ++- .../apisix.apache.org_apisixupstreams.yaml | 56 +++++-- docs/en/latest/reference/api-reference.md | 10 +- docs/en/latest/upgrade-guide.md | 1 - internal/adc/translator/apisixupstream.go | 88 +++++++++++ internal/adc/translator/translator.go | 1 - test/e2e/crds/v2/upstream.go | 138 ++++++++++++++++++ 8 files changed, 293 insertions(+), 26 deletions(-) create mode 100644 test/e2e/crds/v2/upstream.go diff --git a/api/adc/types.go b/api/adc/types.go index 7ff2d6bc5..124094c58 100644 --- a/api/adc/types.go +++ b/api/adc/types.go @@ -204,7 +204,7 @@ type UpstreamActiveHealthCheck struct { Host string `json:"host,omitempty" yaml:"host,omitempty"` Port int32 `json:"port,omitempty" yaml:"port,omitempty"` HTTPPath string `json:"http_path,omitempty" yaml:"http_path,omitempty"` - HTTPSVerifyCert bool `json:"https_verify_certificate,omitempty" yaml:"https_verify_certificate,omitempty"` + HTTPSVerifyCert bool `json:"https_verify_cert,omitempty" yaml:"https_verify_cert,omitempty"` HTTPRequestHeaders []string `json:"req_headers,omitempty" yaml:"req_headers,omitempty"` Healthy UpstreamActiveHealthCheckHealthy `json:"healthy,omitempty" yaml:"healthy,omitempty"` Unhealthy UpstreamActiveHealthCheckUnhealthy `json:"unhealthy,omitempty" yaml:"unhealthy,omitempty"` diff --git a/api/v2/apisixupstream_types.go b/api/v2/apisixupstream_types.go index 3c0f814cd..a1562618a 100644 --- a/api/v2/apisixupstream_types.go +++ b/api/v2/apisixupstream_types.go @@ -91,7 +91,10 @@ type ApisixUpstreamExternalNode struct { Weight *int `json:"weight,omitempty" yaml:"weight"` // Port specifies the port number on which the external node is accepting traffic. + // // +kubebuilder:validation:Optional + // +kubebuilder:validation:Minimum=1 + // +kubebuilder:validation:Maximum=65535 Port *int `json:"port,omitempty" yaml:"port"` } @@ -118,7 +121,6 @@ type ApisixUpstreamConfig struct { Timeout *UpstreamTimeout `json:"timeout,omitempty" yaml:"timeout,omitempty"` // HealthCheck defines the active and passive health check configuration for the upstream. - // Deprecated: no longer supported in standalone mode. // +kubebuilder:validation:Optional HealthCheck *HealthCheck `json:"healthCheck,omitempty" yaml:"healthCheck,omitempty"` @@ -160,6 +162,8 @@ type PortLevelSettings struct { ApisixUpstreamConfig `json:",inline" yaml:",inline"` // Port is a Kubernetes Service port. + // +kubebuilder:validation:Minimum=1 + // +kubebuilder:validation:Maximum=65535 Port int32 `json:"port" yaml:"port"` } @@ -221,9 +225,11 @@ type Discovery struct { // ActiveHealthCheck defines the active upstream health check configuration. type ActiveHealthCheck struct { + // Type is the health check type. Can be `http`, `https`, or `tcp`. + // // +kubebuilder:validation:Optional // +kubebuilder:validation:Enum=http;https;tcp; - // Type is the health check type. Can be `http`, `https`, or `tcp`. + // +kubebuilder:default=http Type string `json:"type,omitempty" yaml:"type,omitempty"` // Timeout sets health check timeout in seconds. Timeout time.Duration `json:"timeout,omitempty" yaml:"timeout,omitempty"` @@ -232,9 +238,10 @@ type ActiveHealthCheck struct { Concurrency int `json:"concurrency,omitempty" yaml:"concurrency,omitempty"` // Host sets the upstream host. Host string `json:"host,omitempty" yaml:"host,omitempty"` - // +kubebuilder:validation:Minimum=0 - // +kubebuilder:validation:Maximum=65535 // Port sets the upstream port. + // + // +kubebuilder:validation:Minimum=1 + // +kubebuilder:validation:Maximum=65535 Port int32 `json:"port,omitempty" yaml:"port,omitempty"` // HTTPPath sets the HTTP probe request path. HTTPPath string `json:"httpPath,omitempty" yaml:"httpPath,omitempty"` @@ -254,6 +261,10 @@ type ActiveHealthCheck struct { type PassiveHealthCheck struct { // Type specifies the type of passive health check. // Can be `http`, `https`, or `tcp`. + // + // +kubebuilder:validation:Optional + // +kubebuilder:validation:Enum=http;https;tcp; + // +kubebuilder:default=http Type string `json:"type,omitempty" yaml:"type,omitempty"` // Healthy defines the conditions under which an upstream node is considered healthy. Healthy *PassiveHealthCheckHealthy `json:"healthy,omitempty" yaml:"healthy,omitempty"` @@ -304,6 +315,10 @@ type PassiveHealthCheckUnhealthy struct { // TCPFailures define the number of TCP failures to define an unhealthy target. TCPFailures int `json:"tcpFailures,omitempty" yaml:"tcpFailures,omitempty"` // Timeout sets health check timeout in seconds. + // https://github.com/apache/apisix/blob/0151d9e35bba63d7c316187272d88e19db0be634/apisix/schema_def.lua#L196 + // + // +kubebuilder:validation:Minimum=1 + // +kubebuilder:validation:Maximum=254 Timeouts int `json:"timeout,omitempty" yaml:"timeout,omitempty"` } diff --git a/config/crd/bases/apisix.apache.org_apisixupstreams.yaml b/config/crd/bases/apisix.apache.org_apisixupstreams.yaml index 346795191..2dcb2b632 100644 --- a/config/crd/bases/apisix.apache.org_apisixupstreams.yaml +++ b/config/crd/bases/apisix.apache.org_apisixupstreams.yaml @@ -81,6 +81,8 @@ spec: port: description: Port specifies the port number on which the external node is accepting traffic. + maximum: 65535 + minimum: 1 type: integer type: description: Type indicates the kind of external node. Can be @@ -95,9 +97,8 @@ spec: minItems: 1 type: array healthCheck: - description: |- - HealthCheck defines the active and passive health check configuration for the upstream. - Deprecated: no longer supported in standalone mode. + description: HealthCheck defines the active and passive health check + configuration for the upstream. properties: active: description: Active health checks proactively send requests to @@ -140,7 +141,7 @@ spec: description: Port sets the upstream port. format: int32 maximum: 65535 - minimum: 0 + minimum: 1 type: integer requestHeaders: description: RequestHeaders sets the request headers. @@ -155,6 +156,7 @@ spec: format: int64 type: integer type: + default: http description: Type is the health check type. Can be `http`, `https`, or `tcp`. enum: @@ -190,7 +192,11 @@ spec: minimum: 0 type: integer timeout: - description: Timeout sets health check timeout in seconds. + description: |- + Timeout sets health check timeout in seconds. + https://github.com/apache/apisix/blob/0151d9e35bba63d7c316187272d88e19db0be634/apisix/schema_def.lua#L196 + maximum: 254 + minimum: 1 type: integer type: object type: object @@ -217,9 +223,14 @@ spec: type: integer type: object type: + default: http description: |- Type specifies the type of passive health check. Can be `http`, `https`, or `tcp`. + enum: + - http + - https + - tcp type: string unhealthy: description: Unhealthy defines the conditions under which @@ -245,7 +256,11 @@ spec: minimum: 0 type: integer timeout: - description: Timeout sets health check timeout in seconds. + description: |- + Timeout sets health check timeout in seconds. + https://github.com/apache/apisix/blob/0151d9e35bba63d7c316187272d88e19db0be634/apisix/schema_def.lua#L196 + maximum: 254 + minimum: 1 type: integer type: object type: object @@ -344,9 +359,8 @@ spec: - type type: object healthCheck: - description: |- - HealthCheck defines the active and passive health check configuration for the upstream. - Deprecated: no longer supported in standalone mode. + description: HealthCheck defines the active and passive health + check configuration for the upstream. properties: active: description: Active health checks proactively send requests @@ -389,7 +403,7 @@ spec: description: Port sets the upstream port. format: int32 maximum: 65535 - minimum: 0 + minimum: 1 type: integer requestHeaders: description: RequestHeaders sets the request headers. @@ -404,6 +418,7 @@ spec: format: int64 type: integer type: + default: http description: Type is the health check type. Can be `http`, `https`, or `tcp`. enum: @@ -439,8 +454,11 @@ spec: minimum: 0 type: integer timeout: - description: Timeout sets health check timeout in - seconds. + description: |- + Timeout sets health check timeout in seconds. + https://github.com/apache/apisix/blob/0151d9e35bba63d7c316187272d88e19db0be634/apisix/schema_def.lua#L196 + maximum: 254 + minimum: 1 type: integer type: object type: object @@ -467,9 +485,14 @@ spec: type: integer type: object type: + default: http description: |- Type specifies the type of passive health check. Can be `http`, `https`, or `tcp`. + enum: + - http + - https + - tcp type: string unhealthy: description: Unhealthy defines the conditions under @@ -495,8 +518,11 @@ spec: minimum: 0 type: integer timeout: - description: Timeout sets health check timeout in - seconds. + description: |- + Timeout sets health check timeout in seconds. + https://github.com/apache/apisix/blob/0151d9e35bba63d7c316187272d88e19db0be634/apisix/schema_def.lua#L196 + maximum: 254 + minimum: 1 type: integer type: object type: object @@ -559,6 +585,8 @@ spec: port: description: Port is a Kubernetes Service port. format: int32 + maximum: 65535 + minimum: 1 type: integer retries: description: |- diff --git a/docs/en/latest/reference/api-reference.md b/docs/en/latest/reference/api-reference.md index 9dbcdd19a..06203d6ff 100644 --- a/docs/en/latest/reference/api-reference.md +++ b/docs/en/latest/reference/api-reference.md @@ -661,7 +661,7 @@ UpstreamActiveHealthCheckHealthy defines the conditions used to actively determi | `httpCodes` _integer array_ | HTTPCodes define a list of HTTP status codes that are considered unhealthy. | | `httpFailures` _integer_ | HTTPFailures define the number of HTTP failures to define an unhealthy target. | | `tcpFailures` _integer_ | TCPFailures define the number of TCP failures to define an unhealthy target. | -| `timeout` _integer_ | Timeout sets health check timeout in seconds. | +| `timeout` _integer_ | Timeout sets health check timeout in seconds. https://github.com/apache/apisix/blob/0151d9e35bba63d7c316187272d88e19db0be634/apisix/schema_def.lua#L196 | | `interval` _[Duration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#duration-v1-meta)_ | Interval defines the time interval for checking targets, in seconds. | @@ -1320,7 +1320,7 @@ ApisixUpstreamConfig defines configuration for upstream services. | `scheme` _string_ | Scheme is the protocol used to communicate with the upstream. Default is `http`. Can be `http`, `https`, `grpc`, or `grpcs`. | | `retries` _integer_ | Retries defines the number of retry attempts APISIX should make when a failure occurs. Failures include timeouts, network errors, or 5xx status codes. | | `timeout` _[UpstreamTimeout](#upstreamtimeout)_ | Timeout specifies the connection, send, and read timeouts for upstream requests. | -| `healthCheck` _[HealthCheck](#healthcheck)_ | HealthCheck defines the active and passive health check configuration for the upstream. Deprecated: no longer supported in standalone mode. | +| `healthCheck` _[HealthCheck](#healthcheck)_ | HealthCheck defines the active and passive health check configuration for the upstream. | | `tlsSecret` _[ApisixSecret](#apisixsecret)_ | TLSSecret references a Kubernetes Secret that contains the client certificate and key for mutual TLS when connecting to the upstream. | | `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 | @@ -1380,7 +1380,7 @@ definitions and custom configuration. | `scheme` _string_ | Scheme is the protocol used to communicate with the upstream. Default is `http`. Can be `http`, `https`, `grpc`, or `grpcs`. | | `retries` _integer_ | Retries defines the number of retry attempts APISIX should make when a failure occurs. Failures include timeouts, network errors, or 5xx status codes. | | `timeout` _[UpstreamTimeout](#upstreamtimeout)_ | Timeout specifies the connection, send, and read timeouts for upstream requests. | -| `healthCheck` _[HealthCheck](#healthcheck)_ | HealthCheck defines the active and passive health check configuration for the upstream. Deprecated: no longer supported in standalone mode. | +| `healthCheck` _[HealthCheck](#healthcheck)_ | HealthCheck defines the active and passive health check configuration for the upstream. | | `tlsSecret` _[ApisixSecret](#apisixsecret)_ | TLSSecret references a Kubernetes Secret that contains the client certificate and key for mutual TLS when connecting to the upstream. | | `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 | @@ -1528,7 +1528,7 @@ UpstreamPassiveHealthCheckUnhealthy defines the conditions used to passively det | `httpCodes` _integer array_ | HTTPCodes define a list of HTTP status codes that are considered unhealthy. | | `httpFailures` _integer_ | HTTPFailures define the number of HTTP failures to define an unhealthy target. | | `tcpFailures` _integer_ | TCPFailures define the number of TCP failures to define an unhealthy target. | -| `timeout` _integer_ | Timeout sets health check timeout in seconds. | +| `timeout` _integer_ | Timeout sets health check timeout in seconds. https://github.com/apache/apisix/blob/0151d9e35bba63d7c316187272d88e19db0be634/apisix/schema_def.lua#L196 | _Appears in:_ @@ -1550,7 +1550,7 @@ them if they are set on the port level. | `scheme` _string_ | Scheme is the protocol used to communicate with the upstream. Default is `http`. Can be `http`, `https`, `grpc`, or `grpcs`. | | `retries` _integer_ | Retries defines the number of retry attempts APISIX should make when a failure occurs. Failures include timeouts, network errors, or 5xx status codes. | | `timeout` _[UpstreamTimeout](#upstreamtimeout)_ | Timeout specifies the connection, send, and read timeouts for upstream requests. | -| `healthCheck` _[HealthCheck](#healthcheck)_ | HealthCheck defines the active and passive health check configuration for the upstream. Deprecated: no longer supported in standalone mode. | +| `healthCheck` _[HealthCheck](#healthcheck)_ | HealthCheck defines the active and passive health check configuration for the upstream. | | `tlsSecret` _[ApisixSecret](#apisixsecret)_ | TLSSecret references a Kubernetes Secret that contains the client certificate and key for mutual TLS when connecting to the upstream. | | `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 | diff --git a/docs/en/latest/upgrade-guide.md b/docs/en/latest/upgrade-guide.md index 11216386b..a8b3a6d28 100644 --- a/docs/en/latest/upgrade-guide.md +++ b/docs/en/latest/upgrade-guide.md @@ -143,7 +143,6 @@ spec: Due to current limitations in the [ADC](https://github.com/api7/adc) component, the following fields are not yet supported: * `spec.discovery`: Service Discovery -* `spec.healthCheck`: Health Checking More details: [ADC Backend Differences](https://github.com/api7/adc/blob/2449ca81e3c61169f8c1e59efb4c1173a766bce2/libs/backend-apisix-standalone/README.md#differences-in-upstream) diff --git a/internal/adc/translator/apisixupstream.go b/internal/adc/translator/apisixupstream.go index f4faf2980..5a76025ec 100644 --- a/internal/adc/translator/apisixupstream.go +++ b/internal/adc/translator/apisixupstream.go @@ -39,6 +39,7 @@ func (t *Translator) translateApisixUpstream(tctx *provider.TranslateContext, au translateApisixUpstreamLoadBalancer, translateApisixUpstreamRetriesAndTimeout, translateApisixUpstreamPassHost, + translateUpstreamHealthCheck, } { if err = f(au, ups); err != nil { return @@ -252,3 +253,90 @@ func translateApisixUpstreamExternalNodesService(tctx *provider.TranslateContext return nil } + +func translateUpstreamHealthCheck(au *apiv2.ApisixUpstream, ups *adc.Upstream) error { + if au == nil { + return nil + } + healcheck := au.Spec.HealthCheck + if healcheck == nil || (healcheck.Passive == nil && healcheck.Active == nil) { + return nil + } + var hc adc.UpstreamHealthCheck + if healcheck.Passive != nil { + hc.Passive = translateUpstreamPassiveHealthCheck(healcheck.Passive) + } + + if healcheck.Active != nil { + active, err := translateUpstreamActiveHealthCheck(healcheck.Active) + if err != nil { + return err + } + hc.Active = active + } + + ups.Checks = &hc + return nil +} + +func translateUpstreamActiveHealthCheck(config *apiv2.ActiveHealthCheck) (*adc.UpstreamActiveHealthCheck, error) { + var active adc.UpstreamActiveHealthCheck + if config.Type == "" { + config.Type = apiv2.HealthCheckHTTP + } + + active.Timeout = int(config.Timeout.Seconds()) + active.Port = config.Port + active.Concurrency = config.Concurrency + active.Host = config.Host + active.HTTPPath = config.HTTPPath + active.HTTPRequestHeaders = config.RequestHeaders + + if config.StrictTLS == nil || *config.StrictTLS { + active.HTTPSVerifyCert = true + } + + if config.Healthy != nil { + active.Healthy.Successes = config.Healthy.Successes + active.Healthy.HTTPStatuses = config.Healthy.HTTPCodes + + if config.Healthy.Interval.Duration < apiv2.ActiveHealthCheckMinInterval { + return nil, fmt.Errorf(`"healthCheck.active.healthy.interval" has invalid value`) + } + active.Healthy.Interval = int(config.Healthy.Interval.Seconds()) + } + + if config.Unhealthy != nil { + active.Unhealthy.HTTPFailures = config.Unhealthy.HTTPFailures + active.Unhealthy.TCPFailures = config.Unhealthy.TCPFailures + active.Unhealthy.Timeouts = config.Unhealthy.Timeouts + active.Unhealthy.HTTPStatuses = config.Unhealthy.HTTPCodes + + if config.Unhealthy.Interval.Duration < apiv2.ActiveHealthCheckMinInterval { + return nil, fmt.Errorf(`"healthCheck.active.unhealthy.interval" has invalid value`) + } + active.Unhealthy.Interval = int(config.Unhealthy.Interval.Seconds()) + } + + return &active, nil +} + +func translateUpstreamPassiveHealthCheck(config *apiv2.PassiveHealthCheck) *adc.UpstreamPassiveHealthCheck { + var passive adc.UpstreamPassiveHealthCheck + if config.Type == "" { + config.Type = apiv2.HealthCheckHTTP + } + + if config.Healthy != nil { + passive.Healthy.Successes = config.Healthy.Successes + passive.Healthy.HTTPStatuses = config.Healthy.HTTPCodes + } + + if config.Unhealthy != nil { + passive.Unhealthy.HTTPFailures = config.Unhealthy.HTTPFailures + passive.Unhealthy.TCPFailures = config.Unhealthy.TCPFailures + passive.Unhealthy.Timeouts = config.Unhealthy.Timeouts + passive.Unhealthy.HTTPStatuses = config.Unhealthy.HTTPCodes + } + return &passive +} diff --git a/internal/adc/translator/translator.go b/internal/adc/translator/translator.go index 0d50661a0..4c9bf0d84 100644 --- a/internal/adc/translator/translator.go +++ b/internal/adc/translator/translator.go @@ -34,7 +34,6 @@ func NewTranslator(log logr.Logger) *Translator { } type TranslateResult struct { - Routes []*adctypes.Route Services []*adctypes.Service SSL []*adctypes.SSL GlobalRules adctypes.GlobalRule diff --git a/test/e2e/crds/v2/upstream.go b/test/e2e/crds/v2/upstream.go new file mode 100644 index 000000000..7d7d9a614 --- /dev/null +++ b/test/e2e/crds/v2/upstream.go @@ -0,0 +1,138 @@ +// 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 v2 + +import ( + "context" + "fmt" + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "k8s.io/apimachinery/pkg/types" + + apiv2 "github.com/apache/apisix-ingress-controller/api/v2" + "github.com/apache/apisix-ingress-controller/test/e2e/framework" + "github.com/apache/apisix-ingress-controller/test/e2e/scaffold" +) + +var _ = Describe("Test ApisixUpstream", Label("apisix.apache.org", "v2", "apisixupstream"), func() { + var ( + s = scaffold.NewDefaultScaffold() + applier = framework.NewApplier(s.GinkgoT, s.K8sClient, s.CreateResourceFromString) + ) + BeforeEach(func() { + By("create GatewayProxy") + err := s.CreateResourceFromString(s.GetGatewayProxySpec()) + Expect(err).NotTo(HaveOccurred(), "creating GatewayProxy") + time.Sleep(5 * time.Second) + + By("create IngressClass") + err = s.CreateResourceFromStringWithNamespace(s.GetIngressClassYaml(), "") + Expect(err).NotTo(HaveOccurred(), "creating IngressClass") + time.Sleep(5 * time.Second) + }) + + Context("Health Check", func() { + It("active and passive", func() { + auWithHealthcheck := ` +apiVersion: apisix.apache.org/v2 +kind: ApisixUpstream +metadata: + name: active +spec: + ingressClassName: %s + externalNodes: + - type: Domain + name: httpbin-service-e2e-test + - type: Domain + name: invalid.httpbin.host + - type: Domain + name: invalid1.httpbin.host + retries: 1 + healthCheck: + active: + type: http + httpPath: /ip + healthy: + httpCodes: [200] + interval: 1s + unhealthy: + httpFailures: 2 + interval: 1s + passive: + healthy: + httpCodes: [200] + unhealthy: + httpCodes: [502] +` + applier.MustApplyAPIv2(types.NamespacedName{Namespace: s.Namespace(), Name: "active"}, + &apiv2.ApisixUpstream{}, fmt.Sprintf(auWithHealthcheck, s.Namespace())) + + 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: active +` + applier.MustApplyAPIv2(types.NamespacedName{Namespace: s.Namespace(), Name: "httpbin-route"}, + &apiv2.ApisixRoute{}, fmt.Sprintf(ar, s.Namespace())) + + By("triggering the health check") + s.RequestAssert(&scaffold.RequestAssert{ + Method: "GET", + Path: "/ip", + Host: "httpbin.org", + }) + time.Sleep(2 * time.Second) + + ups, err := s.Deployer.DefaultDataplaneResource().Upstream().List(context.Background()) + Expect(err).ToNot(HaveOccurred(), "listing upstreams") + Expect(ups).To(HaveLen(1), "the number of upstreams") + Expect(ups[0].Nodes).To(HaveLen(3), "the number of upstream nodes") + Expect(ups[0].Checks).ToNot(BeNil(), "the healthcheck configuration") + Expect(ups[0].Checks.Active).ToNot(BeNil(), "the active healthcheck configuration") + Expect(ups[0].Checks.Active.Healthy).ToNot(BeNil(), "the active healthy configuration") + Expect(ups[0].Checks.Active.Unhealthy).ToNot(BeNil(), "the active unhealthy configuration") + Expect(ups[0].Checks.Active.Healthy.Interval).To(Equal(1), "the healthy interval") + Expect(ups[0].Checks.Active.Healthy.HTTPStatuses).To(Equal([]int{200}), "the healthy http status") + Expect(ups[0].Checks.Active.Unhealthy.Interval).To(Equal(1), "the unhealthy interval") + Expect(ups[0].Checks.Active.Unhealthy.HTTPFailures).To(Equal(2), "the unhealthy http failures") + Expect(ups[0].Checks.Passive).ToNot(BeNil(), "the passive healthcheck configuration") + Expect(ups[0].Checks.Passive.Healthy).ToNot(BeNil(), "the passive healthy configuration") + Expect(ups[0].Checks.Passive.Unhealthy).ToNot(BeNil(), "the passive unhealthy configuration") + Expect(ups[0].Checks.Passive.Healthy.HTTPStatuses).To(Equal([]int{200}), "the passive healthy http status") + Expect(ups[0].Checks.Passive.Unhealthy.HTTPStatuses).To(Equal([]int{502}), "the passive unhealthy http status") + + for range 100 { + s.NewAPISIXClient().GET("/ip").WithHost("httpbin.org").Expect().Status(200) + } + }) + }) +})