Skip to content

Commit

Permalink
Add Deployment and ConfigMap upgrade path
Browse files Browse the repository at this point in the history
  • Loading branch information
grzpiotrowski committed Nov 20, 2023
1 parent 9b0edd8 commit 5a9d701
Show file tree
Hide file tree
Showing 3 changed files with 295 additions and 14 deletions.
48 changes: 34 additions & 14 deletions controllers/limitador_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import (
limitadorv1alpha1 "github.com/kuadrant/limitador-operator/api/v1alpha1"
"github.com/kuadrant/limitador-operator/pkg/limitador"
"github.com/kuadrant/limitador-operator/pkg/reconcilers"
upgrades "github.com/kuadrant/limitador-operator/pkg/upgrades"
)

// LimitadorReconciler reconciles a Limitador object
Expand Down Expand Up @@ -79,7 +80,7 @@ func (r *LimitadorReconciler) Reconcile(eventCtx context.Context, req ctrl.Reque
return ctrl.Result{}, nil
}

specErr := r.reconcileSpec(ctx, limitadorObj)
specResult, specErr := r.reconcileSpec(ctx, limitadorObj)

statusResult, statusErr := r.reconcileStatus(ctx, limitadorObj, specErr)

Expand All @@ -91,6 +92,11 @@ func (r *LimitadorReconciler) Reconcile(eventCtx context.Context, req ctrl.Reque
return ctrl.Result{}, statusErr
}

if specResult.Requeue {
logger.V(1).Info("Reconciling spec not finished. Requeueing.")
return specResult, nil
}

