Skip to content

Commit

Permalink
add tests for main perses controller
Browse files Browse the repository at this point in the history
Signed-off-by: Gabriel Bernal <[email protected]>
  • Loading branch information
jgbernalp committed Nov 15, 2023
1 parent b3342dc commit a05d4de
Show file tree
Hide file tree
Showing 12 changed files with 402 additions and 177 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,5 @@ Dockerfile.cross
*.swp
*.swo
*~
.vscode

4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,10 @@ vet: ## Run go vet against code.
test: manifests generate fmt vet envtest ## Run tests.
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test ./... -coverprofile cover.out

.PHONY: lint
lint: ## Run linting.
golangci-lint run

##@ Build

.PHONY: build
Expand Down
21 changes: 11 additions & 10 deletions controllers/configmap_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,33 +50,31 @@ func (r *PersesReconciler) reconcileConfigMap(ctx context.Context, req ctrl.Requ
if err != nil && apierrors.IsNotFound(err) {
cm, err := createPersesConfigMap(r, perses)
if err != nil {
cmlog.Error(err, "Failed to define new ConfigMap resource for perses")
cmlog.WithError(err).Error("Failed to define new ConfigMap resource for perses")

meta.SetStatusCondition(&perses.Status.Conditions, metav1.Condition{Type: common.TypeAvailablePerses,
Status: metav1.ConditionFalse, Reason: "Reconciling",
Message: fmt.Sprintf("Failed to create ConfigMap for the custom resource (%s): (%s)", perses.Name, err)})

if err := r.Status().Update(ctx, perses); err != nil {
cmlog.Error(err, "Failed to update perses status")
cmlog.WithError(err).Error("Failed to update perses status")
return subreconciler.RequeueWithError(err)
}

return subreconciler.RequeueWithError(err)
}

cmlog.Info("Creating a new ConfigMap",
"ConfigMap.Namespace", cm.Namespace, "ConfigMap.Name", cm.Name)
cmlog.Infof("Creating a new ConfigMap: ConfigMap.Namespace %s ConfigMap.Name %s", cm.Namespace, cm.Name)
if err = r.Create(ctx, cm); err != nil {
cmlog.Error(err, "Failed to create new ConfigMap",
"ConfigMap.Namespace", cm.Namespace, "ConfigMap.Name", cm.Name)
cmlog.WithError(err).Errorf("Failed to create new ConfigMap: ConfigMap.Namespace %s ConfigMap.Name %s", cm.Namespace, cm.Name)
return subreconciler.RequeueWithError(err)
}

return subreconciler.RequeueWithDelay(time.Minute)
}

if err != nil {
cmlog.Error(err, "Failed to get Deployment")
cmlog.WithError(err).Error("Failed to get ConfigMap")
return subreconciler.RequeueWithError(err)
}

Expand All @@ -85,13 +83,16 @@ func (r *PersesReconciler) reconcileConfigMap(ctx context.Context, req ctrl.Requ

func createPersesConfigMap(r *PersesReconciler, perses *v1alpha1.Perses) (*corev1.ConfigMap, error) {
configName := common.GetConfigName(perses.Name)
ls := common.LabelsForPerses(r.Config.PersesImage, configName, perses.Name)
ls, err := common.LabelsForPerses(r.Config.PersesImage, configName, perses.Name)

if err != nil {
return nil, err
}

persesConfig, err := yaml.Marshal(perses.Spec.Config)

if err != nil {
cmlog.Error(err, "Failed to marshal configmap data",
"ConfigMap.Namespace", perses.Namespace, "ConfigMap.Name", configName)
cmlog.WithError(err).Errorf("Failed to marshal configmap data: ConfigMap.Namespace %s ConfigMap.Name %s", perses.Namespace, configName)
return nil, err
}

Expand Down
5 changes: 4 additions & 1 deletion controllers/deployment_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,10 @@ func (r *PersesReconciler) createPersesDeployment(
perses *v1alpha1.Perses) (*appsv1.Deployment, error) {
configName := common.GetConfigName(perses.Name)

ls := common.LabelsForPerses(r.Config.PersesImage, perses.Name, perses.Name)
ls, err := common.LabelsForPerses(r.Config.PersesImage, perses.Name, perses.Name)
if err != nil {
return nil, err
}

// Get the Operand image
image, err := common.ImageForPerses(r.Config.PersesImage)
Expand Down
11 changes: 6 additions & 5 deletions controllers/perses_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package controllers
import (
"context"
"fmt"
"time"

"github.com/perses/perses-operator/api/v1alpha1"
common "github.com/perses/perses-operator/internal/perses/common"
Expand Down Expand Up @@ -109,7 +110,7 @@ func (r *PersesReconciler) setStatusToUnknown(ctx context.Context, req ctrl.Requ
meta.SetStatusCondition(&perses.Status.Conditions, metav1.Condition{Type: common.TypeAvailablePerses, Status: metav1.ConditionUnknown, Reason: "Reconciling", Message: "Starting reconciliation"})
if err := r.Status().Update(ctx, perses); err != nil {
log.WithError(err).Error("Failed to update Perses status")
return subreconciler.RequeueWithError(err)
return subreconciler.RequeueWithDelayAndError(time.Second*10, err)
}
}

Expand All @@ -132,12 +133,12 @@ func (r *PersesReconciler) addFinalizer(ctx context.Context, req ctrl.Request) (
log.Info("Adding Finalizer for Perses")
if ok := controllerutil.AddFinalizer(perses, common.PersesFinalizer); !ok {
log.Error("Failed to add finalizer into the custom resource")
return subreconciler.Requeue()
return subreconciler.RequeueWithDelay(time.Second * 10)
}

if err := r.Update(ctx, perses); err != nil {
log.WithError(err).Error("Failed to update custom resource to add finalizer")
return subreconciler.RequeueWithError(err)
return subreconciler.RequeueWithDelayAndError(time.Second*10, err)
}
}

Expand Down Expand Up @@ -167,7 +168,7 @@ func (r *PersesReconciler) handleDelete(ctx context.Context, req ctrl.Request) (

if err := r.Status().Update(ctx, perses); err != nil {
log.WithError(err).Error("Failed to update Perses status")
return subreconciler.RequeueWithError(err)
return subreconciler.RequeueWithDelayAndError(time.Second*10, err)
}

// Perform all operations required before remove the finalizer and allow
Expand All @@ -193,7 +194,7 @@ func (r *PersesReconciler) handleDelete(ctx context.Context, req ctrl.Request) (

if err := r.Status().Update(ctx, perses); err != nil {
log.WithError(err).Error("Failed to update Perses status")
return subreconciler.RequeueWithError(err)
return subreconciler.RequeueWithDelayAndError(time.Second*10, err)
}

log.Info("Removing Finalizer for Perses after successfully perform the operations")
Expand Down
202 changes: 202 additions & 0 deletions controllers/perses_controller_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
package controllers

import (
"context"
"fmt"
"os"
"time"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
persesv1alpha1 "github.com/perses/perses-operator/api/v1alpha1"
common "github.com/perses/perses-operator/internal/perses/common"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)

var _ = Describe("Perses controller", func() {
Context("Perses controller test", func() {
const PersesName = "test-perses"

ctx := context.Background()

namespace := &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: PersesName,
Namespace: PersesName,
},
}

typeNamespaceName := types.NamespacedName{Name: PersesName, Namespace: PersesName}
configMapNamespaceName := types.NamespacedName{Name: common.GetConfigName(PersesName), Namespace: PersesName}
persesImage := "perses-dev.io/perses:test"

BeforeEach(func() {
By("Creating the Namespace to perform the tests")
err := k8sClient.Create(ctx, namespace)
Expect(err).To(Not(HaveOccurred()))

By("Setting the Image ENV VAR which stores the Operand image")
err = os.Setenv("PERSES_IMAGE", persesImage)
Expect(err).To(Not(HaveOccurred()))
})

AfterEach(func() {
By("Deleting the Namespace to perform the tests")
_ = k8sClient.Delete(ctx, namespace)

By("Removing the Image ENV VAR which stores the Operand image")
_ = os.Unsetenv("PERSES_IMAGE")
})

It("should successfully reconcile a custom resource for Perses", func() {
By("Creating the custom resource for the Kind Perses")
perses := &persesv1alpha1.Perses{}
err := k8sClient.Get(ctx, typeNamespaceName, perses)
if err != nil && errors.IsNotFound(err) {
perses := &persesv1alpha1.Perses{
ObjectMeta: metav1.ObjectMeta{
Name: PersesName,
Namespace: namespace.Name,
},
Spec: persesv1alpha1.PersesSpec{
Config: persesv1alpha1.PersesConfig{
Readonly: false,
},
ContainerPort: 8080,
},
}

err = k8sClient.Create(ctx, perses)
Expect(err).To(Not(HaveOccurred()))
}

By("Checking if the custom resource was successfully created")
Eventually(func() error {
found := &persesv1alpha1.Perses{}
return k8sClient.Get(ctx, typeNamespaceName, found)
}, time.Minute, time.Second).Should(Succeed())

By("Reconciling the custom resource created")
persesReconciler := &PersesReconciler{
Client: k8sClient,
Scheme: k8sClient.Scheme(),
}

// Errors might arise during reconciliation, but we are checking the final state of the resources
_, err = persesReconciler.Reconcile(ctx, reconcile.Request{
NamespacedName: typeNamespaceName,
})

By("Checking if Service was successfully created in the reconciliation")
Eventually(func() error {
found := &corev1.Service{}
err = k8sClient.Get(ctx, typeNamespaceName, found)

if err == nil {
if len(found.Spec.Ports) < 1 {
return fmt.Errorf("The number of ports used in the service is not the one defined in the custom resource")
}
if found.Spec.Ports[0].Port != 8080 {
return fmt.Errorf("The port used in the service is not the one defined in the custom resource")
}
if found.Spec.Ports[0].TargetPort.IntVal != 8080 {
return fmt.Errorf("The target port used in the service is not the one defined in the custom resource")
}
if found.Spec.Selector["app.kubernetes.io/instance"] != PersesName {
return fmt.Errorf("The selector used in the service is not the one defined in the custom resource")
}
}

return err
}, time.Minute, time.Second).Should(Succeed())

By("Checking if ConfigMap was successfully created in the reconciliation")
Eventually(func() error {
found := &corev1.ConfigMap{}
return k8sClient.Get(ctx, configMapNamespaceName, found)
}, time.Minute*3, time.Second).Should(Succeed())

By("Checking if Deployment was successfully created in the reconciliation")
Eventually(func() error {
found := &appsv1.Deployment{}
err = k8sClient.Get(ctx, typeNamespaceName, found)

if err == nil {
if len(found.Spec.Template.Spec.Containers) < 1 {
return fmt.Errorf("The number of containers used in the deployment is not the one expected")
}
if found.Spec.Template.Spec.Containers[0].Image != persesImage {
return fmt.Errorf("The image used in the deployment is not the one expected")
}
if len(found.Spec.Template.Spec.Containers[0].Ports) < 1 && found.Spec.Template.Spec.Containers[0].Ports[0].ContainerPort != 8080 {
return fmt.Errorf("The port used in the deployment is not the one defined in the custom resource")
}
if len(found.Spec.Template.Spec.Containers[0].Args) < 1 && found.Spec.Template.Spec.Containers[0].Args[0] != "--config=/etc/perses/config/config.yaml" {
return fmt.Errorf("The config path used in the deployment is not the one defined in the custom resource")
}
}

return err
}, time.Minute, time.Second).Should(Succeed())

By("Checking the latest Status Condition added to the Perses instance")
Eventually(func() error {
if perses.Status.Conditions != nil && len(perses.Status.Conditions) != 0 {
latestStatusCondition := perses.Status.Conditions[len(perses.Status.Conditions)-1]
expectedLatestStatusCondition := metav1.Condition{Type: common.TypeAvailablePerses,
Status: metav1.ConditionTrue, Reason: "Reconciling",
Message: fmt.Sprintf("Deployment for custom resource (%s) created successfully", perses.Name)}
if latestStatusCondition != expectedLatestStatusCondition {
return fmt.Errorf("The latest status condition added to the perses instance is not as expected")
}
}
return nil
}, time.Minute, time.Second).Should(Succeed())

persesToDelete := &persesv1alpha1.Perses{}
err = k8sClient.Get(ctx, typeNamespaceName, persesToDelete)
Expect(err).To(Not(HaveOccurred()))

By("Deleting the custom resource")
err = k8sClient.Delete(ctx, persesToDelete)
Expect(err).To(Not(HaveOccurred()))

By("Checking if Deployment was successfully deleted in the reconciliation")
Eventually(func() error {
found := &appsv1.Deployment{}
return k8sClient.Get(ctx, typeNamespaceName, found)
}, time.Minute, time.Second).Should(Succeed())

By("Checking if Service was successfully deleted in the reconciliation")
Eventually(func() error {
found := &appsv1.Deployment{}
return k8sClient.Get(ctx, typeNamespaceName, found)
}, time.Minute, time.Second).Should(Succeed())

By("Checking if ConfigMap was successfully deleted in the reconciliation")
Eventually(func() error {
found := &corev1.ConfigMap{}
return k8sClient.Get(ctx, configMapNamespaceName, found)
}, time.Minute, time.Second).Should(Succeed())

By("Checking the latest Status Condition added to the Perses instance")
Eventually(func() error {
if perses.Status.Conditions != nil && len(perses.Status.Conditions) != 0 {
latestStatusCondition := perses.Status.Conditions[len(perses.Status.Conditions)-1]
expectedLatestStatusCondition := metav1.Condition{Type: common.TypeAvailablePerses,
Status: metav1.ConditionTrue, Reason: "Finalizing",
Message: fmt.Sprintf("Finalizer operations for custom resource %s name were successfully accomplished", perses.Name)}
if latestStatusCondition != expectedLatestStatusCondition {
return fmt.Errorf("The latest status condition added to the perses instance is not as expected")
}
}
return nil
}, time.Minute, time.Second).Should(Succeed())
})
})
})
6 changes: 5 additions & 1 deletion controllers/service_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,11 @@ func (r *PersesReconciler) reconcileService(ctx context.Context, req ctrl.Reques

func (r *PersesReconciler) createPersesService(
perses *v1alpha1.Perses) (*corev1.Service, error) {
ls := common.LabelsForPerses(r.Config.PersesImage, perses.Name, perses.Name)
ls, err := common.LabelsForPerses(r.Config.PersesImage, perses.Name, perses.Name)

if err != nil {
return nil, err
}

ser := &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Expand Down
Loading

0 comments on commit a05d4de

Please sign in to comment.