From 1573729f1e86255a7cd4e720a15065cee067cace Mon Sep 17 00:00:00 2001 From: rongxin Date: Thu, 19 Jun 2025 08:28:02 +0800 Subject: [PATCH 01/13] feat: support ApisixConsumer --- Dockerfile.dev | 2 +- api/adc/types.go | 10 +- api/adc/zz_generated.deepcopy.go | 8 +- api/v2/apisixroute_types.go | 2 +- api/v2/zz_generated.deepcopy.go | 8 +- .../apisix.apache.org_apisixglobalrules.yaml | 4 +- ...apisix.apache.org_apisixpluginconfigs.yaml | 4 +- .../bases/apisix.apache.org_apisixroutes.yaml | 8 +- docs/crd/api.md | 13 +- .../controller/apisixconsumer_controller.go | 185 +++++++- internal/controller/apisixroute_controller.go | 72 ++-- internal/controller/utils.go | 208 +++++++++ internal/manager/controllers.go | 7 + internal/provider/adc/adc.go | 7 + internal/provider/adc/store.go | 1 + .../provider/adc/translator/apisixconsumer.go | 358 ++++++++++++++++ .../provider/adc/translator/apisixroute.go | 6 +- internal/provider/adc/translator/consumer.go | 11 +- internal/provider/adc/translator/gateway.go | 13 +- .../provider/adc/translator/globalrule.go | 21 +- internal/provider/adc/translator/httproute.go | 2 +- internal/types/plugin.go | 282 +++++++++++++ internal/types/zz_generated.deepcopy.go | 394 ++++++++++++++++++ test/e2e/api7/gatewayproxy.go | 267 ++++++++++++ test/e2e/crds/consumer.go | 2 +- test/e2e/e2e_test.go | 1 + 26 files changed, 1766 insertions(+), 130 deletions(-) create mode 100644 internal/provider/adc/translator/apisixconsumer.go create mode 100644 internal/types/plugin.go create mode 100644 internal/types/zz_generated.deepcopy.go create mode 100644 test/e2e/api7/gatewayproxy.go diff --git a/Dockerfile.dev b/Dockerfile.dev index f06154106..bb0708ed3 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -8,7 +8,7 @@ WORKDIR /app RUN apt update \ && apt install -y git \ - && git clone --branch main https://github.com/api7/adc.git \ + && git clone --depth 1 --branch main https://github.com/api7/adc.git \ && cd adc \ && corepack enable pnpm \ && pnpm install \ diff --git a/api/adc/types.go b/api/adc/types.go index d9b0dab6a..211fa6006 100644 --- a/api/adc/types.go +++ b/api/adc/types.go @@ -128,11 +128,11 @@ type ConsumerGroup struct { // +k8s:deepcopy-gen=true type Consumer struct { - Credentials []Credential `json:"credentials,omitempty" yaml:"credentials,omitempty"` - Description string `json:"description,omitempty" yaml:"description,omitempty"` - Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"` - Plugins Plugins `json:"plugins,omitempty" yaml:"plugins,omitempty"` - Username string `json:"username" yaml:"username"` + Metadata `json:",inline" yaml:",inline"` + + Credentials []Credential `json:"credentials,omitempty" yaml:"credentials,omitempty"` + Plugins Plugins `json:"plugins,omitempty" yaml:"plugins,omitempty"` + Username string `json:"username" yaml:"username"` } // +k8s:deepcopy-gen=true diff --git a/api/adc/zz_generated.deepcopy.go b/api/adc/zz_generated.deepcopy.go index 8051a98bc..bd9250957 100644 --- a/api/adc/zz_generated.deepcopy.go +++ b/api/adc/zz_generated.deepcopy.go @@ -65,6 +65,7 @@ func (in *ClientClass) DeepCopy() *ClientClass { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Consumer) DeepCopyInto(out *Consumer) { *out = *in + in.Metadata.DeepCopyInto(&out.Metadata) if in.Credentials != nil { in, out := &in.Credentials, &out.Credentials *out = make([]Credential, len(*in)) @@ -72,13 +73,6 @@ func (in *Consumer) DeepCopyInto(out *Consumer) { (*in)[i].DeepCopyInto(&(*out)[i]) } } - if in.Labels != nil { - in, out := &in.Labels, &out.Labels - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } out.Plugins = in.Plugins.DeepCopy() } diff --git a/api/v2/apisixroute_types.go b/api/v2/apisixroute_types.go index 989667425..6208b35ef 100644 --- a/api/v2/apisixroute_types.go +++ b/api/v2/apisixroute_types.go @@ -140,7 +140,7 @@ type ApisixRoutePlugin struct { Enable bool `json:"enable" yaml:"enable"` // Plugin configuration. // +kubebuilder:validation:Optional - Config ApisixRoutePluginConfig `json:"config" yaml:"config"` + Config apiextensionsv1.JSON `json:"config" yaml:"config"` // Plugin configuration secretRef. // +kubebuilder:validation:Optional SecretRef string `json:"secretRef" yaml:"secretRef"` diff --git a/api/v2/zz_generated.deepcopy.go b/api/v2/zz_generated.deepcopy.go index 917d1885e..4cb900f5f 100644 --- a/api/v2/zz_generated.deepcopy.go +++ b/api/v2/zz_generated.deepcopy.go @@ -936,13 +936,7 @@ func (in *ApisixRouteList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ApisixRoutePlugin) DeepCopyInto(out *ApisixRoutePlugin) { *out = *in - if in.Config != nil { - in, out := &in.Config, &out.Config - *out = make(ApisixRoutePluginConfig, len(*in)) - for key, val := range *in { - (*out)[key] = *val.DeepCopy() - } - } + in.Config.DeepCopyInto(&out.Config) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApisixRoutePlugin. diff --git a/config/crd/bases/apisix.apache.org_apisixglobalrules.yaml b/config/crd/bases/apisix.apache.org_apisixglobalrules.yaml index 797528d2b..670c495b4 100644 --- a/config/crd/bases/apisix.apache.org_apisixglobalrules.yaml +++ b/config/crd/bases/apisix.apache.org_apisixglobalrules.yaml @@ -50,10 +50,8 @@ spec: description: ApisixRoutePlugin represents an APISIX plugin. properties: config: - additionalProperties: - x-kubernetes-preserve-unknown-fields: true description: Plugin configuration. - type: object + x-kubernetes-preserve-unknown-fields: true enable: default: true description: Whether this plugin is in use, default is true. diff --git a/config/crd/bases/apisix.apache.org_apisixpluginconfigs.yaml b/config/crd/bases/apisix.apache.org_apisixpluginconfigs.yaml index b967d2395..4af1bcbc1 100644 --- a/config/crd/bases/apisix.apache.org_apisixpluginconfigs.yaml +++ b/config/crd/bases/apisix.apache.org_apisixpluginconfigs.yaml @@ -51,10 +51,8 @@ spec: description: ApisixRoutePlugin represents an APISIX plugin. properties: config: - additionalProperties: - x-kubernetes-preserve-unknown-fields: true description: Plugin configuration. - type: object + x-kubernetes-preserve-unknown-fields: true enable: default: true description: Whether this plugin is in use, default is true. diff --git a/config/crd/bases/apisix.apache.org_apisixroutes.yaml b/config/crd/bases/apisix.apache.org_apisixroutes.yaml index be66dd25f..13ce72edb 100644 --- a/config/crd/bases/apisix.apache.org_apisixroutes.yaml +++ b/config/crd/bases/apisix.apache.org_apisixroutes.yaml @@ -247,10 +247,8 @@ spec: description: ApisixRoutePlugin represents an APISIX plugin. properties: config: - additionalProperties: - x-kubernetes-preserve-unknown-fields: true description: Plugin configuration. - type: object + x-kubernetes-preserve-unknown-fields: true enable: default: true description: Whether this plugin is in use, default is @@ -364,10 +362,8 @@ spec: description: ApisixRoutePlugin represents an APISIX plugin. properties: config: - additionalProperties: - x-kubernetes-preserve-unknown-fields: true description: Plugin configuration. - type: object + x-kubernetes-preserve-unknown-fields: true enable: default: true description: Whether this plugin is in use, default is diff --git a/docs/crd/api.md b/docs/crd/api.md index a337c2db5..4ef3e2a1a 100644 --- a/docs/crd/api.md +++ b/docs/crd/api.md @@ -1159,7 +1159,7 @@ ApisixRoutePlugin represents an APISIX plugin. | --- | --- | | `name` _string_ | The plugin name. | | `enable` _boolean_ | Whether this plugin is in use, default is true. | -| `config` _[ApisixRoutePluginConfig](#apisixroutepluginconfig)_ | Plugin configuration. | +| `config` _[JSON](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#json-v1-apiextensions-k8s-io)_ | Plugin configuration. | | `secretRef` _string_ | Plugin configuration secretRef. | @@ -1169,18 +1169,7 @@ _Appears in:_ - [ApisixRouteHTTP](#apisixroutehttp) - [ApisixRouteStream](#apisixroutestream) -#### ApisixRoutePluginConfig -_Base type:_ `[JSON](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#json-v1-apiextensions-k8s-io)` -ApisixRoutePluginConfig is the configuration for -any plugins. - - - - - -_Appears in:_ -- [ApisixRoutePlugin](#apisixrouteplugin) #### ApisixRouteSpec diff --git a/internal/controller/apisixconsumer_controller.go b/internal/controller/apisixconsumer_controller.go index 97a247593..9f6842ac6 100644 --- a/internal/controller/apisixconsumer_controller.go +++ b/internal/controller/apisixconsumer_controller.go @@ -14,15 +14,29 @@ package controller import ( "context" + "fmt" + "github.com/api7/gopkg/pkg/log" "github.com/go-logr/logr" + corev1 "k8s.io/api/core/v1" + networkingv1 "k8s.io/api/networking/v1" + k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/predicate" + "sigs.k8s.io/controller-runtime/pkg/reconcile" gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" + "github.com/apache/apisix-ingress-controller/api/v1alpha1" apiv2 "github.com/apache/apisix-ingress-controller/api/v2" + "github.com/apache/apisix-ingress-controller/internal/controller/status" + "github.com/apache/apisix-ingress-controller/internal/provider" + "github.com/apache/apisix-ingress-controller/internal/utils" ) // ApisixConsumerReconciler reconciles a ApisixConsumer object @@ -30,40 +44,185 @@ type ApisixConsumerReconciler struct { client.Client Scheme *runtime.Scheme Log logr.Logger + + Provider provider.Provider + Updater status.Updater } // Reconcile FIXME: implement the reconcile logic (For now, it dose nothing other than directly accepting) func (r *ApisixConsumerReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { r.Log.Info("reconcile", "request", req.NamespacedName) - var obj apiv2.ApisixConsumer - if err := r.Get(ctx, req.NamespacedName, &obj); err != nil { + ac := &apiv2.ApisixConsumer{} + if err := r.Get(ctx, req.NamespacedName, ac); err != nil { + if k8serrors.IsNotFound(err) { + ac.Namespace = req.Namespace + ac.Name = req.Name + ac.TypeMeta = metav1.TypeMeta{ + Kind: KindApisixConsumer, + APIVersion: apiv2.GroupVersion.String(), + } + if err := r.Provider.Delete(ctx, ac); err != nil { + r.Log.Error(err, "failed to delete provider", "ApisixConsumer", ac) + return ctrl.Result{}, err + } + } r.Log.Error(err, "failed to get ApisixConsumer", "request", req.NamespacedName) return ctrl.Result{}, err } - obj.Status.Conditions = []metav1.Condition{ - { - Type: string(gatewayv1.RouteConditionAccepted), - Status: metav1.ConditionTrue, - ObservedGeneration: obj.GetGeneration(), - LastTransitionTime: metav1.Now(), - Reason: string(gatewayv1.RouteReasonAccepted), - }, + tctx := provider.NewDefaultTranslateContext(ctx) + + ingressClass, err := GetIngressClass(tctx, r.Client, r.Log, ac.Spec.IngressClassName) + if err != nil { + log.Error(err, "failed to get IngressClass") + return ctrl.Result{}, err } - if err := r.Status().Update(ctx, &obj); err != nil { - r.Log.Error(err, "failed to update status", "request", req.NamespacedName) + if err := ProcessIngressClassParameters(tctx, r.Client, r.Log, ac, ingressClass); err != nil { + log.Error(err, "failed to process IngressClass parameters", "ingressClass", ingressClass.Name) return ctrl.Result{}, err } + if err := r.Provider.Update(ctx, tctx, ac); err != nil { + r.Log.Error(err, "failed to update provider", "ApisixConsumer", ac) + // Update status with failure condition + r.updateStatus(ac, metav1.Condition{ + Type: string(apiv2.ConditionTypeAccepted), + Status: metav1.ConditionFalse, + ObservedGeneration: ac.Generation, + LastTransitionTime: metav1.Now(), + Reason: string(apiv2.ConditionReasonSyncFailed), + Message: err.Error(), + }) + return ctrl.Result{}, err + } + + // Update status with success condition + r.updateStatus(ac, metav1.Condition{ + Type: string(gatewayv1.RouteConditionAccepted), + Status: metav1.ConditionTrue, + ObservedGeneration: ac.Generation, + LastTransitionTime: metav1.Now(), + Reason: string(gatewayv1.RouteReasonAccepted), + Message: "The ApisixConsumer has been accepted by the apisix-ingress-controller", + }) return ctrl.Result{}, nil } // SetupWithManager sets up the controller with the Manager. func (r *ApisixConsumerReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). - For(&apiv2.ApisixConsumer{}). + For(&apiv2.ApisixConsumer{}, + builder.WithPredicates( + predicate.NewPredicateFuncs(r.checkIngressClass), + )). + WithEventFilter( + predicate.Or( + predicate.GenerationChangedPredicate{}, + predicate.AnnotationChangedPredicate{}, + ), + ). + Watches( + &networkingv1.IngressClass{}, + handler.EnqueueRequestsFromMapFunc(r.listApisixConsumerForIngressClass), + builder.WithPredicates( + predicate.NewPredicateFuncs(matchesIngressController), + ), + ). + Watches(&v1alpha1.GatewayProxy{}, + handler.EnqueueRequestsFromMapFunc(r.listApisixConsumerForGatewayProxy), + ). Named("apisixconsumer"). Complete(r) } + +func (r *ApisixConsumerReconciler) checkIngressClass(obj client.Object) bool { + ac, ok := obj.(*apiv2.ApisixConsumer) + if !ok { + return false + } + + return matchesIngressClass(r.Client, r.Log, ac.Spec.IngressClassName) +} + +func (r *ApisixConsumerReconciler) listApisixConsumerForGatewayProxy(ctx context.Context, obj client.Object) []reconcile.Request { + return listIngressClassRequestsForGatewayProxy(ctx, r.Client, obj, r.Log, r.listApisixConsumerForIngressClass) +} + +func (r *ApisixConsumerReconciler) listApisixConsumerForIngressClass(ctx context.Context, obj client.Object) []reconcile.Request { + ingressClass, ok := obj.(*networkingv1.IngressClass) + if !ok { + return nil + } + + return ListMatchingRequests( + ctx, + r.Client, + r.Log, + &apiv2.ApisixConsumerList{}, + func(obj client.Object) bool { + ac, ok := obj.(*apiv2.ApisixConsumer) + if !ok { + r.Log.Error(fmt.Errorf("expected ApisixConsumer, got %T", obj), "failed to match object type") + return false + } + return (IsDefaultIngressClass(ingressClass) && ac.Spec.IngressClassName == "") || ac.Spec.IngressClassName == ingressClass.Name + }, + ) +} + +func (r *ApisixConsumerReconciler) processSpec(ctx context.Context, tctx *provider.TranslateContext, ac *apiv2.ApisixConsumer) error { + var secretRef *corev1.LocalObjectReference + if ac.Spec.AuthParameter.KeyAuth != nil { + secretRef = ac.Spec.AuthParameter.KeyAuth.SecretRef + } else if ac.Spec.AuthParameter.BasicAuth != nil { + secretRef = ac.Spec.AuthParameter.BasicAuth.SecretRef + } else if ac.Spec.AuthParameter.JwtAuth != nil { + secretRef = ac.Spec.AuthParameter.JwtAuth.SecretRef + } else if ac.Spec.AuthParameter.WolfRBAC != nil { + secretRef = ac.Spec.AuthParameter.WolfRBAC.SecretRef + } else if ac.Spec.AuthParameter.HMACAuth != nil { + secretRef = ac.Spec.AuthParameter.HMACAuth.SecretRef + } else if ac.Spec.AuthParameter.LDAPAuth != nil { + secretRef = ac.Spec.AuthParameter.LDAPAuth.SecretRef + } + if secretRef == nil { + return nil + } + + namespacedName := types.NamespacedName{ + Name: secretRef.Name, + Namespace: ac.Namespace, + } + + secret := &corev1.Secret{} + if err := r.Get(ctx, namespacedName, secret); err != nil { + if k8serrors.IsNotFound(err) { + r.Log.Info("secret not found", "secret", namespacedName.String()) + return nil + } else { + r.Log.Error(err, "failed to get secret", "secret", namespacedName.String()) + return err + } + } + tctx.Secrets[namespacedName] = secret + return nil +} + +func (r *ApisixConsumerReconciler) updateStatus(consumer *apiv2.ApisixConsumer, condition metav1.Condition) { + r.Updater.Update(status.Update{ + NamespacedName: utils.NamespacedName(consumer), + Resource: &apiv2.ApisixConsumer{}, + Mutator: status.MutatorFunc(func(obj client.Object) client.Object { + ac, ok := obj.(*apiv2.ApisixConsumer) + if !ok { + err := fmt.Errorf("unsupported object type %T", obj) + panic(err) + } + acCopy := ac.DeepCopy() + acCopy.Status.Conditions = []metav1.Condition{condition} + return acCopy + }), + }) +} diff --git a/internal/controller/apisixroute_controller.go b/internal/controller/apisixroute_controller.go index 7b20957d3..9cf2df8eb 100644 --- a/internal/controller/apisixroute_controller.go +++ b/internal/controller/apisixroute_controller.go @@ -151,6 +151,16 @@ func (r *ApisixRouteReconciler) processApisixRoute(ctx context.Context, tc *prov } rules[http.Name] = struct{}{} + // check secret + for _, plugin := range http.Plugins { + if !plugin.Enable { + continue + } + // check secret + if err := r.validateSecrets(ctx, tc, in, plugin.SecretRef); err != nil { + return err + } + } // check plugin config reference if http.PluginConfigName != "" { if err := r.validatePluginConfig(ctx, tc, in, http); err != nil { @@ -158,11 +168,6 @@ func (r *ApisixRouteReconciler) processApisixRoute(ctx context.Context, tc *prov } } - // check secret - if err := r.validateSecrets(ctx, tc, in, http); err != nil { - return err - } - // check vars // todo: cache the result to tctx if _, err := http.Match.NginxVars.ToVars(); err != nil { @@ -231,52 +236,37 @@ func (r *ApisixRouteReconciler) validatePluginConfig(ctx context.Context, tc *pr // Also check secrets referenced by plugin config for _, plugin := range pc.Spec.Plugins { - if !plugin.Enable || plugin.Config == nil || plugin.SecretRef == "" { + if !plugin.Enable { continue } - var ( - secret = corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: plugin.SecretRef, - Namespace: pc.Namespace, - }, - } - secretNN = utils.NamespacedName(&secret) - ) - if err := r.Get(ctx, secretNN, &secret); err != nil { - return ReasonError{ - Reason: string(apiv2.ConditionReasonInvalidSpec), - Message: fmt.Sprintf("failed to get Secret: %s", secretNN), - } + if err := r.validateSecrets(ctx, tc, in, plugin.SecretRef); err != nil { + return err } - tc.Secrets[secretNN] = &secret } return nil } -func (r *ApisixRouteReconciler) validateSecrets(ctx context.Context, tc *provider.TranslateContext, in *apiv2.ApisixRoute, http apiv2.ApisixRouteHTTP) error { - for _, plugin := range http.Plugins { - if !plugin.Enable || plugin.Config == nil || plugin.SecretRef == "" { - continue +func (r *ApisixRouteReconciler) validateSecrets(ctx context.Context, tc *provider.TranslateContext, in *apiv2.ApisixRoute, secretRef string) error { + if secretRef == "" { + return nil + } + var ( + secret = corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: secretRef, + Namespace: in.Namespace, + }, } - var ( - secret = corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: plugin.SecretRef, - Namespace: in.Namespace, - }, - } - secretNN = utils.NamespacedName(&secret) - ) - if err := r.Get(ctx, secretNN, &secret); err != nil { - return ReasonError{ - Reason: string(apiv2.ConditionReasonInvalidSpec), - Message: fmt.Sprintf("failed to get Secret: %s", secretNN), - } + secretNN = utils.NamespacedName(&secret) + ) + if err := r.Get(ctx, secretNN, &secret); err != nil { + return ReasonError{ + Reason: string(apiv2.ConditionReasonInvalidSpec), + Message: fmt.Sprintf("failed to get Secret: %s", secretNN), } - - tc.Secrets[utils.NamespacedName(&secret)] = &secret } + + tc.Secrets[utils.NamespacedName(&secret)] = &secret return nil } diff --git a/internal/controller/utils.go b/internal/controller/utils.go index e4a2c8bd0..20ce88030 100644 --- a/internal/controller/utils.go +++ b/internal/controller/utils.go @@ -24,10 +24,12 @@ import ( "strings" "github.com/api7/gopkg/pkg/log" + "github.com/go-logr/logr" "github.com/samber/lo" "go.uber.org/zap" corev1 "k8s.io/api/core/v1" networkingv1 "k8s.io/api/networking/v1" + "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" k8stypes "k8s.io/apimachinery/pkg/types" @@ -41,6 +43,7 @@ import ( "github.com/apache/apisix-ingress-controller/api/v1alpha1" apiv2 "github.com/apache/apisix-ingress-controller/api/v2" "github.com/apache/apisix-ingress-controller/internal/controller/config" + "github.com/apache/apisix-ingress-controller/internal/controller/indexer" "github.com/apache/apisix-ingress-controller/internal/provider" "github.com/apache/apisix-ingress-controller/internal/types" "github.com/apache/apisix-ingress-controller/internal/utils" @@ -58,6 +61,7 @@ const ( KindApisixRoute = "ApisixRoute" KindApisixGlobalRule = "ApisixGlobalRule" KindApisixPluginConfig = "ApisixPluginConfig" + KindApisixConsumer = "ApisixConsumer" ) const defaultIngressClassAnnotation = "ingressclass.kubernetes.io/is-default-class" @@ -1171,3 +1175,207 @@ func checkReferenceGrant(ctx context.Context, cli client.Client, obj v1beta1.Ref } return false } + +func ListMatchingRequests( + ctx context.Context, + c client.Client, + logger logr.Logger, + listObj client.ObjectList, + matchFunc func(obj client.Object) bool, +) []reconcile.Request { + if err := c.List(ctx, listObj); err != nil { + logger.Error(err, "failed to list resource") + return nil + } + + items, err := meta.ExtractList(listObj) + if err != nil { + logger.Error(err, "failed to extract list items") + return nil + } + + var requests []reconcile.Request + for _, item := range items { + obj, ok := item.(client.Object) + if !ok { + continue + } + + if matchFunc(obj) { + requests = append(requests, reconcile.Request{ + NamespacedName: utils.NamespacedName(obj), + }) + } + } + return requests +} + +func listIngressClassRequestsForGatewayProxy( + ctx context.Context, + c client.Client, + obj client.Object, + logger logr.Logger, + listFunc func(context.Context, client.Object) []reconcile.Request, +) []reconcile.Request { + gatewayProxy, ok := obj.(*v1alpha1.GatewayProxy) + if !ok { + return nil + } + + ingressClassList := &networkingv1.IngressClassList{} + if err := c.List(ctx, ingressClassList, client.MatchingFields{ + indexer.IngressClassParametersRef: indexer.GenIndexKey(gatewayProxy.GetNamespace(), gatewayProxy.GetName()), + }); err != nil { + logger.Error(err, "failed to list ingress classes for gateway proxy", "gatewayproxy", gatewayProxy.GetName()) + return nil + } + + requestSet := make(map[string]reconcile.Request) + for _, ingressClass := range ingressClassList.Items { + for _, req := range listFunc(ctx, &ingressClass) { + requestSet[req.String()] = req + } + } + + requests := make([]reconcile.Request, 0, len(requestSet)) + for _, req := range requestSet { + requests = append(requests, req) + } + return requests +} + +func matchesIngressController(obj client.Object) bool { + ingressClass, ok := obj.(*networkingv1.IngressClass) + if !ok { + return false + } + return matchesController(ingressClass.Spec.Controller) +} + +func matchesIngressClass(c client.Client, log logr.Logger, ingressClassName string) bool { + if ingressClassName == "" { + // Check for default ingress class + ingressClassList := &networkingv1.IngressClassList{} + if err := c.List(context.Background(), ingressClassList, client.MatchingFields{ + indexer.IngressClass: config.GetControllerName(), + }); err != nil { + log.Error(err, "failed to list ingress classes") + return false + } + + // Find the ingress class that is marked as default + for _, ic := range ingressClassList.Items { + if IsDefaultIngressClass(&ic) && matchesController(ic.Spec.Controller) { + return true + } + } + return false + } + + // Check if the specified ingress class is controlled by us + var ingressClass networkingv1.IngressClass + if err := c.Get(context.Background(), client.ObjectKey{Name: ingressClassName}, &ingressClass); err != nil { + log.Error(err, "failed to get ingress class", "ingressClass", ingressClassName) + return false + } + + return matchesController(ingressClass.Spec.Controller) +} + +func ProcessIngressClassParameters(tctx *provider.TranslateContext, c client.Client, log logr.Logger, object client.Object, ingressClass *networkingv1.IngressClass) error { + if ingressClass == nil || ingressClass.Spec.Parameters == nil { + return nil + } + + ingressClassKind := utils.NamespacedNameKind(ingressClass) + objKind := utils.NamespacedNameKind(object) + + parameters := ingressClass.Spec.Parameters + // check if the parameters reference GatewayProxy + if parameters.APIGroup != nil && *parameters.APIGroup == v1alpha1.GroupVersion.Group && parameters.Kind == KindGatewayProxy { + ns := object.GetNamespace() + if parameters.Namespace != nil { + ns = *parameters.Namespace + } + + gatewayProxy := &v1alpha1.GatewayProxy{} + if err := c.Get(tctx, client.ObjectKey{ + Namespace: ns, + Name: parameters.Name, + }, gatewayProxy); err != nil { + log.Error(err, "failed to get GatewayProxy", "namespace", ns, "name", parameters.Name) + return err + } + + log.Info("found GatewayProxy for IngressClass", "ingressClass", ingressClass.Name, "gatewayproxy", gatewayProxy.Name) + tctx.GatewayProxies[ingressClassKind] = *gatewayProxy + tctx.ResourceParentRefs[objKind] = append(tctx.ResourceParentRefs[objKind], ingressClassKind) + + // check if the provider field references a secret + if gatewayProxy.Spec.Provider != nil && gatewayProxy.Spec.Provider.Type == v1alpha1.ProviderTypeControlPlane { + if gatewayProxy.Spec.Provider.ControlPlane != nil && + gatewayProxy.Spec.Provider.ControlPlane.Auth.Type == v1alpha1.AuthTypeAdminKey && + gatewayProxy.Spec.Provider.ControlPlane.Auth.AdminKey != nil && + gatewayProxy.Spec.Provider.ControlPlane.Auth.AdminKey.ValueFrom != nil && + gatewayProxy.Spec.Provider.ControlPlane.Auth.AdminKey.ValueFrom.SecretKeyRef != nil { + + secretRef := gatewayProxy.Spec.Provider.ControlPlane.Auth.AdminKey.ValueFrom.SecretKeyRef + secret := &corev1.Secret{} + if err := c.Get(tctx, client.ObjectKey{ + Namespace: ns, + Name: secretRef.Name, + }, secret); err != nil { + log.Error(err, "failed to get secret for GatewayProxy provider", + "namespace", ns, + "name", secretRef.Name) + return err + } + + log.Info("found secret for GatewayProxy provider", + "ingressClass", ingressClass.Name, + "gatewayproxy", gatewayProxy.Name, + "secret", secretRef.Name) + + tctx.Secrets[k8stypes.NamespacedName{ + Namespace: ns, + Name: secretRef.Name, + }] = secret + } + } + } + + return nil +} + +func GetIngressClass(ctx context.Context, c client.Client, log logr.Logger, ingressClassName string) (*networkingv1.IngressClass, error) { + if ingressClassName == "" { + // Check for default ingress class + ingressClassList := &networkingv1.IngressClassList{} + if err := c.List(ctx, ingressClassList, client.MatchingFields{ + indexer.IngressClass: config.GetControllerName(), + }); err != nil { + log.Error(err, "failed to list ingress classes") + return nil, err + } + + // Find the ingress class that is marked as default + for _, ic := range ingressClassList.Items { + if IsDefaultIngressClass(&ic) && matchesController(ic.Spec.Controller) { + return &ic, nil + } + } + return nil, errors.New("no default ingress class found") + } + + // Check if the specified ingress class is controlled by us + var ingressClass networkingv1.IngressClass + if err := c.Get(ctx, client.ObjectKey{Name: ingressClassName}, &ingressClass); err != nil { + return nil, err + } + + if matchesController(ingressClass.Spec.Controller) { + return &ingressClass, nil + } + + return nil, errors.New("ingress class is not controlled by us") +} diff --git a/internal/manager/controllers.go b/internal/manager/controllers.go index ac7fcf4f3..397d19dbd 100644 --- a/internal/manager/controllers.go +++ b/internal/manager/controllers.go @@ -134,6 +134,13 @@ func setupControllers(ctx context.Context, mgr manager.Manager, pro provider.Pro Provider: pro, Updater: updater, }, + &controller.ApisixConsumerReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Log: ctrl.LoggerFrom(ctx).WithName("controllers").WithName("ApisixConsumer"), + Provider: pro, + Updater: updater, + }, &controller.ApisixPluginConfigReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), diff --git a/internal/provider/adc/adc.go b/internal/provider/adc/adc.go index ca80f06e8..48b084cc0 100644 --- a/internal/provider/adc/adc.go +++ b/internal/provider/adc/adc.go @@ -108,6 +108,7 @@ func (d *adcClient) Update(ctx context.Context, tctx *provider.TranslateContext, result, err = d.translator.TranslateGateway(tctx, t.DeepCopy()) resourceTypes = append(resourceTypes, "global_rule", "ssl", "plugin_metadata") case *networkingv1.Ingress: + log.Warnw("ingress is deprecated, please use gateway api instead") result, err = d.translator.TranslateIngress(tctx, t.DeepCopy()) resourceTypes = append(resourceTypes, "service", "ssl") case *v1alpha1.Consumer: @@ -122,6 +123,9 @@ func (d *adcClient) Update(ctx context.Context, tctx *provider.TranslateContext, case *apiv2.ApisixGlobalRule: result, err = d.translator.TranslateApisixGlobalRule(tctx, t.DeepCopy()) resourceTypes = append(resourceTypes, "global_rule") + case *apiv2.ApisixConsumer: + result, err = d.translator.TranslateApisixConsumer(tctx, t.DeepCopy()) + resourceTypes = append(resourceTypes, "consumer") } if err != nil { return err @@ -217,6 +221,9 @@ func (d *adcClient) Delete(ctx context.Context, obj client.Object) error { case *apiv2.ApisixGlobalRule: resourceTypes = append(resourceTypes, "global_rule") labels = label.GenLabel(obj) + case *apiv2.ApisixConsumer: + resourceTypes = append(resourceTypes, "consumer") + labels = label.GenLabel(obj) } rk := utils.NamespacedNameKind(obj) diff --git a/internal/provider/adc/store.go b/internal/provider/adc/store.go index 7a1800f18..1591663b4 100644 --- a/internal/provider/adc/store.go +++ b/internal/provider/adc/store.go @@ -41,6 +41,7 @@ func NewStore() *Store { func (s *Store) Insert(name string, resourceTypes []string, resources adctypes.Resources, Labels map[string]string) error { s.Lock() defer s.Unlock() + log.Warnw("insert resources into cache", zap.Any("resources", resources)) targetCache, ok := s.cacheMap[name] if !ok { db, err := cache.NewMemDBCache() diff --git a/internal/provider/adc/translator/apisixconsumer.go b/internal/provider/adc/translator/apisixconsumer.go new file mode 100644 index 000000000..96fd07be0 --- /dev/null +++ b/internal/provider/adc/translator/apisixconsumer.go @@ -0,0 +1,358 @@ +// 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 translator + +import ( + "fmt" + "strconv" + + adctypes "github.com/apache/apisix-ingress-controller/api/adc" + v2 "github.com/apache/apisix-ingress-controller/api/v2" + "github.com/apache/apisix-ingress-controller/internal/controller/label" + "github.com/apache/apisix-ingress-controller/internal/provider" + "github.com/apache/apisix-ingress-controller/internal/types" + "github.com/pkg/errors" + k8stypes "k8s.io/apimachinery/pkg/types" +) + +var ( + _errKeyNotFoundOrInvalid = errors.New("key \"key\" not found or invalid in secret") + _errUsernameNotFoundOrInvalid = errors.New("key \"username\" not found or invalid in secret") + _errPasswordNotFoundOrInvalid = errors.New("key \"password\" not found or invalid in secret") + + _jwtAuthExpDefaultValue = int64(868400) + + _hmacAuthAlgorithmDefaultValue = "hmac-sha256" + _hmacAuthClockSkewDefaultValue = int64(0) + _hmacAuthKeepHeadersDefaultValue = false + _hmacAuthEncodeURIParamsDefaultValue = true + _hmacAuthValidateRequestBodyDefaultValue = false + _hmacAuthMaxReqBodyDefaultValue = int64(524288) +) + +func (t *Translator) TranslateApisixConsumer(tctx *provider.TranslateContext, ac *v2.ApisixConsumer) (*TranslateResult, error) { + result := &TranslateResult{} + plugins := make(adctypes.Plugins) + if ac.Spec.AuthParameter.KeyAuth != nil { + cfg, err := t.translateConsumerKeyAuthPluginV2(tctx, ac.Namespace, ac.Spec.AuthParameter.KeyAuth) + if err != nil { + return nil, fmt.Errorf("invalid key auth config: %s", err) + } + plugins["key-auth"] = cfg + } else if ac.Spec.AuthParameter.BasicAuth != nil { + cfg, err := t.translateConsumerBasicAuthPluginV2(tctx, ac.Namespace, ac.Spec.AuthParameter.BasicAuth) + if err != nil { + return nil, fmt.Errorf("invalid basic auth config: %s", err) + } + plugins["basic-auth"] = cfg + } else if ac.Spec.AuthParameter.JwtAuth != nil { + cfg, err := t.translateConsumerJwtAuthPluginV2(tctx, ac.Namespace, ac.Spec.AuthParameter.JwtAuth) + if err != nil { + return nil, fmt.Errorf("invalid jwt auth config: %s", err) + } + plugins["jwt-auth"] = cfg + } else if ac.Spec.AuthParameter.WolfRBAC != nil { + cfg, err := t.translateConsumerWolfRBACPluginV2(tctx, ac.Namespace, ac.Spec.AuthParameter.WolfRBAC) + if err != nil { + return nil, fmt.Errorf("invalid wolf rbac config: %s", err) + } + plugins["wolf-rbac"] = cfg + } else if ac.Spec.AuthParameter.HMACAuth != nil { + cfg, err := t.translateConsumerHMACAuthPluginV2(tctx, ac.Namespace, ac.Spec.AuthParameter.HMACAuth) + if err != nil { + return nil, fmt.Errorf("invalid hmac auth config: %s", err) + } + plugins["hmac-auth"] = cfg + } else if ac.Spec.AuthParameter.LDAPAuth != nil { + cfg, err := t.translateConsumerLDAPAuthPluginV2(tctx, ac.Namespace, ac.Spec.AuthParameter.LDAPAuth) + if err != nil { + return nil, fmt.Errorf("invalid ldap auth config: %s", err) + } + plugins["ldap-auth"] = cfg + } + + username := adctypes.ComposeConsumerName(ac.Namespace, ac.Name) + consumer := &adctypes.Consumer{ + Username: username, + } + consumer.Plugins = plugins + consumer.Labels = label.GenLabel(ac) + result.Consumers = append(result.Consumers, consumer) + return result, nil +} + +func (t *Translator) translateConsumerKeyAuthPluginV2(tctx *provider.TranslateContext, consumerNamespace string, cfg *v2.ApisixConsumerKeyAuth) (*types.KeyAuthConsumerConfig, error) { + if cfg.Value != nil { + return &types.KeyAuthConsumerConfig{Key: cfg.Value.Key}, nil + } + + sec := tctx.Secrets[k8stypes.NamespacedName{ + Namespace: consumerNamespace, + Name: cfg.SecretRef.Name, + }] + if sec == nil { + return nil, fmt.Errorf("secret %s/%s not found", consumerNamespace, cfg.SecretRef.Name) + } + raw, ok := sec.Data["key"] + if !ok || len(raw) == 0 { + return nil, _errKeyNotFoundOrInvalid + } + return &types.KeyAuthConsumerConfig{Key: string(raw)}, nil +} + +func (t *Translator) translateConsumerBasicAuthPluginV2(tctx *provider.TranslateContext, consumerNamespace string, cfg *v2.ApisixConsumerBasicAuth) (*types.BasicAuthConsumerConfig, error) { + if cfg.Value != nil { + return &types.BasicAuthConsumerConfig{ + Username: cfg.Value.Username, + Password: cfg.Value.Password, + }, nil + } + + sec := tctx.Secrets[k8stypes.NamespacedName{ + Namespace: consumerNamespace, + Name: cfg.SecretRef.Name, + }] + if sec == nil { + return nil, fmt.Errorf("secret %s/%s not found", consumerNamespace, cfg.SecretRef.Name) + } + raw1, ok := sec.Data["username"] + if !ok || len(raw1) == 0 { + return nil, _errUsernameNotFoundOrInvalid + } + raw2, ok := sec.Data["password"] + if !ok || len(raw2) == 0 { + return nil, _errPasswordNotFoundOrInvalid + } + return &types.BasicAuthConsumerConfig{ + Username: string(raw1), + Password: string(raw2), + }, nil +} + +func (t *Translator) translateConsumerWolfRBACPluginV2(tctx *provider.TranslateContext, consumerNamespace string, cfg *v2.ApisixConsumerWolfRBAC) (*types.WolfRBACConsumerConfig, error) { + if cfg.Value != nil { + return &types.WolfRBACConsumerConfig{ + Server: cfg.Value.Server, + Appid: cfg.Value.Appid, + HeaderPrefix: cfg.Value.HeaderPrefix, + }, nil + } + sec := tctx.Secrets[k8stypes.NamespacedName{ + Namespace: consumerNamespace, + Name: cfg.SecretRef.Name, + }] + if sec == nil { + return nil, fmt.Errorf("secret %s/%s not found", consumerNamespace, cfg.SecretRef.Name) + } + raw1 := sec.Data["server"] + raw2 := sec.Data["appid"] + raw3 := sec.Data["header_prefix"] + return &types.WolfRBACConsumerConfig{ + Server: string(raw1), + Appid: string(raw2), + HeaderPrefix: string(raw3), + }, nil +} + +func (t *Translator) translateConsumerJwtAuthPluginV2(tctx *provider.TranslateContext, consumerNamespace string, cfg *v2.ApisixConsumerJwtAuth) (*types.JwtAuthConsumerConfig, error) { + if cfg.Value != nil { + // The field exp must be a positive integer, default value 86400. + if cfg.Value.Exp < 1 { + cfg.Value.Exp = _jwtAuthExpDefaultValue + } + return &types.JwtAuthConsumerConfig{ + Key: cfg.Value.Key, + Secret: cfg.Value.Secret, + PublicKey: cfg.Value.PublicKey, + PrivateKey: cfg.Value.PrivateKey, + Algorithm: cfg.Value.Algorithm, + Exp: cfg.Value.Exp, + Base64Secret: cfg.Value.Base64Secret, + LifetimeGracePeriod: cfg.Value.LifetimeGracePeriod, + }, nil + } + + sec := tctx.Secrets[k8stypes.NamespacedName{ + Namespace: consumerNamespace, + Name: cfg.SecretRef.Name, + }] + if sec == nil { + return nil, fmt.Errorf("secret %s/%s not found", consumerNamespace, cfg.SecretRef.Name) + } + keyRaw, ok := sec.Data["key"] + if !ok || len(keyRaw) == 0 { + return nil, _errKeyNotFoundOrInvalid + } + base64SecretRaw := sec.Data["base64_secret"] + var base64Secret bool + if string(base64SecretRaw) == "true" { + base64Secret = true + } + expRaw := sec.Data["exp"] + exp, _ := strconv.ParseInt(string(expRaw), 10, 64) + // The field exp must be a positive integer, default value 86400. + if exp < 1 { + exp = _jwtAuthExpDefaultValue + } + lifetimeGracePeriodRaw := sec.Data["lifetime_grace_period"] + lifetimeGracePeriod, _ := strconv.ParseInt(string(lifetimeGracePeriodRaw), 10, 64) + secretRaw := sec.Data["secret"] + publicKeyRaw := sec.Data["public_key"] + privateKeyRaw := sec.Data["private_key"] + algorithmRaw := sec.Data["algorithm"] + + return &types.JwtAuthConsumerConfig{ + Key: string(keyRaw), + Secret: string(secretRaw), + PublicKey: string(publicKeyRaw), + PrivateKey: string(privateKeyRaw), + Algorithm: string(algorithmRaw), + Exp: exp, + Base64Secret: base64Secret, + LifetimeGracePeriod: lifetimeGracePeriod, + }, nil +} + +func (t *Translator) translateConsumerHMACAuthPluginV2(tctx *provider.TranslateContext, consumerNamespace string, cfg *v2.ApisixConsumerHMACAuth) (*types.HMACAuthConsumerConfig, error) { + if cfg.Value != nil { + return &types.HMACAuthConsumerConfig{ + AccessKey: cfg.Value.AccessKey, + SecretKey: cfg.Value.SecretKey, + Algorithm: cfg.Value.Algorithm, + ClockSkew: cfg.Value.ClockSkew, + SignedHeaders: cfg.Value.SignedHeaders, + KeepHeaders: cfg.Value.KeepHeaders, + EncodeURIParams: cfg.Value.EncodeURIParams, + ValidateRequestBody: cfg.Value.ValidateRequestBody, + MaxReqBody: cfg.Value.MaxReqBody, + }, nil + } + + sec := tctx.Secrets[k8stypes.NamespacedName{ + Namespace: consumerNamespace, + Name: cfg.SecretRef.Name, + }] + if sec == nil { + return nil, fmt.Errorf("secret %s/%s not found", consumerNamespace, cfg.SecretRef.Name) + } + + accessKeyRaw, ok := sec.Data["access_key"] + if !ok || len(accessKeyRaw) == 0 { + return nil, _errKeyNotFoundOrInvalid + } + + secretKeyRaw, ok := sec.Data["secret_key"] + if !ok || len(secretKeyRaw) == 0 { + return nil, _errKeyNotFoundOrInvalid + } + + algorithmRaw, ok := sec.Data["algorithm"] + var algorithm string + if !ok { + algorithm = _hmacAuthAlgorithmDefaultValue + } else { + algorithm = string(algorithmRaw) + } + + clockSkewRaw := sec.Data["clock_skew"] + clockSkew, _ := strconv.ParseInt(string(clockSkewRaw), 10, 64) + if clockSkew < 0 { + clockSkew = _hmacAuthClockSkewDefaultValue + } + + var signedHeaders []string + signedHeadersRaw := sec.Data["signed_headers"] + for _, b := range signedHeadersRaw { + signedHeaders = append(signedHeaders, string(b)) + } + + var keepHeader bool + keepHeaderRaw, ok := sec.Data["keep_headers"] + if !ok { + keepHeader = _hmacAuthKeepHeadersDefaultValue + } else { + if string(keepHeaderRaw) == "true" { + keepHeader = true + } else { + keepHeader = false + } + } + + var encodeURIParams bool + encodeURIParamsRaw, ok := sec.Data["encode_uri_params"] + if !ok { + encodeURIParams = _hmacAuthEncodeURIParamsDefaultValue + } else { + if string(encodeURIParamsRaw) == "true" { + encodeURIParams = true + } else { + encodeURIParams = false + } + } + + var validateRequestBody bool + validateRequestBodyRaw, ok := sec.Data["validate_request_body"] + if !ok { + validateRequestBody = _hmacAuthValidateRequestBodyDefaultValue + } else { + if string(validateRequestBodyRaw) == "true" { + validateRequestBody = true + } else { + validateRequestBody = false + } + } + + maxReqBodyRaw := sec.Data["max_req_body"] + maxReqBody, _ := strconv.ParseInt(string(maxReqBodyRaw), 10, 64) + if maxReqBody < 0 { + maxReqBody = _hmacAuthMaxReqBodyDefaultValue + } + + return &types.HMACAuthConsumerConfig{ + AccessKey: string(accessKeyRaw), + SecretKey: string(secretKeyRaw), + Algorithm: algorithm, + ClockSkew: clockSkew, + SignedHeaders: signedHeaders, + KeepHeaders: keepHeader, + EncodeURIParams: encodeURIParams, + ValidateRequestBody: validateRequestBody, + MaxReqBody: maxReqBody, + }, nil +} + +func (t *Translator) translateConsumerLDAPAuthPluginV2(tctx *provider.TranslateContext, consumerNamespace string, cfg *v2.ApisixConsumerLDAPAuth) (*types.LDAPAuthConsumerConfig, error) { + if cfg.Value != nil { + return &types.LDAPAuthConsumerConfig{ + UserDN: cfg.Value.UserDN, + }, nil + } + + sec := tctx.Secrets[k8stypes.NamespacedName{ + Namespace: consumerNamespace, + Name: cfg.SecretRef.Name, + }] + if sec == nil { + return nil, fmt.Errorf("secret %s/%s not found", consumerNamespace, cfg.SecretRef.Name) + } + userDNRaw, ok := sec.Data["user_dn"] + if !ok || len(userDNRaw) == 0 { + return nil, _errKeyNotFoundOrInvalid + } + + return &types.LDAPAuthConsumerConfig{ + UserDN: string(userDNRaw), + }, nil +} diff --git a/internal/provider/adc/translator/apisixroute.go b/internal/provider/adc/translator/apisixroute.go index 6936cdda6..f9ab6f29a 100644 --- a/internal/provider/adc/translator/apisixroute.go +++ b/internal/provider/adc/translator/apisixroute.go @@ -129,9 +129,9 @@ func (t *Translator) loadRoutePlugins(tctx *provider.TranslateContext, ar *apiv2 func (t *Translator) buildPluginConfig(plugin apiv2.ApisixRoutePlugin, namespace string, secrets map[types.NamespacedName]*v1.Secret) map[string]any { config := make(map[string]any) - if plugin.Config != nil { - for key, value := range plugin.Config { - config[key] = json.RawMessage(value.Raw) + if len(plugin.Config.Raw) > 0 { + if err := json.Unmarshal(plugin.Config.Raw, &config); err != nil { + t.Log.Error(err, "failed to unmarshal plugin config") } } if plugin.SecretRef != "" { diff --git a/internal/provider/adc/translator/consumer.go b/internal/provider/adc/translator/consumer.go index 2b5cb9641..82841967e 100644 --- a/internal/provider/adc/translator/consumer.go +++ b/internal/provider/adc/translator/consumer.go @@ -19,6 +19,7 @@ import ( adctypes "github.com/apache/apisix-ingress-controller/api/adc" "github.com/apache/apisix-ingress-controller/api/v1alpha1" + "github.com/apache/apisix-ingress-controller/internal/controller/label" "github.com/apache/apisix-ingress-controller/internal/provider" ) @@ -65,14 +66,16 @@ func (t *Translator) TranslateConsumerV1alpha1(tctx *provider.TranslateContext, credentials = append(credentials, credential) } consumer.Credentials = credentials - + consumer.Labels = label.GenLabel(consumerV) plugins := adctypes.Plugins{} for _, plugin := range consumerV.Spec.Plugins { pluginName := plugin.Name pluginConfig := make(map[string]any) - if err := json.Unmarshal(plugin.Config.Raw, &pluginConfig); err != nil { - t.Log.Error(err, "failed to unmarshal plugin config", "plugin", plugin) - continue + if len(plugin.Config.Raw) > 0 { + if err := json.Unmarshal(plugin.Config.Raw, &pluginConfig); err != nil { + t.Log.Error(err, "failed to unmarshal plugin config", "plugin", plugin) + continue + } } plugins[pluginName] = pluginConfig } diff --git a/internal/provider/adc/translator/gateway.go b/internal/provider/adc/translator/gateway.go index cdef8b97c..0c35f1205 100644 --- a/internal/provider/adc/translator/gateway.go +++ b/internal/provider/adc/translator/gateway.go @@ -209,15 +209,16 @@ func (t *Translator) fillPluginsFromGatewayProxy(plugins adctypes.GlobalRule, ga } pluginName := plugin.Name - var pluginConfig map[string]any - if err := json.Unmarshal(plugin.Config.Raw, &pluginConfig); err != nil { - log.Errorw("gateway proxy plugin config unmarshal failed", zap.Error(err), zap.String("plugin", pluginName)) - continue + pluginConfig := map[string]any{} + if len(plugin.Config.Raw) > 0 { + if err := json.Unmarshal(plugin.Config.Raw, &pluginConfig); err != nil { + log.Errorw("gateway proxy plugin config unmarshal failed", zap.Error(err), zap.String("plugin", pluginName)) + continue + } } - - log.Debugw("fill plugin from gateway proxy", zap.String("plugin", pluginName), zap.Any("config", pluginConfig)) plugins[pluginName] = pluginConfig } + log.Debugw("fill plugins for gateway proxy", zap.Any("plugins", plugins)) } func (t *Translator) fillPluginMetadataFromGatewayProxy(pluginMetadata adctypes.PluginMetadata, gatewayProxy *v1alpha1.GatewayProxy) { diff --git a/internal/provider/adc/translator/globalrule.go b/internal/provider/adc/translator/globalrule.go index 525ba4d21..757831cab 100644 --- a/internal/provider/adc/translator/globalrule.go +++ b/internal/provider/adc/translator/globalrule.go @@ -40,22 +40,11 @@ func (t *Translator) TranslateApisixGlobalRule(tctx *provider.TranslateContext, continue } - // Parse plugin configuration - var pluginConfig map[string]any - if plugin.Config != nil { - pluginConfig = make(map[string]any) - // Convert map[string]apiextensionsv1.JSON to map[string]any - for key, jsonValue := range plugin.Config { - var value any - if err := json.Unmarshal(jsonValue.Raw, &value); err != nil { - log.Errorw("failed to parse plugin config", - zap.String("plugin", plugin.Name), - zap.String("key", key), - zap.Error(err), - ) - return nil, err - } - pluginConfig[key] = value + pluginConfig := make(map[string]any) + if len(plugin.Config.Raw) > 0 { + if err := json.Unmarshal(plugin.Config.Raw, &pluginConfig); err != nil { + log.Errorw("failed to unmarshal plugin config", zap.String("plugin", plugin.Name), zap.Error(err)) + continue } } plugins[plugin.Name] = pluginConfig diff --git a/internal/provider/adc/translator/httproute.go b/internal/provider/adc/translator/httproute.go index 1e8b5973f..12866441c 100644 --- a/internal/provider/adc/translator/httproute.go +++ b/internal/provider/adc/translator/httproute.go @@ -72,7 +72,7 @@ func (t *Translator) fillPluginFromExtensionRef(plugins adctypes.Plugins, namesp } for _, plugin := range pluginconfig.Spec.Plugins { pluginName := plugin.Name - var pluginconfig map[string]any + pluginconfig := make(map[string]any) if len(plugin.Config.Raw) > 0 { if err := json.Unmarshal(plugin.Config.Raw, &pluginconfig); err != nil { log.Errorw("plugin config unmarshal failed", zap.Error(err)) diff --git a/internal/types/plugin.go b/internal/types/plugin.go new file mode 100644 index 000000000..ebf1bce82 --- /dev/null +++ b/internal/types/plugin.go @@ -0,0 +1,282 @@ +// 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 types + +import ( + "encoding/json" + "fmt" + "strings" + + "github.com/incubator4/go-resty-expr/expr" +) + +// TrafficSplitConfig is the config of traffic-split plugin. +// +k8s:deepcopy-gen=true +type TrafficSplitConfig struct { + Rules []TrafficSplitConfigRule `json:"rules"` +} + +// TrafficSplitConfigRule is the rule config in traffic-split plugin config. +// +k8s:deepcopy-gen=true +type TrafficSplitConfigRule struct { + WeightedUpstreams []TrafficSplitConfigRuleWeightedUpstream `json:"weighted_upstreams"` +} + +// TrafficSplitConfigRuleWeightedUpstream is the weighted upstream config in +// the traffic split plugin rule. +// +k8s:deepcopy-gen=true +type TrafficSplitConfigRuleWeightedUpstream struct { + UpstreamID string `json:"upstream_id,omitempty"` + Weight int `json:"weight"` +} + +// IPRestrictConfig is the rule config for ip-restriction plugin. +// +k8s:deepcopy-gen=true +type IPRestrictConfig struct { + Allowlist []string `json:"whitelist,omitempty"` + Blocklist []string `json:"blacklist,omitempty"` +} + +// CorsConfig is the rule config for cors plugin. +// +k8s:deepcopy-gen=true +type CorsConfig struct { + AllowOrigins string `json:"allow_origins,omitempty"` + AllowMethods string `json:"allow_methods,omitempty"` + AllowHeaders string `json:"allow_headers,omitempty"` +} + +// CSRfConfig is the rule config for csrf plugin. +// +k8s:deepcopy-gen=true +type CSRFConfig struct { + Key string `json:"key"` +} + +// KeyAuthConsumerConfig is the rule config for key-auth plugin +// used in Consumer object. +// +k8s:deepcopy-gen=true +type KeyAuthConsumerConfig struct { + Key string `json:"key"` +} + +// KeyAuthRouteConfig is the rule config for key-auth plugin +// used in Route object. +type KeyAuthRouteConfig struct { + Header string `json:"header,omitempty"` +} + +// BasicAuthConsumerConfig is the rule config for basic-auth plugin +// used in Consumer object. +// +k8s:deepcopy-gen=true +type BasicAuthConsumerConfig struct { + Username string `json:"username"` + Password string `json:"password"` +} + +// JwtAuthConsumerConfig is the rule config for jwt-auth plugin +// used in Consumer object. +// +k8s:deepcopy-gen=true +type JwtAuthConsumerConfig struct { + Key string `json:"key" yaml:"key"` + Secret string `json:"secret,omitempty" yaml:"secret,omitempty"` + PublicKey string `json:"public_key,omitempty" yaml:"public_key,omitempty"` + PrivateKey string `json:"private_key" yaml:"private_key,omitempty"` + Algorithm string `json:"algorithm,omitempty" yaml:"algorithm,omitempty"` + Exp int64 `json:"exp,omitempty" yaml:"exp,omitempty"` + Base64Secret bool `json:"base64_secret,omitempty" yaml:"base64_secret,omitempty"` + LifetimeGracePeriod int64 `json:"lifetime_grace_period,omitempty" yaml:"lifetime_grace_period,omitempty"` +} + +// HMACAuthConsumerConfig is the rule config for hmac-auth plugin +// used in Consumer object. +// +k8s:deepcopy-gen=true +type HMACAuthConsumerConfig struct { + AccessKey string `json:"access_key" yaml:"access_key"` + SecretKey string `json:"secret_key" yaml:"secret_key"` + Algorithm string `json:"algorithm,omitempty" yaml:"algorithm,omitempty"` + ClockSkew int64 `json:"clock_skew,omitempty" yaml:"clock_skew,omitempty"` + SignedHeaders []string `json:"signed_headers,omitempty" yaml:"signed_headers,omitempty"` + KeepHeaders bool `json:"keep_headers,omitempty" yaml:"keep_headers,omitempty"` + EncodeURIParams bool `json:"encode_uri_params,omitempty" yaml:"encode_uri_params,omitempty"` + ValidateRequestBody bool `json:"validate_request_body,omitempty" yaml:"validate_request_body,omitempty"` + MaxReqBody int64 `json:"max_req_body,omitempty" yaml:"max_req_body,omitempty"` +} + +// LDAPAuthConsumerConfig is the rule config for ldap-auth plugin +// used in Consumer object. +// +k8s:deepcopy-gen=true +type LDAPAuthConsumerConfig struct { + UserDN string `json:"user_dn"` +} + +// BasicAuthRouteConfig is the rule config for basic-auth plugin +// used in Route object. +// +k8s:deepcopy-gen=true +type BasicAuthRouteConfig struct{} + +// WolfRBACConsumerConfig is the rule config for wolf-rbac plugin +// used in Consumer object. +// +k8s:deepcopy-gen=true +type WolfRBACConsumerConfig struct { + Server string `json:"server,omitempty"` + Appid string `json:"appid,omitempty"` + HeaderPrefix string `json:"header_prefix,omitempty"` +} + +// RewriteConfig is the rule config for proxy-rewrite plugin. +// +k8s:deepcopy-gen=true +type RewriteConfig struct { + RewriteTarget string `json:"uri,omitempty"` + RewriteTargetRegex []string `json:"regex_uri,omitempty"` + Headers Headers `json:"headers,omitempty"` +} + +// ResponseRewriteConfig is the rule config for response-rewrite plugin. +// +k8s:deepcopy-gen=true +type ResponseRewriteConfig struct { + StatusCode int `json:"status_code,omitempty"` + Body string `json:"body,omitempty"` + BodyBase64 bool `json:"body_base64,omitempty"` + Headers Headers `json:"headers,omitempty"` + LuaRestyExpr []expr.Expr `json:"vars,omitempty"` + Filters []map[string]string `json:"filters,omitempty"` +} + +// RedirectConfig is the rule config for redirect plugin. +// +k8s:deepcopy-gen=true +type RedirectConfig struct { + HttpToHttps bool `json:"http_to_https,omitempty"` + URI string `json:"uri,omitempty"` + RetCode int `json:"ret_code,omitempty"` +} + +// ForwardAuthConfig is the rule config for forward-auth plugin. +// +k8s:deepcopy-gen=true +type ForwardAuthConfig struct { + URI string `json:"uri"` + SSLVerify bool `json:"ssl_verify"` + RequestHeaders []string `json:"request_headers,omitempty"` + UpstreamHeaders []string `json:"upstream_headers,omitempty"` + ClientHeaders []string `json:"client_headers,omitempty"` +} + +// BasicAuthConfig is the rule config for basic-auth plugin. +// +k8s:deepcopy-gen=true +type BasicAuthConfig struct { +} + +// KeyAuthConfig is the rule config for key-auth plugin. +// +k8s:deepcopy-gen=true +type KeyAuthConfig struct { +} + +// RequestMirror is the rule config for proxy-mirror plugin. +// +k8s:deepcopy-gen=true +type RequestMirror struct { + Host string `json:"host"` +} + +type Headers map[string]any + +func (p *Headers) DeepCopyInto(out *Headers) { + b, _ := json.Marshal(&p) + _ = json.Unmarshal(b, out) +} + +func (p *Headers) DeepCopy() *Headers { + if p == nil { + return nil + } + out := new(Headers) + p.DeepCopyInto(out) + return out +} + +func (p *Headers) Add(headersToAdd []string) { + if p == nil { + return + } + if headersToAdd != nil { + addedHeader := make([]string, 0) + for _, h := range headersToAdd { + kv := strings.Split(h, ":") + if len(kv) < 2 { + continue + } + addedHeader = append(addedHeader, fmt.Sprintf("%s:%s", kv[0], kv[1])) + } + (*p)["add"] = addedHeader + } +} + +func (p *Headers) GetAddedHeaders() []string { + if p == nil || (*p)["add"] == nil { + return nil + } + addedheaders, ok := (*p)["add"].([]string) + if ok { + return addedheaders + } + return nil +} + +func (p *Headers) Set(headersToSet []string) { + if p == nil { + return + } + if headersToSet != nil { + setHeaders := make(map[string]string, 0) + for _, h := range headersToSet { + kv := strings.Split(h, ":") + if len(kv) < 2 { + continue + } + setHeaders[kv[0]] = kv[1] + } + (*p)["set"] = setHeaders + } +} + +func (p *Headers) GetSetHeaders() map[string]string { + if p == nil || (*p)["set"] == nil { + return nil + } + addedheaders, ok := (*p)["set"].(map[string]string) + if ok { + return addedheaders + } + return nil +} + +func (p *Headers) Remove(headersToRemove []string) { + if p == nil { + return + } + if headersToRemove != nil { + removeHeaders := make([]string, 0) + removeHeaders = append(removeHeaders, headersToRemove...) + (*p)["remove"] = removeHeaders + } +} + +func (p *Headers) GetRemovedHeaders() []string { + if p == nil || (*p)["remove"] == nil { + return nil + } + removedHeaders, ok := (*p)["remove"].([]string) + if ok { + return removedHeaders + } + return nil +} diff --git a/internal/types/zz_generated.deepcopy.go b/internal/types/zz_generated.deepcopy.go new file mode 100644 index 000000000..3f2566ada --- /dev/null +++ b/internal/types/zz_generated.deepcopy.go @@ -0,0 +1,394 @@ +//go:build !ignore_autogenerated + +/* +Copyright 2024. + +Licensed 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. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package types + +import ( + "github.com/incubator4/go-resty-expr/expr" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BasicAuthConfig) DeepCopyInto(out *BasicAuthConfig) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BasicAuthConfig. +func (in *BasicAuthConfig) DeepCopy() *BasicAuthConfig { + if in == nil { + return nil + } + out := new(BasicAuthConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BasicAuthConsumerConfig) DeepCopyInto(out *BasicAuthConsumerConfig) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BasicAuthConsumerConfig. +func (in *BasicAuthConsumerConfig) DeepCopy() *BasicAuthConsumerConfig { + if in == nil { + return nil + } + out := new(BasicAuthConsumerConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BasicAuthRouteConfig) DeepCopyInto(out *BasicAuthRouteConfig) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BasicAuthRouteConfig. +func (in *BasicAuthRouteConfig) DeepCopy() *BasicAuthRouteConfig { + if in == nil { + return nil + } + out := new(BasicAuthRouteConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CSRFConfig) DeepCopyInto(out *CSRFConfig) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CSRFConfig. +func (in *CSRFConfig) DeepCopy() *CSRFConfig { + if in == nil { + return nil + } + out := new(CSRFConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CorsConfig) DeepCopyInto(out *CorsConfig) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CorsConfig. +func (in *CorsConfig) DeepCopy() *CorsConfig { + if in == nil { + return nil + } + out := new(CorsConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ForwardAuthConfig) DeepCopyInto(out *ForwardAuthConfig) { + *out = *in + if in.RequestHeaders != nil { + in, out := &in.RequestHeaders, &out.RequestHeaders + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.UpstreamHeaders != nil { + in, out := &in.UpstreamHeaders, &out.UpstreamHeaders + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.ClientHeaders != nil { + in, out := &in.ClientHeaders, &out.ClientHeaders + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ForwardAuthConfig. +func (in *ForwardAuthConfig) DeepCopy() *ForwardAuthConfig { + if in == nil { + return nil + } + out := new(ForwardAuthConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HMACAuthConsumerConfig) DeepCopyInto(out *HMACAuthConsumerConfig) { + *out = *in + if in.SignedHeaders != nil { + in, out := &in.SignedHeaders, &out.SignedHeaders + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HMACAuthConsumerConfig. +func (in *HMACAuthConsumerConfig) DeepCopy() *HMACAuthConsumerConfig { + if in == nil { + return nil + } + out := new(HMACAuthConsumerConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *IPRestrictConfig) DeepCopyInto(out *IPRestrictConfig) { + *out = *in + if in.Allowlist != nil { + in, out := &in.Allowlist, &out.Allowlist + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Blocklist != nil { + in, out := &in.Blocklist, &out.Blocklist + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IPRestrictConfig. +func (in *IPRestrictConfig) DeepCopy() *IPRestrictConfig { + if in == nil { + return nil + } + out := new(IPRestrictConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *JwtAuthConsumerConfig) DeepCopyInto(out *JwtAuthConsumerConfig) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JwtAuthConsumerConfig. +func (in *JwtAuthConsumerConfig) DeepCopy() *JwtAuthConsumerConfig { + if in == nil { + return nil + } + out := new(JwtAuthConsumerConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KeyAuthConfig) DeepCopyInto(out *KeyAuthConfig) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KeyAuthConfig. +func (in *KeyAuthConfig) DeepCopy() *KeyAuthConfig { + if in == nil { + return nil + } + out := new(KeyAuthConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KeyAuthConsumerConfig) DeepCopyInto(out *KeyAuthConsumerConfig) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KeyAuthConsumerConfig. +func (in *KeyAuthConsumerConfig) DeepCopy() *KeyAuthConsumerConfig { + if in == nil { + return nil + } + out := new(KeyAuthConsumerConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LDAPAuthConsumerConfig) DeepCopyInto(out *LDAPAuthConsumerConfig) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LDAPAuthConsumerConfig. +func (in *LDAPAuthConsumerConfig) DeepCopy() *LDAPAuthConsumerConfig { + if in == nil { + return nil + } + out := new(LDAPAuthConsumerConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RedirectConfig) DeepCopyInto(out *RedirectConfig) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RedirectConfig. +func (in *RedirectConfig) DeepCopy() *RedirectConfig { + if in == nil { + return nil + } + out := new(RedirectConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RequestMirror) DeepCopyInto(out *RequestMirror) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RequestMirror. +func (in *RequestMirror) DeepCopy() *RequestMirror { + if in == nil { + return nil + } + out := new(RequestMirror) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ResponseRewriteConfig) DeepCopyInto(out *ResponseRewriteConfig) { + *out = *in + in.Headers.DeepCopyInto(&out.Headers) + if in.LuaRestyExpr != nil { + in, out := &in.LuaRestyExpr, &out.LuaRestyExpr + *out = make([]expr.Expr, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Filters != nil { + in, out := &in.Filters, &out.Filters + *out = make([]map[string]string, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResponseRewriteConfig. +func (in *ResponseRewriteConfig) DeepCopy() *ResponseRewriteConfig { + if in == nil { + return nil + } + out := new(ResponseRewriteConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RewriteConfig) DeepCopyInto(out *RewriteConfig) { + *out = *in + if in.RewriteTargetRegex != nil { + in, out := &in.RewriteTargetRegex, &out.RewriteTargetRegex + *out = make([]string, len(*in)) + copy(*out, *in) + } + in.Headers.DeepCopyInto(&out.Headers) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RewriteConfig. +func (in *RewriteConfig) DeepCopy() *RewriteConfig { + if in == nil { + return nil + } + out := new(RewriteConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TrafficSplitConfig) DeepCopyInto(out *TrafficSplitConfig) { + *out = *in + if in.Rules != nil { + in, out := &in.Rules, &out.Rules + *out = make([]TrafficSplitConfigRule, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrafficSplitConfig. +func (in *TrafficSplitConfig) DeepCopy() *TrafficSplitConfig { + if in == nil { + return nil + } + out := new(TrafficSplitConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TrafficSplitConfigRule) DeepCopyInto(out *TrafficSplitConfigRule) { + *out = *in + if in.WeightedUpstreams != nil { + in, out := &in.WeightedUpstreams, &out.WeightedUpstreams + *out = make([]TrafficSplitConfigRuleWeightedUpstream, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrafficSplitConfigRule. +func (in *TrafficSplitConfigRule) DeepCopy() *TrafficSplitConfigRule { + if in == nil { + return nil + } + out := new(TrafficSplitConfigRule) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TrafficSplitConfigRuleWeightedUpstream) DeepCopyInto(out *TrafficSplitConfigRuleWeightedUpstream) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrafficSplitConfigRuleWeightedUpstream. +func (in *TrafficSplitConfigRuleWeightedUpstream) DeepCopy() *TrafficSplitConfigRuleWeightedUpstream { + if in == nil { + return nil + } + out := new(TrafficSplitConfigRuleWeightedUpstream) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WolfRBACConsumerConfig) DeepCopyInto(out *WolfRBACConsumerConfig) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WolfRBACConsumerConfig. +func (in *WolfRBACConsumerConfig) DeepCopy() *WolfRBACConsumerConfig { + if in == nil { + return nil + } + out := new(WolfRBACConsumerConfig) + in.DeepCopyInto(out) + return out +} diff --git a/test/e2e/api7/gatewayproxy.go b/test/e2e/api7/gatewayproxy.go new file mode 100644 index 000000000..6e589d9ad --- /dev/null +++ b/test/e2e/api7/gatewayproxy.go @@ -0,0 +1,267 @@ +// Licensed 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 gatewayapi + +import ( + "fmt" + "net/http" + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/apache/apisix-ingress-controller/test/e2e/scaffold" +) + +var _ = Describe("Test GatewayProxy", func() { + s := scaffold.NewDefaultScaffold() + + var defaultGatewayClass = ` +apiVersion: gateway.networking.k8s.io/v1 +kind: GatewayClass +metadata: + name: %s +spec: + controllerName: %s +` + + var gatewayWithProxy = ` +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: apisix +spec: + gatewayClassName: %s + listeners: + - name: http + protocol: HTTP + port: 80 + infrastructure: + parametersRef: + group: apisix.apache.org + kind: GatewayProxy + name: apisix-proxy-config +` + + var gatewayProxyWithEnabledPlugin = ` +apiVersion: apisix.apache.org/v1alpha1 +kind: GatewayProxy +metadata: + name: apisix-proxy-config +spec: + provider: + type: ControlPlane + controlPlane: + endpoints: + - %s + auth: + type: AdminKey + adminKey: + value: "%s" + plugins: + - name: response-rewrite + enabled: true + config: + headers: + X-Proxy-Test: "enabled" +` + var ( + gatewayProxyWithPluginMetadata0 = ` +apiVersion: apisix.apache.org/v1alpha1 +kind: GatewayProxy +metadata: + name: apisix-proxy-config +spec: + provider: + type: ControlPlane + controlPlane: + endpoints: + - %s + auth: + type: AdminKey + adminKey: + value: "%s" + plugins: + - name: error-page + enabled: true + config: {} + pluginMetadata: + error-page: { + "enable": true, + "error_404": { + "body": "404 from plugin metadata", + "content-type": "text/plain" + } + } +` + gatewayProxyWithPluginMetadata1 = ` +apiVersion: apisix.apache.org/v1alpha1 +kind: GatewayProxy +metadata: + name: apisix-proxy-config +spec: + provider: + type: ControlPlane + controlPlane: + endpoints: + - %s + auth: + type: AdminKey + adminKey: + value: "%s" + plugins: + - name: error-page + enabled: true + config: {} + pluginMetadata: + error-page: { + "enable": false, + "error_404": { + "body": "404 from plugin metadata", + "content-type": "text/plain" + } + } +` + ) + + var httpRouteForTest = ` +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: test-route +spec: + parentRefs: + - name: %s + hostnames: + - example.com + rules: + - matches: + - path: + type: Exact + value: /get + backendRefs: + - name: httpbin-service-e2e-test + port: 80 +` + + var resourceApplied = func(resourceType, resourceName, resourceRaw string, observedGeneration int) { + Expect(s.CreateResourceFromString(resourceRaw)). + NotTo(HaveOccurred(), fmt.Sprintf("creating %s", resourceType)) + + Eventually(func() string { + hryaml, err := s.GetResourceYaml(resourceType, resourceName) + Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("getting %s yaml", resourceType)) + return hryaml + }).WithTimeout(8*time.Second).ProbeEvery(2*time.Second). + Should( + SatisfyAll( + ContainSubstring(`status: "True"`), + ContainSubstring(fmt.Sprintf("observedGeneration: %d", observedGeneration)), + ), + fmt.Sprintf("checking %s condition status", resourceType), + ) + time.Sleep(3 * time.Second) + } + + var ( + gatewayClassName string + ) + + BeforeEach(func() { + By("Create GatewayClass") + gatewayClassName = fmt.Sprintf("apisix-%d", time.Now().Unix()) + err := s.CreateResourceFromStringWithNamespace(fmt.Sprintf(defaultGatewayClass, gatewayClassName, s.GetControllerName()), "") + Expect(err).NotTo(HaveOccurred(), "creating GatewayClass") + time.Sleep(5 * time.Second) + + By("Check GatewayClass condition") + gcYaml, err := s.GetResourceYaml("GatewayClass", gatewayClassName) + Expect(err).NotTo(HaveOccurred(), "getting GatewayClass yaml") + Expect(gcYaml).To(ContainSubstring(`status: "True"`), "checking GatewayClass condition status") + Expect(gcYaml).To(ContainSubstring("message: the gatewayclass has been accepted by the apisix-ingress-controller"), "checking GatewayClass condition message") + + By("Create GatewayProxy with enabled plugin") + err = s.CreateResourceFromString(fmt.Sprintf(gatewayProxyWithEnabledPlugin, s.Deployer.GetAdminEndpoint(), s.AdminKey())) + Expect(err).NotTo(HaveOccurred(), "creating GatewayProxy with enabled plugin") + time.Sleep(5 * time.Second) + + By("Create Gateway with GatewayProxy") + err = s.CreateResourceFromStringWithNamespace(fmt.Sprintf(gatewayWithProxy, gatewayClassName), s.Namespace()) + Expect(err).NotTo(HaveOccurred(), "creating Gateway with GatewayProxy") + time.Sleep(5 * time.Second) + + By("check Gateway condition") + gwyaml, err := s.GetResourceYaml("Gateway", "apisix") + Expect(err).NotTo(HaveOccurred(), "getting Gateway yaml") + Expect(gwyaml).To(ContainSubstring(`status: "True"`), "checking Gateway condition status") + Expect(gwyaml).To(ContainSubstring("message: the gateway has been accepted by the apisix-ingress-controller"), "checking Gateway condition message") + }) + + AfterEach(func() { + By("Clean up resources") + _ = s.DeleteResourceFromString(fmt.Sprintf(httpRouteForTest, "apisix")) + _ = s.DeleteResourceFromString(fmt.Sprintf(gatewayWithProxy, gatewayClassName)) + _ = s.DeleteResourceFromString(fmt.Sprintf(gatewayProxyWithEnabledPlugin, s.Deployer.GetAdminEndpoint(), s.AdminKey())) + }) + + Context("Test Gateway with PluginMetadata", func() { + var ( + err error + ) + + PIt("Should work OK with error-page", func() { + By("Update GatewayProxy with PluginMetadata") + err = s.CreateResourceFromString(fmt.Sprintf(gatewayProxyWithPluginMetadata0, s.Deployer.GetAdminEndpoint(), s.AdminKey())) + Expect(err).ShouldNot(HaveOccurred()) + time.Sleep(5 * time.Second) + + By("Create HTTPRoute for Gateway with GatewayProxy") + resourceApplied("HTTPRoute", "test-route", fmt.Sprintf(httpRouteForTest, "apisix"), 1) + + time.Sleep(5 * time.Second) + By("Check PluginMetadata working") + s.NewAPISIXClient(). + GET("/not-found"). + WithHost("example.com"). + Expect(). + Status(http.StatusNotFound). + Body().Contains("404 from plugin metadata") + + By("Update GatewayProxy with PluginMetadata") + err = s.CreateResourceFromString(fmt.Sprintf(gatewayProxyWithPluginMetadata1, s.Deployer.GetAdminEndpoint(), s.AdminKey())) + Expect(err).ShouldNot(HaveOccurred()) + time.Sleep(5 * time.Second) + + By("Check PluginMetadata working") + s.NewAPISIXClient(). + GET("/not-found"). + WithHost("example.com"). + Expect(). + Status(http.StatusNotFound). + Body().Contains(`{"error_msg":"404 Route Not Found"}`) + + By("Delete GatewayProxy") + err = s.DeleteResourceFromString(fmt.Sprintf(gatewayProxyWithPluginMetadata0, s.Deployer.GetAdminEndpoint(), s.AdminKey())) + Expect(err).ShouldNot(HaveOccurred()) + time.Sleep(5 * time.Second) + + By("Check PluginMetadata is not working") + s.NewAPISIXClient(). + GET("/not-found"). + WithHost("example.com"). + Expect(). + Status(http.StatusNotFound). + Body().Contains(`{"error_msg":"404 Route Not Found"}`) + }) + }) +}) diff --git a/test/e2e/crds/consumer.go b/test/e2e/crds/consumer.go index f87abb9a4..ae060e3fb 100644 --- a/test/e2e/crds/consumer.go +++ b/test/e2e/crds/consumer.go @@ -23,7 +23,7 @@ import ( "github.com/apache/apisix-ingress-controller/test/e2e/scaffold" ) -var _ = PDescribe("Test Consumer", func() { +var _ = Describe("Test Consumer", func() { s := scaffold.NewDefaultScaffold() var defaultGatewayProxy = ` diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go index ae1503e4b..c756d75e2 100644 --- a/test/e2e/e2e_test.go +++ b/test/e2e/e2e_test.go @@ -19,6 +19,7 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + _ "github.com/apache/apisix-ingress-controller/test/e2e/api7" _ "github.com/apache/apisix-ingress-controller/test/e2e/crds" "github.com/apache/apisix-ingress-controller/test/e2e/framework" _ "github.com/apache/apisix-ingress-controller/test/e2e/gatewayapi" From edf34581fe970bc53461639c8126a00a4573397c Mon Sep 17 00:00:00 2001 From: rongxin Date: Thu, 19 Jun 2025 09:44:45 +0800 Subject: [PATCH 02/13] refacotr: apisixglobal --- .../controller/apisixconsumer_controller.go | 42 ++-- .../controller/apisixglobalrule_controller.go | 190 ++---------------- .../provider/adc/translator/apisixconsumer.go | 12 +- test/e2e/crds/consumer.go | 2 +- test/e2e/e2e_test.go | 1 - 5 files changed, 43 insertions(+), 204 deletions(-) diff --git a/internal/controller/apisixconsumer_controller.go b/internal/controller/apisixconsumer_controller.go index 9f6842ac6..33cd23bbd 100644 --- a/internal/controller/apisixconsumer_controller.go +++ b/internal/controller/apisixconsumer_controller.go @@ -16,7 +16,6 @@ import ( "context" "fmt" - "github.com/api7/gopkg/pkg/log" "github.com/go-logr/logr" corev1 "k8s.io/api/core/v1" networkingv1 "k8s.io/api/networking/v1" @@ -30,7 +29,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/reconcile" - gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" "github.com/apache/apisix-ingress-controller/api/v1alpha1" apiv2 "github.com/apache/apisix-ingress-controller/api/v2" @@ -71,42 +69,33 @@ func (r *ApisixConsumerReconciler) Reconcile(ctx context.Context, req ctrl.Reque return ctrl.Result{}, err } - tctx := provider.NewDefaultTranslateContext(ctx) + var ( + tctx = provider.NewDefaultTranslateContext(ctx) + err error + ) + defer func() { + r.updateStatus(ac, err) + }() ingressClass, err := GetIngressClass(tctx, r.Client, r.Log, ac.Spec.IngressClassName) if err != nil { - log.Error(err, "failed to get IngressClass") + r.Log.Error(err, "failed to get IngressClass") return ctrl.Result{}, err } if err := ProcessIngressClassParameters(tctx, r.Client, r.Log, ac, ingressClass); err != nil { - log.Error(err, "failed to process IngressClass parameters", "ingressClass", ingressClass.Name) + r.Log.Error(err, "failed to process IngressClass parameters", "ingressClass", ingressClass.Name) + return ctrl.Result{}, err + } + + if err := r.processSpec(ctx, tctx, ac); err != nil { return ctrl.Result{}, err } if err := r.Provider.Update(ctx, tctx, ac); err != nil { r.Log.Error(err, "failed to update provider", "ApisixConsumer", ac) - // Update status with failure condition - r.updateStatus(ac, metav1.Condition{ - Type: string(apiv2.ConditionTypeAccepted), - Status: metav1.ConditionFalse, - ObservedGeneration: ac.Generation, - LastTransitionTime: metav1.Now(), - Reason: string(apiv2.ConditionReasonSyncFailed), - Message: err.Error(), - }) return ctrl.Result{}, err } - - // Update status with success condition - r.updateStatus(ac, metav1.Condition{ - Type: string(gatewayv1.RouteConditionAccepted), - Status: metav1.ConditionTrue, - ObservedGeneration: ac.Generation, - LastTransitionTime: metav1.Now(), - Reason: string(gatewayv1.RouteReasonAccepted), - Message: "The ApisixConsumer has been accepted by the apisix-ingress-controller", - }) return ctrl.Result{}, nil } @@ -210,7 +199,8 @@ func (r *ApisixConsumerReconciler) processSpec(ctx context.Context, tctx *provid return nil } -func (r *ApisixConsumerReconciler) updateStatus(consumer *apiv2.ApisixConsumer, condition metav1.Condition) { +func (r *ApisixConsumerReconciler) updateStatus(consumer *apiv2.ApisixConsumer, err error) { + SetApisixCRDConditionAccepted(&consumer.Status, consumer.GetGeneration(), err) r.Updater.Update(status.Update{ NamespacedName: utils.NamespacedName(consumer), Resource: &apiv2.ApisixConsumer{}, @@ -221,7 +211,7 @@ func (r *ApisixConsumerReconciler) updateStatus(consumer *apiv2.ApisixConsumer, panic(err) } acCopy := ac.DeepCopy() - acCopy.Status.Conditions = []metav1.Condition{condition} + acCopy.Status = consumer.Status return acCopy }), }) diff --git a/internal/controller/apisixglobalrule_controller.go b/internal/controller/apisixglobalrule_controller.go index e327ca468..9dc89fbe9 100644 --- a/internal/controller/apisixglobalrule_controller.go +++ b/internal/controller/apisixglobalrule_controller.go @@ -14,16 +14,13 @@ package controller import ( "context" - "errors" "fmt" "github.com/api7/gopkg/pkg/log" "github.com/go-logr/logr" - corev1 "k8s.io/api/core/v1" networkingv1 "k8s.io/api/networking/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" @@ -79,14 +76,14 @@ func (r *ApisixGlobalRuleReconciler) Reconcile(ctx context.Context, req ctrl.Req tctx := provider.NewDefaultTranslateContext(ctx) // get the ingress class - ingressClass, err := r.getIngressClass(&globalRule) + ingressClass, err := GetIngressClass(tctx, r.Client, r.Log, globalRule.Spec.IngressClassName) if err != nil { log.Error(err, "failed to get IngressClass") return ctrl.Result{}, err } // process IngressClass parameters if they reference GatewayProxy - if err := r.processIngressClassParameters(ctx, tctx, &globalRule, ingressClass); err != nil { + if err := ProcessIngressClassParameters(tctx, r.Client, r.Log, &globalRule, ingressClass); err != nil { log.Error(err, "failed to process IngressClass parameters", "ingressClass", ingressClass.Name) return ctrl.Result{}, err } @@ -137,12 +134,13 @@ func (r *ApisixGlobalRuleReconciler) SetupWithManager(mgr ctrl.Manager) error { &networkingv1.IngressClass{}, handler.EnqueueRequestsFromMapFunc(r.listGlobalRulesForIngressClass), builder.WithPredicates( - predicate.NewPredicateFuncs(r.matchesIngressController), + predicate.NewPredicateFuncs(matchesIngressController), ), ). Watches(&v1alpha1.GatewayProxy{}, handler.EnqueueRequestsFromMapFunc(r.listGlobalRulesForGatewayProxy), ). + Named("apisixglobalrule"). Complete(r) } @@ -187,15 +185,6 @@ func (r *ApisixGlobalRuleReconciler) matchesIngressClass(ingressClassName string return matchesController(ingressClass.Spec.Controller) } -// matchesIngressController check if the ingress class is controlled by us -func (r *ApisixGlobalRuleReconciler) matchesIngressController(obj client.Object) bool { - ingressClass, ok := obj.(*networkingv1.IngressClass) - if !ok { - return false - } - return matchesController(ingressClass.Spec.Controller) -} - // listGlobalRulesForIngressClass list all global rules that use a specific ingress class func (r *ApisixGlobalRuleReconciler) listGlobalRulesForIngressClass(ctx context.Context, obj client.Object) []reconcile.Request { ingressClass, ok := obj.(*networkingv1.IngressClass) @@ -203,165 +192,24 @@ func (r *ApisixGlobalRuleReconciler) listGlobalRulesForIngressClass(ctx context. return nil } - var requests []reconcile.Request - - // List all global rules and filter based on ingress class - globalRuleList := &apiv2.ApisixGlobalRuleList{} - if err := r.List(ctx, globalRuleList); err != nil { - r.Log.Error(err, "failed to list global rules") - return nil - } - - isDefaultClass := IsDefaultIngressClass(ingressClass) - for _, globalRule := range globalRuleList.Items { - if (isDefaultClass && globalRule.Spec.IngressClassName == "") || - globalRule.Spec.IngressClassName == ingressClass.Name { - requests = append(requests, reconcile.Request{ - NamespacedName: client.ObjectKey{ - Namespace: globalRule.Namespace, - Name: globalRule.Name, - }, - }) - } - } - - return requests -} - -// listGlobalRulesForGatewayProxy list all global rules that use a specific gateway proxy -func (r *ApisixGlobalRuleReconciler) listGlobalRulesForGatewayProxy(ctx context.Context, obj client.Object) []reconcile.Request { - gatewayProxy, ok := obj.(*v1alpha1.GatewayProxy) - if !ok { - return nil - } - - // Find all ingress classes that reference this gateway proxy - ingressClassList := &networkingv1.IngressClassList{} - if err := r.List(ctx, ingressClassList, client.MatchingFields{ - indexer.IngressClassParametersRef: indexer.GenIndexKey(gatewayProxy.GetNamespace(), gatewayProxy.GetName()), - }); err != nil { - r.Log.Error(err, "failed to list ingress classes for gateway proxy", "gatewayproxy", gatewayProxy.GetName()) - return nil - } - - var requests []reconcile.Request - for _, ingressClass := range ingressClassList.Items { - requests = append(requests, r.listGlobalRulesForIngressClass(ctx, &ingressClass)...) - } - - // Remove duplicates - uniqueRequests := make(map[string]reconcile.Request) - for _, request := range requests { - uniqueRequests[request.String()] = request - } - - distinctRequests := make([]reconcile.Request, 0, len(uniqueRequests)) - for _, request := range uniqueRequests { - distinctRequests = append(distinctRequests, request) - } - - return distinctRequests -} - -// getIngressClass get the ingress class for the global rule -func (r *ApisixGlobalRuleReconciler) getIngressClass(globalRule *apiv2.ApisixGlobalRule) (*networkingv1.IngressClass, error) { - if globalRule.Spec.IngressClassName == "" { - // Check for default ingress class - ingressClassList := &networkingv1.IngressClassList{} - if err := r.List(context.Background(), ingressClassList, client.MatchingFields{ - indexer.IngressClass: config.GetControllerName(), - }); err != nil { - r.Log.Error(err, "failed to list ingress classes") - return nil, err - } - - // Find the ingress class that is marked as default - for _, ic := range ingressClassList.Items { - if IsDefaultIngressClass(&ic) && matchesController(ic.Spec.Controller) { - return &ic, nil + return ListMatchingRequests( + ctx, + r.Client, + r.Log, + &apiv2.ApisixGlobalRuleList{}, + func(obj client.Object) bool { + agr, ok := obj.(*apiv2.ApisixGlobalRule) + if !ok { + r.Log.Error(fmt.Errorf("expected ApisixGlobalRule, got %T", obj), "failed to match object type") + return false } - } - log.Debugw("no default ingress class found") - return nil, errors.New("no default ingress class found") - } - - // Check if the specified ingress class is controlled by us - var ingressClass networkingv1.IngressClass - if err := r.Get(context.Background(), client.ObjectKey{Name: globalRule.Spec.IngressClassName}, &ingressClass); err != nil { - return nil, err - } - - if matchesController(ingressClass.Spec.Controller) { - return &ingressClass, nil - } - - return nil, errors.New("ingress class is not controlled by us") + return (IsDefaultIngressClass(ingressClass) && agr.Spec.IngressClassName == "") || agr.Spec.IngressClassName == ingressClass.Name + }, + ) } -// processIngressClassParameters processes the IngressClass parameters that reference GatewayProxy -func (r *ApisixGlobalRuleReconciler) processIngressClassParameters(ctx context.Context, tctx *provider.TranslateContext, globalRule *apiv2.ApisixGlobalRule, ingressClass *networkingv1.IngressClass) error { - if ingressClass == nil || ingressClass.Spec.Parameters == nil { - return nil - } - - ingressClassKind := utils.NamespacedNameKind(ingressClass) - globalRuleKind := utils.NamespacedNameKind(globalRule) - - parameters := ingressClass.Spec.Parameters - // check if the parameters reference GatewayProxy - if parameters.APIGroup != nil && *parameters.APIGroup == v1alpha1.GroupVersion.Group && parameters.Kind == KindGatewayProxy { - ns := globalRule.GetNamespace() - if parameters.Namespace != nil { - ns = *parameters.Namespace - } - - gatewayProxy := &v1alpha1.GatewayProxy{} - if err := r.Get(ctx, client.ObjectKey{ - Namespace: ns, - Name: parameters.Name, - }, gatewayProxy); err != nil { - r.Log.Error(err, "failed to get GatewayProxy", "namespace", ns, "name", parameters.Name) - return err - } - - r.Log.Info("found GatewayProxy for IngressClass", "ingressClass", ingressClass.Name, "gatewayproxy", gatewayProxy.Name) - tctx.GatewayProxies[ingressClassKind] = *gatewayProxy - tctx.ResourceParentRefs[globalRuleKind] = append(tctx.ResourceParentRefs[globalRuleKind], ingressClassKind) - - // check if the provider field references a secret - if gatewayProxy.Spec.Provider != nil && gatewayProxy.Spec.Provider.Type == v1alpha1.ProviderTypeControlPlane { - if gatewayProxy.Spec.Provider.ControlPlane != nil && - gatewayProxy.Spec.Provider.ControlPlane.Auth.Type == v1alpha1.AuthTypeAdminKey && - gatewayProxy.Spec.Provider.ControlPlane.Auth.AdminKey != nil && - gatewayProxy.Spec.Provider.ControlPlane.Auth.AdminKey.ValueFrom != nil && - gatewayProxy.Spec.Provider.ControlPlane.Auth.AdminKey.ValueFrom.SecretKeyRef != nil { - - secretRef := gatewayProxy.Spec.Provider.ControlPlane.Auth.AdminKey.ValueFrom.SecretKeyRef - secret := &corev1.Secret{} - if err := r.Get(ctx, client.ObjectKey{ - Namespace: ns, - Name: secretRef.Name, - }, secret); err != nil { - r.Log.Error(err, "failed to get secret for GatewayProxy provider", - "namespace", ns, - "name", secretRef.Name) - return err - } - - r.Log.Info("found secret for GatewayProxy provider", - "ingressClass", ingressClass.Name, - "gatewayproxy", gatewayProxy.Name, - "secret", secretRef.Name) - - tctx.Secrets[types.NamespacedName{ - Namespace: ns, - Name: secretRef.Name, - }] = secret - } - } - } - - return nil +func (r *ApisixGlobalRuleReconciler) listGlobalRulesForGatewayProxy(ctx context.Context, obj client.Object) []reconcile.Request { + return listIngressClassRequestsForGatewayProxy(ctx, r.Client, obj, r.Log, r.listGlobalRulesForIngressClass) } // updateStatus updates the ApisixGlobalRule status with the given condition diff --git a/internal/provider/adc/translator/apisixconsumer.go b/internal/provider/adc/translator/apisixconsumer.go index 96fd07be0..4eecb6915 100644 --- a/internal/provider/adc/translator/apisixconsumer.go +++ b/internal/provider/adc/translator/apisixconsumer.go @@ -40,6 +40,8 @@ var ( _hmacAuthEncodeURIParamsDefaultValue = true _hmacAuthValidateRequestBodyDefaultValue = false _hmacAuthMaxReqBodyDefaultValue = int64(524288) + + _stringTrue = "true" ) func (t *Translator) TranslateApisixConsumer(tctx *provider.TranslateContext, ac *v2.ApisixConsumer) (*TranslateResult, error) { @@ -197,7 +199,7 @@ func (t *Translator) translateConsumerJwtAuthPluginV2(tctx *provider.TranslateCo } base64SecretRaw := sec.Data["base64_secret"] var base64Secret bool - if string(base64SecretRaw) == "true" { + if string(base64SecretRaw) == _stringTrue { base64Secret = true } expRaw := sec.Data["exp"] @@ -272,8 +274,8 @@ func (t *Translator) translateConsumerHMACAuthPluginV2(tctx *provider.TranslateC clockSkew = _hmacAuthClockSkewDefaultValue } - var signedHeaders []string signedHeadersRaw := sec.Data["signed_headers"] + signedHeaders := make([]string, 0, len(signedHeadersRaw)) for _, b := range signedHeadersRaw { signedHeaders = append(signedHeaders, string(b)) } @@ -283,7 +285,7 @@ func (t *Translator) translateConsumerHMACAuthPluginV2(tctx *provider.TranslateC if !ok { keepHeader = _hmacAuthKeepHeadersDefaultValue } else { - if string(keepHeaderRaw) == "true" { + if string(keepHeaderRaw) == _stringTrue { keepHeader = true } else { keepHeader = false @@ -295,7 +297,7 @@ func (t *Translator) translateConsumerHMACAuthPluginV2(tctx *provider.TranslateC if !ok { encodeURIParams = _hmacAuthEncodeURIParamsDefaultValue } else { - if string(encodeURIParamsRaw) == "true" { + if string(encodeURIParamsRaw) == _stringTrue { encodeURIParams = true } else { encodeURIParams = false @@ -307,7 +309,7 @@ func (t *Translator) translateConsumerHMACAuthPluginV2(tctx *provider.TranslateC if !ok { validateRequestBody = _hmacAuthValidateRequestBodyDefaultValue } else { - if string(validateRequestBodyRaw) == "true" { + if string(validateRequestBodyRaw) == _stringTrue { validateRequestBody = true } else { validateRequestBody = false diff --git a/test/e2e/crds/consumer.go b/test/e2e/crds/consumer.go index ae060e3fb..f87abb9a4 100644 --- a/test/e2e/crds/consumer.go +++ b/test/e2e/crds/consumer.go @@ -23,7 +23,7 @@ import ( "github.com/apache/apisix-ingress-controller/test/e2e/scaffold" ) -var _ = Describe("Test Consumer", func() { +var _ = PDescribe("Test Consumer", func() { s := scaffold.NewDefaultScaffold() var defaultGatewayProxy = ` diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go index c756d75e2..ae1503e4b 100644 --- a/test/e2e/e2e_test.go +++ b/test/e2e/e2e_test.go @@ -19,7 +19,6 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - _ "github.com/apache/apisix-ingress-controller/test/e2e/api7" _ "github.com/apache/apisix-ingress-controller/test/e2e/crds" "github.com/apache/apisix-ingress-controller/test/e2e/framework" _ "github.com/apache/apisix-ingress-controller/test/e2e/gatewayapi" From 6d7e530a1695b1f471e443549fe3d28309215fc9 Mon Sep 17 00:00:00 2001 From: rongxin Date: Thu, 19 Jun 2025 10:00:44 +0800 Subject: [PATCH 03/13] refactor: apisixroute --- internal/controller/apisixroute_controller.go | 161 ++--------- internal/provider/adc/adc.go | 1 - internal/provider/adc/store.go | 1 - test/e2e/api7/gatewayproxy.go | 267 ------------------ test/e2e/apisix/consumer.go | 192 +++++++++++++ 5 files changed, 213 insertions(+), 409 deletions(-) delete mode 100644 test/e2e/api7/gatewayproxy.go create mode 100644 test/e2e/apisix/consumer.go diff --git a/internal/controller/apisixroute_controller.go b/internal/controller/apisixroute_controller.go index 9cf2df8eb..b462037f1 100644 --- a/internal/controller/apisixroute_controller.go +++ b/internal/controller/apisixroute_controller.go @@ -13,7 +13,6 @@ package controller import ( - "cmp" "context" "errors" "fmt" @@ -35,7 +34,6 @@ import ( "github.com/apache/apisix-ingress-controller/api/v1alpha1" apiv2 "github.com/apache/apisix-ingress-controller/api/v2" - "github.com/apache/apisix-ingress-controller/internal/controller/config" "github.com/apache/apisix-ingress-controller/internal/controller/indexer" "github.com/apache/apisix-ingress-controller/internal/controller/status" "github.com/apache/apisix-ingress-controller/internal/provider" @@ -66,9 +64,9 @@ func (r *ApisixRouteReconciler) SetupWithManager(mgr ctrl.Manager) error { ), ). Watches(&networkingv1.IngressClass{}, - handler.EnqueueRequestsFromMapFunc(r.listApiRouteForIngressClass), + handler.EnqueueRequestsFromMapFunc(r.listApisixRouteForIngressClass), builder.WithPredicates( - predicate.NewPredicateFuncs(r.matchesIngressController), + predicate.NewPredicateFuncs(matchesIngressController), ), ). Watches(&v1alpha1.GatewayProxy{}, @@ -116,10 +114,10 @@ func (r *ApisixRouteReconciler) Reconcile(ctx context.Context, req ctrl.Request) r.updateStatus(&ar, err) }() - if ic, err = r.getIngressClass(&ar); err != nil { + if ic, err = GetIngressClass(tctx, r.Client, r.Log, ar.Spec.IngressClassName); err != nil { return ctrl.Result{}, err } - if err = r.processIngressClassParameters(ctx, tctx, &ar, ic); err != nil { + if err = ProcessIngressClassParameters(tctx, r.Client, r.Log, &ar, ic); err != nil { return ctrl.Result{}, err } if err = r.processApisixRoute(ctx, tctx, &ar); err != nil { @@ -404,147 +402,30 @@ func (r *ApisixRouteReconciler) listApisixRoutesForSecret(ctx context.Context, o return pkgutils.DedupComparable(allRequests) } -func (r *ApisixRouteReconciler) listApiRouteForIngressClass(ctx context.Context, object client.Object) (requests []reconcile.Request) { - ic, ok := object.(*networkingv1.IngressClass) +func (r *ApisixRouteReconciler) listApisixRouteForIngressClass(ctx context.Context, object client.Object) (requests []reconcile.Request) { + ingressClass, ok := object.(*networkingv1.IngressClass) if !ok { return nil } - isDefaultIngressClass := IsDefaultIngressClass(ic) - var arList apiv2.ApisixRouteList - if err := r.List(ctx, &arList); err != nil { - return nil - } - for _, ar := range arList.Items { - if ar.Spec.IngressClassName == ic.Name || (isDefaultIngressClass && ar.Spec.IngressClassName == "") { - requests = append(requests, reconcile.Request{NamespacedName: utils.NamespacedName(&ar)}) - } - } - return pkgutils.DedupComparable(requests) + return ListMatchingRequests( + ctx, + r.Client, + r.Log, + &apiv2.ApisixRouteList{}, + func(obj client.Object) bool { + ar, ok := obj.(*apiv2.ApisixRoute) + if !ok { + r.Log.Error(fmt.Errorf("expected ApisixRoute, got %T", obj), "failed to match object type") + return false + } + return (IsDefaultIngressClass(ingressClass) && ar.Spec.IngressClassName == "") || ar.Spec.IngressClassName == ingressClass.Name + }, + ) } func (r *ApisixRouteReconciler) listApisixRouteForGatewayProxy(ctx context.Context, object client.Object) (requests []reconcile.Request) { - gp, ok := object.(*v1alpha1.GatewayProxy) - if !ok { - return nil - } - - var icList networkingv1.IngressClassList - if err := r.List(ctx, &icList, client.MatchingFields{ - indexer.IngressClassParametersRef: indexer.GenIndexKey(gp.GetNamespace(), gp.GetName()), - }); err != nil { - r.Log.Error(err, "failed to list ingress classes for gateway proxy", "gatewayproxy", gp.GetName()) - return nil - } - - for _, ic := range icList.Items { - requests = append(requests, r.listApiRouteForIngressClass(ctx, &ic)...) - } - - return pkgutils.DedupComparable(requests) -} - -func (r *ApisixRouteReconciler) matchesIngressController(obj client.Object) bool { - ingressClass, ok := obj.(*networkingv1.IngressClass) - if !ok { - return false - } - return matchesController(ingressClass.Spec.Controller) -} - -func (r *ApisixRouteReconciler) getIngressClass(ar *apiv2.ApisixRoute) (*networkingv1.IngressClass, error) { - if ar.Spec.IngressClassName == "" { - return r.getDefaultIngressClass() - } - - var ic networkingv1.IngressClass - if err := r.Get(context.Background(), client.ObjectKey{Name: ar.Spec.IngressClassName}, &ic); err != nil { - return nil, err - } - return &ic, nil -} - -func (r *ApisixRouteReconciler) getDefaultIngressClass() (*networkingv1.IngressClass, error) { - var icList networkingv1.IngressClassList - if err := r.List(context.Background(), &icList, client.MatchingFields{ - indexer.IngressClass: config.GetControllerName(), - }); err != nil { - r.Log.Error(err, "failed to list ingress classes") - return nil, err - } - for _, ic := range icList.Items { - if IsDefaultIngressClass(&ic) && matchesController(ic.Spec.Controller) { - return &ic, nil - } - } - return nil, ReasonError{ - Reason: string(metav1.StatusReasonNotFound), - Message: "default ingress class not found or dose not match the controller", - } -} - -// processIngressClassParameters processes the IngressClass parameters that reference GatewayProxy -func (r *ApisixRouteReconciler) processIngressClassParameters(ctx context.Context, tc *provider.TranslateContext, ar *apiv2.ApisixRoute, ingressClass *networkingv1.IngressClass) error { - if ingressClass == nil || ingressClass.Spec.Parameters == nil { - return nil - } - - var ( - ingressClassKind = utils.NamespacedNameKind(ingressClass) - globalRuleKind = utils.NamespacedNameKind(ar) - parameters = ingressClass.Spec.Parameters - ) - if parameters.APIGroup == nil || *parameters.APIGroup != v1alpha1.GroupVersion.Group || parameters.Kind != KindGatewayProxy { - return nil - } - - // check if the parameters reference GatewayProxy - var ( - gatewayProxy v1alpha1.GatewayProxy - ns = *cmp.Or(parameters.Namespace, &ar.Namespace) - ) - - if err := r.Get(ctx, client.ObjectKey{Namespace: ns, Name: parameters.Name}, &gatewayProxy); err != nil { - r.Log.Error(err, "failed to get GatewayProxy", "namespace", ns, "name", parameters.Name) - return err - } - - tc.GatewayProxies[ingressClassKind] = gatewayProxy - tc.ResourceParentRefs[globalRuleKind] = append(tc.ResourceParentRefs[globalRuleKind], ingressClassKind) - - // check if the provider field references a secret - if gatewayProxy.Spec.Provider != nil && gatewayProxy.Spec.Provider.Type == v1alpha1.ProviderTypeControlPlane { - if gatewayProxy.Spec.Provider.ControlPlane != nil && - gatewayProxy.Spec.Provider.ControlPlane.Auth.Type == v1alpha1.AuthTypeAdminKey && - gatewayProxy.Spec.Provider.ControlPlane.Auth.AdminKey != nil && - gatewayProxy.Spec.Provider.ControlPlane.Auth.AdminKey.ValueFrom != nil && - gatewayProxy.Spec.Provider.ControlPlane.Auth.AdminKey.ValueFrom.SecretKeyRef != nil { - - secretRef := gatewayProxy.Spec.Provider.ControlPlane.Auth.AdminKey.ValueFrom.SecretKeyRef - secret := &corev1.Secret{} - if err := r.Get(ctx, client.ObjectKey{ - Namespace: ns, - Name: secretRef.Name, - }, secret); err != nil { - r.Log.Error(err, "failed to get secret for GatewayProxy provider", - "namespace", ns, - "name", secretRef.Name) - return err - } - - r.Log.Info("found secret for GatewayProxy provider", - "ingressClass", ingressClass.Name, - "gatewayproxy", gatewayProxy.Name, - "secret", secretRef.Name) - - tc.Secrets[types.NamespacedName{ - Namespace: ns, - Name: secretRef.Name, - }] = secret - } - } - - return nil + return listIngressClassRequestsForGatewayProxy(ctx, r.Client, object, r.Log, r.listApisixRouteForIngressClass) } func (r *ApisixRouteReconciler) updateStatus(ar *apiv2.ApisixRoute, err error) { diff --git a/internal/provider/adc/adc.go b/internal/provider/adc/adc.go index 48b084cc0..eadc572dd 100644 --- a/internal/provider/adc/adc.go +++ b/internal/provider/adc/adc.go @@ -108,7 +108,6 @@ func (d *adcClient) Update(ctx context.Context, tctx *provider.TranslateContext, result, err = d.translator.TranslateGateway(tctx, t.DeepCopy()) resourceTypes = append(resourceTypes, "global_rule", "ssl", "plugin_metadata") case *networkingv1.Ingress: - log.Warnw("ingress is deprecated, please use gateway api instead") result, err = d.translator.TranslateIngress(tctx, t.DeepCopy()) resourceTypes = append(resourceTypes, "service", "ssl") case *v1alpha1.Consumer: diff --git a/internal/provider/adc/store.go b/internal/provider/adc/store.go index 1591663b4..7a1800f18 100644 --- a/internal/provider/adc/store.go +++ b/internal/provider/adc/store.go @@ -41,7 +41,6 @@ func NewStore() *Store { func (s *Store) Insert(name string, resourceTypes []string, resources adctypes.Resources, Labels map[string]string) error { s.Lock() defer s.Unlock() - log.Warnw("insert resources into cache", zap.Any("resources", resources)) targetCache, ok := s.cacheMap[name] if !ok { db, err := cache.NewMemDBCache() diff --git a/test/e2e/api7/gatewayproxy.go b/test/e2e/api7/gatewayproxy.go deleted file mode 100644 index 6e589d9ad..000000000 --- a/test/e2e/api7/gatewayproxy.go +++ /dev/null @@ -1,267 +0,0 @@ -// Licensed 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 gatewayapi - -import ( - "fmt" - "net/http" - "time" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - - "github.com/apache/apisix-ingress-controller/test/e2e/scaffold" -) - -var _ = Describe("Test GatewayProxy", func() { - s := scaffold.NewDefaultScaffold() - - var defaultGatewayClass = ` -apiVersion: gateway.networking.k8s.io/v1 -kind: GatewayClass -metadata: - name: %s -spec: - controllerName: %s -` - - var gatewayWithProxy = ` -apiVersion: gateway.networking.k8s.io/v1 -kind: Gateway -metadata: - name: apisix -spec: - gatewayClassName: %s - listeners: - - name: http - protocol: HTTP - port: 80 - infrastructure: - parametersRef: - group: apisix.apache.org - kind: GatewayProxy - name: apisix-proxy-config -` - - var gatewayProxyWithEnabledPlugin = ` -apiVersion: apisix.apache.org/v1alpha1 -kind: GatewayProxy -metadata: - name: apisix-proxy-config -spec: - provider: - type: ControlPlane - controlPlane: - endpoints: - - %s - auth: - type: AdminKey - adminKey: - value: "%s" - plugins: - - name: response-rewrite - enabled: true - config: - headers: - X-Proxy-Test: "enabled" -` - var ( - gatewayProxyWithPluginMetadata0 = ` -apiVersion: apisix.apache.org/v1alpha1 -kind: GatewayProxy -metadata: - name: apisix-proxy-config -spec: - provider: - type: ControlPlane - controlPlane: - endpoints: - - %s - auth: - type: AdminKey - adminKey: - value: "%s" - plugins: - - name: error-page - enabled: true - config: {} - pluginMetadata: - error-page: { - "enable": true, - "error_404": { - "body": "404 from plugin metadata", - "content-type": "text/plain" - } - } -` - gatewayProxyWithPluginMetadata1 = ` -apiVersion: apisix.apache.org/v1alpha1 -kind: GatewayProxy -metadata: - name: apisix-proxy-config -spec: - provider: - type: ControlPlane - controlPlane: - endpoints: - - %s - auth: - type: AdminKey - adminKey: - value: "%s" - plugins: - - name: error-page - enabled: true - config: {} - pluginMetadata: - error-page: { - "enable": false, - "error_404": { - "body": "404 from plugin metadata", - "content-type": "text/plain" - } - } -` - ) - - var httpRouteForTest = ` -apiVersion: gateway.networking.k8s.io/v1 -kind: HTTPRoute -metadata: - name: test-route -spec: - parentRefs: - - name: %s - hostnames: - - example.com - rules: - - matches: - - path: - type: Exact - value: /get - backendRefs: - - name: httpbin-service-e2e-test - port: 80 -` - - var resourceApplied = func(resourceType, resourceName, resourceRaw string, observedGeneration int) { - Expect(s.CreateResourceFromString(resourceRaw)). - NotTo(HaveOccurred(), fmt.Sprintf("creating %s", resourceType)) - - Eventually(func() string { - hryaml, err := s.GetResourceYaml(resourceType, resourceName) - Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("getting %s yaml", resourceType)) - return hryaml - }).WithTimeout(8*time.Second).ProbeEvery(2*time.Second). - Should( - SatisfyAll( - ContainSubstring(`status: "True"`), - ContainSubstring(fmt.Sprintf("observedGeneration: %d", observedGeneration)), - ), - fmt.Sprintf("checking %s condition status", resourceType), - ) - time.Sleep(3 * time.Second) - } - - var ( - gatewayClassName string - ) - - BeforeEach(func() { - By("Create GatewayClass") - gatewayClassName = fmt.Sprintf("apisix-%d", time.Now().Unix()) - err := s.CreateResourceFromStringWithNamespace(fmt.Sprintf(defaultGatewayClass, gatewayClassName, s.GetControllerName()), "") - Expect(err).NotTo(HaveOccurred(), "creating GatewayClass") - time.Sleep(5 * time.Second) - - By("Check GatewayClass condition") - gcYaml, err := s.GetResourceYaml("GatewayClass", gatewayClassName) - Expect(err).NotTo(HaveOccurred(), "getting GatewayClass yaml") - Expect(gcYaml).To(ContainSubstring(`status: "True"`), "checking GatewayClass condition status") - Expect(gcYaml).To(ContainSubstring("message: the gatewayclass has been accepted by the apisix-ingress-controller"), "checking GatewayClass condition message") - - By("Create GatewayProxy with enabled plugin") - err = s.CreateResourceFromString(fmt.Sprintf(gatewayProxyWithEnabledPlugin, s.Deployer.GetAdminEndpoint(), s.AdminKey())) - Expect(err).NotTo(HaveOccurred(), "creating GatewayProxy with enabled plugin") - time.Sleep(5 * time.Second) - - By("Create Gateway with GatewayProxy") - err = s.CreateResourceFromStringWithNamespace(fmt.Sprintf(gatewayWithProxy, gatewayClassName), s.Namespace()) - Expect(err).NotTo(HaveOccurred(), "creating Gateway with GatewayProxy") - time.Sleep(5 * time.Second) - - By("check Gateway condition") - gwyaml, err := s.GetResourceYaml("Gateway", "apisix") - Expect(err).NotTo(HaveOccurred(), "getting Gateway yaml") - Expect(gwyaml).To(ContainSubstring(`status: "True"`), "checking Gateway condition status") - Expect(gwyaml).To(ContainSubstring("message: the gateway has been accepted by the apisix-ingress-controller"), "checking Gateway condition message") - }) - - AfterEach(func() { - By("Clean up resources") - _ = s.DeleteResourceFromString(fmt.Sprintf(httpRouteForTest, "apisix")) - _ = s.DeleteResourceFromString(fmt.Sprintf(gatewayWithProxy, gatewayClassName)) - _ = s.DeleteResourceFromString(fmt.Sprintf(gatewayProxyWithEnabledPlugin, s.Deployer.GetAdminEndpoint(), s.AdminKey())) - }) - - Context("Test Gateway with PluginMetadata", func() { - var ( - err error - ) - - PIt("Should work OK with error-page", func() { - By("Update GatewayProxy with PluginMetadata") - err = s.CreateResourceFromString(fmt.Sprintf(gatewayProxyWithPluginMetadata0, s.Deployer.GetAdminEndpoint(), s.AdminKey())) - Expect(err).ShouldNot(HaveOccurred()) - time.Sleep(5 * time.Second) - - By("Create HTTPRoute for Gateway with GatewayProxy") - resourceApplied("HTTPRoute", "test-route", fmt.Sprintf(httpRouteForTest, "apisix"), 1) - - time.Sleep(5 * time.Second) - By("Check PluginMetadata working") - s.NewAPISIXClient(). - GET("/not-found"). - WithHost("example.com"). - Expect(). - Status(http.StatusNotFound). - Body().Contains("404 from plugin metadata") - - By("Update GatewayProxy with PluginMetadata") - err = s.CreateResourceFromString(fmt.Sprintf(gatewayProxyWithPluginMetadata1, s.Deployer.GetAdminEndpoint(), s.AdminKey())) - Expect(err).ShouldNot(HaveOccurred()) - time.Sleep(5 * time.Second) - - By("Check PluginMetadata working") - s.NewAPISIXClient(). - GET("/not-found"). - WithHost("example.com"). - Expect(). - Status(http.StatusNotFound). - Body().Contains(`{"error_msg":"404 Route Not Found"}`) - - By("Delete GatewayProxy") - err = s.DeleteResourceFromString(fmt.Sprintf(gatewayProxyWithPluginMetadata0, s.Deployer.GetAdminEndpoint(), s.AdminKey())) - Expect(err).ShouldNot(HaveOccurred()) - time.Sleep(5 * time.Second) - - By("Check PluginMetadata is not working") - s.NewAPISIXClient(). - GET("/not-found"). - WithHost("example.com"). - Expect(). - Status(http.StatusNotFound). - Body().Contains(`{"error_msg":"404 Route Not Found"}`) - }) - }) -}) diff --git a/test/e2e/apisix/consumer.go b/test/e2e/apisix/consumer.go new file mode 100644 index 000000000..8b591b519 --- /dev/null +++ b/test/e2e/apisix/consumer.go @@ -0,0 +1,192 @@ +// Licensed 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" + "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" +) + +type Headers map[string]string + +var _ = Describe("Test ApisixConsumer", func() { + var ( + s = scaffold.NewScaffold(&scaffold.Options{ + ControllerName: "apisix.apache.org/apisix-ingress-controller", + }) + applier = framework.NewApplier(s.GinkgoT, s.K8sClient, s.CreateResourceFromString) + ) + + BeforeEach(func() { + By("create GatewayProxy") + gatewayProxy := fmt.Sprintf(gatewayProxyYaml, s.Deployer.GetAdminEndpoint(), s.AdminKey()) + err := s.CreateResourceFromStringWithNamespace(gatewayProxy, "default") + Expect(err).NotTo(HaveOccurred(), "creating GatewayProxy") + time.Sleep(5 * time.Second) + + By("create IngressClass") + err = s.CreateResourceFromStringWithNamespace(ingressClassYaml, "") + Expect(err).NotTo(HaveOccurred(), "creating IngressClass") + time.Sleep(5 * time.Second) + }) + + Context("Test KeyAuth", func() { + const keyAuth = ` +apiVersion: apisix.apache.org/v2 +kind: ApisixConsumer +metadata: + name: test-consumer +spec: + ingressClassName: apisix + authParameter: + keyAuth: + value: + key: test-key +` + const defaultApisixRoute = ` +apiVersion: apisix.apache.org/v2 +kind: ApisixRoute +metadata: + name: default +spec: + ingressClassName: apisix + http: + - name: rule0 + match: + hosts: + - httpbin + paths: + - /get + - /headers + - /anything + backends: + - serviceName: httpbin-service-e2e-test + servicePort: 80 + authentication: + enable: true + type: keyAuth +` + request := func(path string, headers Headers) int { + return s.NewAPISIXClient().GET(path).WithHeaders(headers).WithHost("httpbin").Expect().Raw().StatusCode + } + + It("Basic tests", func() { + By("apply ApisixRoute") + applier.MustApplyAPIv2(types.NamespacedName{Namespace: s.Namespace(), Name: "default"}, &apiv2.ApisixRoute{}, defaultApisixRoute) + + By("apply ApisixConsumer") + applier.MustApplyAPIv2(types.NamespacedName{Namespace: s.Namespace(), Name: "test-consumer"}, &apiv2.ApisixConsumer{}, keyAuth) + + By("verify ApisixRoute with ApisixConsumer") + Eventually(request).WithArguments("/get", Headers{ + "apikey": "invalid-key", + }).WithTimeout(5 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusUnauthorized)) + + Eventually(request).WithArguments("/get", Headers{ + "apikey": "test-key", + }).WithTimeout(5 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusOK)) + + By("Delete ApisixConsumer") + err := s.DeleteResource("ApisixConsumer", "test-consumer") + Expect(err).ShouldNot(HaveOccurred(), "deleting ApisixConsumer") + Eventually(request).WithArguments("/get", Headers{ + "apikey": "test-key", + }).WithTimeout(5 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusUnauthorized)) + + By("delete ApisixRoute") + err = s.DeleteResource("ApisixRoute", "default") + Expect(err).ShouldNot(HaveOccurred(), "deleting ApisixRoute") + Eventually(request).WithArguments("/headers", Headers{}).WithTimeout(5 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusNotFound)) + }) + + It("SecretRef tests", func() { + }) + }) + + Context("Test KeyAuth", func() { + const basicAuth = ` +apiVersion: apisix.apache.org/v2 +kind: ApisixConsumer +metadata: + name: test-consumer +spec: + ingressClassName: apisix + authParameter: + basicAuth: + value: + username: test-user + password: test-password +` + const defaultApisixRoute = ` +apiVersion: apisix.apache.org/v2 +kind: ApisixRoute +metadata: + name: default +spec: + ingressClassName: apisix + http: + - name: rule0 + match: + hosts: + - httpbin + paths: + - /get + - /headers + - /anything + backends: + - serviceName: httpbin-service-e2e-test + servicePort: 80 + authentication: + enable: true + type: basicAuth +` + + request := func(path string, username, password string) int { + return s.NewAPISIXClient().GET(path).WithBasicAuth(username, password).WithHost("httpbin").Expect().Raw().StatusCode + } + FIt("Basic tests", func() { + By("apply ApisixRoute") + applier.MustApplyAPIv2(types.NamespacedName{Namespace: s.Namespace(), Name: "default"}, &apiv2.ApisixRoute{}, defaultApisixRoute) + + By("apply ApisixConsumer") + applier.MustApplyAPIv2(types.NamespacedName{Namespace: s.Namespace(), Name: "test-consumer"}, &apiv2.ApisixConsumer{}, basicAuth) + + By("verify ApisixRoute with ApisixConsumer") + Eventually(request).WithArguments("/get", "invalid-username", "invalid-password").WithTimeout(5 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusUnauthorized)) + + Eventually(request).WithArguments("/get", "test-user", "test-password").WithTimeout(5 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusOK)) + + By("Delete ApisixConsumer") + err := s.DeleteResource("ApisixConsumer", "test-consumer") + Expect(err).ShouldNot(HaveOccurred(), "deleting ApisixConsumer") + Eventually(request).WithArguments("/get", "test-user", "test-password").WithTimeout(5 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusUnauthorized)) + + By("delete ApisixRoute") + err = s.DeleteResource("ApisixRoute", "default") + Expect(err).ShouldNot(HaveOccurred(), "deleting ApisixRoute") + Eventually(request).WithArguments("/headers", "", "").WithTimeout(5 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusNotFound)) + }) + + It("SecretRef tests", func() { + }) + }) +}) From 151f42dfff04d84b3cf26d3e36248f83b61d6e11 Mon Sep 17 00:00:00 2001 From: rongxin Date: Thu, 19 Jun 2025 10:51:47 +0800 Subject: [PATCH 04/13] fix test --- internal/controller/apisixroute_controller.go | 64 ------------------- test/e2e/apisix/consumer.go | 8 +-- 2 files changed, 4 insertions(+), 68 deletions(-) diff --git a/internal/controller/apisixroute_controller.go b/internal/controller/apisixroute_controller.go index 90bffeb6e..3b6283b0f 100644 --- a/internal/controller/apisixroute_controller.go +++ b/internal/controller/apisixroute_controller.go @@ -550,70 +550,6 @@ func (r *ApisixRouteReconciler) getDefaultIngressClass() (*networkingv1.IngressC } } -// processIngressClassParameters processes the IngressClass parameters that reference GatewayProxy -func (r *ApisixRouteReconciler) processIngressClassParameters(ctx context.Context, tc *provider.TranslateContext, ar *apiv2.ApisixRoute, ingressClass *networkingv1.IngressClass) error { - if ingressClass == nil || ingressClass.Spec.Parameters == nil { - return nil - } - - var ( - ingressClassKind = utils.NamespacedNameKind(ingressClass) - globalRuleKind = utils.NamespacedNameKind(ar) - parameters = ingressClass.Spec.Parameters - ) - if parameters.APIGroup == nil || *parameters.APIGroup != v1alpha1.GroupVersion.Group || parameters.Kind != KindGatewayProxy { - return nil - } - - // check if the parameters reference GatewayProxy - var ( - gatewayProxy v1alpha1.GatewayProxy - ns = *cmp.Or(parameters.Namespace, &ar.Namespace) - ) - - if err := r.Get(ctx, client.ObjectKey{Namespace: ns, Name: parameters.Name}, &gatewayProxy); err != nil { - r.Log.Error(err, "failed to get GatewayProxy", "namespace", ns, "name", parameters.Name) - return err - } - - tc.GatewayProxies[ingressClassKind] = gatewayProxy - tc.ResourceParentRefs[globalRuleKind] = append(tc.ResourceParentRefs[globalRuleKind], ingressClassKind) - - // check if the provider field references a secret - if gatewayProxy.Spec.Provider != nil && gatewayProxy.Spec.Provider.Type == v1alpha1.ProviderTypeControlPlane { - if gatewayProxy.Spec.Provider.ControlPlane != nil && - gatewayProxy.Spec.Provider.ControlPlane.Auth.Type == v1alpha1.AuthTypeAdminKey && - gatewayProxy.Spec.Provider.ControlPlane.Auth.AdminKey != nil && - gatewayProxy.Spec.Provider.ControlPlane.Auth.AdminKey.ValueFrom != nil && - gatewayProxy.Spec.Provider.ControlPlane.Auth.AdminKey.ValueFrom.SecretKeyRef != nil { - - secretRef := gatewayProxy.Spec.Provider.ControlPlane.Auth.AdminKey.ValueFrom.SecretKeyRef - secret := &corev1.Secret{} - if err := r.Get(ctx, client.ObjectKey{ - Namespace: ns, - Name: secretRef.Name, - }, secret); err != nil { - r.Log.Error(err, "failed to get secret for GatewayProxy provider", - "namespace", ns, - "name", secretRef.Name) - return err - } - - r.Log.Info("found secret for GatewayProxy provider", - "ingressClass", ingressClass.Name, - "gatewayproxy", gatewayProxy.Name, - "secret", secretRef.Name) - - tc.Secrets[types.NamespacedName{ - Namespace: ns, - Name: secretRef.Name, - }] = secret - } - } - - return nil -} - func (r *ApisixRouteReconciler) updateStatus(ar *apiv2.ApisixRoute, err error) { SetApisixCRDConditionAccepted(&ar.Status, ar.GetGeneration(), err) r.Updater.Update(status.Update{ diff --git a/test/e2e/apisix/consumer.go b/test/e2e/apisix/consumer.go index 8b591b519..329f7e0bf 100644 --- a/test/e2e/apisix/consumer.go +++ b/test/e2e/apisix/consumer.go @@ -118,11 +118,11 @@ spec: Eventually(request).WithArguments("/headers", Headers{}).WithTimeout(5 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusNotFound)) }) - It("SecretRef tests", func() { + PIt("SecretRef tests", func() { }) }) - Context("Test KeyAuth", func() { + Context("Test BasicAuth", func() { const basicAuth = ` apiVersion: apisix.apache.org/v2 kind: ApisixConsumer @@ -163,7 +163,7 @@ spec: request := func(path string, username, password string) int { return s.NewAPISIXClient().GET(path).WithBasicAuth(username, password).WithHost("httpbin").Expect().Raw().StatusCode } - FIt("Basic tests", func() { + It("Basic tests", func() { By("apply ApisixRoute") applier.MustApplyAPIv2(types.NamespacedName{Namespace: s.Namespace(), Name: "default"}, &apiv2.ApisixRoute{}, defaultApisixRoute) @@ -186,7 +186,7 @@ spec: Eventually(request).WithArguments("/headers", "", "").WithTimeout(5 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusNotFound)) }) - It("SecretRef tests", func() { + PIt("SecretRef tests", func() { }) }) }) From 94d7da74576cc7c7090782dac93a91f0c25055b1 Mon Sep 17 00:00:00 2001 From: rongxin Date: Thu, 19 Jun 2025 16:37:08 +0800 Subject: [PATCH 05/13] add secret test --- .../controller/apisixconsumer_controller.go | 38 ++++- internal/controller/apisixroute_controller.go | 40 ----- internal/controller/consumer_controller.go | 26 +-- internal/controller/httproutepolicy.go | 2 +- internal/controller/indexer/indexer.go | 36 ++++ internal/controller/ingress_controller.go | 161 +----------------- internal/controller/utils.go | 34 ++++ .../provider/adc/translator/apisixconsumer.go | 36 ++-- test/e2e/apisix/consumer.go | 154 ++++++++++++++++- test/e2e/scaffold/apisix_deployer.go | 4 +- 10 files changed, 287 insertions(+), 244 deletions(-) diff --git a/internal/controller/apisixconsumer_controller.go b/internal/controller/apisixconsumer_controller.go index 33cd23bbd..90a7e3807 100644 --- a/internal/controller/apisixconsumer_controller.go +++ b/internal/controller/apisixconsumer_controller.go @@ -32,6 +32,8 @@ import ( "github.com/apache/apisix-ingress-controller/api/v1alpha1" apiv2 "github.com/apache/apisix-ingress-controller/api/v2" + v2 "github.com/apache/apisix-ingress-controller/api/v2" + "github.com/apache/apisix-ingress-controller/internal/controller/indexer" "github.com/apache/apisix-ingress-controller/internal/controller/status" "github.com/apache/apisix-ingress-controller/internal/provider" "github.com/apache/apisix-ingress-controller/internal/utils" @@ -70,30 +72,34 @@ func (r *ApisixConsumerReconciler) Reconcile(ctx context.Context, req ctrl.Reque } var ( - tctx = provider.NewDefaultTranslateContext(ctx) - err error + tctx = provider.NewDefaultTranslateContext(ctx) + reasonErr error ) defer func() { - r.updateStatus(ac, err) + r.updateStatus(ac, reasonErr) }() ingressClass, err := GetIngressClass(tctx, r.Client, r.Log, ac.Spec.IngressClassName) if err != nil { r.Log.Error(err, "failed to get IngressClass") + reasonErr = err return ctrl.Result{}, err } if err := ProcessIngressClassParameters(tctx, r.Client, r.Log, ac, ingressClass); err != nil { r.Log.Error(err, "failed to process IngressClass parameters", "ingressClass", ingressClass.Name) + reasonErr = err return ctrl.Result{}, err } if err := r.processSpec(ctx, tctx, ac); err != nil { + reasonErr = err return ctrl.Result{}, err } if err := r.Provider.Update(ctx, tctx, ac); err != nil { r.Log.Error(err, "failed to update provider", "ApisixConsumer", ac) + reasonErr = err return ctrl.Result{}, err } return ctrl.Result{}, nil @@ -122,6 +128,9 @@ func (r *ApisixConsumerReconciler) SetupWithManager(mgr ctrl.Manager) error { Watches(&v1alpha1.GatewayProxy{}, handler.EnqueueRequestsFromMapFunc(r.listApisixConsumerForGatewayProxy), ). + Watches(&corev1.Secret{}, + handler.EnqueueRequestsFromMapFunc(r.listApisixConsumerForSecret), + ). Named("apisixconsumer"). Complete(r) } @@ -161,6 +170,23 @@ func (r *ApisixConsumerReconciler) listApisixConsumerForIngressClass(ctx context ) } +func (r *ApisixConsumerReconciler) listApisixConsumerForSecret(ctx context.Context, obj client.Object) []reconcile.Request { + secret, ok := obj.(*corev1.Secret) + if !ok { + r.Log.Error(nil, "failed to convert to Secret", "object", obj) + return nil + } + return ListRequests( + ctx, + r.Client, + r.Log, + &v2.ApisixConsumerList{}, + client.MatchingFields{ + indexer.SecretIndexRef: indexer.GenIndexKey(secret.GetNamespace(), secret.GetName()), + }, + ) +} + func (r *ApisixConsumerReconciler) processSpec(ctx context.Context, tctx *provider.TranslateContext, ac *apiv2.ApisixConsumer) error { var secretRef *corev1.LocalObjectReference if ac.Spec.AuthParameter.KeyAuth != nil { @@ -205,11 +231,7 @@ func (r *ApisixConsumerReconciler) updateStatus(consumer *apiv2.ApisixConsumer, NamespacedName: utils.NamespacedName(consumer), Resource: &apiv2.ApisixConsumer{}, Mutator: status.MutatorFunc(func(obj client.Object) client.Object { - ac, ok := obj.(*apiv2.ApisixConsumer) - if !ok { - err := fmt.Errorf("unsupported object type %T", obj) - panic(err) - } + ac := obj.(*apiv2.ApisixConsumer) acCopy := ac.DeepCopy() acCopy.Status = consumer.Status return acCopy diff --git a/internal/controller/apisixroute_controller.go b/internal/controller/apisixroute_controller.go index 3b6283b0f..105cfe669 100644 --- a/internal/controller/apisixroute_controller.go +++ b/internal/controller/apisixroute_controller.go @@ -35,7 +35,6 @@ import ( "github.com/apache/apisix-ingress-controller/api/v1alpha1" apiv2 "github.com/apache/apisix-ingress-controller/api/v2" - "github.com/apache/apisix-ingress-controller/internal/controller/config" "github.com/apache/apisix-ingress-controller/internal/controller/indexer" "github.com/apache/apisix-ingress-controller/internal/controller/status" "github.com/apache/apisix-ingress-controller/internal/provider" @@ -511,45 +510,6 @@ func (r *ApisixRouteReconciler) listApisixRouteForApisixUpstream(ctx context.Con return pkgutils.DedupComparable(requests) } -func (r *ApisixRouteReconciler) matchesIngressController(obj client.Object) bool { - ingressClass, ok := obj.(*networkingv1.IngressClass) - if !ok { - return false - } - return matchesController(ingressClass.Spec.Controller) -} - -func (r *ApisixRouteReconciler) getIngressClass(ar *apiv2.ApisixRoute) (*networkingv1.IngressClass, error) { - if ar.Spec.IngressClassName == "" { - return r.getDefaultIngressClass() - } - - var ic networkingv1.IngressClass - if err := r.Get(context.Background(), client.ObjectKey{Name: ar.Spec.IngressClassName}, &ic); err != nil { - return nil, err - } - return &ic, nil -} - -func (r *ApisixRouteReconciler) getDefaultIngressClass() (*networkingv1.IngressClass, error) { - var icList networkingv1.IngressClassList - if err := r.List(context.Background(), &icList, client.MatchingFields{ - indexer.IngressClass: config.GetControllerName(), - }); err != nil { - r.Log.Error(err, "failed to list ingress classes") - return nil, err - } - for _, ic := range icList.Items { - if IsDefaultIngressClass(&ic) && matchesController(ic.Spec.Controller) { - return &ic, nil - } - } - return nil, ReasonError{ - Reason: string(metav1.StatusReasonNotFound), - Message: "default ingress class not found or dose not match the controller", - } -} - func (r *ApisixRouteReconciler) updateStatus(ar *apiv2.ApisixRoute, err error) { SetApisixCRDConditionAccepted(&ar.Status, ar.GetGeneration(), err) r.Updater.Update(status.Update{ diff --git a/internal/controller/consumer_controller.go b/internal/controller/consumer_controller.go index d82c8fe91..8f03496c0 100644 --- a/internal/controller/consumer_controller.go +++ b/internal/controller/consumer_controller.go @@ -100,23 +100,15 @@ func (r *ConsumerReconciler) listConsumersForSecret(ctx context.Context, obj cli r.Log.Error(nil, "failed to convert to Secret", "object", obj) return nil } - consumerList := &v1alpha1.ConsumerList{} - if err := r.List(ctx, consumerList, client.MatchingFields{ - indexer.SecretIndexRef: indexer.GenIndexKey(secret.GetNamespace(), secret.GetName()), - }); err != nil { - r.Log.Error(err, "failed to list consumers") - return nil - } - requests := make([]reconcile.Request, 0, len(consumerList.Items)) - for _, consumer := range consumerList.Items { - requests = append(requests, reconcile.Request{ - NamespacedName: client.ObjectKey{ - Name: consumer.Name, - Namespace: consumer.Namespace, - }, - }) - } - return requests + return ListRequests( + ctx, + r.Client, + r.Log, + &v1alpha1.ConsumerList{}, + client.MatchingFields{ + indexer.SecretIndexRef: indexer.GenIndexKey(secret.GetNamespace(), secret.GetName()), + }, + ) } func (r *ConsumerReconciler) listConsumersForGateway(ctx context.Context, obj client.Object) []reconcile.Request { diff --git a/internal/controller/httproutepolicy.go b/internal/controller/httproutepolicy.go index 863555f60..8c2eee147 100644 --- a/internal/controller/httproutepolicy.go +++ b/internal/controller/httproutepolicy.go @@ -202,7 +202,7 @@ func (r *IngressReconciler) updateHTTPRoutePolicyStatusOnDeleting(ctx context.Co if err := r.Get(ctx, namespacedName, &ingress); err != nil { continue } - ingressClass, err := r.getIngressClass(&ingress) + ingressClass, err := r.getIngressClass(ctx, &ingress) if err != nil { continue } diff --git a/internal/controller/indexer/indexer.go b/internal/controller/indexer/indexer.go index 252931185..7136e0895 100644 --- a/internal/controller/indexer/indexer.go +++ b/internal/controller/indexer/indexer.go @@ -16,6 +16,7 @@ import ( "cmp" "context" + corev1 "k8s.io/api/core/v1" networkingv1 "k8s.io/api/networking/v1" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" @@ -56,6 +57,7 @@ func SetupIndexer(mgr ctrl.Manager) error { setupGatewayClassIndexer, setupApisixRouteIndexer, setupApisixPluginConfigIndexer, + setupApisixConsumerIndexer, } { if err := setup(mgr); err != nil { return err @@ -124,6 +126,18 @@ func setupApisixPluginConfigIndexer(mgr ctrl.Manager) error { return nil } +func setupApisixConsumerIndexer(mgr ctrl.Manager) error { + if err := mgr.GetFieldIndexer().IndexField( + context.Background(), + &apiv2.ApisixConsumer{}, + SecretIndexRef, + ApisixConsumerSecretIndexFunc, + ); err != nil { + return err + } + return nil +} + func ConsumerSecretIndexFunc(rawObj client.Object) []string { consumer := rawObj.(*v1alpha1.Consumer) secretKeys := make([]string, 0) @@ -638,3 +652,25 @@ func ApisixPluginConfigSecretIndexFunc(obj client.Object) (keys []string) { } return } + +func ApisixConsumerSecretIndexFunc(rawObj client.Object) (keys []string) { + ac := rawObj.(*apiv2.ApisixConsumer) + var secretRef *corev1.LocalObjectReference + if ac.Spec.AuthParameter.KeyAuth != nil { + secretRef = ac.Spec.AuthParameter.KeyAuth.SecretRef + } else if ac.Spec.AuthParameter.BasicAuth != nil { + secretRef = ac.Spec.AuthParameter.BasicAuth.SecretRef + } else if ac.Spec.AuthParameter.JwtAuth != nil { + secretRef = ac.Spec.AuthParameter.JwtAuth.SecretRef + } else if ac.Spec.AuthParameter.WolfRBAC != nil { + secretRef = ac.Spec.AuthParameter.WolfRBAC.SecretRef + } else if ac.Spec.AuthParameter.HMACAuth != nil { + secretRef = ac.Spec.AuthParameter.HMACAuth.SecretRef + } else if ac.Spec.AuthParameter.LDAPAuth != nil { + secretRef = ac.Spec.AuthParameter.LDAPAuth.SecretRef + } + if secretRef != nil { + keys = append(keys, GenIndexKey(ac.GetNamespace(), secretRef.Name)) + } + return +} diff --git a/internal/controller/ingress_controller.go b/internal/controller/ingress_controller.go index 74403089a..8380d3650 100644 --- a/internal/controller/ingress_controller.go +++ b/internal/controller/ingress_controller.go @@ -14,7 +14,6 @@ package controller import ( "context" - "errors" "fmt" "reflect" @@ -39,7 +38,6 @@ import ( gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" "github.com/apache/apisix-ingress-controller/api/v1alpha1" - "github.com/apache/apisix-ingress-controller/internal/controller/config" "github.com/apache/apisix-ingress-controller/internal/controller/indexer" "github.com/apache/apisix-ingress-controller/internal/controller/status" "github.com/apache/apisix-ingress-controller/internal/provider" @@ -144,7 +142,7 @@ func (r *IngressReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct // create a translate context tctx := provider.NewDefaultTranslateContext(ctx) - ingressClass, err := r.getIngressClass(ingress) + ingressClass, err := r.getIngressClass(ctx, ingress) if err != nil { r.Log.Error(err, "failed to get IngressClass") return ctrl.Result{}, err @@ -157,7 +155,7 @@ func (r *IngressReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct }) // process IngressClass parameters if they reference GatewayProxy - if err := r.processIngressClassParameters(ctx, tctx, ingress, ingressClass); err != nil { + if err := ProcessIngressClassParameters(tctx, r.Client, r.Log, ingress, ingressClass); err != nil { r.Log.Error(err, "failed to process IngressClass parameters", "ingressClass", ingressClass.Name) return ctrl.Result{}, err } @@ -201,48 +199,18 @@ func (r *IngressReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct } // getIngressClass get the ingress class for the ingress -func (r *IngressReconciler) getIngressClass(obj client.Object) (*networkingv1.IngressClass, error) { +func (r *IngressReconciler) getIngressClass(ctx context.Context, obj client.Object) (*networkingv1.IngressClass, error) { ingress := obj.(*networkingv1.Ingress) - - if ingress.Spec.IngressClassName == nil { - // handle the case where IngressClassName is not specified - // find all ingress classes and check if any of them is marked as default - ingressClassList := &networkingv1.IngressClassList{} - if err := r.List(context.Background(), ingressClassList, client.MatchingFields{ - indexer.IngressClass: config.GetControllerName(), - }); err != nil { - r.Log.Error(err, "failed to list ingress classes") - return nil, err - } - - // find the ingress class that is marked as default - for _, ic := range ingressClassList.Items { - if IsDefaultIngressClass(&ic) && matchesController(ic.Spec.Controller) { - log.Debugw("match the default ingress class") - return &ic, nil - } - } - - log.Debugw("no default ingress class found") - return nil, errors.New("no default ingress class found") + var ingressClassName string + if ingress.Spec.IngressClassName != nil { + ingressClassName = *ingress.Spec.IngressClassName } - - // if it does not match, check if the ingress class is controlled by us - ingressClass := networkingv1.IngressClass{} - if err := r.Get(context.Background(), client.ObjectKey{Name: *ingress.Spec.IngressClassName}, &ingressClass); err != nil { - return nil, err - } - - if matchesController(ingressClass.Spec.Controller) { - return &ingressClass, nil - } - - return nil, errors.New("ingress class is not controlled by us") + return GetIngressClass(ctx, r.Client, r.Log, ingressClassName) } // checkIngressClass check if the ingress uses the ingress class that we control func (r *IngressReconciler) checkIngressClass(obj client.Object) bool { - _, err := r.getIngressClass(obj) + _, err := r.getIngressClass(context.Background(), obj) return err == nil } @@ -680,118 +648,7 @@ func (r *IngressReconciler) updateStatus(ctx context.Context, tctx *provider.Tra return nil } -// processIngressClassParameters processes the IngressClass parameters that reference GatewayProxy -func (r *IngressReconciler) processIngressClassParameters(ctx context.Context, tctx *provider.TranslateContext, ingress *networkingv1.Ingress, ingressClass *networkingv1.IngressClass) error { - if ingressClass.Spec.Parameters == nil { - return nil - } - - ingressClassKind := utils.NamespacedNameKind(ingressClass) - ingressKind := utils.NamespacedNameKind(ingress) - - parameters := ingressClass.Spec.Parameters - // check if the parameters reference GatewayProxy - if parameters.APIGroup != nil && *parameters.APIGroup == v1alpha1.GroupVersion.Group && parameters.Kind == KindGatewayProxy { - ns := ingress.GetNamespace() - if parameters.Namespace != nil { - ns = *parameters.Namespace - } - - gatewayProxy := &v1alpha1.GatewayProxy{} - if err := r.Get(ctx, client.ObjectKey{ - Namespace: ns, - Name: parameters.Name, - }, gatewayProxy); err != nil { - r.Log.Error(err, "failed to get GatewayProxy", "namespace", ns, "name", parameters.Name) - return err - } - - r.Log.Info("found GatewayProxy for IngressClass", "ingressClass", ingressClass.Name, "gatewayproxy", gatewayProxy.Name) - tctx.GatewayProxies[ingressClassKind] = *gatewayProxy - tctx.ResourceParentRefs[ingressKind] = append(tctx.ResourceParentRefs[ingressKind], ingressClassKind) - - // check if the provider field references a secret - if gatewayProxy.Spec.Provider != nil && gatewayProxy.Spec.Provider.Type == v1alpha1.ProviderTypeControlPlane { - if gatewayProxy.Spec.Provider.ControlPlane != nil && - gatewayProxy.Spec.Provider.ControlPlane.Auth.Type == v1alpha1.AuthTypeAdminKey && - gatewayProxy.Spec.Provider.ControlPlane.Auth.AdminKey != nil && - gatewayProxy.Spec.Provider.ControlPlane.Auth.AdminKey.ValueFrom != nil && - gatewayProxy.Spec.Provider.ControlPlane.Auth.AdminKey.ValueFrom.SecretKeyRef != nil { - - secretRef := gatewayProxy.Spec.Provider.ControlPlane.Auth.AdminKey.ValueFrom.SecretKeyRef - secret := &corev1.Secret{} - if err := r.Get(ctx, client.ObjectKey{ - Namespace: ns, - Name: secretRef.Name, - }, secret); err != nil { - r.Log.Error(err, "failed to get secret for GatewayProxy provider", - "namespace", ns, - "name", secretRef.Name) - return err - } - - r.Log.Info("found secret for GatewayProxy provider", - "ingressClass", ingressClass.Name, - "gatewayproxy", gatewayProxy.Name, - "secret", secretRef.Name) - - tctx.Secrets[types.NamespacedName{ - Namespace: ns, - Name: secretRef.Name, - }] = secret - } - } - } - - // if gateway proxy is not found, return error - _, ok := tctx.GatewayProxies[ingressClassKind] - if !ok { - r.Log.Error(fmt.Errorf("no gateway proxy found for ingress class"), "failed to process IngressClass parameters", "ingressClass", ingressClass.Name) - return fmt.Errorf("no gateway proxy found for ingress class") - } - - return nil -} - // listIngressesForGatewayProxy list all ingresses that use a specific gateway proxy func (r *IngressReconciler) listIngressesForGatewayProxy(ctx context.Context, obj client.Object) []reconcile.Request { - gatewayProxy, ok := obj.(*v1alpha1.GatewayProxy) - if !ok { - r.Log.Error(fmt.Errorf("unexpected object type"), "failed to convert object to GatewayProxy") - return nil - } - - // find all ingress classes that reference this gateway proxy - ingressClassList := &networkingv1.IngressClassList{} - if err := r.List(ctx, ingressClassList, client.MatchingFields{ - indexer.IngressClassParametersRef: indexer.GenIndexKey(gatewayProxy.GetNamespace(), gatewayProxy.GetName()), - }); err != nil { - r.Log.Error(err, "failed to list ingress classes for gateway proxy", "gatewayproxy", gatewayProxy.GetName()) - return nil - } - - var requests []reconcile.Request - - for _, ingressClass := range ingressClassList.Items { - requests = append(requests, r.listIngressForIngressClass(ctx, &ingressClass)...) - } - - // the requests may contain duplicates, distinct the requests - requests = distinctRequests(requests) - - return requests -} - -// distinctRequests distinct the requests -func distinctRequests(requests []reconcile.Request) []reconcile.Request { - uniqueRequests := make(map[string]reconcile.Request) - for _, request := range requests { - uniqueRequests[request.String()] = request - } - - distinctRequests := make([]reconcile.Request, 0, len(uniqueRequests)) - for _, request := range uniqueRequests { - distinctRequests = append(distinctRequests, request) - } - return distinctRequests + return listIngressClassRequestsForGatewayProxy(ctx, r.Client, obj, r.Log, r.listIngressForIngressClass) } diff --git a/internal/controller/utils.go b/internal/controller/utils.go index 20ce88030..5ef9fa304 100644 --- a/internal/controller/utils.go +++ b/internal/controller/utils.go @@ -1176,12 +1176,32 @@ func checkReferenceGrant(ctx context.Context, cli client.Client, obj v1beta1.Ref return false } +func ListRequests( + ctx context.Context, + c client.Client, + logger logr.Logger, + listObj client.ObjectList, + opts ...client.ListOption, +) []reconcile.Request { + return ListMatchingRequests( + ctx, + c, + logger, + listObj, + func(obj client.Object) bool { + return true + }, + opts..., + ) +} + func ListMatchingRequests( ctx context.Context, c client.Client, logger logr.Logger, listObj client.ObjectList, matchFunc func(obj client.Object) bool, + opts ...client.ListOption, ) []reconcile.Request { if err := c.List(ctx, listObj); err != nil { logger.Error(err, "failed to list resource") @@ -1379,3 +1399,17 @@ func GetIngressClass(ctx context.Context, c client.Client, log logr.Logger, ingr return nil, errors.New("ingress class is not controlled by us") } + +// distinctRequests distinct the requests +func distinctRequests(requests []reconcile.Request) []reconcile.Request { + uniqueRequests := make(map[string]reconcile.Request) + for _, request := range requests { + uniqueRequests[request.String()] = request + } + + distinctRequests := make([]reconcile.Request, 0, len(uniqueRequests)) + for _, request := range uniqueRequests { + distinctRequests = append(distinctRequests, request) + } + return distinctRequests +} diff --git a/internal/provider/adc/translator/apisixconsumer.go b/internal/provider/adc/translator/apisixconsumer.go index 4eecb6915..310f67626 100644 --- a/internal/provider/adc/translator/apisixconsumer.go +++ b/internal/provider/adc/translator/apisixconsumer.go @@ -31,7 +31,9 @@ var ( _errKeyNotFoundOrInvalid = errors.New("key \"key\" not found or invalid in secret") _errUsernameNotFoundOrInvalid = errors.New("key \"username\" not found or invalid in secret") _errPasswordNotFoundOrInvalid = errors.New("key \"password\" not found or invalid in secret") +) +const ( _jwtAuthExpDefaultValue = int64(868400) _hmacAuthAlgorithmDefaultValue = "hmac-sha256" @@ -41,44 +43,44 @@ var ( _hmacAuthValidateRequestBodyDefaultValue = false _hmacAuthMaxReqBodyDefaultValue = int64(524288) - _stringTrue = "true" + _true = "true" ) func (t *Translator) TranslateApisixConsumer(tctx *provider.TranslateContext, ac *v2.ApisixConsumer) (*TranslateResult, error) { result := &TranslateResult{} plugins := make(adctypes.Plugins) if ac.Spec.AuthParameter.KeyAuth != nil { - cfg, err := t.translateConsumerKeyAuthPluginV2(tctx, ac.Namespace, ac.Spec.AuthParameter.KeyAuth) + cfg, err := t.translateConsumerKeyAuthPlugin(tctx, ac.Namespace, ac.Spec.AuthParameter.KeyAuth) if err != nil { return nil, fmt.Errorf("invalid key auth config: %s", err) } plugins["key-auth"] = cfg } else if ac.Spec.AuthParameter.BasicAuth != nil { - cfg, err := t.translateConsumerBasicAuthPluginV2(tctx, ac.Namespace, ac.Spec.AuthParameter.BasicAuth) + cfg, err := t.translateConsumerBasicAuthPlugin(tctx, ac.Namespace, ac.Spec.AuthParameter.BasicAuth) if err != nil { return nil, fmt.Errorf("invalid basic auth config: %s", err) } plugins["basic-auth"] = cfg } else if ac.Spec.AuthParameter.JwtAuth != nil { - cfg, err := t.translateConsumerJwtAuthPluginV2(tctx, ac.Namespace, ac.Spec.AuthParameter.JwtAuth) + cfg, err := t.translateConsumerJwtAuthPlugin(tctx, ac.Namespace, ac.Spec.AuthParameter.JwtAuth) if err != nil { return nil, fmt.Errorf("invalid jwt auth config: %s", err) } plugins["jwt-auth"] = cfg } else if ac.Spec.AuthParameter.WolfRBAC != nil { - cfg, err := t.translateConsumerWolfRBACPluginV2(tctx, ac.Namespace, ac.Spec.AuthParameter.WolfRBAC) + cfg, err := t.translateConsumerWolfRBACPlugin(tctx, ac.Namespace, ac.Spec.AuthParameter.WolfRBAC) if err != nil { return nil, fmt.Errorf("invalid wolf rbac config: %s", err) } plugins["wolf-rbac"] = cfg } else if ac.Spec.AuthParameter.HMACAuth != nil { - cfg, err := t.translateConsumerHMACAuthPluginV2(tctx, ac.Namespace, ac.Spec.AuthParameter.HMACAuth) + cfg, err := t.translateConsumerHMACAuthPlugin(tctx, ac.Namespace, ac.Spec.AuthParameter.HMACAuth) if err != nil { return nil, fmt.Errorf("invalid hmac auth config: %s", err) } plugins["hmac-auth"] = cfg } else if ac.Spec.AuthParameter.LDAPAuth != nil { - cfg, err := t.translateConsumerLDAPAuthPluginV2(tctx, ac.Namespace, ac.Spec.AuthParameter.LDAPAuth) + cfg, err := t.translateConsumerLDAPAuthPlugin(tctx, ac.Namespace, ac.Spec.AuthParameter.LDAPAuth) if err != nil { return nil, fmt.Errorf("invalid ldap auth config: %s", err) } @@ -95,7 +97,7 @@ func (t *Translator) TranslateApisixConsumer(tctx *provider.TranslateContext, ac return result, nil } -func (t *Translator) translateConsumerKeyAuthPluginV2(tctx *provider.TranslateContext, consumerNamespace string, cfg *v2.ApisixConsumerKeyAuth) (*types.KeyAuthConsumerConfig, error) { +func (t *Translator) translateConsumerKeyAuthPlugin(tctx *provider.TranslateContext, consumerNamespace string, cfg *v2.ApisixConsumerKeyAuth) (*types.KeyAuthConsumerConfig, error) { if cfg.Value != nil { return &types.KeyAuthConsumerConfig{Key: cfg.Value.Key}, nil } @@ -114,7 +116,7 @@ func (t *Translator) translateConsumerKeyAuthPluginV2(tctx *provider.TranslateCo return &types.KeyAuthConsumerConfig{Key: string(raw)}, nil } -func (t *Translator) translateConsumerBasicAuthPluginV2(tctx *provider.TranslateContext, consumerNamespace string, cfg *v2.ApisixConsumerBasicAuth) (*types.BasicAuthConsumerConfig, error) { +func (t *Translator) translateConsumerBasicAuthPlugin(tctx *provider.TranslateContext, consumerNamespace string, cfg *v2.ApisixConsumerBasicAuth) (*types.BasicAuthConsumerConfig, error) { if cfg.Value != nil { return &types.BasicAuthConsumerConfig{ Username: cfg.Value.Username, @@ -143,7 +145,7 @@ func (t *Translator) translateConsumerBasicAuthPluginV2(tctx *provider.Translate }, nil } -func (t *Translator) translateConsumerWolfRBACPluginV2(tctx *provider.TranslateContext, consumerNamespace string, cfg *v2.ApisixConsumerWolfRBAC) (*types.WolfRBACConsumerConfig, error) { +func (t *Translator) translateConsumerWolfRBACPlugin(tctx *provider.TranslateContext, consumerNamespace string, cfg *v2.ApisixConsumerWolfRBAC) (*types.WolfRBACConsumerConfig, error) { if cfg.Value != nil { return &types.WolfRBACConsumerConfig{ Server: cfg.Value.Server, @@ -168,7 +170,7 @@ func (t *Translator) translateConsumerWolfRBACPluginV2(tctx *provider.TranslateC }, nil } -func (t *Translator) translateConsumerJwtAuthPluginV2(tctx *provider.TranslateContext, consumerNamespace string, cfg *v2.ApisixConsumerJwtAuth) (*types.JwtAuthConsumerConfig, error) { +func (t *Translator) translateConsumerJwtAuthPlugin(tctx *provider.TranslateContext, consumerNamespace string, cfg *v2.ApisixConsumerJwtAuth) (*types.JwtAuthConsumerConfig, error) { if cfg.Value != nil { // The field exp must be a positive integer, default value 86400. if cfg.Value.Exp < 1 { @@ -199,7 +201,7 @@ func (t *Translator) translateConsumerJwtAuthPluginV2(tctx *provider.TranslateCo } base64SecretRaw := sec.Data["base64_secret"] var base64Secret bool - if string(base64SecretRaw) == _stringTrue { + if string(base64SecretRaw) == _true { base64Secret = true } expRaw := sec.Data["exp"] @@ -227,7 +229,7 @@ func (t *Translator) translateConsumerJwtAuthPluginV2(tctx *provider.TranslateCo }, nil } -func (t *Translator) translateConsumerHMACAuthPluginV2(tctx *provider.TranslateContext, consumerNamespace string, cfg *v2.ApisixConsumerHMACAuth) (*types.HMACAuthConsumerConfig, error) { +func (t *Translator) translateConsumerHMACAuthPlugin(tctx *provider.TranslateContext, consumerNamespace string, cfg *v2.ApisixConsumerHMACAuth) (*types.HMACAuthConsumerConfig, error) { if cfg.Value != nil { return &types.HMACAuthConsumerConfig{ AccessKey: cfg.Value.AccessKey, @@ -285,7 +287,7 @@ func (t *Translator) translateConsumerHMACAuthPluginV2(tctx *provider.TranslateC if !ok { keepHeader = _hmacAuthKeepHeadersDefaultValue } else { - if string(keepHeaderRaw) == _stringTrue { + if string(keepHeaderRaw) == _true { keepHeader = true } else { keepHeader = false @@ -297,7 +299,7 @@ func (t *Translator) translateConsumerHMACAuthPluginV2(tctx *provider.TranslateC if !ok { encodeURIParams = _hmacAuthEncodeURIParamsDefaultValue } else { - if string(encodeURIParamsRaw) == _stringTrue { + if string(encodeURIParamsRaw) == _true { encodeURIParams = true } else { encodeURIParams = false @@ -309,7 +311,7 @@ func (t *Translator) translateConsumerHMACAuthPluginV2(tctx *provider.TranslateC if !ok { validateRequestBody = _hmacAuthValidateRequestBodyDefaultValue } else { - if string(validateRequestBodyRaw) == _stringTrue { + if string(validateRequestBodyRaw) == _true { validateRequestBody = true } else { validateRequestBody = false @@ -335,7 +337,7 @@ func (t *Translator) translateConsumerHMACAuthPluginV2(tctx *provider.TranslateC }, nil } -func (t *Translator) translateConsumerLDAPAuthPluginV2(tctx *provider.TranslateContext, consumerNamespace string, cfg *v2.ApisixConsumerLDAPAuth) (*types.LDAPAuthConsumerConfig, error) { +func (t *Translator) translateConsumerLDAPAuthPlugin(tctx *provider.TranslateContext, consumerNamespace string, cfg *v2.ApisixConsumerLDAPAuth) (*types.LDAPAuthConsumerConfig, error) { if cfg.Value != nil { return &types.LDAPAuthConsumerConfig{ UserDN: cfg.Value.UserDN, diff --git a/test/e2e/apisix/consumer.go b/test/e2e/apisix/consumer.go index 329f7e0bf..0569dc86b 100644 --- a/test/e2e/apisix/consumer.go +++ b/test/e2e/apisix/consumer.go @@ -50,7 +50,8 @@ var _ = Describe("Test ApisixConsumer", func() { }) Context("Test KeyAuth", func() { - const keyAuth = ` + const ( + keyAuth = ` apiVersion: apisix.apache.org/v2 kind: ApisixConsumer metadata: @@ -62,7 +63,7 @@ spec: value: key: test-key ` - const defaultApisixRoute = ` + defaultApisixRoute = ` apiVersion: apisix.apache.org/v2 kind: ApisixRoute metadata: @@ -85,6 +86,37 @@ spec: enable: true type: keyAuth ` + secret = ` +apiVersion: v1 +kind: Secret +metadata: + name: keyauth +data: + # foo-key + key: Zm9vLWtleQ== +` + secretUpdated = ` +apiVersion: v1 +kind: Secret +metadata: + name: keyauth +data: + # foo2-key + key: Zm9vMi1rZXk= +` + keyAuthWiwhSecret = ` +apiVersion: apisix.apache.org/v2 +kind: ApisixConsumer +metadata: + name: test-consumer +spec: + ingressClassName: apisix + authParameter: + keyAuth: + secretRef: + name: keyauth +` + ) request := func(path string, headers Headers) int { return s.NewAPISIXClient().GET(path).WithHeaders(headers).WithHost("httpbin").Expect().Raw().StatusCode } @@ -102,7 +134,7 @@ spec: }).WithTimeout(5 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusUnauthorized)) Eventually(request).WithArguments("/get", Headers{ - "apikey": "test-key", + "apikey": "foo-key", }).WithTimeout(5 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusOK)) By("Delete ApisixConsumer") @@ -118,12 +150,55 @@ spec: Eventually(request).WithArguments("/headers", Headers{}).WithTimeout(5 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusNotFound)) }) - PIt("SecretRef tests", func() { + It("SecretRef tests", func() { + By("apply ApisixRoute") + applier.MustApplyAPIv2(types.NamespacedName{Namespace: s.Namespace(), Name: "default"}, &apiv2.ApisixRoute{}, defaultApisixRoute) + + By("apply Secret") + err := s.CreateResourceFromString(secret) + Expect(err).ShouldNot(HaveOccurred(), "creating Secret for ApisixConsumer") + + By("apply ApisixConsumer") + applier.MustApplyAPIv2(types.NamespacedName{Namespace: s.Namespace(), Name: "test-consumer"}, &apiv2.ApisixConsumer{}, keyAuthWiwhSecret) + + By("verify ApisixRoute with ApisixConsumer") + Eventually(request).WithArguments("/get", Headers{ + "apikey": "invalid-key", + }).WithTimeout(5 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusUnauthorized)) + + Eventually(request).WithArguments("/get", Headers{ + "apikey": "foo-key", + }).WithTimeout(5 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusOK)) + + By("update Secret") + err = s.CreateResourceFromString(secretUpdated) + Expect(err).ShouldNot(HaveOccurred(), "updating Secret for ApisixConsumer") + + Eventually(request).WithArguments("/get", Headers{ + "apikey": "foo-key", + }).WithTimeout(5 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusUnauthorized)) + + Eventually(request).WithArguments("/get", Headers{ + "apikey": "foo2-key", + }).WithTimeout(5 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusOK)) + + By("Delete ApisixConsumer") + err = s.DeleteResource("ApisixConsumer", "test-consumer") + Expect(err).ShouldNot(HaveOccurred(), "deleting ApisixConsumer") + Eventually(request).WithArguments("/get", Headers{ + "apikey": "test-key", + }).WithTimeout(5 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusUnauthorized)) + + By("delete ApisixRoute") + err = s.DeleteResource("ApisixRoute", "default") + Expect(err).ShouldNot(HaveOccurred(), "deleting ApisixRoute") + Eventually(request).WithArguments("/headers", Headers{}).WithTimeout(5 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusNotFound)) }) }) Context("Test BasicAuth", func() { - const basicAuth = ` + const ( + basicAuth = ` apiVersion: apisix.apache.org/v2 kind: ApisixConsumer metadata: @@ -136,7 +211,7 @@ spec: username: test-user password: test-password ` - const defaultApisixRoute = ` + defaultApisixRoute = ` apiVersion: apisix.apache.org/v2 kind: ApisixRoute metadata: @@ -160,6 +235,41 @@ spec: type: basicAuth ` + secret = ` +apiVersion: v1 +kind: Secret +metadata: + name: basic +data: + # foo:bar + username: Zm9v + password: YmFy +` + secretUpdated = ` +apiVersion: v1 +kind: Secret +metadata: + name: basic +data: + # foo-new-user:bar-new-password + username: Zm9vLW5ldy11c2Vy + password: YmFyLW5ldy1wYXNzd29yZA== +` + + basicAuthWithSecret = ` +apiVersion: apisix.apache.org/v2 +kind: ApisixConsumer +metadata: + name: test-consumer +spec: + ingressClassName: apisix + authParameter: + basicAuth: + secretRef: + name: basic +` + ) + request := func(path string, username, password string) int { return s.NewAPISIXClient().GET(path).WithBasicAuth(username, password).WithHost("httpbin").Expect().Raw().StatusCode } @@ -186,7 +296,37 @@ spec: Eventually(request).WithArguments("/headers", "", "").WithTimeout(5 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusNotFound)) }) - PIt("SecretRef tests", func() { + It("SecretRef tests", func() { + By("apply ApisixRoute") + applier.MustApplyAPIv2(types.NamespacedName{Namespace: s.Namespace(), Name: "default"}, &apiv2.ApisixRoute{}, defaultApisixRoute) + + By("apply Secret") + err := s.CreateResourceFromString(secret) + Expect(err).ShouldNot(HaveOccurred(), "creating Secret for ApisixConsumer") + + By("apply ApisixConsumer") + applier.MustApplyAPIv2(types.NamespacedName{Namespace: s.Namespace(), Name: "test-consumer"}, &apiv2.ApisixConsumer{}, basicAuthWithSecret) + + By("verify ApisixRoute with ApisixConsumer") + Eventually(request).WithArguments("/get", "", "").WithTimeout(5 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusUnauthorized)) + Eventually(request).WithArguments("/get", "foo", "bar").WithTimeout(5 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusOK)) + + By("update Secret") + err = s.CreateResourceFromString(secretUpdated) + Expect(err).ShouldNot(HaveOccurred(), "updating Secret for ApisixConsumer") + + Eventually(request).WithArguments("/get", "foo", "bar").WithTimeout(5 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusUnauthorized)) + Eventually(request).WithArguments("/get", "foo-new-user", "bar-new-password").WithTimeout(5 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusOK)) + + By("Delete ApisixConsumer") + err = s.DeleteResource("ApisixConsumer", "test-consumer") + Expect(err).ShouldNot(HaveOccurred(), "deleting ApisixConsumer") + Eventually(request).WithArguments("/get", "foo-new-user", "bar-new-password").WithTimeout(5 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusUnauthorized)) + + By("delete ApisixRoute") + err = s.DeleteResource("ApisixRoute", "default") + Expect(err).ShouldNot(HaveOccurred(), "deleting ApisixRoute") + Eventually(request).WithArguments("/get", "", "").WithTimeout(5 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusNotFound)) }) }) }) diff --git a/test/e2e/scaffold/apisix_deployer.go b/test/e2e/scaffold/apisix_deployer.go index 25c88c136..8d53c8362 100644 --- a/test/e2e/scaffold/apisix_deployer.go +++ b/test/e2e/scaffold/apisix_deployer.go @@ -218,7 +218,7 @@ func (s *APISIXDeployer) deployDataplane(opts *APISIXDeployOptions) *corev1.Serv func (s *APISIXDeployer) DeployIngress() { s.Framework.DeployIngress(framework.IngressDeployOpts{ - ProviderSyncPeriod: time.Second, + ProviderSyncPeriod: 200 * time.Millisecond, ControllerName: s.opts.ControllerName, Namespace: s.namespace, Replicas: 1, @@ -227,7 +227,7 @@ func (s *APISIXDeployer) DeployIngress() { func (s *APISIXDeployer) ScaleIngress(replicas int) { s.Framework.DeployIngress(framework.IngressDeployOpts{ - ProviderSyncPeriod: time.Second, + ProviderSyncPeriod: 200 * time.Millisecond, ControllerName: s.opts.ControllerName, Namespace: s.namespace, Replicas: replicas, From 673e825ab90e1a88600b837d245c57ebd8222a93 Mon Sep 17 00:00:00 2001 From: rongxin Date: Thu, 19 Jun 2025 17:41:39 +0800 Subject: [PATCH 06/13] fix lint --- .../controller/apisixconsumer_controller.go | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/internal/controller/apisixconsumer_controller.go b/internal/controller/apisixconsumer_controller.go index 90a7e3807..3ec1757f4 100644 --- a/internal/controller/apisixconsumer_controller.go +++ b/internal/controller/apisixconsumer_controller.go @@ -32,7 +32,6 @@ import ( "github.com/apache/apisix-ingress-controller/api/v1alpha1" apiv2 "github.com/apache/apisix-ingress-controller/api/v2" - v2 "github.com/apache/apisix-ingress-controller/api/v2" "github.com/apache/apisix-ingress-controller/internal/controller/indexer" "github.com/apache/apisix-ingress-controller/internal/controller/status" "github.com/apache/apisix-ingress-controller/internal/provider" @@ -72,34 +71,31 @@ func (r *ApisixConsumerReconciler) Reconcile(ctx context.Context, req ctrl.Reque } var ( - tctx = provider.NewDefaultTranslateContext(ctx) - reasonErr error + tctx = provider.NewDefaultTranslateContext(ctx) + ingressClass *networkingv1.IngressClass + err error ) defer func() { - r.updateStatus(ac, reasonErr) + r.updateStatus(ac, err) }() - ingressClass, err := GetIngressClass(tctx, r.Client, r.Log, ac.Spec.IngressClassName) + ingressClass, err = GetIngressClass(tctx, r.Client, r.Log, ac.Spec.IngressClassName) if err != nil { r.Log.Error(err, "failed to get IngressClass") - reasonErr = err return ctrl.Result{}, err } - if err := ProcessIngressClassParameters(tctx, r.Client, r.Log, ac, ingressClass); err != nil { + if err = ProcessIngressClassParameters(tctx, r.Client, r.Log, ac, ingressClass); err != nil { r.Log.Error(err, "failed to process IngressClass parameters", "ingressClass", ingressClass.Name) - reasonErr = err return ctrl.Result{}, err } - if err := r.processSpec(ctx, tctx, ac); err != nil { - reasonErr = err + if err = r.processSpec(ctx, tctx, ac); err != nil { return ctrl.Result{}, err } - if err := r.Provider.Update(ctx, tctx, ac); err != nil { + if err = r.Provider.Update(ctx, tctx, ac); err != nil { r.Log.Error(err, "failed to update provider", "ApisixConsumer", ac) - reasonErr = err return ctrl.Result{}, err } return ctrl.Result{}, nil @@ -180,7 +176,7 @@ func (r *ApisixConsumerReconciler) listApisixConsumerForSecret(ctx context.Conte ctx, r.Client, r.Log, - &v2.ApisixConsumerList{}, + &apiv2.ApisixConsumerList{}, client.MatchingFields{ indexer.SecretIndexRef: indexer.GenIndexKey(secret.GetNamespace(), secret.GetName()), }, From 8aa0dec14c00a4f6d085fbcca560a315d33600dc Mon Sep 17 00:00:00 2001 From: rongxin Date: Thu, 19 Jun 2025 17:55:13 +0800 Subject: [PATCH 07/13] fix lint2 --- .../provider/adc/translator/apisixconsumer.go | 15 +++++++-------- internal/types/plugin.go | 12 +++++------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/internal/provider/adc/translator/apisixconsumer.go b/internal/provider/adc/translator/apisixconsumer.go index 310f67626..a9638e9fe 100644 --- a/internal/provider/adc/translator/apisixconsumer.go +++ b/internal/provider/adc/translator/apisixconsumer.go @@ -1,9 +1,6 @@ -// 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 +// Licensed 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 // @@ -12,19 +9,21 @@ // 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 translator import ( "fmt" "strconv" + "github.com/pkg/errors" + k8stypes "k8s.io/apimachinery/pkg/types" + adctypes "github.com/apache/apisix-ingress-controller/api/adc" v2 "github.com/apache/apisix-ingress-controller/api/v2" "github.com/apache/apisix-ingress-controller/internal/controller/label" "github.com/apache/apisix-ingress-controller/internal/provider" "github.com/apache/apisix-ingress-controller/internal/types" - "github.com/pkg/errors" - k8stypes "k8s.io/apimachinery/pkg/types" ) var ( diff --git a/internal/types/plugin.go b/internal/types/plugin.go index ebf1bce82..a7e575c75 100644 --- a/internal/types/plugin.go +++ b/internal/types/plugin.go @@ -1,17 +1,15 @@ -// 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 +// Licensed 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 +// 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 types import ( From fdd997d64e44c5403835319137bc4a49bed0b44d Mon Sep 17 00:00:00 2001 From: rongxin Date: Thu, 19 Jun 2025 18:30:31 +0800 Subject: [PATCH 08/13] fix lint license --- internal/provider/adc/translator/apisixconsumer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/provider/adc/translator/apisixconsumer.go b/internal/provider/adc/translator/apisixconsumer.go index a9638e9fe..1209c9d82 100644 --- a/internal/provider/adc/translator/apisixconsumer.go +++ b/internal/provider/adc/translator/apisixconsumer.go @@ -2,7 +2,7 @@ // 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 +// 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, From 1f1ccc53d04e0daa00026e8cc12a1398fa4625d7 Mon Sep 17 00:00:00 2001 From: rongxin Date: Fri, 20 Jun 2025 00:04:52 +0800 Subject: [PATCH 09/13] fix test --- test/e2e/apisix/consumer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/apisix/consumer.go b/test/e2e/apisix/consumer.go index 0569dc86b..e750da9d4 100644 --- a/test/e2e/apisix/consumer.go +++ b/test/e2e/apisix/consumer.go @@ -134,7 +134,7 @@ spec: }).WithTimeout(5 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusUnauthorized)) Eventually(request).WithArguments("/get", Headers{ - "apikey": "foo-key", + "apikey": "test-key", }).WithTimeout(5 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusOK)) By("Delete ApisixConsumer") From b50f5ae9bfa39febb02195eb04e4adcbc2e8df39 Mon Sep 17 00:00:00 2001 From: rongxin Date: Fri, 20 Jun 2025 10:51:42 +0800 Subject: [PATCH 10/13] move plugins.go --- .../plugin.go => api/adc/plugin_types.go | 157 +------ api/adc/zz_generated.deepcopy.go | 225 ++++++++++ .../provider/adc/translator/apisixconsumer.go | 37 +- internal/types/zz_generated.deepcopy.go | 394 ------------------ 4 files changed, 244 insertions(+), 569 deletions(-) rename internal/types/plugin.go => api/adc/plugin_types.go (56%) delete mode 100644 internal/types/zz_generated.deepcopy.go diff --git a/internal/types/plugin.go b/api/adc/plugin_types.go similarity index 56% rename from internal/types/plugin.go rename to api/adc/plugin_types.go index a7e575c75..57e4aef7c 100644 --- a/internal/types/plugin.go +++ b/api/adc/plugin_types.go @@ -10,35 +10,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package types - -import ( - "encoding/json" - "fmt" - "strings" - - "github.com/incubator4/go-resty-expr/expr" -) - -// TrafficSplitConfig is the config of traffic-split plugin. -// +k8s:deepcopy-gen=true -type TrafficSplitConfig struct { - Rules []TrafficSplitConfigRule `json:"rules"` -} - -// TrafficSplitConfigRule is the rule config in traffic-split plugin config. -// +k8s:deepcopy-gen=true -type TrafficSplitConfigRule struct { - WeightedUpstreams []TrafficSplitConfigRuleWeightedUpstream `json:"weighted_upstreams"` -} - -// TrafficSplitConfigRuleWeightedUpstream is the weighted upstream config in -// the traffic split plugin rule. -// +k8s:deepcopy-gen=true -type TrafficSplitConfigRuleWeightedUpstream struct { - UpstreamID string `json:"upstream_id,omitempty"` - Weight int `json:"weight"` -} +package adc // IPRestrictConfig is the rule config for ip-restriction plugin. // +k8s:deepcopy-gen=true @@ -132,33 +104,6 @@ type WolfRBACConsumerConfig struct { HeaderPrefix string `json:"header_prefix,omitempty"` } -// RewriteConfig is the rule config for proxy-rewrite plugin. -// +k8s:deepcopy-gen=true -type RewriteConfig struct { - RewriteTarget string `json:"uri,omitempty"` - RewriteTargetRegex []string `json:"regex_uri,omitempty"` - Headers Headers `json:"headers,omitempty"` -} - -// ResponseRewriteConfig is the rule config for response-rewrite plugin. -// +k8s:deepcopy-gen=true -type ResponseRewriteConfig struct { - StatusCode int `json:"status_code,omitempty"` - Body string `json:"body,omitempty"` - BodyBase64 bool `json:"body_base64,omitempty"` - Headers Headers `json:"headers,omitempty"` - LuaRestyExpr []expr.Expr `json:"vars,omitempty"` - Filters []map[string]string `json:"filters,omitempty"` -} - -// RedirectConfig is the rule config for redirect plugin. -// +k8s:deepcopy-gen=true -type RedirectConfig struct { - HttpToHttps bool `json:"http_to_https,omitempty"` - URI string `json:"uri,omitempty"` - RetCode int `json:"ret_code,omitempty"` -} - // ForwardAuthConfig is the rule config for forward-auth plugin. // +k8s:deepcopy-gen=true type ForwardAuthConfig struct { @@ -178,103 +123,3 @@ type BasicAuthConfig struct { // +k8s:deepcopy-gen=true type KeyAuthConfig struct { } - -// RequestMirror is the rule config for proxy-mirror plugin. -// +k8s:deepcopy-gen=true -type RequestMirror struct { - Host string `json:"host"` -} - -type Headers map[string]any - -func (p *Headers) DeepCopyInto(out *Headers) { - b, _ := json.Marshal(&p) - _ = json.Unmarshal(b, out) -} - -func (p *Headers) DeepCopy() *Headers { - if p == nil { - return nil - } - out := new(Headers) - p.DeepCopyInto(out) - return out -} - -func (p *Headers) Add(headersToAdd []string) { - if p == nil { - return - } - if headersToAdd != nil { - addedHeader := make([]string, 0) - for _, h := range headersToAdd { - kv := strings.Split(h, ":") - if len(kv) < 2 { - continue - } - addedHeader = append(addedHeader, fmt.Sprintf("%s:%s", kv[0], kv[1])) - } - (*p)["add"] = addedHeader - } -} - -func (p *Headers) GetAddedHeaders() []string { - if p == nil || (*p)["add"] == nil { - return nil - } - addedheaders, ok := (*p)["add"].([]string) - if ok { - return addedheaders - } - return nil -} - -func (p *Headers) Set(headersToSet []string) { - if p == nil { - return - } - if headersToSet != nil { - setHeaders := make(map[string]string, 0) - for _, h := range headersToSet { - kv := strings.Split(h, ":") - if len(kv) < 2 { - continue - } - setHeaders[kv[0]] = kv[1] - } - (*p)["set"] = setHeaders - } -} - -func (p *Headers) GetSetHeaders() map[string]string { - if p == nil || (*p)["set"] == nil { - return nil - } - addedheaders, ok := (*p)["set"].(map[string]string) - if ok { - return addedheaders - } - return nil -} - -func (p *Headers) Remove(headersToRemove []string) { - if p == nil { - return - } - if headersToRemove != nil { - removeHeaders := make([]string, 0) - removeHeaders = append(removeHeaders, headersToRemove...) - (*p)["remove"] = removeHeaders - } -} - -func (p *Headers) GetRemovedHeaders() []string { - if p == nil || (*p)["remove"] == nil { - return nil - } - removedHeaders, ok := (*p)["remove"].([]string) - if ok { - return removedHeaders - } - return nil -} diff --git a/api/adc/zz_generated.deepcopy.go b/api/adc/zz_generated.deepcopy.go index 9330480da..b5ce7c00b 100644 --- a/api/adc/zz_generated.deepcopy.go +++ b/api/adc/zz_generated.deepcopy.go @@ -22,6 +22,66 @@ package adc import () +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BasicAuthConfig) DeepCopyInto(out *BasicAuthConfig) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BasicAuthConfig. +func (in *BasicAuthConfig) DeepCopy() *BasicAuthConfig { + if in == nil { + return nil + } + out := new(BasicAuthConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BasicAuthConsumerConfig) DeepCopyInto(out *BasicAuthConsumerConfig) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BasicAuthConsumerConfig. +func (in *BasicAuthConsumerConfig) DeepCopy() *BasicAuthConsumerConfig { + if in == nil { + return nil + } + out := new(BasicAuthConsumerConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BasicAuthRouteConfig) DeepCopyInto(out *BasicAuthRouteConfig) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BasicAuthRouteConfig. +func (in *BasicAuthRouteConfig) DeepCopy() *BasicAuthRouteConfig { + if in == nil { + return nil + } + out := new(BasicAuthRouteConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CSRFConfig) DeepCopyInto(out *CSRFConfig) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CSRFConfig. +func (in *CSRFConfig) DeepCopy() *CSRFConfig { + if in == nil { + return nil + } + out := new(CSRFConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Certificate) DeepCopyInto(out *Certificate) { *out = *in @@ -125,6 +185,21 @@ func (in *ConsumerGroup) DeepCopy() *ConsumerGroup { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CorsConfig) DeepCopyInto(out *CorsConfig) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CorsConfig. +func (in *CorsConfig) DeepCopy() *CorsConfig { + if in == nil { + return nil + } + out := new(CorsConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Credential) DeepCopyInto(out *Credential) { *out = *in @@ -142,6 +217,36 @@ func (in *Credential) DeepCopy() *Credential { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ForwardAuthConfig) DeepCopyInto(out *ForwardAuthConfig) { + *out = *in + if in.RequestHeaders != nil { + in, out := &in.RequestHeaders, &out.RequestHeaders + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.UpstreamHeaders != nil { + in, out := &in.UpstreamHeaders, &out.UpstreamHeaders + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.ClientHeaders != nil { + in, out := &in.ClientHeaders, &out.ClientHeaders + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ForwardAuthConfig. +func (in *ForwardAuthConfig) DeepCopy() *ForwardAuthConfig { + if in == nil { + return nil + } + out := new(ForwardAuthConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *GlobalRuleItem) DeepCopyInto(out *GlobalRuleItem) { *out = *in @@ -159,6 +264,111 @@ func (in *GlobalRuleItem) DeepCopy() *GlobalRuleItem { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HMACAuthConsumerConfig) DeepCopyInto(out *HMACAuthConsumerConfig) { + *out = *in + if in.SignedHeaders != nil { + in, out := &in.SignedHeaders, &out.SignedHeaders + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HMACAuthConsumerConfig. +func (in *HMACAuthConsumerConfig) DeepCopy() *HMACAuthConsumerConfig { + if in == nil { + return nil + } + out := new(HMACAuthConsumerConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *IPRestrictConfig) DeepCopyInto(out *IPRestrictConfig) { + *out = *in + if in.Allowlist != nil { + in, out := &in.Allowlist, &out.Allowlist + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Blocklist != nil { + in, out := &in.Blocklist, &out.Blocklist + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IPRestrictConfig. +func (in *IPRestrictConfig) DeepCopy() *IPRestrictConfig { + if in == nil { + return nil + } + out := new(IPRestrictConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *JwtAuthConsumerConfig) DeepCopyInto(out *JwtAuthConsumerConfig) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JwtAuthConsumerConfig. +func (in *JwtAuthConsumerConfig) DeepCopy() *JwtAuthConsumerConfig { + if in == nil { + return nil + } + out := new(JwtAuthConsumerConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KeyAuthConfig) DeepCopyInto(out *KeyAuthConfig) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KeyAuthConfig. +func (in *KeyAuthConfig) DeepCopy() *KeyAuthConfig { + if in == nil { + return nil + } + out := new(KeyAuthConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KeyAuthConsumerConfig) DeepCopyInto(out *KeyAuthConsumerConfig) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KeyAuthConsumerConfig. +func (in *KeyAuthConsumerConfig) DeepCopy() *KeyAuthConsumerConfig { + if in == nil { + return nil + } + out := new(KeyAuthConsumerConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LDAPAuthConsumerConfig) DeepCopyInto(out *LDAPAuthConsumerConfig) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LDAPAuthConsumerConfig. +func (in *LDAPAuthConsumerConfig) DeepCopy() *LDAPAuthConsumerConfig { + if in == nil { + return nil + } + out := new(LDAPAuthConsumerConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Metadata) DeepCopyInto(out *Metadata) { *out = *in @@ -663,3 +873,18 @@ func (in *UpstreamPassiveHealthCheckUnhealthy) DeepCopy() *UpstreamPassiveHealth in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WolfRBACConsumerConfig) DeepCopyInto(out *WolfRBACConsumerConfig) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WolfRBACConsumerConfig. +func (in *WolfRBACConsumerConfig) DeepCopy() *WolfRBACConsumerConfig { + if in == nil { + return nil + } + out := new(WolfRBACConsumerConfig) + in.DeepCopyInto(out) + return out +} diff --git a/internal/provider/adc/translator/apisixconsumer.go b/internal/provider/adc/translator/apisixconsumer.go index 1209c9d82..e58aeaa85 100644 --- a/internal/provider/adc/translator/apisixconsumer.go +++ b/internal/provider/adc/translator/apisixconsumer.go @@ -23,7 +23,6 @@ import ( v2 "github.com/apache/apisix-ingress-controller/api/v2" "github.com/apache/apisix-ingress-controller/internal/controller/label" "github.com/apache/apisix-ingress-controller/internal/provider" - "github.com/apache/apisix-ingress-controller/internal/types" ) var ( @@ -96,9 +95,9 @@ func (t *Translator) TranslateApisixConsumer(tctx *provider.TranslateContext, ac return result, nil } -func (t *Translator) translateConsumerKeyAuthPlugin(tctx *provider.TranslateContext, consumerNamespace string, cfg *v2.ApisixConsumerKeyAuth) (*types.KeyAuthConsumerConfig, error) { +func (t *Translator) translateConsumerKeyAuthPlugin(tctx *provider.TranslateContext, consumerNamespace string, cfg *v2.ApisixConsumerKeyAuth) (*adctypes.KeyAuthConsumerConfig, error) { if cfg.Value != nil { - return &types.KeyAuthConsumerConfig{Key: cfg.Value.Key}, nil + return &adctypes.KeyAuthConsumerConfig{Key: cfg.Value.Key}, nil } sec := tctx.Secrets[k8stypes.NamespacedName{ @@ -112,12 +111,12 @@ func (t *Translator) translateConsumerKeyAuthPlugin(tctx *provider.TranslateCont if !ok || len(raw) == 0 { return nil, _errKeyNotFoundOrInvalid } - return &types.KeyAuthConsumerConfig{Key: string(raw)}, nil + return &adctypes.KeyAuthConsumerConfig{Key: string(raw)}, nil } -func (t *Translator) translateConsumerBasicAuthPlugin(tctx *provider.TranslateContext, consumerNamespace string, cfg *v2.ApisixConsumerBasicAuth) (*types.BasicAuthConsumerConfig, error) { +func (t *Translator) translateConsumerBasicAuthPlugin(tctx *provider.TranslateContext, consumerNamespace string, cfg *v2.ApisixConsumerBasicAuth) (*adctypes.BasicAuthConsumerConfig, error) { if cfg.Value != nil { - return &types.BasicAuthConsumerConfig{ + return &adctypes.BasicAuthConsumerConfig{ Username: cfg.Value.Username, Password: cfg.Value.Password, }, nil @@ -138,15 +137,15 @@ func (t *Translator) translateConsumerBasicAuthPlugin(tctx *provider.TranslateCo if !ok || len(raw2) == 0 { return nil, _errPasswordNotFoundOrInvalid } - return &types.BasicAuthConsumerConfig{ + return &adctypes.BasicAuthConsumerConfig{ Username: string(raw1), Password: string(raw2), }, nil } -func (t *Translator) translateConsumerWolfRBACPlugin(tctx *provider.TranslateContext, consumerNamespace string, cfg *v2.ApisixConsumerWolfRBAC) (*types.WolfRBACConsumerConfig, error) { +func (t *Translator) translateConsumerWolfRBACPlugin(tctx *provider.TranslateContext, consumerNamespace string, cfg *v2.ApisixConsumerWolfRBAC) (*adctypes.WolfRBACConsumerConfig, error) { if cfg.Value != nil { - return &types.WolfRBACConsumerConfig{ + return &adctypes.WolfRBACConsumerConfig{ Server: cfg.Value.Server, Appid: cfg.Value.Appid, HeaderPrefix: cfg.Value.HeaderPrefix, @@ -162,20 +161,20 @@ func (t *Translator) translateConsumerWolfRBACPlugin(tctx *provider.TranslateCon raw1 := sec.Data["server"] raw2 := sec.Data["appid"] raw3 := sec.Data["header_prefix"] - return &types.WolfRBACConsumerConfig{ + return &adctypes.WolfRBACConsumerConfig{ Server: string(raw1), Appid: string(raw2), HeaderPrefix: string(raw3), }, nil } -func (t *Translator) translateConsumerJwtAuthPlugin(tctx *provider.TranslateContext, consumerNamespace string, cfg *v2.ApisixConsumerJwtAuth) (*types.JwtAuthConsumerConfig, error) { +func (t *Translator) translateConsumerJwtAuthPlugin(tctx *provider.TranslateContext, consumerNamespace string, cfg *v2.ApisixConsumerJwtAuth) (*adctypes.JwtAuthConsumerConfig, error) { if cfg.Value != nil { // The field exp must be a positive integer, default value 86400. if cfg.Value.Exp < 1 { cfg.Value.Exp = _jwtAuthExpDefaultValue } - return &types.JwtAuthConsumerConfig{ + return &adctypes.JwtAuthConsumerConfig{ Key: cfg.Value.Key, Secret: cfg.Value.Secret, PublicKey: cfg.Value.PublicKey, @@ -216,7 +215,7 @@ func (t *Translator) translateConsumerJwtAuthPlugin(tctx *provider.TranslateCont privateKeyRaw := sec.Data["private_key"] algorithmRaw := sec.Data["algorithm"] - return &types.JwtAuthConsumerConfig{ + return &adctypes.JwtAuthConsumerConfig{ Key: string(keyRaw), Secret: string(secretRaw), PublicKey: string(publicKeyRaw), @@ -228,9 +227,9 @@ func (t *Translator) translateConsumerJwtAuthPlugin(tctx *provider.TranslateCont }, nil } -func (t *Translator) translateConsumerHMACAuthPlugin(tctx *provider.TranslateContext, consumerNamespace string, cfg *v2.ApisixConsumerHMACAuth) (*types.HMACAuthConsumerConfig, error) { +func (t *Translator) translateConsumerHMACAuthPlugin(tctx *provider.TranslateContext, consumerNamespace string, cfg *v2.ApisixConsumerHMACAuth) (*adctypes.HMACAuthConsumerConfig, error) { if cfg.Value != nil { - return &types.HMACAuthConsumerConfig{ + return &adctypes.HMACAuthConsumerConfig{ AccessKey: cfg.Value.AccessKey, SecretKey: cfg.Value.SecretKey, Algorithm: cfg.Value.Algorithm, @@ -323,7 +322,7 @@ func (t *Translator) translateConsumerHMACAuthPlugin(tctx *provider.TranslateCon maxReqBody = _hmacAuthMaxReqBodyDefaultValue } - return &types.HMACAuthConsumerConfig{ + return &adctypes.HMACAuthConsumerConfig{ AccessKey: string(accessKeyRaw), SecretKey: string(secretKeyRaw), Algorithm: algorithm, @@ -336,9 +335,9 @@ func (t *Translator) translateConsumerHMACAuthPlugin(tctx *provider.TranslateCon }, nil } -func (t *Translator) translateConsumerLDAPAuthPlugin(tctx *provider.TranslateContext, consumerNamespace string, cfg *v2.ApisixConsumerLDAPAuth) (*types.LDAPAuthConsumerConfig, error) { +func (t *Translator) translateConsumerLDAPAuthPlugin(tctx *provider.TranslateContext, consumerNamespace string, cfg *v2.ApisixConsumerLDAPAuth) (*adctypes.LDAPAuthConsumerConfig, error) { if cfg.Value != nil { - return &types.LDAPAuthConsumerConfig{ + return &adctypes.LDAPAuthConsumerConfig{ UserDN: cfg.Value.UserDN, }, nil } @@ -355,7 +354,7 @@ func (t *Translator) translateConsumerLDAPAuthPlugin(tctx *provider.TranslateCon return nil, _errKeyNotFoundOrInvalid } - return &types.LDAPAuthConsumerConfig{ + return &adctypes.LDAPAuthConsumerConfig{ UserDN: string(userDNRaw), }, nil } diff --git a/internal/types/zz_generated.deepcopy.go b/internal/types/zz_generated.deepcopy.go deleted file mode 100644 index 3f2566ada..000000000 --- a/internal/types/zz_generated.deepcopy.go +++ /dev/null @@ -1,394 +0,0 @@ -//go:build !ignore_autogenerated - -/* -Copyright 2024. - -Licensed 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. -*/ - -// Code generated by controller-gen. DO NOT EDIT. - -package types - -import ( - "github.com/incubator4/go-resty-expr/expr" -) - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *BasicAuthConfig) DeepCopyInto(out *BasicAuthConfig) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BasicAuthConfig. -func (in *BasicAuthConfig) DeepCopy() *BasicAuthConfig { - if in == nil { - return nil - } - out := new(BasicAuthConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *BasicAuthConsumerConfig) DeepCopyInto(out *BasicAuthConsumerConfig) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BasicAuthConsumerConfig. -func (in *BasicAuthConsumerConfig) DeepCopy() *BasicAuthConsumerConfig { - if in == nil { - return nil - } - out := new(BasicAuthConsumerConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *BasicAuthRouteConfig) DeepCopyInto(out *BasicAuthRouteConfig) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BasicAuthRouteConfig. -func (in *BasicAuthRouteConfig) DeepCopy() *BasicAuthRouteConfig { - if in == nil { - return nil - } - out := new(BasicAuthRouteConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CSRFConfig) DeepCopyInto(out *CSRFConfig) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CSRFConfig. -func (in *CSRFConfig) DeepCopy() *CSRFConfig { - if in == nil { - return nil - } - out := new(CSRFConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CorsConfig) DeepCopyInto(out *CorsConfig) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CorsConfig. -func (in *CorsConfig) DeepCopy() *CorsConfig { - if in == nil { - return nil - } - out := new(CorsConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ForwardAuthConfig) DeepCopyInto(out *ForwardAuthConfig) { - *out = *in - if in.RequestHeaders != nil { - in, out := &in.RequestHeaders, &out.RequestHeaders - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.UpstreamHeaders != nil { - in, out := &in.UpstreamHeaders, &out.UpstreamHeaders - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.ClientHeaders != nil { - in, out := &in.ClientHeaders, &out.ClientHeaders - *out = make([]string, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ForwardAuthConfig. -func (in *ForwardAuthConfig) DeepCopy() *ForwardAuthConfig { - if in == nil { - return nil - } - out := new(ForwardAuthConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *HMACAuthConsumerConfig) DeepCopyInto(out *HMACAuthConsumerConfig) { - *out = *in - if in.SignedHeaders != nil { - in, out := &in.SignedHeaders, &out.SignedHeaders - *out = make([]string, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HMACAuthConsumerConfig. -func (in *HMACAuthConsumerConfig) DeepCopy() *HMACAuthConsumerConfig { - if in == nil { - return nil - } - out := new(HMACAuthConsumerConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *IPRestrictConfig) DeepCopyInto(out *IPRestrictConfig) { - *out = *in - if in.Allowlist != nil { - in, out := &in.Allowlist, &out.Allowlist - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.Blocklist != nil { - in, out := &in.Blocklist, &out.Blocklist - *out = make([]string, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IPRestrictConfig. -func (in *IPRestrictConfig) DeepCopy() *IPRestrictConfig { - if in == nil { - return nil - } - out := new(IPRestrictConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *JwtAuthConsumerConfig) DeepCopyInto(out *JwtAuthConsumerConfig) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JwtAuthConsumerConfig. -func (in *JwtAuthConsumerConfig) DeepCopy() *JwtAuthConsumerConfig { - if in == nil { - return nil - } - out := new(JwtAuthConsumerConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *KeyAuthConfig) DeepCopyInto(out *KeyAuthConfig) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KeyAuthConfig. -func (in *KeyAuthConfig) DeepCopy() *KeyAuthConfig { - if in == nil { - return nil - } - out := new(KeyAuthConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *KeyAuthConsumerConfig) DeepCopyInto(out *KeyAuthConsumerConfig) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KeyAuthConsumerConfig. -func (in *KeyAuthConsumerConfig) DeepCopy() *KeyAuthConsumerConfig { - if in == nil { - return nil - } - out := new(KeyAuthConsumerConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *LDAPAuthConsumerConfig) DeepCopyInto(out *LDAPAuthConsumerConfig) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LDAPAuthConsumerConfig. -func (in *LDAPAuthConsumerConfig) DeepCopy() *LDAPAuthConsumerConfig { - if in == nil { - return nil - } - out := new(LDAPAuthConsumerConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RedirectConfig) DeepCopyInto(out *RedirectConfig) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RedirectConfig. -func (in *RedirectConfig) DeepCopy() *RedirectConfig { - if in == nil { - return nil - } - out := new(RedirectConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RequestMirror) DeepCopyInto(out *RequestMirror) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RequestMirror. -func (in *RequestMirror) DeepCopy() *RequestMirror { - if in == nil { - return nil - } - out := new(RequestMirror) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ResponseRewriteConfig) DeepCopyInto(out *ResponseRewriteConfig) { - *out = *in - in.Headers.DeepCopyInto(&out.Headers) - if in.LuaRestyExpr != nil { - in, out := &in.LuaRestyExpr, &out.LuaRestyExpr - *out = make([]expr.Expr, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.Filters != nil { - in, out := &in.Filters, &out.Filters - *out = make([]map[string]string, len(*in)) - for i := range *in { - if (*in)[i] != nil { - in, out := &(*in)[i], &(*out)[i] - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResponseRewriteConfig. -func (in *ResponseRewriteConfig) DeepCopy() *ResponseRewriteConfig { - if in == nil { - return nil - } - out := new(ResponseRewriteConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RewriteConfig) DeepCopyInto(out *RewriteConfig) { - *out = *in - if in.RewriteTargetRegex != nil { - in, out := &in.RewriteTargetRegex, &out.RewriteTargetRegex - *out = make([]string, len(*in)) - copy(*out, *in) - } - in.Headers.DeepCopyInto(&out.Headers) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RewriteConfig. -func (in *RewriteConfig) DeepCopy() *RewriteConfig { - if in == nil { - return nil - } - out := new(RewriteConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *TrafficSplitConfig) DeepCopyInto(out *TrafficSplitConfig) { - *out = *in - if in.Rules != nil { - in, out := &in.Rules, &out.Rules - *out = make([]TrafficSplitConfigRule, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrafficSplitConfig. -func (in *TrafficSplitConfig) DeepCopy() *TrafficSplitConfig { - if in == nil { - return nil - } - out := new(TrafficSplitConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *TrafficSplitConfigRule) DeepCopyInto(out *TrafficSplitConfigRule) { - *out = *in - if in.WeightedUpstreams != nil { - in, out := &in.WeightedUpstreams, &out.WeightedUpstreams - *out = make([]TrafficSplitConfigRuleWeightedUpstream, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrafficSplitConfigRule. -func (in *TrafficSplitConfigRule) DeepCopy() *TrafficSplitConfigRule { - if in == nil { - return nil - } - out := new(TrafficSplitConfigRule) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *TrafficSplitConfigRuleWeightedUpstream) DeepCopyInto(out *TrafficSplitConfigRuleWeightedUpstream) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrafficSplitConfigRuleWeightedUpstream. -func (in *TrafficSplitConfigRuleWeightedUpstream) DeepCopy() *TrafficSplitConfigRuleWeightedUpstream { - if in == nil { - return nil - } - out := new(TrafficSplitConfigRuleWeightedUpstream) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *WolfRBACConsumerConfig) DeepCopyInto(out *WolfRBACConsumerConfig) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WolfRBACConsumerConfig. -func (in *WolfRBACConsumerConfig) DeepCopy() *WolfRBACConsumerConfig { - if in == nil { - return nil - } - out := new(WolfRBACConsumerConfig) - in.DeepCopyInto(out) - return out -} From c369ced5b3d53ac3817cdd515c5e0e1109a83fb3 Mon Sep 17 00:00:00 2001 From: AlinsRan Date: Fri, 20 Jun 2025 14:47:41 +0800 Subject: [PATCH 11/13] Update internal/provider/adc/translator/apisixconsumer.go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 悟空 --- internal/provider/adc/translator/apisixconsumer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/provider/adc/translator/apisixconsumer.go b/internal/provider/adc/translator/apisixconsumer.go index e58aeaa85..70aca33d9 100644 --- a/internal/provider/adc/translator/apisixconsumer.go +++ b/internal/provider/adc/translator/apisixconsumer.go @@ -39,7 +39,7 @@ const ( _hmacAuthKeepHeadersDefaultValue = false _hmacAuthEncodeURIParamsDefaultValue = true _hmacAuthValidateRequestBodyDefaultValue = false - _hmacAuthMaxReqBodyDefaultValue = int64(524288) + _hmacAuthMaxReqBodyDefaultValue = 524288 _true = "true" ) From f3599d63f50ed954bf4d0701d06acd237f6626a3 Mon Sep 17 00:00:00 2001 From: rongxin Date: Fri, 20 Jun 2025 15:19:33 +0800 Subject: [PATCH 12/13] perf apisixtls --- internal/controller/apisixtls_controller.go | 221 +++----------------- 1 file changed, 26 insertions(+), 195 deletions(-) diff --git a/internal/controller/apisixtls_controller.go b/internal/controller/apisixtls_controller.go index 08d631602..dc4b614a3 100644 --- a/internal/controller/apisixtls_controller.go +++ b/internal/controller/apisixtls_controller.go @@ -14,7 +14,6 @@ package controller import ( "context" - "errors" "fmt" "github.com/api7/gopkg/pkg/log" @@ -67,7 +66,7 @@ func (r *ApisixTlsReconciler) SetupWithManager(mgr ctrl.Manager) error { &networkingv1.IngressClass{}, handler.EnqueueRequestsFromMapFunc(r.listApisixTlsForIngressClass), builder.WithPredicates( - predicate.NewPredicateFuncs(r.matchesIngressController), + predicate.NewPredicateFuncs(matchesIngressController), ), ). Watches(&v1alpha1.GatewayProxy{}, @@ -108,7 +107,7 @@ func (r *ApisixTlsReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( tctx := provider.NewDefaultTranslateContext(ctx) // get the ingress class - ingressClass, err := r.getIngressClass(&tls) + ingressClass, err := GetIngressClass(tctx, r.Client, r.Log, tls.Spec.IngressClassName) if err != nil { log.Error(err, "failed to get IngressClass") r.updateStatus(&tls, metav1.Condition{ @@ -123,7 +122,7 @@ func (r *ApisixTlsReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( } // process IngressClass parameters if they reference GatewayProxy - if err := r.processIngressClassParameters(ctx, tctx, &tls, ingressClass); err != nil { + if err := ProcessIngressClassParameters(tctx, r.Client, r.Log, &tls, ingressClass); err != nil { log.Error(err, "failed to process IngressClass parameters", "ingressClass", ingressClass.Name) r.updateStatus(&tls, metav1.Condition{ Type: string(apiv2.ConditionTypeAccepted), @@ -208,107 +207,6 @@ func (r *ApisixTlsReconciler) validateSecret(ctx context.Context, tc *provider.T return nil } -// getIngressClass get the ingress class for the TLS -func (r *ApisixTlsReconciler) getIngressClass(tls *apiv2.ApisixTls) (*networkingv1.IngressClass, error) { - if tls.Spec.IngressClassName == "" { - // Check for default ingress class - ingressClassList := &networkingv1.IngressClassList{} - if err := r.List(context.Background(), ingressClassList, client.MatchingFields{ - indexer.IngressClass: config.GetControllerName(), - }); err != nil { - r.Log.Error(err, "failed to list ingress classes") - return nil, err - } - - // Find the ingress class that is marked as default - for _, ic := range ingressClassList.Items { - if IsDefaultIngressClass(&ic) && matchesController(ic.Spec.Controller) { - return &ic, nil - } - } - log.Debugw("no default ingress class found") - return nil, errors.New("no default ingress class found") - } - - // Check if the specified ingress class is controlled by us - var ingressClass networkingv1.IngressClass - if err := r.Get(context.Background(), client.ObjectKey{Name: tls.Spec.IngressClassName}, &ingressClass); err != nil { - return nil, err - } - - if matchesController(ingressClass.Spec.Controller) { - return &ingressClass, nil - } - - return nil, errors.New("ingress class is not controlled by us") -} - -// processIngressClassParameters processes the IngressClass parameters that reference GatewayProxy -func (r *ApisixTlsReconciler) processIngressClassParameters(ctx context.Context, tctx *provider.TranslateContext, tls *apiv2.ApisixTls, ingressClass *networkingv1.IngressClass) error { - if ingressClass == nil || ingressClass.Spec.Parameters == nil { - return nil - } - - ingressClassKind := utils.NamespacedNameKind(ingressClass) - tlsKind := utils.NamespacedNameKind(tls) - - parameters := ingressClass.Spec.Parameters - // check if the parameters reference GatewayProxy - if parameters.APIGroup != nil && *parameters.APIGroup == v1alpha1.GroupVersion.Group && parameters.Kind == KindGatewayProxy { - ns := tls.GetNamespace() - if parameters.Namespace != nil { - ns = *parameters.Namespace - } - - gatewayProxy := &v1alpha1.GatewayProxy{} - if err := r.Get(ctx, client.ObjectKey{ - Namespace: ns, - Name: parameters.Name, - }, gatewayProxy); err != nil { - r.Log.Error(err, "failed to get GatewayProxy", "namespace", ns, "name", parameters.Name) - return err - } - - r.Log.Info("found GatewayProxy for IngressClass", "ingressClass", ingressClass.Name, "gatewayproxy", gatewayProxy.Name) - tctx.GatewayProxies[ingressClassKind] = *gatewayProxy - tctx.ResourceParentRefs[tlsKind] = append(tctx.ResourceParentRefs[tlsKind], ingressClassKind) - - // check if the provider field references a secret - if gatewayProxy.Spec.Provider != nil && gatewayProxy.Spec.Provider.Type == v1alpha1.ProviderTypeControlPlane { - if gatewayProxy.Spec.Provider.ControlPlane != nil && - gatewayProxy.Spec.Provider.ControlPlane.Auth.Type == v1alpha1.AuthTypeAdminKey && - gatewayProxy.Spec.Provider.ControlPlane.Auth.AdminKey != nil && - gatewayProxy.Spec.Provider.ControlPlane.Auth.AdminKey.ValueFrom != nil && - gatewayProxy.Spec.Provider.ControlPlane.Auth.AdminKey.ValueFrom.SecretKeyRef != nil { - - secretRef := gatewayProxy.Spec.Provider.ControlPlane.Auth.AdminKey.ValueFrom.SecretKeyRef - secret := &corev1.Secret{} - if err := r.Get(ctx, client.ObjectKey{ - Namespace: ns, - Name: secretRef.Name, - }, secret); err != nil { - r.Log.Error(err, "failed to get secret for GatewayProxy provider", - "namespace", ns, - "name", secretRef.Name) - return err - } - - r.Log.Info("found secret for GatewayProxy provider", - "ingressClass", ingressClass.Name, - "gatewayproxy", gatewayProxy.Name, - "secret", secretRef.Name) - - tctx.Secrets[types.NamespacedName{ - Namespace: ns, - Name: secretRef.Name, - }] = secret - } - } - } - - return nil -} - // updateStatus updates the ApisixTls status with the given condition func (r *ApisixTlsReconciler) updateStatus(tls *apiv2.ApisixTls, condition metav1.Condition) { r.Updater.Update(status.Update{ @@ -374,32 +272,15 @@ func (r *ApisixTlsReconciler) listApisixTlsForSecret(ctx context.Context, obj cl return nil } - // Use index to find all ApisixTls that reference this secret - var tlsList apiv2.ApisixTlsList - if err := r.List(ctx, &tlsList, client.MatchingFields{ - indexer.SecretIndexRef: indexer.GenIndexKey(secret.Namespace, secret.Name), - }); err != nil { - r.Log.Error(err, "failed to list ApisixTls by secret index") - return nil - } - - requests := make([]reconcile.Request, 0, len(tlsList.Items)) - for _, tls := range tlsList.Items { - requests = append(requests, reconcile.Request{ - NamespacedName: utils.NamespacedName(&tls), - }) - } - - return requests -} - -func (r *ApisixTlsReconciler) matchesIngressController(obj client.Object) bool { - ingressClass, ok := obj.(*networkingv1.IngressClass) - if !ok { - r.Log.Error(fmt.Errorf("unexpected object type"), "failed to convert object to IngressClass") - return false - } - return matchesController(ingressClass.Spec.Controller) + return ListRequests( + ctx, + r.Client, + r.Log, + &apiv2.ApisixConsumerList{}, + client.MatchingFields{ + indexer.SecretIndexRef: indexer.GenIndexKey(secret.GetNamespace(), secret.GetName()), + }, + ) } // listApisixTlsForIngressClass list all TLS that use a specific ingress class @@ -409,73 +290,23 @@ func (r *ApisixTlsReconciler) listApisixTlsForIngressClass(ctx context.Context, return nil } - // Use index to find all ApisixTls that reference this ingress class - tlsList := &apiv2.ApisixTlsList{} - requests := make([]reconcile.Request, 0, len(tlsList.Items)) - if err := r.List(ctx, tlsList, client.MatchingFields{ - indexer.IngressClassRef: ingressClass.Name, - }); err != nil { - r.Log.Error(err, "failed to list ApisixTls by ingress class index") - return nil - } - - for _, tls := range tlsList.Items { - requests = append(requests, reconcile.Request{ - NamespacedName: utils.NamespacedName(&tls), - }) - } - - // If this is the default ingress class, also find TLS with empty ingress class - if IsDefaultIngressClass(ingressClass) { - var tlsListWithoutClass apiv2.ApisixTlsList - if err := r.List(ctx, &tlsListWithoutClass); err != nil { - r.Log.Error(err, "failed to list all ApisixTls") - return requests - } - - for _, tls := range tlsListWithoutClass.Items { - if tls.Spec.IngressClassName == "" { - requests = append(requests, reconcile.Request{ - NamespacedName: utils.NamespacedName(&tls), - }) + return ListMatchingRequests( + ctx, + r.Client, + r.Log, + &apiv2.ApisixTlsList{}, + func(obj client.Object) bool { + atls, ok := obj.(*apiv2.ApisixTls) + if !ok { + r.Log.Error(fmt.Errorf("expected ApisixTls, got %T", obj), "failed to match object type") + return false } - } - } - - return requests + return (IsDefaultIngressClass(ingressClass) && atls.Spec.IngressClassName == "") || atls.Spec.IngressClassName == ingressClass.Name + }, + ) } // listApisixTlsForGatewayProxy list all TLS that use a specific gateway proxy func (r *ApisixTlsReconciler) listApisixTlsForGatewayProxy(ctx context.Context, obj client.Object) []reconcile.Request { - gatewayProxy, ok := obj.(*v1alpha1.GatewayProxy) - if !ok { - return nil - } - - // Find all ingress classes that reference this gateway proxy - ingressClassList := &networkingv1.IngressClassList{} - if err := r.List(ctx, ingressClassList, client.MatchingFields{ - indexer.IngressClassParametersRef: indexer.GenIndexKey(gatewayProxy.GetNamespace(), gatewayProxy.GetName()), - }); err != nil { - r.Log.Error(err, "failed to list ingress classes for gateway proxy", "gatewayproxy", gatewayProxy.GetName()) - return nil - } - - var requests []reconcile.Request - for _, ingressClass := range ingressClassList.Items { - requests = append(requests, r.listApisixTlsForIngressClass(ctx, &ingressClass)...) - } - - // Remove duplicates - uniqueRequests := make(map[string]reconcile.Request) - for _, request := range requests { - uniqueRequests[request.String()] = request - } - - distinctRequests := make([]reconcile.Request, 0, len(uniqueRequests)) - for _, request := range uniqueRequests { - distinctRequests = append(distinctRequests, request) - } - - return distinctRequests + return listIngressClassRequestsForGatewayProxy(ctx, r.Client, obj, r.Log, r.listApisixTlsForIngressClass) } From 878740fecc3711f139966e1c9e393018c12b6589 Mon Sep 17 00:00:00 2001 From: rongxin Date: Fri, 20 Jun 2025 15:57:19 +0800 Subject: [PATCH 13/13] fix jwtaath --- internal/provider/adc/translator/apisixconsumer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/provider/adc/translator/apisixconsumer.go b/internal/provider/adc/translator/apisixconsumer.go index 70aca33d9..5c3f44066 100644 --- a/internal/provider/adc/translator/apisixconsumer.go +++ b/internal/provider/adc/translator/apisixconsumer.go @@ -32,7 +32,7 @@ var ( ) const ( - _jwtAuthExpDefaultValue = int64(868400) + _jwtAuthExpDefaultValue = 86400 _hmacAuthAlgorithmDefaultValue = "hmac-sha256" _hmacAuthClockSkewDefaultValue = int64(0)