if statusResult.Requeue {
logger.V(1).Info("Reconciling status not finished. Requeueing.")
return statusResult, nil
Expand All @@ -100,24 +106,31 @@ func (r *LimitadorReconciler) Reconcile(eventCtx context.Context, req ctrl.Reque
return ctrl.Result{}, nil
}

func (r *LimitadorReconciler) reconcileSpec(ctx context.Context, limitadorObj *limitadorv1alpha1.Limitador) error {
func (r *LimitadorReconciler) reconcileSpec(ctx context.Context, limitadorObj *limitadorv1alpha1.Limitador) (ctrl.Result, error) {
if err := r.reconcileService(ctx, limitadorObj); err != nil {
return err
return ctrl.Result{}, err
}

if err := r.reconcilePVC(ctx, limitadorObj); err != nil {
return err
return ctrl.Result{}, err
}

if err := r.reconcileDeployment(ctx, limitadorObj); err != nil {
return err
result, err := r.reconcileDeployment(ctx, limitadorObj)
if result.Requeue {
return result, nil
}
if err != nil {
return ctrl.Result{}, err
}

if err := r.reconcileLimitsConfigMap(ctx, limitadorObj); err != nil {
return err
return ctrl.Result{}, err
}

return r.reconcilePdb(ctx, limitadorObj)
if err := r.reconcilePdb(ctx, limitadorObj); err != nil {
return ctrl.Result{}, err
}
return ctrl.Result{}, nil
}

func (r *LimitadorReconciler) reconcilePdb(ctx context.Context, limitadorObj *limitadorv1alpha1.Limitador) error {
Expand Down Expand Up @@ -162,15 +175,15 @@ func (r *LimitadorReconciler) reconcilePdb(ctx context.Context, limitadorObj *li
return nil
}

func (r *LimitadorReconciler) reconcileDeployment(ctx context.Context, limitadorObj *limitadorv1alpha1.Limitador) error {
func (r *LimitadorReconciler) reconcileDeployment(ctx context.Context, limitadorObj *limitadorv1alpha1.Limitador) (ctrl.Result, error) {
logger, err := logr.FromContext(ctx)
if err != nil {
return err
return ctrl.Result{}, err
}

deploymentOptions, err := r.getDeploymentOptions(ctx, limitadorObj)
if err != nil {
return err
return ctrl.Result{}, err
}

deploymentMutators := make([]reconcilers.DeploymentMutateFn, 0)
Expand All @@ -192,15 +205,16 @@ func (r *LimitadorReconciler) reconcileDeployment(ctx context.Context, limitador
deployment := limitador.Deployment(limitadorObj, deploymentOptions)
// controller reference
if err := r.SetOwnerReference(limitadorObj, deployment); err != nil {
return err
return ctrl.Result{}, err
}
err = r.ReconcileDeployment(ctx, deployment, reconcilers.DeploymentMutator(deploymentMutators...))
logger.V(1).Info("reconcile deployment", "error", err)
if err != nil {
return err
return ctrl.Result{}, err
}

return nil
// TODO: To be deleted when the upgrade path is no longer needed.
return upgrades.UpgradeDeploymentTov070(ctx, r.Client(), limitadorObj, client.ObjectKeyFromObject(deployment))
}

func (r *LimitadorReconciler) reconcileService(ctx context.Context, limitadorObj *limitadorv1alpha1.Limitador) error {
Expand Down Expand Up @@ -267,6 +281,12 @@ func (r *LimitadorReconciler) reconcileLimitsConfigMap(ctx context.Context, limi
return err
}

// TODO: To be deleted when the upgrade path is no longer needed.
err = upgrades.UpgradeConfigMapTov070(ctx, r.Client(), limitadorObj)
if err != nil {
return err
}

return nil
}

Expand Down
73 changes: 73 additions & 0 deletions pkg/upgrades/v0_7_0.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package upgrades

import (
"context"
"fmt"

appsv1 "k8s.io/api/apps/v1"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"

limitadorv1alpha1 "github.com/kuadrant/limitador-operator/api/v1alpha1"
"github.com/kuadrant/limitador-operator/pkg/helpers"

"github.com/go-logr/logr"
)

func UpgradeDeploymentTov070(ctx context.Context, cli client.Client, limitadorObj *limitadorv1alpha1.Limitador, newDeploymentKey client.ObjectKey) (ctrl.Result, error) {
logger := logr.FromContextOrDiscard(ctx)
logger.V(1).Info("Upgrading Deployment to v0.7.0", "deployment", newDeploymentKey.Name)

newDeployment := &appsv1.Deployment{}
if err := cli.Get(ctx, newDeploymentKey, newDeployment); err != nil {
if errors.IsNotFound(err) {
logger.V(1).Info("New deployment not found")
return ctrl.Result{Requeue: true}, nil
}
return ctrl.Result{}, err
}

availableCondition := helpers.FindDeploymentStatusCondition(newDeployment.Status.Conditions, "Available")

if availableCondition.Status == v1.ConditionTrue {
oldDeployment := &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: ReleaseV060DeploymentName(limitadorObj),
Namespace: limitadorObj.Namespace,
},
}
if err := cli.Delete(ctx, oldDeployment); err != nil {
if errors.IsNotFound(err) {
return ctrl.Result{}, nil
}
return ctrl.Result{}, err
}
}
return ctrl.Result{}, nil
}

func UpgradeConfigMapTov070(ctx context.Context, cli client.Client, limitadorObj *limitadorv1alpha1.Limitador) error {
oldConfigMap := &v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: ReleaseV060LimitsConfigMapName(limitadorObj),
Namespace: limitadorObj.Namespace,
},
}

if err := cli.Delete(ctx, oldConfigMap); err != nil && !errors.IsNotFound(err) {
return err
}

return nil
}

func ReleaseV060DeploymentName(limitadorObj *limitadorv1alpha1.Limitador) string {
return limitadorObj.Name
}

func ReleaseV060LimitsConfigMapName(limitadorObj *limitadorv1alpha1.Limitador) string {
return fmt.Sprintf("limits-config-%s", limitadorObj.Name)
}
188 changes: 188 additions & 0 deletions pkg/upgrades/v0_7_0_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
package upgrades

import (
"context"
"testing"

appsv1 "k8s.io/api/apps/v1"
v1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
"sigs.k8s.io/controller-runtime/pkg/log"

"github.com/go-logr/logr"
limitadorv1alpha1 "github.com/kuadrant/limitador-operator/api/v1alpha1"
"github.com/kuadrant/limitador-operator/pkg/limitador"
)

func TestUpgradeDeploymentTov070(t *testing.T) {
var (
limitadorName = "test-limitador"
limitadorNamespace = "default"
)
logger := log.Log.WithName("upgrades_test")
baseCtx := context.Background()
ctx := logr.NewContext(baseCtx, logger)

s := runtime.NewScheme()
_ = appsv1.AddToScheme(s)
_ = limitadorv1alpha1.AddToScheme(s)
_ = v1.AddToScheme(s)

// Create dummy Limitador object
limitadorObj := &limitadorv1alpha1.Limitador{
ObjectMeta: metav1.ObjectMeta{
Name: limitadorName,
Namespace: limitadorNamespace,
},
}

// Create a dummy Deployment object
newDeployment := &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: limitador.DeploymentName(limitadorObj),
Namespace: limitadorNamespace,
},
Status: appsv1.DeploymentStatus{
Conditions: []appsv1.DeploymentCondition{
{
Type: appsv1.DeploymentAvailable,
Status: v1.ConditionTrue,
},
},
},
}

// Old Deployment to simulate the upgrade
oldDeployment := &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: ReleaseV060DeploymentName(limitadorObj),
Namespace: limitadorNamespace,
},
}

