From 17b6d58300acbc5ec980675c68288c66cc6786b9 Mon Sep 17 00:00:00 2001 From: faizanahmad055 Date: Thu, 19 Jul 2018 18:04:07 +0500 Subject: [PATCH] Add handler testcases --- internal/pkg/controller/controller_test.go | 208 ++---------- internal/pkg/handler/updated-handler.go | 60 ++-- .../pkg/handlerTester/updated-handler_test.go | 261 +++++++++++++++ internal/pkg/helper/testUtils.go | 310 ++++++++++++++++++ 4 files changed, 628 insertions(+), 211 deletions(-) create mode 100644 internal/pkg/handlerTester/updated-handler_test.go create mode 100644 internal/pkg/helper/testUtils.go diff --git a/internal/pkg/controller/controller_test.go b/internal/pkg/controller/controller_test.go index 2c1e1c8e..d608bc24 100644 --- a/internal/pkg/controller/controller_test.go +++ b/internal/pkg/controller/controller_test.go @@ -1,37 +1,22 @@ package controller import ( - "math/rand" - "strings" "testing" "time" "github.com/sirupsen/logrus" helper "github.com/stakater/Reloader/internal/pkg/helper" "github.com/stakater/Reloader/pkg/kube" - "k8s.io/api/core/v1" "k8s.io/api/extensions/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" ) var ( - configmapNamePrefix = "testconfigmap-reloader" - secretNamePrefix = "testsecret-reloader" - letters = []rune("abcdefghijklmnopqrstuvwxyz") - configmapUpdateOnChangeAnnotation = "reloader.stakater.com/configmap.update-on-change" - secretUpdateOnChangeAnnotation = "reloader.stakater.com/secret.update-on-change" + configmapNamePrefix = "testconfigmap-reloader" + secretNamePrefix = "testsecret-reloader" ) -func randSeq(n int) string { - rand.Seed(time.Now().UnixNano()) - b := make([]rune, n) - for i := range b { - b[i] = letters[rand.Intn(len(letters))] - } - return string(b) -} - // Creating a Controller to do a rolling upgrade upon updating the configmap or secret func TestControllerForUpdatingConfigmapShouldUpdateDeployment(t *testing.T) { client, err := kube.GetClient() @@ -41,8 +26,8 @@ func TestControllerForUpdatingConfigmapShouldUpdateDeployment(t *testing.T) { } namespace := "test-reloader" logrus.Infof("Step 1: Create namespace") - createNamespace(t, namespace, client) - defer deleteNamespace(t, namespace, client) + helper.CreateNamespace(namespace, client) + defer helper.DeleteNamespace(namespace, client) logrus.Infof("Step 2: Create controller") controller, err := NewController(client, "configMaps", namespace) @@ -56,13 +41,13 @@ func TestControllerForUpdatingConfigmapShouldUpdateDeployment(t *testing.T) { go controller.Run(1, stop) time.Sleep(10 * time.Second) - configmapName := configmapNamePrefix + "-update-" + randSeq(5) + configmapName := configmapNamePrefix + "-update-" + helper.RandSeq(5) configmapClient := client.CoreV1().ConfigMaps(namespace) logrus.Infof("Step 4: Create configmap") - _, err = configmapClient.Create(initConfigmap(namespace, configmapName)) + _, err = configmapClient.Create(helper.GetConfigmap(namespace, configmapName, "www.google.com")) if err != nil { - logrus.Fatalf("Fatal error in configmap creation: %v", err) + t.Errorf("Error in configmap creation: %v", err) } logrus.Infof("Created Configmap %q.\n", configmapName) time.Sleep(10 * time.Second) @@ -76,7 +61,7 @@ func TestControllerForUpdatingConfigmapShouldUpdateDeployment(t *testing.T) { if err != nil { logrus.Errorf("Error while getting configmap %v", err) } - _, updateErr := configmapClient.Update(updateConfigmap(namespace, configmapName, "www.stakater.com")) + _, updateErr := configmapClient.Update(helper.GetConfigmap(namespace, configmapName, "www.stakater.com")) if updateErr != nil { err = controller.client.CoreV1().ConfigMaps(namespace).Delete(configmapName, &metav1.DeleteOptions{}) @@ -88,19 +73,20 @@ func TestControllerForUpdatingConfigmapShouldUpdateDeployment(t *testing.T) { time.Sleep(10 * time.Second) logrus.Infof("Step 7: Verify deployment update for first time") - - updated := verifyDeploymentUpdate(client, namespace, configmapName, "_CONFIGMAP", "www.stakater.com") + shaData := helper.ConvertConfigmapToSHA(helper.GetConfigmap(namespace, configmapName, "www.stakater.com")) + updated := helper.VerifyDeploymentUpdate(client, namespace, configmapName, "_CONFIGMAP", shaData) if !updated { t.Errorf("Deployment was not updated") } time.Sleep(10 * time.Second) logrus.Infof("Step 8: Update configmap for Second time") - _, updateErr = configmapClient.Update(updateConfigmap(namespace, configmapName, "aurorasolutions.io")) + _, updateErr = configmapClient.Update(helper.GetConfigmap(namespace, configmapName, "aurorasolutions.io")) time.Sleep(10 * time.Second) logrus.Infof("Step 9: Verify deployment update for second time") - updated = verifyDeploymentUpdate(client, namespace, configmapName, "_CONFIGMAP", "aurorasolutions.io") + shaData = helper.ConvertConfigmapToSHA(helper.GetConfigmap(namespace, configmapName, "aurorasolutions.io")) + updated = helper.VerifyDeploymentUpdate(client, namespace, configmapName, "_CONFIGMAP", shaData) if !updated { t.Errorf("Deployment was not updated") } @@ -110,7 +96,7 @@ func TestControllerForUpdatingConfigmapShouldUpdateDeployment(t *testing.T) { logrus.Infof("Deleting Deployment %q.\n", deployment.GetObjectMeta().GetName()) deploymentError := controller.client.ExtensionsV1beta1().Deployments(namespace).Delete(configmapName, &metav1.DeleteOptions{}) if deploymentError != nil { - logrus.Fatalf("Error while deleting the configmap %v", deploymentError) + logrus.Errorf("Error while deleting the configmap %v", deploymentError) } logrus.Infof("Step 11: Delete Configmap") @@ -122,60 +108,12 @@ func TestControllerForUpdatingConfigmapShouldUpdateDeployment(t *testing.T) { time.Sleep(15 * time.Second) } -func verifyDeploymentUpdate(client kubernetes.Interface, namespace string, name string, resourceType string, change string) bool { - deployments, err := client.ExtensionsV1beta1().Deployments(namespace).List(metav1.ListOptions{}) - if err != nil { - logrus.Errorf("Failed to list deployments %v", err) - } - for _, d := range deployments.Items { - containers := d.Spec.Template.Spec.Containers - // match deployments with the correct annotation - annotationValue := d.ObjectMeta.Annotations[configmapUpdateOnChangeAnnotation] - if annotationValue != "" { - values := strings.Split(annotationValue, ",") - matches := false - for _, value := range values { - if value == name { - matches = true - break - } - } - if matches { - sshData := helper.ConvertConfigmapToSHA(updateConfigmap(namespace, name, change)) - envName := "STAKATER_" + helper.ConvertToEnvVarName(annotationValue) + resourceType - updated := getResourceSsh(containers, envName) - logrus.Infof("sshData %s", sshData) - logrus.Infof("updated %s", updated) - - if updated != sshData { - return false - } else { - return true - } - } - } - } - return false -} - -func getResourceSsh(containers []v1.Container, envar string) string { - for i := range containers { - envs := containers[i].Env - for j := range envs { - if envs[j].Name == envar { - return envs[j].Value - } - } - } - return "" -} - func createDeployment(deploymentName string, namespace string, client kubernetes.Interface) *v1beta1.Deployment { deploymentClient := client.ExtensionsV1beta1().Deployments(namespace) - deployment := initDeployment(namespace, deploymentName) + deployment := helper.GetDeployment(namespace, deploymentName) deployment, err := deploymentClient.Create(deployment) if err != nil { - logrus.Fatalf("Fatal error in deployment creation: %v", err) + logrus.Errorf("Error in deployment creation: %v", err) } logrus.Infof("Created Deployment %q.\n", deployment.GetObjectMeta().GetName()) return deployment @@ -188,8 +126,8 @@ func TestControllerForUpdatingSecretShouldUpdateDeployment(t *testing.T) { return } namespace := "test-reloader-secrets" - createNamespace(t, namespace, client) - defer deleteNamespace(t, namespace, client) + helper.CreateNamespace(namespace, client) + defer helper.DeleteNamespace(namespace, client) controller, err := NewController(client, "secrets", namespace) if err != nil { @@ -201,11 +139,12 @@ func TestControllerForUpdatingSecretShouldUpdateDeployment(t *testing.T) { go controller.Run(1, stop) time.Sleep(10 * time.Second) - secretName := secretNamePrefix + "-update-" + randSeq(5) + secretName := secretNamePrefix + "-update-" + helper.RandSeq(5) secretClient := client.CoreV1().Secrets(namespace) - _, err = secretClient.Create(initSecret(namespace, secretName)) + data := "dGVzdFNlY3JldEVuY29kaW5nRm9yUmVsb2FkZXI=" + _, err = secretClient.Create(helper.GetSecret(namespace, secretName, data)) if err != nil { - logrus.Fatalf("Fatal error in secret creation: %v", err) + logrus.Errorf("Error in secret creation: %v", err) } logrus.Infof("Created Secret %q.\n", secretName) time.Sleep(10 * time.Second) @@ -215,7 +154,8 @@ func TestControllerForUpdatingSecretShouldUpdateDeployment(t *testing.T) { if err != nil { logrus.Errorf("Error while getting secret %v", err) } - _, updateErr := secretClient.Update(updateSecret(namespace, secretName)) + data = "dGVzdFVwZGF0ZWRTZWNyZXRFbmNvZGluZ0ZvclJlbG9hZGVy" + _, updateErr := secretClient.Update(helper.GetSecret(namespace, secretName, data)) // TODO: Add functionality to verify reloader functionality here @@ -234,103 +174,3 @@ func TestControllerForUpdatingSecretShouldUpdateDeployment(t *testing.T) { } time.Sleep(15 * time.Second) } - -func initConfigmap(namespace string, configmapName string) *v1.ConfigMap { - return &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: configmapName, - Namespace: namespace, - Labels: map[string]string{"firstLabel": "temp"}, - }, - Data: map[string]string{"test.url": "www.google.com"}, - } -} - -func initDeployment(namespace string, deploymentName string) *v1beta1.Deployment { - replicaset := int32(1) - return &v1beta1.Deployment{ - ObjectMeta: metav1.ObjectMeta{ - Name: deploymentName, - Namespace: namespace, - Labels: map[string]string{"firstLabel": "temp"}, - Annotations: map[string]string{"reloader.stakater.com/configmap.update-on-change": deploymentName}, - }, - Spec: v1beta1.DeploymentSpec{ - Replicas: &replicaset, - Strategy: v1beta1.DeploymentStrategy{ - Type: v1beta1.RollingUpdateDeploymentStrategyType, - }, - Template: v1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{"secondLabel": "temp"}, - }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ - { - Image: "tutum/hello-world", - Name: deploymentName, - Env: []v1.EnvVar{ - { - Name: "BUCKET_NAME", - Value: "test", - }, - }, - }, - }, - }, - }, - }, - } -} - -func initSecret(namespace string, secretName string) *v1.Secret { - return &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: secretName, - Namespace: namespace, - Labels: map[string]string{"firstLabel": "temp"}, - }, - Data: map[string][]byte{"test.url": []byte("dGVzdFNlY3JldEVuY29kaW5nRm9yUmVsb2FkZXI=")}, - } -} - -func createNamespace(t *testing.T, namespace string, client kubernetes.Interface) { - _, err := client.CoreV1().Namespaces().Create(&v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: namespace}}) - if err != nil { - t.Error("Failed to create namespace for testing", err) - } else { - logrus.Infof("Creating namespace for testing = %s", namespace) - } -} - -func deleteNamespace(t *testing.T, namespace string, client kubernetes.Interface) { - logrus.Infof("Step 12: Delete Namespace") - err := client.CoreV1().Namespaces().Delete(namespace, &metav1.DeleteOptions{}) - if err != nil { - t.Error("Failed to delete namespace that was created for testing", err) - } else { - logrus.Infof("Deleting namespace for testing = %s", namespace) - } -} - -func updateConfigmap(namespace string, configmapName string, testData string) *v1.ConfigMap { - return &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: configmapName, - Namespace: namespace, - Labels: map[string]string{"firstLabel": "temp"}, - }, - Data: map[string]string{"test.url": testData}, - } -} - -func updateSecret(namespace string, secretName string) *v1.Secret { - return &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: secretName, - Namespace: namespace, - Labels: map[string]string{"firstLabel": "temp"}, - }, - Data: map[string][]byte{"test.url": []byte("dGVzdFVwZGF0ZWRTZWNyZXRFbmNvZGluZ0ZvclJlbG9hZGVy")}, - } -} diff --git a/internal/pkg/handler/updated-handler.go b/internal/pkg/handler/updated-handler.go index 428bf713..361e1146 100644 --- a/internal/pkg/handler/updated-handler.go +++ b/internal/pkg/handler/updated-handler.go @@ -52,32 +52,33 @@ func rollingUpgrade(r ResourceUpdatedHandler, resourceType string, rollingUpgrad if err != nil { logrus.Fatalf("Unable to create Kubernetes client error = %v", err) } - var namespace, name, sshData, envName string + var namespace, name, shaData, envName string if resourceType == "configmaps" { namespace = r.Resource.(*v1.ConfigMap).Namespace name = r.Resource.(*v1.ConfigMap).Name - sshData = helper.ConvertConfigmapToSHA(r.Resource.(*v1.ConfigMap)) + shaData = helper.ConvertConfigmapToSHA(r.Resource.(*v1.ConfigMap)) envName = "_CONFIGMAP" } else if resourceType == "secrets" { namespace = r.Resource.(*v1.Secret).Namespace name = r.Resource.(*v1.Secret).Name - sshData = helper.ConvertSecretToSHA(r.Resource.(*v1.Secret)) + shaData = helper.ConvertSecretToSHA(r.Resource.(*v1.Secret)) envName = "_SECRET" } if rollingUpgradeType == "deployments" { - rollingUpgradeForDeployment(client, namespace, name, sshData, envName) + RollingUpgradeForDeployment(client, namespace, name, shaData, envName) } else if rollingUpgradeType == "daemonsets" { - rollingUpgradeForDaemonSets(client, namespace, name, sshData, envName) + RollingUpgradeForDaemonSets(client, namespace, name, shaData, envName) } else if rollingUpgradeType == "statefulSets" { - rollingUpgradeForStatefulSets(client, namespace, name, sshData, envName) + RollingUpgradeForStatefulSets(client, namespace, name, shaData, envName) } } -func rollingUpgradeForDeployment(client kubernetes.Interface, namespace string, name string, sshData string, envName string) error { +// RollingUpgradeForDeployment upgrades the deployment if there is any change in configmap or secret data +func RollingUpgradeForDeployment(client kubernetes.Interface, namespace string, name string, shaData string, envName string) error { deployments, err := client.ExtensionsV1beta1().Deployments(namespace).List(meta_v1.ListOptions{}) if err != nil { - logrus.Fatalf("Failed to list deployments %v", err) + logrus.Errorf("Failed to list deployments %v", err) } var updateOnChangeAnnotation string if envName == "_CONFIGMAP" { @@ -100,7 +101,7 @@ func rollingUpgradeForDeployment(client kubernetes.Interface, namespace string, } } if matches { - updated := updateContainers(containers, name, sshData, envName) + updated := updateContainers(containers, name, shaData, envName) if !updated { logrus.Warnf("Rolling upgrade did not happen") @@ -108,9 +109,10 @@ func rollingUpgradeForDeployment(client kubernetes.Interface, namespace string, // update the deployment _, err := client.ExtensionsV1beta1().Deployments(namespace).Update(&d) if err != nil { - logrus.Fatalf("Update deployment failed %v", err) + logrus.Errorf("Update deployment failed %v", err) + } else { + logrus.Infof("Updated Deployment %s", d.Name) } - logrus.Infof("Updated Deployment %s", d.Name) } } } @@ -118,10 +120,11 @@ func rollingUpgradeForDeployment(client kubernetes.Interface, namespace string, return nil } -func rollingUpgradeForDaemonSets(client kubernetes.Interface, namespace string, name string, sshData string, envName string) error { +// RollingUpgradeForDaemonSets upgrades the daemonset if there is any change in configmap or secret data +func RollingUpgradeForDaemonSets(client kubernetes.Interface, namespace string, name string, shaData string, envName string) error { daemonSets, err := client.ExtensionsV1beta1().DaemonSets(namespace).List(meta_v1.ListOptions{}) if err != nil { - logrus.Fatalf("Failed to list daemonSets %v", err) + logrus.Errorf("Failed to list daemonSets %v", err) } var updateOnChangeAnnotation string @@ -145,7 +148,7 @@ func rollingUpgradeForDaemonSets(client kubernetes.Interface, namespace string, } } if matches { - updated := updateContainers(containers, name, sshData, envName) + updated := updateContainers(containers, name, shaData, envName) if !updated { logrus.Warnf("Rolling upgrade did not happen") @@ -153,9 +156,10 @@ func rollingUpgradeForDaemonSets(client kubernetes.Interface, namespace string, // update the daemonSet _, err := client.ExtensionsV1beta1().DaemonSets(namespace).Update(&d) if err != nil { - logrus.Fatalf("Update daemonSet failed %v", err) + logrus.Errorf("Update daemonSet failed %v", err) + } else { + logrus.Infof("Updated daemonSet %s", d.Name) } - logrus.Infof("Updated daemonSet %s", d.Name) } } } @@ -163,10 +167,11 @@ func rollingUpgradeForDaemonSets(client kubernetes.Interface, namespace string, return nil } -func rollingUpgradeForStatefulSets(client kubernetes.Interface, namespace string, name string, sshData string, envName string) error { +// RollingUpgradeForStatefulSets upgrades the statefulset if there is any change in configmap or secret data +func RollingUpgradeForStatefulSets(client kubernetes.Interface, namespace string, name string, shaData string, envName string) error { statefulSets, err := client.AppsV1beta1().StatefulSets(namespace).List(meta_v1.ListOptions{}) if err != nil { - logrus.Fatalf("Failed to list statefulSets %v", err) + logrus.Errorf("Failed to list statefulSets %v", err) } var updateOnChangeAnnotation string if envName == "_CONFIGMAP" { @@ -189,7 +194,7 @@ func rollingUpgradeForStatefulSets(client kubernetes.Interface, namespace string } } if matches { - updated := updateContainers(containers, name, sshData, envName) + updated := updateContainers(containers, name, shaData, envName) if !updated { logrus.Warnf("Rolling upgrade did not happen") @@ -197,9 +202,10 @@ func rollingUpgradeForStatefulSets(client kubernetes.Interface, namespace string // update the statefulSet _, err := client.AppsV1beta1().StatefulSets(namespace).Update(&d) if err != nil { - logrus.Fatalf("Update statefulSet failed %v", err) + logrus.Errorf("Update statefulSet failed %v", err) + } else { + logrus.Infof("Updated statefulSet %s", d.Name) } - logrus.Infof("Updated statefulSet %s", d.Name) } } } @@ -207,7 +213,7 @@ func rollingUpgradeForStatefulSets(client kubernetes.Interface, namespace string return nil } -func updateContainers(containers []v1.Container, annotationValue string, sshData string, resourceType string) bool { +func updateContainers(containers []v1.Container, annotationValue string, shaData string, resourceType string) bool { updated := false envar := "STAKATER_" + helper.ConvertToEnvVarName(annotationValue) + resourceType logrus.Infof("Generated environment variable: %s", envar) @@ -219,9 +225,9 @@ func updateContainers(containers []v1.Container, annotationValue string, sshData if envs[j].Name == envar { matched = true logrus.Infof("%s environment variable found", envar) - if envs[j].Value != sshData { - logrus.Infof("Updating %s to %s", envar, sshData) - envs[j].Value = sshData + if envs[j].Value != shaData { + logrus.Infof("Updating %s to %s", envar, shaData) + envs[j].Value = shaData updated = true } } @@ -230,11 +236,11 @@ func updateContainers(containers []v1.Container, annotationValue string, sshData if !matched { e := v1.EnvVar{ Name: envar, - Value: sshData, + Value: shaData, } containers[i].Env = append(containers[i].Env, e) updated = true - logrus.Infof("%s environment variable does not found, creating a new env with value %s", envar, sshData) + logrus.Infof("%s environment variable does not found, creating a new env with value %s", envar, shaData) } } return updated diff --git a/internal/pkg/handlerTester/updated-handler_test.go b/internal/pkg/handlerTester/updated-handler_test.go new file mode 100644 index 00000000..ec01a846 --- /dev/null +++ b/internal/pkg/handlerTester/updated-handler_test.go @@ -0,0 +1,261 @@ +package handlerTester + +import ( + "os" + "testing" + "time" + + "github.com/sirupsen/logrus" + "github.com/stakater/Reloader/internal/pkg/controller" + "github.com/stakater/Reloader/internal/pkg/handler" + helper "github.com/stakater/Reloader/internal/pkg/helper" + "github.com/stakater/Reloader/pkg/kube" + v1_beta1 "k8s.io/api/apps/v1beta1" + "k8s.io/api/extensions/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" +) + +var ( + client = getClient() + namespace = "test-handler" + configmapName = "testconfigmap-handler-update-" + helper.RandSeq(5) + secretName = "testsecret-handler-update-" + helper.RandSeq(5) +) + +func TestMain(m *testing.M) { + + logrus.Infof("Creating namespace %s", namespace) + helper.CreateNamespace(namespace, client) + + logrus.Infof("Creating controller") + newController, err := controller.NewController(client, "configMaps", namespace) + if err != nil { + logrus.Errorf("Unable to create NewController error = %v", err) + return + } + + stop := make(chan struct{}) + defer close(stop) + logrus.Infof("Starting controller") + go newController.Run(1, stop) + time.Sleep(10 * time.Second) + + logrus.Infof("Setting up the test resources") + setup() + + logrus.Infof("Running Testcases") + retCode := m.Run() + + logrus.Infof("tearing down the test resources") + teardown() + + os.Exit(retCode) +} + +func getClient() *kubernetes.Clientset { + newClient, err := kube.GetClient() + if err != nil { + logrus.Fatalf("Unable to create Kubernetes client error = %v", err) + } + return newClient +} + +func setup() { + logrus.Infof("Creating configmap") + configmapClient := client.CoreV1().ConfigMaps(namespace) + _, err := configmapClient.Create(helper.GetConfigmap(namespace, configmapName, "www.google.com")) + if err != nil { + logrus.Errorf("Error in configmap creation: %v", err) + } + time.Sleep(10 * time.Second) + + logrus.Infof("Creating secret") + secretClient := client.CoreV1().Secrets(namespace) + data := "dGVzdFNlY3JldEVuY29kaW5nRm9yUmVsb2FkZXI=" + _, err = secretClient.Create(helper.GetSecret(namespace, secretName, data)) + if err != nil { + logrus.Errorf("Error in secret creation: %v", err) + } + time.Sleep(10 * time.Second) + + logrus.Infof("Creating Deployment with configmap") + createDeployment(configmapName, namespace) + + logrus.Infof("Creating Deployment with secret") + createDeployment(secretName, namespace) + + logrus.Infof("Creating Daemonset with configmap") + createDaemonset(configmapName, namespace) + + logrus.Infof("Creating Daemonset with secret") + createDaemonset(secretName, namespace) + + logrus.Infof("Creating Statefulset with configmap") + createStatefulset(configmapName, namespace) + + logrus.Infof("Creating Statefulset with secret") + createStatefulset(secretName, namespace) + +} + +func teardown() { + logrus.Infof("Deleting Deployment with configmap") + deploymentError := client.ExtensionsV1beta1().Deployments(namespace).Delete(configmapName, &metav1.DeleteOptions{}) + if deploymentError != nil { + logrus.Errorf("Error while deleting deployment with configmap %v", deploymentError) + } + + logrus.Infof("Deleting Deployment with secret") + deploymentError = client.ExtensionsV1beta1().Deployments(namespace).Delete(secretName, &metav1.DeleteOptions{}) + if deploymentError != nil { + logrus.Errorf("Error while deleting deployment with secret %v", deploymentError) + } + + logrus.Infof("Deleting Daemonset with configmap") + daemonsetError := client.ExtensionsV1beta1().DaemonSets(namespace).Delete(configmapName, &metav1.DeleteOptions{}) + if daemonsetError != nil { + logrus.Errorf("Error while deleting daemonset with configmap %v", daemonsetError) + } + + logrus.Infof("Deleting Deployment with secret") + daemonsetError = client.ExtensionsV1beta1().DaemonSets(namespace).Delete(secretName, &metav1.DeleteOptions{}) + if daemonsetError != nil { + logrus.Errorf("Error while deleting daemonset with secret %v", daemonsetError) + } + + logrus.Infof("Deleting Statefulset with configmap") + statefulsetError := client.AppsV1beta1().StatefulSets(namespace).Delete(configmapName, &metav1.DeleteOptions{}) + if statefulsetError != nil { + logrus.Errorf("Error while deleting statefulset with configmap %v", statefulsetError) + } + + logrus.Infof("Deleting Deployment with secret") + statefulsetError = client.AppsV1beta1().StatefulSets(namespace).Delete(secretName, &metav1.DeleteOptions{}) + if statefulsetError != nil { + logrus.Errorf("Error while deleting statefulset with secret %v", statefulsetError) + } + + logrus.Infof("Deleting Configmap %q.\n", configmapName) + err := client.CoreV1().ConfigMaps(namespace).Delete(configmapName, &metav1.DeleteOptions{}) + if err != nil { + logrus.Errorf("Error while deleting the configmap %v", err) + } + time.Sleep(5 * time.Second) + + logrus.Infof("Deleting Secret %q.\n", secretName) + err = client.CoreV1().Secrets(namespace).Delete(secretName, &metav1.DeleteOptions{}) + if err != nil { + logrus.Errorf("Error while deleting the secret %v", err) + } + time.Sleep(5 * time.Second) + + logrus.Infof("Deleting namespace %q.\n", namespace) + helper.DeleteNamespace(namespace, client) + +} + +func createDeployment(deploymentName string, namespace string) *v1beta1.Deployment { + deploymentClient := client.ExtensionsV1beta1().Deployments(namespace) + deployment := helper.GetDeployment(namespace, deploymentName) + deployment, err := deploymentClient.Create(deployment) + if err != nil { + logrus.Errorf("Error in deployment creation: %v", err) + } + logrus.Infof("Created Deployment %q.\n", deployment.GetObjectMeta().GetName()) + return deployment +} + +func createDaemonset(daemonsetName string, namespace string) *v1beta1.DaemonSet { + daemonsetClient := client.ExtensionsV1beta1().DaemonSets(namespace) + daemonset := helper.GetDaemonset(namespace, daemonsetName) + daemonset, err := daemonsetClient.Create(daemonset) + if err != nil { + logrus.Errorf("Error in daemonset creation: %v", err) + } + logrus.Infof("Created Deployment %q.\n", daemonset.GetObjectMeta().GetName()) + return daemonset +} + +func createStatefulset(statefulsetName string, namespace string) *v1_beta1.StatefulSet { + statefulsetClient := client.AppsV1beta1().StatefulSets(namespace) + statefulset := helper.GetStatefulset(namespace, statefulsetName) + statefulset, err := statefulsetClient.Create(statefulset) + if err != nil { + logrus.Errorf("Error in statefulset creation: %v", err) + } + logrus.Infof("Created Statefulset %q.\n", statefulset.GetObjectMeta().GetName()) + return statefulset +} + +func TestRollingUpgradeForDeploymentWithConfigmap(t *testing.T) { + shaData := helper.ConvertConfigmapToSHA(helper.GetConfigmap(namespace, configmapName, "www.stakater.com")) + handler.RollingUpgradeForDeployment(client, namespace, configmapName, shaData, "_CONFIGMAP") + time.Sleep(5 * time.Second) + + logrus.Infof("Verifying deployment update") + updated := helper.VerifyDeploymentUpdate(client, namespace, configmapName, "_CONFIGMAP", shaData) + if !updated { + t.Errorf("Deployment was not updated") + } +} + +func TestRollingUpgradeForDeploymentWithSecret(t *testing.T) { + shaData := helper.ConvertSecretToSHA(helper.GetSecret(namespace, secretName, "dGVzdFVwZGF0ZWRTZWNyZXRFbmNvZGluZ0ZvclJlbG9hZGVy")) + handler.RollingUpgradeForDeployment(client, namespace, secretName, shaData, "_SECRET") + time.Sleep(5 * time.Second) + + logrus.Infof("Verifying deployment update") + updated := helper.VerifyDeploymentUpdate(client, namespace, secretName, "_SECRET", shaData) + if !updated { + t.Errorf("Deployment was not updated") + } +} + +func TestRollingUpgradeForDaemonsetWithConfigmap(t *testing.T) { + shaData := helper.ConvertConfigmapToSHA(helper.GetConfigmap(namespace, configmapName, "www.facebook.com")) + handler.RollingUpgradeForDaemonSets(client, namespace, configmapName, shaData, "_CONFIGMAP") + time.Sleep(5 * time.Second) + + logrus.Infof("Verifying daemonset update") + updated := helper.VerifyDaemonsetUpdate(client, namespace, configmapName, "_CONFIGMAP", shaData) + if !updated { + t.Errorf("Daemonset was not updated") + } +} + +func TestRollingUpgradeForDaemonsetWithSecret(t *testing.T) { + shaData := helper.ConvertSecretToSHA(helper.GetSecret(namespace, secretName, "d3d3LmZhY2Vib29rLmNvbQ==")) + handler.RollingUpgradeForDaemonSets(client, namespace, secretName, shaData, "_SECRET") + time.Sleep(5 * time.Second) + + logrus.Infof("Verifying daemonset update") + updated := helper.VerifyDaemonsetUpdate(client, namespace, secretName, "_SECRET", shaData) + if !updated { + t.Errorf("Daemonset was not updated") + } +} + +func TestRollingUpgradeForStatefulsetWithConfigmap(t *testing.T) { + shaData := helper.ConvertConfigmapToSHA(helper.GetConfigmap(namespace, configmapName, "www.twitter.com")) + handler.RollingUpgradeForStatefulSets(client, namespace, configmapName, shaData, "_CONFIGMAP") + time.Sleep(5 * time.Second) + + logrus.Infof("Verifying statefulset update") + updated := helper.VerifyStatefulsetUpdate(client, namespace, configmapName, "_CONFIGMAP", shaData) + if !updated { + t.Errorf("Statefulset was not updated") + } +} + +func TestRollingUpgradeForStatefulsetWithSecret(t *testing.T) { + shaData := helper.ConvertSecretToSHA(helper.GetSecret(namespace, secretName, "d3d3LnR3aXR0ZXIuY29t")) + handler.RollingUpgradeForStatefulSets(client, namespace, secretName, shaData, "_SECRET") + time.Sleep(5 * time.Second) + + logrus.Infof("Verifying statefulset update") + updated := helper.VerifyStatefulsetUpdate(client, namespace, secretName, "_SECRET", shaData) + if !updated { + t.Errorf("Statefulset was not updated") + } +} diff --git a/internal/pkg/helper/testUtils.go b/internal/pkg/helper/testUtils.go new file mode 100644 index 00000000..80e2ed35 --- /dev/null +++ b/internal/pkg/helper/testUtils.go @@ -0,0 +1,310 @@ +package handler + +import ( + "math/rand" + "strings" + "time" + + "github.com/sirupsen/logrus" + v1_beta1 "k8s.io/api/apps/v1beta1" + "k8s.io/api/core/v1" + "k8s.io/api/extensions/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" +) + +var ( + letters = []rune("abcdefghijklmnopqrstuvwxyz") + configmapUpdateOnChangeAnnotation = "reloader.stakater.com/configmap.update-on-change" + secretUpdateOnChangeAnnotation = "reloader.stakater.com/secret.update-on-change" +) + +// RandSeq generates a random sequence +func RandSeq(n int) string { + rand.Seed(time.Now().UnixNano()) + b := make([]rune, n) + for i := range b { + b[i] = letters[rand.Intn(len(letters))] + } + return string(b) +} + +// CreateNamespace creates namespace for testing +func CreateNamespace(namespace string, client kubernetes.Interface) { + _, err := client.CoreV1().Namespaces().Create(&v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: namespace}}) + if err != nil { + logrus.Fatalf("Failed to create namespace for testing", err) + } else { + logrus.Infof("Creating namespace for testing = %s", namespace) + } +} + +// DeleteNamespace deletes namespace for testing +func DeleteNamespace(namespace string, client kubernetes.Interface) { + err := client.CoreV1().Namespaces().Delete(namespace, &metav1.DeleteOptions{}) + if err != nil { + logrus.Fatalf("Failed to delete namespace that was created for testing", err) + } else { + logrus.Infof("Deleting namespace for testing = %s", namespace) + } +} + +// GetDeployment provides deployment for testing +func GetDeployment(namespace string, deploymentName string) *v1beta1.Deployment { + replicaset := int32(1) + return &v1beta1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: deploymentName, + Namespace: namespace, + Labels: map[string]string{"firstLabel": "temp"}, + Annotations: map[string]string{ + "reloader.stakater.com/configmap.update-on-change": deploymentName, + "reloader.stakater.com/secret.update-on-change": deploymentName}, + }, + Spec: v1beta1.DeploymentSpec{ + Replicas: &replicaset, + Strategy: v1beta1.DeploymentStrategy{ + Type: v1beta1.RollingUpdateDeploymentStrategyType, + }, + Template: v1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"secondLabel": "temp"}, + }, + Spec: v1.PodSpec{ + Containers: []v1.Container{ + { + Image: "tutum/hello-world", + Name: deploymentName, + Env: []v1.EnvVar{ + { + Name: "BUCKET_NAME", + Value: "test", + }, + }, + }, + }, + }, + }, + }, + } +} + +// GetDaemonset provides daemonset for testing +func GetDaemonset(namespace string, daemonsetName string) *v1beta1.DaemonSet { + return &v1beta1.DaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: daemonsetName, + Namespace: namespace, + Labels: map[string]string{"firstLabel": "temp"}, + Annotations: map[string]string{ + "reloader.stakater.com/configmap.update-on-change": daemonsetName, + "reloader.stakater.com/secret.update-on-change": daemonsetName}, + }, + Spec: v1beta1.DaemonSetSpec{ + UpdateStrategy: v1beta1.DaemonSetUpdateStrategy{ + Type: v1beta1.RollingUpdateDaemonSetStrategyType, + }, + Template: v1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"secondLabel": "temp"}, + }, + Spec: v1.PodSpec{ + Containers: []v1.Container{ + { + Image: "tutum/hello-world", + Name: daemonsetName, + Env: []v1.EnvVar{ + { + Name: "BUCKET_NAME", + Value: "test", + }, + }, + }, + }, + }, + }, + }, + } +} + +// GetStatefulset provides statefulset for testing +func GetStatefulset(namespace string, statefulsetName string) *v1_beta1.StatefulSet { + return &v1_beta1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: statefulsetName, + Namespace: namespace, + Labels: map[string]string{"firstLabel": "temp"}, + Annotations: map[string]string{ + "reloader.stakater.com/configmap.update-on-change": statefulsetName, + "reloader.stakater.com/secret.update-on-change": statefulsetName}, + }, + Spec: v1_beta1.StatefulSetSpec{ + UpdateStrategy: v1_beta1.StatefulSetUpdateStrategy{ + Type: v1_beta1.RollingUpdateStatefulSetStrategyType, + }, + Template: v1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"secondLabel": "temp"}, + }, + Spec: v1.PodSpec{ + Containers: []v1.Container{ + { + Image: "tutum/hello-world", + Name: statefulsetName, + Env: []v1.EnvVar{ + { + Name: "BUCKET_NAME", + Value: "test", + }, + }, + }, + }, + }, + }, + }, + } +} + +// GetConfigmap provides configmap for testing +func GetConfigmap(namespace string, configmapName string, testData string) *v1.ConfigMap { + return &v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: configmapName, + Namespace: namespace, + Labels: map[string]string{"firstLabel": "temp"}, + }, + Data: map[string]string{"test.url": testData}, + } +} + +// GetSecret provides secret for testing +func GetSecret(namespace string, secretName string, data string) *v1.Secret { + return &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: secretName, + Namespace: namespace, + Labels: map[string]string{"firstLabel": "temp"}, + }, + Data: map[string][]byte{"test.url": []byte(data)}, + } +} + +// VerifyDeploymentUpdate verifies whether deployment has been updated with environment variable or not +func VerifyDeploymentUpdate(client kubernetes.Interface, namespace string, name string, resourceType string, shaData string) bool { + deployments, err := client.ExtensionsV1beta1().Deployments(namespace).List(metav1.ListOptions{}) + if err != nil { + logrus.Errorf("Failed to list deployments %v", err) + } + for _, d := range deployments.Items { + containers := d.Spec.Template.Spec.Containers + // match deployments with the correct annotation + annotationValue := d.ObjectMeta.Annotations[configmapUpdateOnChangeAnnotation] + if annotationValue != "" { + values := strings.Split(annotationValue, ",") + matches := false + for _, value := range values { + if value == name { + matches = true + break + } + } + if matches { + envName := "STAKATER_" + ConvertToEnvVarName(annotationValue) + resourceType + updated := getResourceSHA(containers, envName) + logrus.Infof("shaData %s", shaData) + logrus.Infof("updated %s", updated) + + if updated != shaData { + return false + } else { + return true + } + } + } + } + return false +} + +// VerifyDaemonsetUpdate verifies whether daemonset has been updated with environment variable or not +func VerifyDaemonsetUpdate(client kubernetes.Interface, namespace string, name string, resourceType string, shaData string) bool { + daemonsets, err := client.ExtensionsV1beta1().DaemonSets(namespace).List(metav1.ListOptions{}) + if err != nil { + logrus.Errorf("Failed to list daemonsets %v", err) + } + for _, d := range daemonsets.Items { + containers := d.Spec.Template.Spec.Containers + // match daemonsets with the correct annotation + annotationValue := d.ObjectMeta.Annotations[configmapUpdateOnChangeAnnotation] + if annotationValue != "" { + values := strings.Split(annotationValue, ",") + matches := false + for _, value := range values { + if value == name { + matches = true + break + } + } + if matches { + envName := "STAKATER_" + ConvertToEnvVarName(annotationValue) + resourceType + updated := getResourceSHA(containers, envName) + logrus.Infof("shaData %s", shaData) + logrus.Infof("updated %s", updated) + + if updated != shaData { + return false + } else { + return true + } + } + } + } + return false +} + +// VerifyStatefulsetUpdate verifies whether statefulset has been updated with environment variable or not +func VerifyStatefulsetUpdate(client kubernetes.Interface, namespace string, name string, resourceType string, shaData string) bool { + statefulsets, err := client.AppsV1beta1().StatefulSets(namespace).List(metav1.ListOptions{}) + if err != nil { + logrus.Errorf("Failed to list statefulsets %v", err) + } + for _, d := range statefulsets.Items { + containers := d.Spec.Template.Spec.Containers + // match statefulsets with the correct annotation + annotationValue := d.ObjectMeta.Annotations[configmapUpdateOnChangeAnnotation] + if annotationValue != "" { + values := strings.Split(annotationValue, ",") + matches := false + for _, value := range values { + if value == name { + matches = true + break + } + } + if matches { + envName := "STAKATER_" + ConvertToEnvVarName(annotationValue) + resourceType + updated := getResourceSHA(containers, envName) + logrus.Infof("shaData %s", shaData) + logrus.Infof("updated %s", updated) + + if updated != shaData { + return false + } else { + return true + } + } + } + } + return false +} + +func getResourceSHA(containers []v1.Container, envar string) string { + for i := range containers { + envs := containers[i].Env + for j := range envs { + if envs[j].Name == envar { + return envs[j].Value + } + } + } + return "" +}