Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Dockerfile.dev
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
10 changes: 5 additions & 5 deletions api/adc/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 1 addition & 7 deletions api/adc/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion api/v2/apisixroute_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
Expand Down
8 changes: 1 addition & 7 deletions api/v2/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 1 addition & 3 deletions config/crd/bases/apisix.apache.org_apisixglobalrules.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
4 changes: 1 addition & 3 deletions config/crd/bases/apisix.apache.org_apisixpluginconfigs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
8 changes: 2 additions & 6 deletions config/crd/bases/apisix.apache.org_apisixroutes.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
13 changes: 1 addition & 12 deletions docs/crd/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -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. |


Expand All @@ -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

Expand Down
177 changes: 163 additions & 14 deletions internal/controller/apisixconsumer_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,56 +14,205 @@ package controller

import (
"context"
"fmt"

"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"
gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/predicate"
"sigs.k8s.io/controller-runtime/pkg/reconcile"

"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
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),
},
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 {
r.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 {
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)
return ctrl.Result{}, err
}
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, err error) {
SetApisixCRDConditionAccepted(&consumer.Status, consumer.GetGeneration(), err)
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 = consumer.Status
return acCopy
}),
})
}
Loading
Loading