// Objects to track in the fake client.
objs := []client.Object{limitadorObj, newDeployment, oldDeployment}

// Create a fake client to mock API calls.
clBuilder := fake.NewClientBuilder()
cl := clBuilder.WithScheme(s).WithObjects(objs...).Build()

_, err := UpgradeDeploymentTov070(ctx, cl, limitadorObj, client.ObjectKeyFromObject(newDeployment))
if err != nil {
t.Fatalf("UpgradeDeploymentTov070 failed: %v", err)
}

// Check if the old deployment was deleted
oldDepKey := types.NamespacedName{Name: ReleaseV060DeploymentName(limitadorObj), Namespace: limitadorNamespace}
err = cl.Get(ctx, oldDepKey, &appsv1.Deployment{})
if err == nil {
t.Fatal("Old deployment was not deleted")
}
}

func TestUpgradeDeploymentTov070_NewDeploymentNotFound(t *testing.T) {
var (
limitadorName = "test-limitador"
limitadorNamespace = "default"
)
logger := log.Log.WithName("upgrades_test")
baseCtx := context.Background()
ctx := logr.NewContext(baseCtx, logger)

s := runtime.NewScheme()
_ = appsv1.AddToScheme(s)
_ = limitadorv1alpha1.AddToScheme(s)
_ = v1.AddToScheme(s)

// Create dummy Limitador object
limitadorObj := &limitadorv1alpha1.Limitador{
ObjectMeta: metav1.ObjectMeta{
Name: limitadorName,
Namespace: limitadorNamespace,
},
}

// Old Deployment to simulate the upgrade
oldDeployment := &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: ReleaseV060DeploymentName(limitadorObj),
Namespace: limitadorNamespace,
},
}

newDeployment := &appsv1.Deployment{}

// Objects to track in the fake client.
objs := []client.Object{limitadorObj, oldDeployment}

// Create a fake client to mock API calls.
clBuilder := fake.NewClientBuilder()
cl := clBuilder.WithScheme(s).WithObjects(objs...).Build()

// Testa situation where the new deployment does not exist
_, err := UpgradeDeploymentTov070(ctx, cl, limitadorObj, client.ObjectKeyFromObject(newDeployment))
if err != nil {
t.Fatalf("UpgradeDeploymentTov070 failed: %v", err)
}

// Check if the old deployment still exists
oldDepKey := types.NamespacedName{Name: ReleaseV060DeploymentName(limitadorObj), Namespace: limitadorNamespace}
err = cl.Get(ctx, oldDepKey, &appsv1.Deployment{})
if err != nil {
t.Fatalf("Old deployment should still exist: %v", err)
}
}

func TestUpgradeConfigMapTov070(t *testing.T) {
var (
limitadorName = "test-limitador"
limitadorNamespace = "default"
)
logger := log.Log.WithName("upgrades_test")
baseCtx := context.Background()
ctx := logr.NewContext(baseCtx, logger)

s := runtime.NewScheme()
_ = v1.AddToScheme(s)
_ = limitadorv1alpha1.AddToScheme(s)

// Create dummy Limitador object
limitadorObj := &limitadorv1alpha1.Limitador{
ObjectMeta: metav1.ObjectMeta{
Name: limitadorName,
Namespace: limitadorNamespace,
},
}

// Old ConfigMap to simulate the upgrade
oldConfigMap := &v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: ReleaseV060LimitsConfigMapName(limitadorObj),
Namespace: limitadorNamespace,
},
}

// Objects to track in the fake client.
objs := []client.Object{limitadorObj, oldConfigMap}

// Create a fake client to mock API calls.
clBuilder := fake.NewClientBuilder()
cl := clBuilder.WithScheme(s).WithObjects(objs...).Build()

err := UpgradeConfigMapTov070(ctx, cl, limitadorObj)
if err != nil {
t.Fatalf("UpgradeConfigMapTov070 failed: %v", err)
}

// Check if the old config map was deleted
oldCmKey := types.NamespacedName{Name: ReleaseV060LimitsConfigMapName(limitadorObj), Namespace: limitadorNamespace}
err = cl.Get(ctx, oldCmKey, &v1.ConfigMap{})
if !apierrors.IsNotFound(err) {
t.Fatal("Old ConfigMap Found")
}
}

0 comments on commit 5a9d701

Please sign in to comment.