Skip to content

Commit

Permalink
Merge pull request #28 from Kuadrant/limits-configmap
Browse files Browse the repository at this point in the history
Reconciling limits `ConfigMap`
  • Loading branch information
didierofrivia authored Jun 28, 2022
2 parents d2b943a + b4362dc commit e087ff8
Show file tree
Hide file tree
Showing 11 changed files with 271 additions and 9 deletions.
30 changes: 30 additions & 0 deletions bundle/manifests/limitador-operator.clusterserviceversion.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,25 @@ metadata:
"name": "limitador-sample"
},
"spec": {
"limits": [
{
"conditions": [
"get-toy == yes"
],
"max_value": 2,
"namespace": "toystore-app",
"seconds": 30,
"variables": []
}
],
"listener": {
"grpc": {
"port": 8081
},
"http": {
"port": 8080
}
},
"replicas": 1,
"version": "latest"
}
Expand Down Expand Up @@ -59,6 +78,17 @@ spec:
spec:
clusterPermissions:
- rules:
- apiGroups:
- ""
resources:
- configmaps
verbs:
- create
- delete
- get
- list
- update
- watch
- apiGroups:
- apps
resources:
Expand Down
11 changes: 11 additions & 0 deletions config/deploy/manfiests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,17 @@ metadata:
creationTimestamp: null
name: limitador-operator-manager-role
rules:
- apiGroups:
- ""
resources:
- configmaps
verbs:
- create
- delete
- get
- list
- update
- watch
- apiGroups:
- apps
resources:
Expand Down
11 changes: 11 additions & 0 deletions config/install/manifests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,17 @@ metadata:
creationTimestamp: null
name: limitador-operatormanager-role
rules:
- apiGroups:
- ""
resources:
- configmaps
verbs:
- create
- delete
- get
- list
- update
- watch
- apiGroups:
- apps
resources:
Expand Down
11 changes: 11 additions & 0 deletions config/rbac/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,17 @@ metadata:
creationTimestamp: null
name: manager-role
rules:
- apiGroups:
- ""
resources:
- configmaps
verbs:
- create
- delete
- get
- list
- update
- watch
- apiGroups:
- apps
resources:
Expand Down
11 changes: 11 additions & 0 deletions config/samples/limitador_v1alpha1_limitador.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,14 @@ metadata:
spec:
replicas: 1
version: latest
listener:
http:
port: 8080
grpc:
port: 8081
limits:
- conditions: ["get-toy == yes"]
max_value: 2
namespace: toystore-app
seconds: 30
variables: []
37 changes: 37 additions & 0 deletions controllers/limitador_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"context"
"fmt"
"github.com/kuadrant/limitador-operator/pkg/helpers"
v1 "k8s.io/api/core/v1"
"strconv"

