feat: support deployment of multiple data plane modes (#2647)
diff --git a/api/adc/types.go b/api/adc/types.go
index b18e6fd..e5966ac 100644
--- a/api/adc/types.go
+++ b/api/adc/types.go
@@ -779,6 +779,7 @@
ServerAddrs []string
Token string
TlsVerify bool
+ BackendType string
}
// MarshalJSON implements custom JSON marshaling for adcConfig
diff --git a/api/v1alpha1/gatewayproxy_types.go b/api/v1alpha1/gatewayproxy_types.go
index 620236c..680fa8a 100644
--- a/api/v1alpha1/gatewayproxy_types.go
+++ b/api/v1alpha1/gatewayproxy_types.go
@@ -119,7 +119,13 @@
// ControlPlaneProvider defines configuration for control plane provider.
// +kubebuilder:validation:XValidation:rule="has(self.endpoints) != has(self.service)"
+// +kubebuilder:validation:XValidation:rule="oldSelf == null || (!has(self.mode) && !has(oldSelf.mode)) || self.mode == oldSelf.mode",message="mode is immutable"
type ControlPlaneProvider struct {
+ // Mode specifies the mode of control plane provider.
+ // Can be `apisix` or `apisix-standalone`.
+ //
+ // +kubebuilder:validation:Optional
+ Mode string `json:"mode,omitempty"`
// Endpoints specifies the list of control plane endpoints.
// +kubebuilder:validation:Optional
// +kubebuilder:validation:MinItems=1
diff --git a/config/crd/bases/apisix.apache.org_gatewayproxies.yaml b/config/crd/bases/apisix.apache.org_gatewayproxies.yaml
index 313d030..23a7ed5 100644
--- a/config/crd/bases/apisix.apache.org_gatewayproxies.yaml
+++ b/config/crd/bases/apisix.apache.org_gatewayproxies.yaml
@@ -127,6 +127,11 @@
type: string
minItems: 1
type: array
+ mode:
+ description: |-
+ Mode specifies the mode of control plane provider.
+ Can be `apisix` or `apisix-standalone`.
+ type: string
service:
properties:
name:
@@ -150,6 +155,9 @@
type: object
x-kubernetes-validations:
- rule: has(self.endpoints) != has(self.service)
+ - message: mode is immutable
+ rule: oldSelf == null || (!has(self.mode) && !has(oldSelf.mode))
+ || self.mode == oldSelf.mode
type:
description: Type specifies the type of provider. Can only be
`ControlPlane`.
diff --git a/docs/en/latest/reference/api-reference.md b/docs/en/latest/reference/api-reference.md
index 8655f8e..ec1882a 100644
--- a/docs/en/latest/reference/api-reference.md
+++ b/docs/en/latest/reference/api-reference.md
@@ -229,6 +229,7 @@
| Field | Description |
| --- | --- |
+| `mode` _string_ | Mode specifies the mode of control plane provider. Can be `apisix` or `apisix-standalone`. |
| `endpoints` _string array_ | Endpoints specifies the list of control plane endpoints. |
| `service` _[ProviderService](#providerservice)_ | |
| `tlsVerify` _boolean_ | TlsVerify specifies whether to verify the TLS certificate of the control plane. |
diff --git a/internal/adc/client/client.go b/internal/adc/client/client.go
index b395199..8a498b1 100644
--- a/internal/adc/client/client.go
+++ b/internal/adc/client/client.go
@@ -41,16 +41,17 @@
mu sync.Mutex
*cache.Store
- executor ADCExecutor
- BackendMode string
+ executor ADCExecutor
ConfigManager *common.ConfigManager[types.NamespacedNameKind, adctypes.Config]
ADCDebugProvider *common.ADCDebugProvider
+ defaultMode string
+
log logr.Logger
}
-func New(log logr.Logger, mode string, timeout time.Duration) (*Client, error) {
+func New(log logr.Logger, defaultMode string, timeout time.Duration) (*Client, error) {
serverURL := os.Getenv("ADC_SERVER_URL")
if serverURL == "" {
serverURL = defaultHTTPADCExecutorAddr
@@ -59,15 +60,15 @@
configManager := common.NewConfigManager[types.NamespacedNameKind, adctypes.Config]()
logger := log.WithName("client")
- logger.Info("ADC client initialized", "mode", mode)
+ logger.Info("ADC client initialized")
return &Client{
Store: store,
executor: NewHTTPADCExecutor(log, serverURL, timeout),
- BackendMode: mode,
ConfigManager: configManager,
ADCDebugProvider: common.NewADCDebugProvider(store, configManager),
log: logger,
+ defaultMode: defaultMode,
}, nil
}
@@ -254,8 +255,11 @@
if resourceType == "" {
resourceType = "all"
}
+ if config.BackendType == "" {
+ config.BackendType = c.defaultMode
+ }
- err := c.executor.Execute(ctx, c.BackendMode, config, args)
+ err := c.executor.Execute(ctx, config, args)
duration := time.Since(startTime).Seconds()
status := adctypes.StatusSuccess
diff --git a/internal/adc/client/executor.go b/internal/adc/client/executor.go
index 5d997ef..b919dce 100644
--- a/internal/adc/client/executor.go
+++ b/internal/adc/client/executor.go
@@ -44,7 +44,7 @@
)
type ADCExecutor interface {
- Execute(ctx context.Context, mode string, config adctypes.Config, args []string) error
+ Execute(ctx context.Context, config adctypes.Config, args []string) error
}
type DefaultADCExecutor struct {
@@ -52,17 +52,17 @@
log logr.Logger
}
-func (e *DefaultADCExecutor) Execute(ctx context.Context, mode string, config adctypes.Config, args []string) error {
- return e.runADC(ctx, mode, config, args)
+func (e *DefaultADCExecutor) Execute(ctx context.Context, config adctypes.Config, args []string) error {
+ return e.runADC(ctx, config, args)
}
-func (e *DefaultADCExecutor) runADC(ctx context.Context, mode string, config adctypes.Config, args []string) error {
+func (e *DefaultADCExecutor) runADC(ctx context.Context, config adctypes.Config, args []string) error {
var execErrs = types.ADCExecutionError{
Name: config.Name,
}
for _, addr := range config.ServerAddrs {
- if err := e.runForSingleServerWithTimeout(ctx, addr, mode, config, args); err != nil {
+ if err := e.runForSingleServerWithTimeout(ctx, addr, config, args); err != nil {
e.log.Error(err, "failed to run adc for server", "server", addr)
var execErr types.ADCExecutionServerAddrError
if errors.As(err, &execErr) {
@@ -81,13 +81,13 @@
return nil
}
-func (e *DefaultADCExecutor) runForSingleServerWithTimeout(ctx context.Context, serverAddr, mode string, config adctypes.Config, args []string) error {
+func (e *DefaultADCExecutor) runForSingleServerWithTimeout(ctx context.Context, serverAddr string, config adctypes.Config, args []string) error {
ctx, cancel := context.WithTimeout(ctx, 15*time.Second)
defer cancel()
- return e.runForSingleServer(ctx, serverAddr, mode, config, args)
+ return e.runForSingleServer(ctx, serverAddr, config, args)
}
-func (e *DefaultADCExecutor) runForSingleServer(ctx context.Context, serverAddr, mode string, config adctypes.Config, args []string) error {
+func (e *DefaultADCExecutor) runForSingleServer(ctx context.Context, serverAddr string, config adctypes.Config, args []string) error {
cmdArgs := append([]string{}, args...)
if !config.TlsVerify {
cmdArgs = append(cmdArgs, "--tls-skip-verify")
@@ -95,7 +95,7 @@
cmdArgs = append(cmdArgs, "--timeout", "15s")
- env := e.prepareEnv(serverAddr, mode, config.Token)
+ env := e.prepareEnv(serverAddr, config.BackendType, config.Token)
var stdout, stderr bytes.Buffer
cmd := exec.CommandContext(ctx, "adc", cmdArgs...)
@@ -250,26 +250,26 @@
}
// Execute implements the ADCExecutor interface using HTTP calls
-func (e *HTTPADCExecutor) Execute(ctx context.Context, mode string, config adctypes.Config, args []string) error {
- return e.runHTTPSync(ctx, mode, config, args)
+func (e *HTTPADCExecutor) Execute(ctx context.Context, config adctypes.Config, args []string) error {
+ return e.runHTTPSync(ctx, config, args)
}
// runHTTPSync performs HTTP sync to ADC Server for each server address
-func (e *HTTPADCExecutor) runHTTPSync(ctx context.Context, mode string, config adctypes.Config, args []string) error {
+func (e *HTTPADCExecutor) runHTTPSync(ctx context.Context, config adctypes.Config, args []string) error {
var execErrs = types.ADCExecutionError{
Name: config.Name,
}
serverAddrs := func() []string {
- if mode == "apisix-standalone" {
+ if config.BackendType == "apisix-standalone" {
return []string{strings.Join(config.ServerAddrs, ",")}
}
return config.ServerAddrs
}()
- e.log.V(1).Info("running http sync", "serverAddrs", serverAddrs, "mode", mode)
+ e.log.V(1).Info("running http sync", "serverAddrs", serverAddrs)
for _, addr := range serverAddrs {
- if err := e.runHTTPSyncForSingleServer(ctx, addr, mode, config, args); err != nil {
+ if err := e.runHTTPSyncForSingleServer(ctx, addr, config, args); err != nil {
e.log.Error(err, "failed to run http sync for server", "server", addr)
var execErr types.ADCExecutionServerAddrError
if errors.As(err, &execErr) {
@@ -289,7 +289,7 @@
}
// runHTTPSyncForSingleServer performs HTTP sync to a single ADC Server
-func (e *HTTPADCExecutor) runHTTPSyncForSingleServer(ctx context.Context, serverAddr, mode string, config adctypes.Config, args []string) error {
+func (e *HTTPADCExecutor) runHTTPSyncForSingleServer(ctx context.Context, serverAddr string, config adctypes.Config, args []string) error {
ctx, cancel := context.WithTimeout(ctx, e.httpClient.Timeout)
defer cancel()
@@ -306,7 +306,7 @@
}
// Build HTTP request
- req, err := e.buildHTTPRequest(ctx, serverAddr, mode, config, labels, types, resources)
+ req, err := e.buildHTTPRequest(ctx, serverAddr, config, labels, types, resources)
if err != nil {
return fmt.Errorf("failed to build HTTP request: %w", err)
}
@@ -379,13 +379,13 @@
}
// buildHTTPRequest builds the HTTP request for ADC Server
-func (e *HTTPADCExecutor) buildHTTPRequest(ctx context.Context, serverAddr, mode string, config adctypes.Config, labels map[string]string, types []string, resources *adctypes.Resources) (*http.Request, error) {
+func (e *HTTPADCExecutor) buildHTTPRequest(ctx context.Context, serverAddr string, config adctypes.Config, labels map[string]string, types []string, resources *adctypes.Resources) (*http.Request, error) {
// Prepare request body
tlsVerify := config.TlsVerify
reqBody := ADCServerRequest{
Task: ADCServerTask{
Opts: ADCServerOpts{
- Backend: mode,
+ Backend: config.BackendType,
Server: strings.Split(serverAddr, ","),
Token: config.Token,
LabelSelector: labels,
@@ -407,7 +407,7 @@
e.log.V(1).Info("sending HTTP request to ADC Server",
"url", e.serverURL+"/sync",
"server", serverAddr,
- "mode", mode,
+ "mode", config.BackendType,
"cacheKey", config.Name,
"labelSelector", labels,
"includeResourceType", types,
diff --git a/internal/adc/translator/gatewayproxy.go b/internal/adc/translator/gatewayproxy.go
index 259c2ac..13ace18 100644
--- a/internal/adc/translator/gatewayproxy.go
+++ b/internal/adc/translator/gatewayproxy.go
@@ -31,6 +31,7 @@
types "github.com/apache/apisix-ingress-controller/api/adc"
"github.com/apache/apisix-ingress-controller/api/v1alpha1"
+ "github.com/apache/apisix-ingress-controller/internal/controller/config"
"github.com/apache/apisix-ingress-controller/internal/provider"
"github.com/apache/apisix-ingress-controller/internal/utils"
)
@@ -44,18 +45,20 @@
if provider.Type != v1alpha1.ProviderTypeControlPlane || provider.ControlPlane == nil {
return nil, nil
}
+ cp := provider.ControlPlane
- config := types.Config{
- Name: utils.NamespacedNameKind(gatewayProxy).String(),
+ cfg := types.Config{
+ Name: utils.NamespacedNameKind(gatewayProxy).String(),
+ BackendType: cp.Mode,
}
- if provider.ControlPlane.TlsVerify != nil {
- config.TlsVerify = *provider.ControlPlane.TlsVerify
+ if cp.TlsVerify != nil {
+ cfg.TlsVerify = *cp.TlsVerify
}
- if provider.ControlPlane.Auth.Type == v1alpha1.AuthTypeAdminKey && provider.ControlPlane.Auth.AdminKey != nil {
- if provider.ControlPlane.Auth.AdminKey.ValueFrom != nil && provider.ControlPlane.Auth.AdminKey.ValueFrom.SecretKeyRef != nil {
- secretRef := provider.ControlPlane.Auth.AdminKey.ValueFrom.SecretKeyRef
+ if cp.Auth.Type == v1alpha1.AuthTypeAdminKey && cp.Auth.AdminKey != nil {
+ if cp.Auth.AdminKey.ValueFrom != nil && cp.Auth.AdminKey.ValueFrom.SecretKeyRef != nil {
+ secretRef := cp.Auth.AdminKey.ValueFrom.SecretKeyRef
secret, ok := tctx.Secrets[k8stypes.NamespacedName{
// we should use gateway proxy namespace
Namespace: gatewayProxy.GetNamespace(),
@@ -63,28 +66,34 @@
}]
if ok {
if token, ok := secret.Data[secretRef.Key]; ok {
- config.Token = string(token)
+ cfg.Token = string(token)
}
}
- } else if provider.ControlPlane.Auth.AdminKey.Value != "" {
- config.Token = provider.ControlPlane.Auth.AdminKey.Value
+ } else if cp.Auth.AdminKey.Value != "" {
+ cfg.Token = cp.Auth.AdminKey.Value
}
}
- if config.Token == "" {
+ if cfg.Token == "" {
return nil, errors.New("no token found")
}
- endpoints := provider.ControlPlane.Endpoints
+ endpoints := cp.Endpoints
if len(endpoints) > 0 {
- config.ServerAddrs = endpoints
- return &config, nil
+ cfg.ServerAddrs = endpoints
+ return &cfg, nil
}
- if provider.ControlPlane.Service != nil {
+ // If Mode is empty, use the default static configuration.
+ // If Mode is set, resolve endpoints only when the ControlPlane is in standalone mode.
+ if cp.Mode != "" {
+ resolveEndpoints = cp.Mode == string(config.ProviderTypeStandalone)
+ }
+
+ if cp.Service != nil {
namespacedName := k8stypes.NamespacedName{
Namespace: gatewayProxy.Namespace,
- Name: provider.ControlPlane.Service.Name,
+ Name: cp.Service.Name,
}
svc, ok := tctx.Services[namespacedName]
if !ok {
@@ -100,9 +109,9 @@
}
upstreamNodes, _, err := t.TranslateBackendRefWithFilter(tctx, gatewayv1.BackendRef{
BackendObjectReference: gatewayv1.BackendObjectReference{
- Name: gatewayv1.ObjectName(provider.ControlPlane.Service.Name),
+ Name: gatewayv1.ObjectName(cp.Service.Name),
Namespace: (*gatewayv1.Namespace)(&gatewayProxy.Namespace),
- Port: ptr.To(gatewayv1.PortNumber(provider.ControlPlane.Service.Port)),
+ Port: ptr.To(gatewayv1.PortNumber(cp.Service.Port)),
},
}, func(endpoint *discoveryv1.Endpoint) bool {
if endpoint.Conditions.Terminating != nil && *endpoint.Conditions.Terminating {
@@ -115,21 +124,21 @@
return nil, err
}
for _, node := range upstreamNodes {
- config.ServerAddrs = append(config.ServerAddrs, "http://"+net.JoinHostPort(node.Host, strconv.Itoa(node.Port)))
+ cfg.ServerAddrs = append(cfg.ServerAddrs, "http://"+net.JoinHostPort(node.Host, strconv.Itoa(node.Port)))
}
} else {
- refPort := provider.ControlPlane.Service.Port
+ refPort := cp.Service.Port
var serverAddr string
if svc.Spec.Type == corev1.ServiceTypeExternalName {
serverAddr = fmt.Sprintf("http://%s:%d", svc.Spec.ExternalName, refPort)
} else {
- serverAddr = fmt.Sprintf("http://%s.%s.svc:%d", provider.ControlPlane.Service.Name, gatewayProxy.Namespace, refPort)
+ serverAddr = fmt.Sprintf("http://%s.%s.svc:%d", cp.Service.Name, gatewayProxy.Namespace, refPort)
}
- config.ServerAddrs = []string{serverAddr}
+ cfg.ServerAddrs = []string{serverAddr}
}
- t.Log.V(1).Info("add server address to config.ServiceAddrs", "config.ServerAddrs", config.ServerAddrs)
+ t.Log.V(1).Info("add server address to config.ServiceAddrs", "config.ServerAddrs", cfg.ServerAddrs)
}
- return &config, nil
+ return &cfg, nil
}
diff --git a/internal/manager/run.go b/internal/manager/run.go
index fe16baf..d8f07cf 100644
--- a/internal/manager/run.go
+++ b/internal/manager/run.go
@@ -183,7 +183,6 @@
SyncTimeout: config.ControllerConfig.ExecADCTimeout.Duration,
SyncPeriod: config.ControllerConfig.ProviderConfig.SyncPeriod.Duration,
InitSyncDelay: config.ControllerConfig.ProviderConfig.InitSyncDelay.Duration,
- BackendMode: string(config.ControllerConfig.ProviderConfig.Type),
}
provider, err := provider.New(providerType, logger, updater.Writer(), readier, providerOptions)
if err != nil {
diff --git a/internal/provider/apisix/provider.go b/internal/provider/apisix/provider.go
index 0151ad0..d0d8e48 100644
--- a/internal/provider/apisix/provider.go
+++ b/internal/provider/apisix/provider.go
@@ -72,11 +72,11 @@
func New(log logr.Logger, updater status.Updater, readier readiness.ReadinessManager, opts ...provider.Option) (provider.Provider, error) {
o := provider.Options{}
o.ApplyOptions(opts)
- if o.BackendMode == "" {
- o.BackendMode = ProviderTypeAPISIX
+ if o.DefaultBackendMode == "" {
+ o.DefaultBackendMode = ProviderTypeAPISIX
}
- cli, err := adcclient.New(log, o.BackendMode, o.SyncTimeout)
+ cli, err := adcclient.New(log, o.DefaultBackendMode, o.SyncTimeout)
if err != nil {
return nil, err
}
@@ -239,7 +239,7 @@
func (d *apisixProvider) buildConfig(tctx *provider.TranslateContext, nnk types.NamespacedNameKind) (map[types.NamespacedNameKind]adctypes.Config, error) {
configs := make(map[types.NamespacedNameKind]adctypes.Config, len(tctx.ResourceParentRefs[nnk]))
for _, gp := range tctx.GatewayProxies {
- config, err := d.translator.TranslateGatewayProxyToConfig(tctx, &gp, d.ResolveEndpoints)
+ config, err := d.translator.TranslateGatewayProxyToConfig(tctx, &gp, d.DefaultResolveEndpoints)
if err != nil {
return nil, err
}
@@ -307,7 +307,7 @@
// updateConfigForGatewayProxy update config for all referrers of the GatewayProxy
func (d *apisixProvider) updateConfigForGatewayProxy(tctx *provider.TranslateContext, gp *v1alpha1.GatewayProxy) error {
- config, err := d.translator.TranslateGatewayProxyToConfig(tctx, gp, d.ResolveEndpoints)
+ config, err := d.translator.TranslateGatewayProxyToConfig(tctx, gp, d.DefaultResolveEndpoints)
if err != nil {
return err
}
diff --git a/internal/provider/init/init.go b/internal/provider/init/init.go
index be21c07..5400cb2 100644
--- a/internal/provider/init/init.go
+++ b/internal/provider/init/init.go
@@ -34,8 +34,8 @@
readinessManager readiness.ReadinessManager,
opts ...provider.Option,
) (provider.Provider, error) {
- opts = append(opts, provider.WithBackendMode("apisix-standalone"))
- opts = append(opts, provider.WithResolveEndpoints())
+ opts = append(opts, provider.WithDefaultBackendMode("apisix-standalone"))
+ opts = append(opts, provider.WithDefaultResolveEndpoints())
return apisix.New(log, statusUpdater, readinessManager, opts...)
})
}
diff --git a/internal/provider/options.go b/internal/provider/options.go
index 379e8a0..dbb0760 100644
--- a/internal/provider/options.go
+++ b/internal/provider/options.go
@@ -26,11 +26,11 @@
}
type Options struct {
- SyncTimeout time.Duration
- SyncPeriod time.Duration
- InitSyncDelay time.Duration
- BackendMode string
- ResolveEndpoints bool
+ SyncTimeout time.Duration
+ SyncPeriod time.Duration
+ InitSyncDelay time.Duration
+ DefaultBackendMode string
+ DefaultResolveEndpoints bool
}
func (o *Options) ApplyToList(lo *Options) {
@@ -43,11 +43,11 @@
if o.InitSyncDelay > 0 {
lo.InitSyncDelay = o.InitSyncDelay
}
- if o.BackendMode != "" {
- lo.BackendMode = o.BackendMode
+ if o.DefaultBackendMode != "" {
+ lo.DefaultBackendMode = o.DefaultBackendMode
}
- if o.ResolveEndpoints {
- lo.ResolveEndpoints = o.ResolveEndpoints
+ if o.DefaultResolveEndpoints {
+ lo.DefaultResolveEndpoints = o.DefaultResolveEndpoints
}
}
@@ -58,22 +58,22 @@
return o
}
-type backendModeOption string
+type defaultBackendModeOption string
-func (b backendModeOption) ApplyToList(o *Options) {
- o.BackendMode = string(b)
+func (b defaultBackendModeOption) ApplyToList(o *Options) {
+ o.DefaultBackendMode = string(b)
}
-func WithBackendMode(mode string) Option {
- return backendModeOption(mode)
+func WithDefaultBackendMode(mode string) Option {
+ return defaultBackendModeOption(mode)
}
-type resolveEndpointsOption bool
+type defaultResolveEndpointsOption bool
-func (r resolveEndpointsOption) ApplyToList(o *Options) {
- o.ResolveEndpoints = bool(r)
+func (r defaultResolveEndpointsOption) ApplyToList(o *Options) {
+ o.DefaultResolveEndpoints = bool(r)
}
-func WithResolveEndpoints() Option {
- return resolveEndpointsOption(true)
+func WithDefaultResolveEndpoints() Option {
+ return defaultResolveEndpointsOption(true)
}
diff --git a/test/e2e/apisix/mode.go b/test/e2e/apisix/mode.go
new file mode 100644
index 0000000..17fe62f
--- /dev/null
+++ b/test/e2e/apisix/mode.go
@@ -0,0 +1,183 @@
+// 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 apisix
+
+import (
+ "fmt"
+ "net/http"
+
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+
+ "github.com/apache/apisix-ingress-controller/test/e2e/framework"
+ "github.com/apache/apisix-ingress-controller/test/e2e/scaffold"
+)
+
+var _ = Describe("Test Multi-Mode Deployment", Label("networking.k8s.io", "ingress"), func() {
+ s := scaffold.NewDefaultScaffold()
+
+ Context("apisix and apisix-standalone", func() {
+ var ns1 string
+ var gatewayProxyYaml = `
+apiVersion: apisix.apache.org/v1alpha1
+kind: GatewayProxy
+metadata:
+ name: apisix-proxy-config
+spec:
+ provider:
+ type: ControlPlane
+ controlPlane:
+ mode: %s
+ service:
+ name: %s
+ port: 9180
+ auth:
+ type: AdminKey
+ adminKey:
+ value: "%s"
+`
+
+ const ingressClassYaml = `
+apiVersion: networking.k8s.io/v1
+kind: IngressClass
+metadata:
+ name: %s
+spec:
+ controller: %s
+ parameters:
+ apiGroup: "apisix.apache.org"
+ kind: "GatewayProxy"
+ name: "apisix-proxy-config"
+ namespace: %s
+ scope: Namespace
+`
+ var ingressHttpbin = `
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ name: httpbin
+spec:
+ ingressClassName: %s
+ rules:
+ - host: httpbin.example
+ http:
+ paths:
+ - path: /get
+ pathType: Exact
+ backend:
+ service:
+ name: httpbin-service-e2e-test
+ port:
+ number: 80
+`
+ var ingressHttpbin2 = `
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ name: httpbin2
+spec:
+ ingressClassName: %s
+ rules:
+ - host: httpbin2.example
+ http:
+ paths:
+ - path: /get
+ pathType: Exact
+ backend:
+ service:
+ name: httpbin-service-e2e-test
+ port:
+ number: 80
+`
+
+ It("apisix and apisix-standalone", func() {
+ gateway1, svc1, err := s.Deployer.CreateAdditionalGatewayWithOptions("multi-mode-v1", scaffold.DeployDataplaneOptions{
+ ProviderType: framework.ProviderTypeAPISIX,
+ })
+ Expect(err).NotTo(HaveOccurred(), "creating Additional Gateway")
+
+ resources1, exists := s.GetAdditionalGateway(gateway1)
+ Expect(exists).To(BeTrue(), "additional gateway group should exist")
+ ns1 = resources1.Namespace
+
+ By("create GatewayProxy for Additional Gateway")
+ err = s.CreateResourceFromStringWithNamespace(fmt.Sprintf(gatewayProxyYaml, framework.ProviderTypeAPISIX, svc1.Name, resources1.AdminAPIKey), resources1.Namespace)
+ Expect(err).NotTo(HaveOccurred(), "creating GatewayProxy for Additional Gateway")
+
+ By("create IngressClass for Additional Gateway")
+ err = s.CreateResourceFromStringWithNamespace(fmt.Sprintf(ingressClassYaml, ns1, s.GetControllerName(), resources1.Namespace), "")
+ Expect(err).NotTo(HaveOccurred(), "creating IngressClass for Additional Gateway")
+
+ gateway2, svc2, err := s.Deployer.CreateAdditionalGatewayWithOptions("multi-mode-v2", scaffold.DeployDataplaneOptions{
+ ProviderType: framework.ProviderTypeAPISIXStandalone,
+ })
+ Expect(err).NotTo(HaveOccurred(), "creating Additional Gateway")
+
+ resources2, exists := s.GetAdditionalGateway(gateway2)
+ Expect(exists).To(BeTrue(), "additional gateway group should exist")
+ ns2 := resources2.Namespace
+
+ By("create GatewayProxy for Additional Gateway")
+ err = s.CreateResourceFromStringWithNamespace(fmt.Sprintf(gatewayProxyYaml, framework.ProviderTypeAPISIXStandalone, svc2.Name, resources2.AdminAPIKey), resources2.Namespace)
+ Expect(err).NotTo(HaveOccurred(), "creating GatewayProxy for Additional Gateway")
+
+ By("create IngressClass for Additional Gateway")
+ err = s.CreateResourceFromStringWithNamespace(fmt.Sprintf(ingressClassYaml, ns2, s.GetControllerName(), resources2.Namespace), "")
+ Expect(err).NotTo(HaveOccurred(), "creating IngressClass for Additional Gateway")
+
+ Expect(s.CreateResourceFromString(fmt.Sprintf(ingressHttpbin, ns1))).ShouldNot(HaveOccurred(), "creating Ingress in ns1")
+ Expect(s.CreateResourceFromString(fmt.Sprintf(ingressHttpbin2, ns2))).ShouldNot(HaveOccurred(), "creating Ingress in ns2")
+
+ client1, err := s.NewAPISIXClientForGateway(gateway1)
+ Expect(err).NotTo(HaveOccurred(), "creating APISIX client for gateway1")
+
+ client2, err := s.NewAPISIXClientForGateway(gateway2)
+ Expect(err).NotTo(HaveOccurred(), "creating APISIX client for gateway2")
+
+ s.RequestAssert(&scaffold.RequestAssert{
+ Client: client1,
+ Method: "GET",
+ Path: "/get",
+ Host: "httpbin.example",
+ Check: scaffold.WithExpectedStatus(http.StatusOK),
+ })
+ s.RequestAssert(&scaffold.RequestAssert{
+ Client: client2,
+ Method: "GET",
+ Path: "/get",
+ Host: "httpbin.example",
+ Check: scaffold.WithExpectedStatus(http.StatusNotFound),
+ })
+
+ s.RequestAssert(&scaffold.RequestAssert{
+ Client: client1,
+ Method: "GET",
+ Path: "/get",
+ Host: "httpbin2.example",
+ Check: scaffold.WithExpectedStatus(http.StatusNotFound),
+ })
+ s.RequestAssert(&scaffold.RequestAssert{
+ Client: client2,
+ Method: "GET",
+ Path: "/get",
+ Host: "httpbin2.example",
+ Check: scaffold.WithExpectedStatus(http.StatusOK),
+ })
+ })
+ })
+})
diff --git a/test/e2e/framework/apisix_consts.go b/test/e2e/framework/apisix_consts.go
index 11451a0..cbaa91b 100644
--- a/test/e2e/framework/apisix_consts.go
+++ b/test/e2e/framework/apisix_consts.go
@@ -33,6 +33,9 @@
const (
ProviderTypeAPISIX = "apisix"
ProviderTypeAPISIXStandalone = "apisix-standalone"
+
+ ConfigProviderTypeYaml = "yaml"
+ ConfigProviderTypeEtcd = "etcd"
)
var (
diff --git a/test/e2e/scaffold/apisix_deployer.go b/test/e2e/scaffold/apisix_deployer.go
index 6e2986d..51d16d2 100644
--- a/test/e2e/scaffold/apisix_deployer.go
+++ b/test/e2e/scaffold/apisix_deployer.go
@@ -204,13 +204,17 @@
if opts.ServiceHTTPSPort == 0 {
opts.ServiceHTTPSPort = 443
}
- opts.ConfigProvider = "yaml"
kubectlOpts := k8s.NewKubectlOptions("", "", opts.Namespace)
- if framework.ProviderType == framework.ProviderTypeAPISIX {
- opts.ConfigProvider = "etcd"
- // deploy etcd
+ if opts.ConfigProvider == "" {
+ opts.ConfigProvider = framework.ConfigProviderTypeYaml
+ if framework.ProviderType == framework.ProviderTypeAPISIX {
+ opts.ConfigProvider = framework.ConfigProviderTypeEtcd
+ }
+ }
+
+ if opts.ConfigProvider == framework.ConfigProviderTypeEtcd {
k8s.KubectlApplyFromString(s.GinkgoT, kubectlOpts, framework.EtcdSpec)
err := framework.WaitPodsAvailable(s.GinkgoT, kubectlOpts, metav1.ListOptions{
LabelSelector: "app=etcd",
@@ -320,6 +324,10 @@
}
func (s *APISIXDeployer) CreateAdditionalGateway(namePrefix string) (string, *corev1.Service, error) {
+ return s.CreateAdditionalGatewayWithOptions(namePrefix, DeployDataplaneOptions{})
+}
+
+func (s *APISIXDeployer) CreateAdditionalGatewayWithOptions(namePrefix string, opts DeployDataplaneOptions) (string, *corev1.Service, error) {
// Create a new namespace for this additional gateway
additionalNS := fmt.Sprintf("%s-%d", namePrefix, time.Now().Unix())
@@ -344,13 +352,32 @@
}
// Deploy dataplane for this additional gateway
- opts := APISIXDeployOptions{
+ o := APISIXDeployOptions{
Namespace: additionalNS,
AdminKey: adminKey,
ServiceHTTPPort: 9080,
ServiceHTTPSPort: 9443,
}
- svc := s.deployDataplane(&opts)
+ if opts.Namespace != "" {
+ o.Namespace = opts.Namespace
+ }
+ if opts.AdminKey != "" {
+ o.AdminKey = opts.AdminKey
+ }
+ if opts.ServiceHTTPPort != 0 {
+ o.ServiceHTTPPort = opts.ServiceHTTPPort
+ }
+ if opts.ServiceHTTPSPort != 0 {
+ o.ServiceHTTPSPort = opts.ServiceHTTPSPort
+ }
+ if opts.ProviderType != "" {
+ if opts.ProviderType == framework.ProviderTypeAPISIX {
+ o.ConfigProvider = framework.ConfigProviderTypeEtcd
+ } else {
+ o.ConfigProvider = framework.ConfigProviderTypeYaml
+ }
+ }
+ svc := s.deployDataplane(&o)
resources.DataplaneService = svc
diff --git a/test/e2e/scaffold/deployer.go b/test/e2e/scaffold/deployer.go
index 41e7d73..a1b2e9b 100644
--- a/test/e2e/scaffold/deployer.go
+++ b/test/e2e/scaffold/deployer.go
@@ -29,7 +29,8 @@
ScaleDataplane(replicas int)
BeforeEach()
AfterEach()
- CreateAdditionalGateway(namePrefix string) (string, *corev1.Service, error)
+ CreateAdditionalGateway(namePrefix string) (identifier string, svc *corev1.Service, err error)
+ CreateAdditionalGatewayWithOptions(namePrefix string, opts DeployDataplaneOptions) (identifier string, svc *corev1.Service, err error)
CleanupAdditionalGateway(identifier string) error
GetAdminEndpoint(...*corev1.Service) string
GetAdminServiceName() string
@@ -46,4 +47,5 @@
ServiceHTTPSPort int
Replicas *int
AdminKey string
+ ProviderType string
}