"github.com/go-logr/logr"
Expand All @@ -43,6 +44,7 @@ type LimitadorReconciler struct {
//+kubebuilder:rbac:groups=limitador.kuadrant.io,resources=limitadors/finalizers,verbs=update
//+kubebuilder:rbac:groups=core,resources=services,verbs=get;list;watch;create;update;delete
//+kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;delete
//+kubebuilder:rbac:groups="",resources=configmaps,verbs=get;list;watch;create;update;delete

func (r *LimitadorReconciler) Reconcile(eventCtx context.Context, req ctrl.Request) (ctrl.Result, error) {
logger := r.Logger().WithValues("limitador", req.NamespacedName)
Expand Down Expand Up @@ -86,6 +88,17 @@ func (r *LimitadorReconciler) Reconcile(eventCtx context.Context, req ctrl.Reque
return ctrl.Result{}, err
}

// Reconcile Limits ConfigMap
limitsConfigMap, err := limitador.LimitsConfigMap(limitadorObj)
if err != nil {
return ctrl.Result{}, err
}
err = r.ReconcileConfigMap(ctx, limitsConfigMap, mutateLimitsConfigMap)
logger.V(1).Info("reconcile limits ConfigMap", "error", err)
if err != nil {
return ctrl.Result{}, err
}

return ctrl.Result{}, nil
}

Expand Down Expand Up @@ -116,6 +129,30 @@ func buildServiceUrl(limitadorObj *limitadorv1alpha1.Limitador) string {
strconv.Itoa(int(helpers.GetValueOrDefault(*limitadorObj.Spec.Listener.HTTP.Port, limitador.DefaultServiceHTTPPort).(int32)))
}

func mutateLimitsConfigMap(existingObj, desiredObj client.Object) (bool, error) {
existing, ok := existingObj.(*v1.ConfigMap)
if !ok {
return false, fmt.Errorf("%T is not a *v1.ConfigMap", existingObj)
}
desired, ok := desiredObj.(*v1.ConfigMap)
if !ok {
return false, fmt.Errorf("%T is not a *v1.ConfigMap", desiredObj)
}

updated := false

if existing.Data[limitador.LimitadorCMHash] != desired.Data[limitador.LimitadorCMHash] {
for k, v := range map[string]string{
limitador.LimitadorCMHash: desired.Data[limitador.LimitadorCMHash],
limitador.LimitadorConfigFileName: string(desired.Data[limitador.LimitadorConfigFileName]),
} {
existing.Data[k] = v
}
updated = true
}
return updated, nil
}

func mutateLimitadorDeployment(existingObj, desiredObj client.Object) (bool, error) {
existing, ok := existingObj.(*appsv1.Deployment)
if !ok {
Expand Down
79 changes: 78 additions & 1 deletion controllers/limitador_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package controllers

import (
"context"
"github.com/kuadrant/limitador-operator/pkg/limitador"
"time"

. "github.com/onsi/ginkgo"
Expand Down Expand Up @@ -93,7 +94,7 @@ var _ = Describe("Limitador controller", func() {
Expect(k8sClient.Create(context.TODO(), limitadorObj)).Should(Succeed())
})

It("Should create a new deployment with the right number of replicas and version", func() {
It("Should create a new deployment with the right number of replicas, version and config file", func() {
createdLimitadorDeployment := appsv1.Deployment{}
Eventually(func() bool {
err := k8sClient.Get(
Expand All @@ -113,6 +114,15 @@ var _ = Describe("Limitador controller", func() {
Expect(createdLimitadorDeployment.Spec.Template.Spec.Containers[0].Image).Should(
Equal(LimitadorImage + ":" + LimitadorVersion),
)
Expect(createdLimitadorDeployment.Spec.Template.Spec.Containers[0].Env[1]).Should(
Equal(v1.EnvVar{Name: "LIMITS_FILE", Value: "/home/limitador/etc/limitador-config.yaml", ValueFrom: nil}),
)
Expect(createdLimitadorDeployment.Spec.Template.Spec.Containers[0].VolumeMounts[0].MountPath).Should(
Equal("/home/limitador/etc/"),
)
Expect(createdLimitadorDeployment.Spec.Template.Spec.Volumes[0].VolumeSource.ConfigMap.Name).Should(
Equal(limitador.LimitsCMNamePrefix + limitadorObj.Name),
)
})

It("Should create a Limitador service", func() {
Expand Down Expand Up @@ -143,6 +153,27 @@ var _ = Describe("Limitador controller", func() {
}, timeout, interval).Should(Equal("http://" + limitadorObj.Name + ".default.svc.cluster.local:8000"))

})
It("Should create a ConfigMap with the correct limits and hash", func() {
createdConfigMap := v1.ConfigMap{}
Eventually(func() bool {
err := k8sClient.Get(
context.TODO(),
types.NamespacedName{
Namespace: LimitadorNamespace,
Name: limitador.LimitsCMNamePrefix + limitadorObj.Name,
},
&createdConfigMap)

return err == nil
}, timeout, interval).Should(BeTrue())

Expect(createdConfigMap.Data[limitador.LimitadorCMHash]).Should(
Equal("a00c9940ae6bb8de702633ce453e6a97"),
)
Expect(createdConfigMap.Data[limitador.LimitadorConfigFileName]).Should(
Equal("- conditions:\n - req.method == GET\n max_value: 10\n namespace: test-namespace\n seconds: 60\n variables:\n - user_id\n- conditions:\n - req.method == POST\n max_value: 5\n namespace: test-namespace\n seconds: 60\n variables:\n - user_id\n"),
)
})
})

Context("Updating a limitador object", func() {
Expand Down Expand Up @@ -196,5 +227,51 @@ var _ = Describe("Limitador controller", func() {
return correctReplicas && correctImage
}, timeout, interval).Should(BeTrue())
})
It("Should modify the ConfigMap accordingly", func() {
updatedLimitador := limitadorv1alpha1.Limitador{}
Eventually(func() bool {
err := k8sClient.Get(
context.TODO(),
types.NamespacedName{
Namespace: LimitadorNamespace,
Name: limitadorObj.Name,
},
&updatedLimitador)

return err == nil
}, timeout, interval).Should(BeTrue())

limits := []limitadorv1alpha1.RateLimit{
{
Conditions: []string{"req.method == GET"},
MaxValue: 100,
Namespace: "test-namespace",
Seconds: 60,
Variables: []string{"user_id"},
},
}
updatedLimitador.Spec.Limits = limits

Expect(k8sClient.Update(context.TODO(), &updatedLimitador)).Should(Succeed())
updatedLimitadorConfigMap := v1.ConfigMap{}
Eventually(func() bool {
err := k8sClient.Get(
context.TODO(),
types.NamespacedName{
Namespace: LimitadorNamespace,
Name: limitador.LimitsCMNamePrefix + limitadorObj.Name,
},
&updatedLimitadorConfigMap)

if err != nil {
return false
}

return true
}, timeout, interval).Should(BeTrue())
Expect(updatedLimitadorConfigMap.Data[limitador.LimitadorCMHash]).Should(Equal("69b3eab828208274d4200aedc6fd8b19"))
Expect(updatedLimitadorConfigMap.Data[limitador.LimitadorConfigFileName]).Should(Equal("- conditions:\n - req.method == GET\n max_value: 100\n namespace: test-namespace\n seconds: 60\n variables:\n - user_id\n"))

})
})
})
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ require (
github.com/go-logr/logr v0.4.0
github.com/onsi/ginkgo v1.16.4
github.com/onsi/gomega v1.15.0
github.com/stretchr/testify v1.7.0
go.uber.org/zap v1.19.0
gotest.tools v2.2.0+incompatible
k8s.io/api v0.22.1
k8s.io/apimachinery v0.22.1
k8s.io/client-go v0.22.1
k8s.io/klog/v2 v2.9.0
sigs.k8s.io/controller-runtime v0.10.0
sigs.k8s.io/yaml v1.2.0
)
Loading

0 comments on commit e087ff8

Please sign in to comment.