From 6a5341aebfe7de6f0d88f7756f79b9062f1e1dd4 Mon Sep 17 00:00:00 2001 From: faizanahmad055 Date: Mon, 16 Jul 2018 20:03:07 +0500 Subject: [PATCH 01/21] Add initial rolling upgrade changes --- internal/pkg/handler/updated-handler.go | 244 ++++++++++++++++++++++++ 1 file changed, 244 insertions(+) diff --git a/internal/pkg/handler/updated-handler.go b/internal/pkg/handler/updated-handler.go index 478445db..cea4ddbc 100644 --- a/internal/pkg/handler/updated-handler.go +++ b/internal/pkg/handler/updated-handler.go @@ -1,8 +1,21 @@ package handler import ( + "sort" + "strings" + "crypto/sha1" + "strconv" + "bytes" + "github.com/sirupsen/logrus" + "github.com/stakater/Reloader/pkg/kube" "k8s.io/api/core/v1" + "k8s.io/client-go/kubernetes" + meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const ( + updateOnChangeAnnotation = "reloader.stakater.com/update-on-change" ) // ResourceUpdatedHandler contains updated objects @@ -20,11 +33,242 @@ func (r ResourceUpdatedHandler) Handle() error { // process resource based on its type if _, ok := r.Resource.(*v1.ConfigMap); ok { logrus.Infof("Performing 'Updated' action for resource of type 'configmap'") + rollingUpgrade(r, "configmaps", "deployments") + rollingUpgrade(r, "configmaps", "daemonsets") + rollingUpgrade(r, "configmaps", "statefulSets") } else if _, ok := r.Resource.(*v1.Secret); ok { logrus.Infof("Performing 'Updated' action for resource of type 'secret'") + rollingUpgrade(r, "secrets", "deployments") + rollingUpgrade(r, "secrets", "daemonsets") + rollingUpgrade(r, "secrets", "statefulSets") } else { logrus.Infof("Invalid resource") } } return nil } + +func rollingUpgrade(r ResourceUpdatedHandler, resourceType string, rollingUpgradeType string){ + client, err := kube.GetClient() + if err != nil { + logrus.Fatalf("Unable to create Kubernetes client error = %v", err) + } + var namespace, name, sshData, envName string + if resourceType == "configmaps" { + namespace = r.Resource.(*v1.ConfigMap).Namespace + name = r.Resource.(*v1.ConfigMap).Name + sshData = convertConfigmapToSHA(r.Resource.(*v1.ConfigMap)) + envName = "_CONFIGMAP" + } else if resourceType == "secrets" { + namespace = r.Resource.(*v1.Secret).Namespace + name = r.Resource.(*v1.Secret).Name + sshData = convertSecretToSHA(r.Resource.(*v1.Secret)) + envName = "_SECRET" + } + + if rollingUpgradeType == "deployments" { + rollingUpgradeForDeployment(client, r, namespace, name, sshData, envName) + } else if rollingUpgradeType == "daemonsets" { + rollingUpgradeForDaemonSets(client, r, namespace, name, sshData, envName) + } else if rollingUpgradeType == "statefulSets" { + rollingUpgradeForStatefulSets(client, r, namespace, name, sshData, envName) + } +} + +func rollingUpgradeForDeployment(client kubernetes.Interface, r ResourceUpdatedHandler, namespace string, name string, sshData 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) + } + for _, d := range deployments.Items { + containers := d.Spec.Template.Spec.Containers + // match deployments with the correct annotation + annotationValue := d.ObjectMeta.Annotations[updateOnChangeAnnotation] + if annotationValue != "" { + values := strings.Split(annotationValue, ",") + matches := false + for _, value := range values { + if value == name { + matches = true + break + } + } + if matches { + updated := updateContainers(containers, annotationValue, sshData, envName) + + if !updated { + logrus.Warnf("Rolling upgrade did not happen") + } else { + // update the deployment + _, err := client.ExtensionsV1beta1().Deployments(namespace).Update(&d) + if err != nil { + logrus.Fatalf("Update deployment failed %v", err) + } + logrus.Infof("Updated Deployment %s", d.Name) + } + } + } + } + return nil +} + + +func rollingUpgradeForDaemonSets(client kubernetes.Interface, r ResourceUpdatedHandler, namespace string, name string, sshData 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) + } + for _, d := range daemonSets.Items { + containers := d.Spec.Template.Spec.Containers + // match daemonSets with the correct annotation + annotationValue := d.ObjectMeta.Annotations[updateOnChangeAnnotation] + if annotationValue != "" { + values := strings.Split(annotationValue, ",") + matches := false + for _, value := range values { + if value == name { + matches = true + break + } + } + if matches { + updated := updateContainers(containers, annotationValue, sshData, envName) + + if !updated { + logrus.Warnf("Rolling upgrade did not happen") + } else { + // update the daemonSet + _, err := client.ExtensionsV1beta1().DaemonSets(namespace).Update(&d) + if err != nil { + logrus.Fatalf("Update daemonSet failed %v", err) + } + logrus.Infof("Updated daemonSet %s", d.Name) + } + } + } + } + return nil +} + +func rollingUpgradeForStatefulSets(client kubernetes.Interface, r ResourceUpdatedHandler, namespace string, name string, sshData 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) + } + for _, d := range statefulSets.Items { + containers := d.Spec.Template.Spec.Containers + // match statefulSets with the correct annotation + annotationValue := d.ObjectMeta.Annotations[updateOnChangeAnnotation] + if annotationValue != "" { + values := strings.Split(annotationValue, ",") + matches := false + for _, value := range values { + if value == name { + matches = true + break + } + } + if matches { + updated := updateContainers(containers, annotationValue, sshData, envName) + + if !updated { + logrus.Warnf("Rolling upgrade did not happen") + } else { + // update the statefulSet + _, err := client.AppsV1beta1().StatefulSets(namespace).Update(&d) + if err != nil { + logrus.Fatalf("Update statefulSet failed %v", err) + } + logrus.Infof("Updated statefulSet %s", d.Name) + } + } + } + } + return nil +} + +func updateContainers(containers []v1.Container, annotationValue, sshData string, resourceType string) bool { + // we can have multiple resourceTypes to update + updated := false + resourceTypes := strings.Split(annotationValue, ",") + for _, nameToUpdate := range resourceTypes { + envar := "STAKATER_" + convertToEnvVarName(nameToUpdate) + resourceType + + for i := range containers { + envs := containers[i].Env + matched := false + for j := range envs { + if envs[j].Name == envar { + matched = true + if envs[j].Value != sshData { + logrus.Infof("Updating %s to %s", envar, sshData) + envs[j].Value = sshData + updated = true + } + } + } + // if no existing env var exists lets create one + if !matched { + e := v1.EnvVar{ + Name: envar, + Value: sshData, + } + containers[i].Env = append(containers[i].Env, e) + updated = true + } + } + } + return updated +} + +// convertToEnvVarName converts the given text into a usable env var +// removing any special chars with '_' +func convertToEnvVarName(text string) string { + var buffer bytes.Buffer + upper := strings.ToUpper(text) + lastCharValid := false + for i := 0; i < len(upper); i++ { + ch := upper[i] + if (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') { + buffer.WriteString(string(ch)) + lastCharValid = true + } else { + if lastCharValid { + buffer.WriteString("_") + } + lastCharValid = false + } + } + return buffer.String() +} + +func convertConfigmapToSHA(cm *v1.ConfigMap) string { + values := []string{} + for k, v := range cm.Data { + values = append(values, k+"="+v) + } + sort.Strings(values) + bytes := []byte(strings.Join(values, ";")) + sha := generateSHA(bytes) + return sha +} + +func convertSecretToSHA(se *v1.Secret) string { + values := []string{} + for k, v := range se.Data { + values = append(values, k+"="+string(v[:])) + } + sort.Strings(values) + bytes := []byte(strings.Join(values, ";")) + sha := generateSHA(bytes) + return sha +} + +func generateSHA(bytes []byte) string { + hasher := sha1.New() + sha, err := hasher.Write(bytes) + if err != nil { + logrus.Fatalf("Error while generating SHA hash of data %v", err) + } + return strconv.Itoa(sha) +} \ No newline at end of file From d9379d18f2fd97353180bcec7236ee6eee73b785 Mon Sep 17 00:00:00 2001 From: faizanahmad055 Date: Tue, 17 Jul 2018 15:20:01 +0500 Subject: [PATCH 02/21] Add seperate annotation for secret --- internal/pkg/controller/controller.go | 2 +- internal/pkg/handler/created-handler.go | 2 +- internal/pkg/handler/updated-handler.go | 86 ++++++++++++++++--------- 3 files changed, 58 insertions(+), 32 deletions(-) diff --git a/internal/pkg/controller/controller.go b/internal/pkg/controller/controller.go index e14ead7e..2a1f72ae 100644 --- a/internal/pkg/controller/controller.go +++ b/internal/pkg/controller/controller.go @@ -66,7 +66,7 @@ func (c *Controller) Update(old interface{}, new interface{}) { // Delete function to add an object to the queue in case of deleting a resource func (c *Controller) Delete(old interface{}) { // TODO Added this function for future usecase - logrus.Infof("Deleted resource has been detected but no further implementation found to take action") + logrus.Infof("Resource deletion has been detected but no further implementation found to take action") } //Run function for controller which handles the queue diff --git a/internal/pkg/handler/created-handler.go b/internal/pkg/handler/created-handler.go index ac612c4b..e61bd192 100644 --- a/internal/pkg/handler/created-handler.go +++ b/internal/pkg/handler/created-handler.go @@ -22,7 +22,7 @@ func (r ResourceCreatedHandler) Handle() error { } else if _, ok := r.Resource.(*v1.Secret); ok { logrus.Infof("Performing 'Added' action for resource of type 'secret'") } else { - logrus.Warnf("Invalid resource: Resource should be 'Secret' or 'Configmap' but found %v", r.Resource) + logrus.Warnf("Invalid resource: Resource should be 'Secret' or 'Configmap' but found, %v", r.Resource) } } return nil diff --git a/internal/pkg/handler/updated-handler.go b/internal/pkg/handler/updated-handler.go index ee48cdff..0eb1ffdd 100644 --- a/internal/pkg/handler/updated-handler.go +++ b/internal/pkg/handler/updated-handler.go @@ -15,7 +15,9 @@ import ( ) const ( - updateOnChangeAnnotation = "reloader.stakater.com/update-on-change" + configmapUpdateOnChangeAnnotation = "reloader.stakater.com/configmap.update-on-change" + // Adding seperate annotation to differentiate between configmap and secret + secretUpdateOnChangeAnnotation = "reloader.stakater.com/secret.update-on-change" ) // ResourceUpdatedHandler contains updated objects @@ -42,7 +44,7 @@ func (r ResourceUpdatedHandler) Handle() error { rollingUpgrade(r, "secrets", "daemonsets") rollingUpgrade(r, "secrets", "statefulSets") } else { - logrus.Warnf("Invalid resource: Resource should be 'Secret' or 'Configmap' but found %v", r.Resource) + logrus.Warnf("Invalid resource: Resource should be 'Secret' or 'Configmap' but found, %v", r.Resource) } } return nil @@ -80,10 +82,17 @@ func rollingUpgradeForDeployment(client kubernetes.Interface, r ResourceUpdatedH if err != nil { logrus.Fatalf("Failed to list deployments %v", err) } + var updateOnChangeAnnotation string + if envName == "_CONFIGMAP" { + updateOnChangeAnnotation = configmapUpdateOnChangeAnnotation + } else if envName == "_SECRET" { + updateOnChangeAnnotation = secretUpdateOnChangeAnnotation + } for _, d := range deployments.Items { containers := d.Spec.Template.Spec.Containers // match deployments with the correct annotation annotationValue := d.ObjectMeta.Annotations[updateOnChangeAnnotation] + if annotationValue != "" { values := strings.Split(annotationValue, ",") matches := false @@ -94,7 +103,7 @@ func rollingUpgradeForDeployment(client kubernetes.Interface, r ResourceUpdatedH } } if matches { - updated := updateContainers(containers, annotationValue, sshData, envName) + updated := updateContainers(containers, name, sshData, envName) if !updated { logrus.Warnf("Rolling upgrade did not happen") @@ -118,10 +127,17 @@ func rollingUpgradeForDaemonSets(client kubernetes.Interface, r ResourceUpdatedH if err != nil { logrus.Fatalf("Failed to list daemonSets %v", err) } + var updateOnChangeAnnotation string + if envName == "_CONFIGMAP" { + updateOnChangeAnnotation = configmapUpdateOnChangeAnnotation + } else if envName == "_SECRET" { + updateOnChangeAnnotation = secretUpdateOnChangeAnnotation + } for _, d := range daemonSets.Items { containers := d.Spec.Template.Spec.Containers // match daemonSets with the correct annotation annotationValue := d.ObjectMeta.Annotations[updateOnChangeAnnotation] + if annotationValue != "" { values := strings.Split(annotationValue, ",") matches := false @@ -132,7 +148,7 @@ func rollingUpgradeForDaemonSets(client kubernetes.Interface, r ResourceUpdatedH } } if matches { - updated := updateContainers(containers, annotationValue, sshData, envName) + updated := updateContainers(containers, name, sshData, envName) if !updated { logrus.Warnf("Rolling upgrade did not happen") @@ -155,10 +171,17 @@ func rollingUpgradeForStatefulSets(client kubernetes.Interface, r ResourceUpdate if err != nil { logrus.Fatalf("Failed to list statefulSets %v", err) } + var updateOnChangeAnnotation string + if envName == "_CONFIGMAP" { + updateOnChangeAnnotation = configmapUpdateOnChangeAnnotation + } else if envName == "_SECRET" { + updateOnChangeAnnotation = secretUpdateOnChangeAnnotation + } for _, d := range statefulSets.Items { containers := d.Spec.Template.Spec.Containers // match statefulSets with the correct annotation annotationValue := d.ObjectMeta.Annotations[updateOnChangeAnnotation] + if annotationValue != "" { values := strings.Split(annotationValue, ",") matches := false @@ -169,7 +192,7 @@ func rollingUpgradeForStatefulSets(client kubernetes.Interface, r ResourceUpdate } } if matches { - updated := updateContainers(containers, annotationValue, sshData, envName) + updated := updateContainers(containers, name, sshData, envName) if !updated { logrus.Warnf("Rolling upgrade did not happen") @@ -187,42 +210,41 @@ func rollingUpgradeForStatefulSets(client kubernetes.Interface, r ResourceUpdate return nil } -func updateContainers(containers []v1.Container, annotationValue, sshData string, resourceType string) bool { - // we can have multiple resourceTypes to update +func updateContainers(containers []v1.Container, annotationValue string, sshData string, resourceType string) bool { updated := false - resourceTypes := strings.Split(annotationValue, ",") - for _, nameToUpdate := range resourceTypes { - envar := "STAKATER_" + convertToEnvVarName(nameToUpdate) + resourceType + envar := "STAKATER_" + convertToEnvVarName(annotationValue) + resourceType + logrus.Infof("Generated environment variable: %s", envar) - for i := range containers { - envs := containers[i].Env - matched := false - for j := range envs { - if envs[j].Name == envar { - matched = true - if envs[j].Value != sshData { - logrus.Infof("Updating %s to %s", envar, sshData) - envs[j].Value = sshData - updated = true - } + for i := range containers { + envs := containers[i].Env + matched := false + for j := range envs { + if envs[j].Name == envar { + matched = true + logrus.Infof("%s environment variable found") + if envs[j].Value != sshData { + logrus.Infof("Updating %s to %s", envar, sshData) + envs[j].Value = sshData + updated = true } } - // if no existing env var exists lets create one - if !matched { - e := v1.EnvVar{ - Name: envar, - Value: sshData, - } - containers[i].Env = append(containers[i].Env, e) - updated = true + } + // if no existing env var exists lets create one + if !matched { + e := v1.EnvVar{ + Name: envar, + Value: sshData, } + containers[i].Env = append(containers[i].Env, e) + updated = true + logrus.Infof("%s environment variable does not found so creating a new one") } } return updated } // convertToEnvVarName converts the given text into a usable env var -// removing any special chars with '_' +// removing any special chars with '_' and transforming text to upper case func convertToEnvVarName(text string) string { var buffer bytes.Buffer upper := strings.ToUpper(text) @@ -243,6 +265,7 @@ func convertToEnvVarName(text string) string { } func convertConfigmapToSHA(cm *v1.ConfigMap) string { + logrus.Infof("Generating SHA for configmap data") values := []string{} for k, v := range cm.Data { values = append(values, k+"="+v) @@ -250,10 +273,12 @@ func convertConfigmapToSHA(cm *v1.ConfigMap) string { sort.Strings(values) bytes := []byte(strings.Join(values, ";")) sha := generateSHA(bytes) + logrus.Infof("SHA for configmap data: %s", sha) return sha } func convertSecretToSHA(se *v1.Secret) string { + logrus.Infof("Generating SHA for secret data") values := []string{} for k, v := range se.Data { values = append(values, k+"="+string(v[:])) @@ -261,6 +286,7 @@ func convertSecretToSHA(se *v1.Secret) string { sort.Strings(values) bytes := []byte(strings.Join(values, ";")) sha := generateSHA(bytes) + logrus.Infof("SHA for secret data: %s", sha) return sha } From 6bc90724189146a846eaa7fc50b3da662f28dc33 Mon Sep 17 00:00:00 2001 From: faizanahmad055 Date: Tue, 17 Jul 2018 16:43:09 +0500 Subject: [PATCH 03/21] Fix SHA generation --- internal/pkg/handler/updated-handler.go | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/internal/pkg/handler/updated-handler.go b/internal/pkg/handler/updated-handler.go index 0eb1ffdd..f3f0a3f3 100644 --- a/internal/pkg/handler/updated-handler.go +++ b/internal/pkg/handler/updated-handler.go @@ -1,10 +1,10 @@ package handler import ( + "io" "sort" "strings" "crypto/sha1" - "strconv" "bytes" "github.com/sirupsen/logrus" @@ -271,9 +271,8 @@ func convertConfigmapToSHA(cm *v1.ConfigMap) string { values = append(values, k+"="+v) } sort.Strings(values) - bytes := []byte(strings.Join(values, ";")) - sha := generateSHA(bytes) - logrus.Infof("SHA for configmap data: %s", sha) + sha := generateSHA(strings.Join(values, ";")) + logrus.Infof("SHA for configmap data: %x", sha) return sha } @@ -284,17 +283,14 @@ func convertSecretToSHA(se *v1.Secret) string { values = append(values, k+"="+string(v[:])) } sort.Strings(values) - bytes := []byte(strings.Join(values, ";")) - sha := generateSHA(bytes) - logrus.Infof("SHA for secret data: %s", sha) + sha := generateSHA(strings.Join(values, ";")) + logrus.Infof("SHA for secret data: %x", sha) return sha } -func generateSHA(bytes []byte) string { +func generateSHA(data string) string { hasher := sha1.New() - sha, err := hasher.Write(bytes) - if err != nil { - logrus.Fatalf("Error while generating SHA hash of data %v", err) - } - return strconv.Itoa(sha) + io.WriteString(hasher, data) + sha := hasher.Sum(nil) + return string(sha[:]) } \ No newline at end of file From f05d4ed0eb58a90ef443a928994234364b89d89d Mon Sep 17 00:00:00 2001 From: faizanahmad055 Date: Tue, 17 Jul 2018 16:55:54 +0500 Subject: [PATCH 04/21] Fix testcase with updated annotation --- internal/pkg/controller/controller_test.go | 2 +- internal/pkg/handler/updated-handler.go | 15 +++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/internal/pkg/controller/controller_test.go b/internal/pkg/controller/controller_test.go index 0035d0fb..82d65fb7 100644 --- a/internal/pkg/controller/controller_test.go +++ b/internal/pkg/controller/controller_test.go @@ -172,7 +172,7 @@ func initDeployment(namespace string, deploymentName string) *v1beta1.Deployment Name: deploymentName, Namespace: namespace, Labels: map[string]string{"firstLabel": "temp"}, - Annotations: map[string]string{"reloader.stakater.com/update-on-change": deploymentName}, + Annotations: map[string]string{"reloader.stakater.com/configmap.update-on-change": deploymentName}, }, Spec: v1beta1.DeploymentSpec{ Replicas: &replicaset, diff --git a/internal/pkg/handler/updated-handler.go b/internal/pkg/handler/updated-handler.go index f3f0a3f3..d589383e 100644 --- a/internal/pkg/handler/updated-handler.go +++ b/internal/pkg/handler/updated-handler.go @@ -1,17 +1,17 @@ package handler import ( + "bytes" + "crypto/sha1" "io" "sort" "strings" - "crypto/sha1" - "bytes" "github.com/sirupsen/logrus" "github.com/stakater/Reloader/pkg/kube" "k8s.io/api/core/v1" - "k8s.io/client-go/kubernetes" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" ) const ( @@ -50,7 +50,7 @@ func (r ResourceUpdatedHandler) Handle() error { return nil } -func rollingUpgrade(r ResourceUpdatedHandler, resourceType string, rollingUpgradeType string){ +func rollingUpgrade(r ResourceUpdatedHandler, resourceType string, rollingUpgradeType string) { client, err := kube.GetClient() if err != nil { logrus.Fatalf("Unable to create Kubernetes client error = %v", err) @@ -92,7 +92,7 @@ func rollingUpgradeForDeployment(client kubernetes.Interface, r ResourceUpdatedH containers := d.Spec.Template.Spec.Containers // match deployments with the correct annotation annotationValue := d.ObjectMeta.Annotations[updateOnChangeAnnotation] - + if annotationValue != "" { values := strings.Split(annotationValue, ",") matches := false @@ -121,7 +121,6 @@ func rollingUpgradeForDeployment(client kubernetes.Interface, r ResourceUpdatedH return nil } - func rollingUpgradeForDaemonSets(client kubernetes.Interface, r ResourceUpdatedHandler, namespace string, name string, sshData string, envName string) error { daemonSets, err := client.ExtensionsV1beta1().DaemonSets(namespace).List(meta_v1.ListOptions{}) if err != nil { @@ -181,7 +180,7 @@ func rollingUpgradeForStatefulSets(client kubernetes.Interface, r ResourceUpdate containers := d.Spec.Template.Spec.Containers // match statefulSets with the correct annotation annotationValue := d.ObjectMeta.Annotations[updateOnChangeAnnotation] - + if annotationValue != "" { values := strings.Split(annotationValue, ",") matches := false @@ -293,4 +292,4 @@ func generateSHA(data string) string { io.WriteString(hasher, data) sha := hasher.Sum(nil) return string(sha[:]) -} \ No newline at end of file +} From a3f8f30a6fcc80d22f51b562a7153d7ce3b4a57c Mon Sep 17 00:00:00 2001 From: faizanahmad055 Date: Tue, 17 Jul 2018 17:20:13 +0500 Subject: [PATCH 05/21] Update readme with secret annotation --- .gitignore | 1 + README.md | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 234d3a6d..d54621ba 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ release out/ _gopath/ .DS_Store +.vscode vendor \ No newline at end of file diff --git a/README.md b/README.md index 63e2d63a..725a6ba1 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,11 @@ ## WHY NAME RELOADER -In english language, Reloader is a thing/tool that can reload certain stuff. So refereig to that meaning relaoder can reload +In english language, Reloader is a thing/tool that can reload certain stuff. So referring to that meaning reloader can reload ## Problem -We would like to watch if some change happens in `ConfigMap` and `Secret` objects and then perform certain upgrade on relavent `Deployment`, `Deamonset` and `Statefulset` +We would like to watch if some change happens in `ConfigMap` and `Secret` objects and then perform certain upgrade on relevant `Deployment`, `Deamonset` and `Statefulset` ## Solution @@ -21,10 +21,20 @@ For a `Deployment` called `foo` have a `ConfigMap` called `foo`. Then add this a ```yaml metadata: annotations: - reloader.stakater.com/update-on-change: "foo" + reloader.stakater.com/configmap.update-on-change: "foo" ``` -Then, providing `Reloader` is running, whenever you edit the `ConfigMap` called `foo` the Reloader will update the `Deployment` by adding the environment variable: +OR + +For a `Deployment` called `foo` have a `Secret` called `foo`. Then add this annotation to your `Deployment` + +```yaml +metadata: + annotations: + reloader.stakater.com/secret.update-on-change: "foo" +``` + +Then, providing `Reloader` is running, whenever you edit the `ConfigMap` or `Secret` called `foo` the Reloader will update the `Deployment` by adding the environment variable: ``` STAKATER_FOO_REVISION=${reloaderRevision} From a317555db9309fb14a14994937b9786a0789be58 Mon Sep 17 00:00:00 2001 From: faizanahmad055 Date: Wed, 18 Jul 2018 16:19:37 +0500 Subject: [PATCH 06/21] Add helper class and complete integration test --- internal/pkg/controller/controller_test.go | 104 ++++++++++++++++++--- internal/pkg/handler/updated-handler.go | 69 ++------------ internal/pkg/helper/helper.go | 68 ++++++++++++++ 3 files changed, 168 insertions(+), 73 deletions(-) create mode 100644 internal/pkg/helper/helper.go diff --git a/internal/pkg/controller/controller_test.go b/internal/pkg/controller/controller_test.go index 82d65fb7..2c1e1c8e 100644 --- a/internal/pkg/controller/controller_test.go +++ b/internal/pkg/controller/controller_test.go @@ -2,10 +2,12 @@ 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" @@ -14,9 +16,11 @@ import ( ) var ( - configmapNamePrefix = "testconfigmap-reloader" - secretNamePrefix = "testsecret-reloader" - letters = []rune("abcdefghijklmnopqrstuvwxyz") + 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" ) func randSeq(n int) string { @@ -36,9 +40,11 @@ func TestControllerForUpdatingConfigmapShouldUpdateDeployment(t *testing.T) { return } namespace := "test-reloader" + logrus.Infof("Step 1: Create namespace") createNamespace(t, namespace, client) defer deleteNamespace(t, namespace, client) + logrus.Infof("Step 2: Create controller") controller, err := NewController(client, "configMaps", namespace) if err != nil { logrus.Errorf("Unable to create NewController error = %v", err) @@ -46,41 +52,68 @@ func TestControllerForUpdatingConfigmapShouldUpdateDeployment(t *testing.T) { } stop := make(chan struct{}) defer close(stop) + logrus.Infof("Step 3: Start controller") go controller.Run(1, stop) time.Sleep(10 * time.Second) configmapName := configmapNamePrefix + "-update-" + randSeq(5) configmapClient := client.CoreV1().ConfigMaps(namespace) + + logrus.Infof("Step 4: Create configmap") _, err = configmapClient.Create(initConfigmap(namespace, configmapName)) if err != nil { logrus.Fatalf("Fatal error in configmap creation: %v", err) } logrus.Infof("Created Configmap %q.\n", configmapName) time.Sleep(10 * time.Second) - deployment := createDeployement(configmapName, namespace, client) + logrus.Infof("Step 5: Create Deployment") + deployment := createDeployment(configmapName, namespace, client) + + logrus.Infof("Step 6: Update configmap for first time") logrus.Infof("Updating Configmap %q.\n", configmapName) _, err = configmapClient.Get(configmapName, metav1.GetOptions{}) if err != nil { logrus.Errorf("Error while getting configmap %v", err) } - _, updateErr := configmapClient.Update(updateConfigmap(namespace, configmapName)) - - // TODO: Add functionality to verify reloader functionality here + _, updateErr := configmapClient.Update(updateConfigmap(namespace, configmapName, "www.stakater.com")) if updateErr != nil { err = controller.client.CoreV1().ConfigMaps(namespace).Delete(configmapName, &metav1.DeleteOptions{}) if err != nil { logrus.Errorf("Error while deleting the configmap %v", err) } - logrus.Fatalf("Fatal error in configmap update: %v", updateErr) + t.Errorf("Configmap was not updated") } time.Sleep(10 * time.Second) + + logrus.Infof("Step 7: Verify deployment update for first time") + + updated := verifyDeploymentUpdate(client, namespace, configmapName, "_CONFIGMAP", "www.stakater.com") + 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")) + time.Sleep(10 * time.Second) + + logrus.Infof("Step 9: Verify deployment update for second time") + updated = verifyDeploymentUpdate(client, namespace, configmapName, "_CONFIGMAP", "aurorasolutions.io") + if !updated { + t.Errorf("Deployment was not updated") + } + time.Sleep(10 * time.Second) + + logrus.Infof("Step 10: Delete Deployment") 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.Infof("Step 11: Delete Configmap") logrus.Infof("Deleting Configmap %q.\n", configmapName) err = controller.client.CoreV1().ConfigMaps(namespace).Delete(configmapName, &metav1.DeleteOptions{}) if err != nil { @@ -89,7 +122,55 @@ func TestControllerForUpdatingConfigmapShouldUpdateDeployment(t *testing.T) { time.Sleep(15 * time.Second) } -func createDeployement(deploymentName string, namespace string, client kubernetes.Interface) *v1beta1.Deployment { +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, err := deploymentClient.Create(deployment) @@ -223,6 +304,7 @@ func createNamespace(t *testing.T, namespace string, client kubernetes.Interface } 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) @@ -231,14 +313,14 @@ func deleteNamespace(t *testing.T, namespace string, client kubernetes.Interface } } -func updateConfigmap(namespace string, configmapName string) *v1.ConfigMap { +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": "www.stakater.com"}, + Data: map[string]string{"test.url": testData}, } } diff --git a/internal/pkg/handler/updated-handler.go b/internal/pkg/handler/updated-handler.go index d589383e..5d5ce225 100644 --- a/internal/pkg/handler/updated-handler.go +++ b/internal/pkg/handler/updated-handler.go @@ -1,13 +1,10 @@ package handler import ( - "bytes" - "crypto/sha1" - "io" - "sort" "strings" "github.com/sirupsen/logrus" + helper "github.com/stakater/Reloader/internal/pkg/helper" "github.com/stakater/Reloader/pkg/kube" "k8s.io/api/core/v1" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -16,7 +13,7 @@ import ( const ( configmapUpdateOnChangeAnnotation = "reloader.stakater.com/configmap.update-on-change" - // Adding seperate annotation to differentiate between configmap and secret + // Adding separate annotation to differentiate between configmap and secret secretUpdateOnChangeAnnotation = "reloader.stakater.com/secret.update-on-change" ) @@ -59,12 +56,12 @@ func rollingUpgrade(r ResourceUpdatedHandler, resourceType string, rollingUpgrad if resourceType == "configmaps" { namespace = r.Resource.(*v1.ConfigMap).Namespace name = r.Resource.(*v1.ConfigMap).Name - sshData = convertConfigmapToSHA(r.Resource.(*v1.ConfigMap)) + sshData = 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 = convertSecretToSHA(r.Resource.(*v1.Secret)) + sshData = helper.ConvertSecretToSHA(r.Resource.(*v1.Secret)) envName = "_SECRET" } @@ -211,7 +208,7 @@ func rollingUpgradeForStatefulSets(client kubernetes.Interface, r ResourceUpdate func updateContainers(containers []v1.Container, annotationValue string, sshData string, resourceType string) bool { updated := false - envar := "STAKATER_" + convertToEnvVarName(annotationValue) + resourceType + envar := "STAKATER_" + helper.ConvertToEnvVarName(annotationValue) + resourceType logrus.Infof("Generated environment variable: %s", envar) for i := range containers { @@ -220,7 +217,7 @@ func updateContainers(containers []v1.Container, annotationValue string, sshData for j := range envs { if envs[j].Name == envar { matched = true - logrus.Infof("%s environment variable found") + logrus.Infof("%s environment variable found", envar) if envs[j].Value != sshData { logrus.Infof("Updating %s to %s", envar, sshData) envs[j].Value = sshData @@ -236,60 +233,8 @@ func updateContainers(containers []v1.Container, annotationValue string, sshData } containers[i].Env = append(containers[i].Env, e) updated = true - logrus.Infof("%s environment variable does not found so creating a new one") + logrus.Infof("%s environment variable does not found, creating a new env with value %s", envar, sshData) } } return updated } - -// convertToEnvVarName converts the given text into a usable env var -// removing any special chars with '_' and transforming text to upper case -func convertToEnvVarName(text string) string { - var buffer bytes.Buffer - upper := strings.ToUpper(text) - lastCharValid := false - for i := 0; i < len(upper); i++ { - ch := upper[i] - if (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') { - buffer.WriteString(string(ch)) - lastCharValid = true - } else { - if lastCharValid { - buffer.WriteString("_") - } - lastCharValid = false - } - } - return buffer.String() -} - -func convertConfigmapToSHA(cm *v1.ConfigMap) string { - logrus.Infof("Generating SHA for configmap data") - values := []string{} - for k, v := range cm.Data { - values = append(values, k+"="+v) - } - sort.Strings(values) - sha := generateSHA(strings.Join(values, ";")) - logrus.Infof("SHA for configmap data: %x", sha) - return sha -} - -func convertSecretToSHA(se *v1.Secret) string { - logrus.Infof("Generating SHA for secret data") - values := []string{} - for k, v := range se.Data { - values = append(values, k+"="+string(v[:])) - } - sort.Strings(values) - sha := generateSHA(strings.Join(values, ";")) - logrus.Infof("SHA for secret data: %x", sha) - return sha -} - -func generateSHA(data string) string { - hasher := sha1.New() - io.WriteString(hasher, data) - sha := hasher.Sum(nil) - return string(sha[:]) -} diff --git a/internal/pkg/helper/helper.go b/internal/pkg/helper/helper.go new file mode 100644 index 00000000..333ca4e6 --- /dev/null +++ b/internal/pkg/helper/helper.go @@ -0,0 +1,68 @@ +package handler + +import ( + "bytes" + "crypto/sha1" + "fmt" + "io" + "sort" + "strings" + + "github.com/sirupsen/logrus" + "k8s.io/api/core/v1" +) + +// ConvertToEnvVarName converts the given text into a usable env var +// removing any special chars with '_' and transforming text to upper case +func ConvertToEnvVarName(text string) string { + var buffer bytes.Buffer + upper := strings.ToUpper(text) + lastCharValid := false + for i := 0; i < len(upper); i++ { + ch := upper[i] + if (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') { + buffer.WriteString(string(ch)) + lastCharValid = true + } else { + if lastCharValid { + buffer.WriteString("_") + } + lastCharValid = false + } + } + return buffer.String() +} + +// ConvertConfigmapToSHA generates SHA for configmap data +func ConvertConfigmapToSHA(cm *v1.ConfigMap) string { + logrus.Infof("Generating SHA for configmap data") + values := []string{} + for k, v := range cm.Data { + values = append(values, k+"="+v) + } + sort.Strings(values) + sha := GenerateSHA(strings.Join(values, ";")) + logrus.Infof("SHA for configmap data: %s", sha) + return sha +} + +// ConvertSecretToSHA generates SHA for secret data +func ConvertSecretToSHA(se *v1.Secret) string { + logrus.Infof("Generating SHA for secret data") + values := []string{} + for k, v := range se.Data { + values = append(values, k+"="+string(v[:])) + } + sort.Strings(values) + sha := GenerateSHA(strings.Join(values, ";")) + logrus.Infof("SHA for secret data: %s", sha) + return sha +} + +// GenerateSHA generates SHA from string +func GenerateSHA(data string) string { + hasher := sha1.New() + io.WriteString(hasher, data) + sha := hasher.Sum(nil) + return fmt.Sprintf("%x", sha) +} From f89f59c5b26931e4298abd3e0909d879d7e080a4 Mon Sep 17 00:00:00 2001 From: faizanahmad055 Date: Wed, 18 Jul 2018 19:56:12 +0500 Subject: [PATCH 07/21] Fix rbac permission issue --- .../kubernetes/chart/reloader/templates/rbac.yaml | 14 ++++++++++++++ internal/pkg/handler/created-handler.go | 4 ++-- internal/pkg/handler/updated-handler.go | 13 +++++++------ 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/deployments/kubernetes/chart/reloader/templates/rbac.yaml b/deployments/kubernetes/chart/reloader/templates/rbac.yaml index 678725d0..88c2cbb5 100644 --- a/deployments/kubernetes/chart/reloader/templates/rbac.yaml +++ b/deployments/kubernetes/chart/reloader/templates/rbac.yaml @@ -24,6 +24,20 @@ rules: - list - get - watch + - apiGroups: + - "" + - "extensions" + - "apps" + resources: + - deployments + - daemonsets + - statefulsets + verbs: + - list + - get + - update + - patch + - watch --- apiVersion: rbac.authorization.k8s.io/v1beta1 kind: RoleBinding diff --git a/internal/pkg/handler/created-handler.go b/internal/pkg/handler/created-handler.go index e61bd192..67d1debc 100644 --- a/internal/pkg/handler/created-handler.go +++ b/internal/pkg/handler/created-handler.go @@ -18,9 +18,9 @@ func (r ResourceCreatedHandler) Handle() error { logrus.Infof("Detected changes in object %s", r.Resource) // process resource based on its type if _, ok := r.Resource.(*v1.ConfigMap); ok { - logrus.Infof("Performing 'Added' action for resource of type 'configmap'") + logrus.Infof("A 'configmap' has been 'Added' but no implementation found to take action") } else if _, ok := r.Resource.(*v1.Secret); ok { - logrus.Infof("Performing 'Added' action for resource of type 'secret'") + logrus.Infof("A 'secret' has been 'Added' but no implementation found to take action") } else { logrus.Warnf("Invalid resource: Resource should be 'Secret' or 'Configmap' but found, %v", r.Resource) } diff --git a/internal/pkg/handler/updated-handler.go b/internal/pkg/handler/updated-handler.go index 5d5ce225..428bf713 100644 --- a/internal/pkg/handler/updated-handler.go +++ b/internal/pkg/handler/updated-handler.go @@ -66,15 +66,15 @@ func rollingUpgrade(r ResourceUpdatedHandler, resourceType string, rollingUpgrad } if rollingUpgradeType == "deployments" { - rollingUpgradeForDeployment(client, r, namespace, name, sshData, envName) + rollingUpgradeForDeployment(client, namespace, name, sshData, envName) } else if rollingUpgradeType == "daemonsets" { - rollingUpgradeForDaemonSets(client, r, namespace, name, sshData, envName) + rollingUpgradeForDaemonSets(client, namespace, name, sshData, envName) } else if rollingUpgradeType == "statefulSets" { - rollingUpgradeForStatefulSets(client, r, namespace, name, sshData, envName) + rollingUpgradeForStatefulSets(client, namespace, name, sshData, envName) } } -func rollingUpgradeForDeployment(client kubernetes.Interface, r ResourceUpdatedHandler, namespace string, name string, sshData string, envName string) error { +func rollingUpgradeForDeployment(client kubernetes.Interface, namespace string, name string, sshData 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) @@ -118,11 +118,12 @@ func rollingUpgradeForDeployment(client kubernetes.Interface, r ResourceUpdatedH return nil } -func rollingUpgradeForDaemonSets(client kubernetes.Interface, r ResourceUpdatedHandler, namespace string, name string, sshData string, envName string) error { +func rollingUpgradeForDaemonSets(client kubernetes.Interface, namespace string, name string, sshData 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) } + var updateOnChangeAnnotation string if envName == "_CONFIGMAP" { updateOnChangeAnnotation = configmapUpdateOnChangeAnnotation @@ -162,7 +163,7 @@ func rollingUpgradeForDaemonSets(client kubernetes.Interface, r ResourceUpdatedH return nil } -func rollingUpgradeForStatefulSets(client kubernetes.Interface, r ResourceUpdatedHandler, namespace string, name string, sshData string, envName string) error { +func rollingUpgradeForStatefulSets(client kubernetes.Interface, namespace string, name string, sshData 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) From 17b6d58300acbc5ec980675c68288c66cc6786b9 Mon Sep 17 00:00:00 2001 From: faizanahmad055 Date: Thu, 19 Jul 2018 18:04:07 +0500 Subject: [PATCH 08/21] 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 "" +} From 97b7286c2be9f6da44916620995332507e47d586 Mon Sep 17 00:00:00 2001 From: faizanahmad055 Date: Thu, 19 Jul 2018 18:08:49 +0500 Subject: [PATCH 09/21] Fix helper package error --- internal/pkg/controller/controller_test.go | 2 +- internal/pkg/handler/updated-handler.go | 2 +- internal/pkg/handlerTester/updated-handler_test.go | 2 +- internal/pkg/helper/helper.go | 2 +- internal/pkg/helper/testUtils.go | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/pkg/controller/controller_test.go b/internal/pkg/controller/controller_test.go index d608bc24..28b4c33e 100644 --- a/internal/pkg/controller/controller_test.go +++ b/internal/pkg/controller/controller_test.go @@ -5,7 +5,7 @@ import ( "time" "github.com/sirupsen/logrus" - helper "github.com/stakater/Reloader/internal/pkg/helper" + "github.com/stakater/Reloader/internal/pkg/helper" "github.com/stakater/Reloader/pkg/kube" "k8s.io/api/extensions/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" diff --git a/internal/pkg/handler/updated-handler.go b/internal/pkg/handler/updated-handler.go index 361e1146..ed306a76 100644 --- a/internal/pkg/handler/updated-handler.go +++ b/internal/pkg/handler/updated-handler.go @@ -4,7 +4,7 @@ import ( "strings" "github.com/sirupsen/logrus" - helper "github.com/stakater/Reloader/internal/pkg/helper" + "github.com/stakater/Reloader/internal/pkg/helper" "github.com/stakater/Reloader/pkg/kube" "k8s.io/api/core/v1" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" diff --git a/internal/pkg/handlerTester/updated-handler_test.go b/internal/pkg/handlerTester/updated-handler_test.go index ec01a846..1beb6efd 100644 --- a/internal/pkg/handlerTester/updated-handler_test.go +++ b/internal/pkg/handlerTester/updated-handler_test.go @@ -8,7 +8,7 @@ import ( "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/internal/pkg/helper" "github.com/stakater/Reloader/pkg/kube" v1_beta1 "k8s.io/api/apps/v1beta1" "k8s.io/api/extensions/v1beta1" diff --git a/internal/pkg/helper/helper.go b/internal/pkg/helper/helper.go index 333ca4e6..41c40d22 100644 --- a/internal/pkg/helper/helper.go +++ b/internal/pkg/helper/helper.go @@ -1,4 +1,4 @@ -package handler +package helper import ( "bytes" diff --git a/internal/pkg/helper/testUtils.go b/internal/pkg/helper/testUtils.go index 80e2ed35..82710d19 100644 --- a/internal/pkg/helper/testUtils.go +++ b/internal/pkg/helper/testUtils.go @@ -1,4 +1,4 @@ -package handler +package helper import ( "math/rand" From 5befb4d6eb643c77cf4bd581be314b924c2f09ac Mon Sep 17 00:00:00 2001 From: faizanahmad055 Date: Thu, 19 Jul 2018 18:18:51 +0500 Subject: [PATCH 10/21] Optimize rollingUpgrade method --- internal/pkg/handler/updated-handler.go | 38 +++++++++++-------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/internal/pkg/handler/updated-handler.go b/internal/pkg/handler/updated-handler.go index ed306a76..4ede66fd 100644 --- a/internal/pkg/handler/updated-handler.go +++ b/internal/pkg/handler/updated-handler.go @@ -30,47 +30,41 @@ func (r ResourceUpdatedHandler) Handle() error { } else { logrus.Infof("Detected changes in object %s", r.Resource) // process resource based on its type - if _, ok := r.Resource.(*v1.ConfigMap); ok { - logrus.Infof("Performing 'Updated' action for resource of type 'configmap'") - rollingUpgrade(r, "configmaps", "deployments") - rollingUpgrade(r, "configmaps", "daemonsets") - rollingUpgrade(r, "configmaps", "statefulSets") - } else if _, ok := r.Resource.(*v1.Secret); ok { - logrus.Infof("Performing 'Updated' action for resource of type 'secret'") - rollingUpgrade(r, "secrets", "deployments") - rollingUpgrade(r, "secrets", "daemonsets") - rollingUpgrade(r, "secrets", "statefulSets") - } else { - logrus.Warnf("Invalid resource: Resource should be 'Secret' or 'Configmap' but found, %v", r.Resource) - } + rollingUpgrade(r, "deployments") + rollingUpgrade(r, "daemonsets") + rollingUpgrade(r, "statefulSets") } return nil } -func rollingUpgrade(r ResourceUpdatedHandler, resourceType string, rollingUpgradeType string) { +func rollingUpgrade(r ResourceUpdatedHandler, rollingUpgradeType string) { client, err := kube.GetClient() if err != nil { logrus.Fatalf("Unable to create Kubernetes client error = %v", err) } - var namespace, name, shaData, envName string - if resourceType == "configmaps" { + var namespace, name, shaData, envNamePostfix string + if _, ok := r.Resource.(*v1.ConfigMap); ok { + logrus.Infof("Performing 'Updated' action for resource of type 'configmap'") namespace = r.Resource.(*v1.ConfigMap).Namespace name = r.Resource.(*v1.ConfigMap).Name shaData = helper.ConvertConfigmapToSHA(r.Resource.(*v1.ConfigMap)) - envName = "_CONFIGMAP" - } else if resourceType == "secrets" { + envNamePostfix = "_CONFIGMAP" + } else if _, ok := r.Resource.(*v1.Secret); ok { + logrus.Infof("Performing 'Updated' action for resource of type 'secret'") namespace = r.Resource.(*v1.Secret).Namespace name = r.Resource.(*v1.Secret).Name shaData = helper.ConvertSecretToSHA(r.Resource.(*v1.Secret)) - envName = "_SECRET" + envNamePostfix = "_SECRET" + } else { + logrus.Warnf("Invalid resource: Resource should be 'Secret' or 'Configmap' but found, %v", r.Resource) } if rollingUpgradeType == "deployments" { - RollingUpgradeForDeployment(client, namespace, name, shaData, envName) + RollingUpgradeForDeployment(client, namespace, name, shaData, envNamePostfix) } else if rollingUpgradeType == "daemonsets" { - RollingUpgradeForDaemonSets(client, namespace, name, shaData, envName) + RollingUpgradeForDaemonSets(client, namespace, name, shaData, envNamePostfix) } else if rollingUpgradeType == "statefulSets" { - RollingUpgradeForStatefulSets(client, namespace, name, shaData, envName) + RollingUpgradeForStatefulSets(client, namespace, name, shaData, envNamePostfix) } } From 81c7b3ef25afc38eeb37fea0b172da7838f7500b Mon Sep 17 00:00:00 2001 From: faizanahmad055 Date: Mon, 23 Jul 2018 15:24:52 +0500 Subject: [PATCH 11/21] Implement PR-2 review comments --- internal/pkg/common/common.go | 56 +++ internal/pkg/common/common_test.go | 21 + internal/pkg/controller/controller_test.go | 441 +++++++++++++---- internal/pkg/crypto/sha.go | 20 + internal/pkg/crypto/sha_test.go | 15 + .../handler/{created-handler.go => create.go} | 0 internal/pkg/handler/update.go | 226 +++++++++ internal/pkg/handler/update_test.go | 223 +++++++++ internal/pkg/handler/updated-handler.go | 241 ---------- .../pkg/handlerTester/updated-handler_test.go | 261 ---------- internal/pkg/helper/helper.go | 68 --- internal/pkg/helper/testUtils.go | 310 ------------ internal/pkg/testutil/kube.go | 447 ++++++++++++++++++ 13 files changed, 1351 insertions(+), 978 deletions(-) create mode 100644 internal/pkg/common/common.go create mode 100644 internal/pkg/common/common_test.go create mode 100644 internal/pkg/crypto/sha.go create mode 100644 internal/pkg/crypto/sha_test.go rename internal/pkg/handler/{created-handler.go => create.go} (100%) create mode 100644 internal/pkg/handler/update.go create mode 100644 internal/pkg/handler/update_test.go delete mode 100644 internal/pkg/handler/updated-handler.go delete mode 100644 internal/pkg/handlerTester/updated-handler_test.go delete mode 100644 internal/pkg/helper/helper.go delete mode 100644 internal/pkg/helper/testUtils.go create mode 100644 internal/pkg/testutil/kube.go diff --git a/internal/pkg/common/common.go b/internal/pkg/common/common.go new file mode 100644 index 00000000..2030d233 --- /dev/null +++ b/internal/pkg/common/common.go @@ -0,0 +1,56 @@ +package common + +import ( + "bytes" + "math/rand" + "strings" + "time" +) + +var ( + letters = []rune("abcdefghijklmnopqrstuvwxyz") +) + +const ( + // ConfigmapUpdateOnChangeAnnotation is an annotation to detect changes in configmaps + ConfigmapUpdateOnChangeAnnotation = "configmap.reloader.stakater.com/reload" + // SecretUpdateOnChangeAnnotation is an annotation to detect changes in secrets + SecretUpdateOnChangeAnnotation = "secret.reloader.stakater.com/reload" + // ConfigmapEnvarPostfix is a postfix for configmap envVar + ConfigmapEnvarPostfix = "_CONFIGMAP" + // SecretEnvarPostfix is a postfix for secret envVar + SecretEnvarPostfix = "_SECRET" + // EnvVarPrefix is a Prefix for environment variable + EnvVarPrefix = "STAKATER_" +) + +// ConvertToEnvVarName converts the given text into a usable env var +// removing any special chars with '_' and transforming text to upper case +func ConvertToEnvVarName(text string) string { + var buffer bytes.Buffer + upper := strings.ToUpper(text) + lastCharValid := false + for i := 0; i < len(upper); i++ { + ch := upper[i] + if (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') { + buffer.WriteString(string(ch)) + lastCharValid = true + } else { + if lastCharValid { + buffer.WriteString("_") + } + lastCharValid = false + } + } + return buffer.String() +} + +// 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) +} diff --git a/internal/pkg/common/common_test.go b/internal/pkg/common/common_test.go new file mode 100644 index 00000000..7216cddd --- /dev/null +++ b/internal/pkg/common/common_test.go @@ -0,0 +1,21 @@ +package common + +import ( + "testing" +) + +func TestConvertToEnvVarName(t *testing.T) { + data := "www.stakater.com" + envVar := ConvertToEnvVarName(data) + if envVar != "WWW_STAKATER_COM" { + t.Errorf("Failed to convert data into environment variable") + } +} + +func TestRandSeq(t *testing.T) { + data := RandSeq(5) + newData := RandSeq(5) + if data == newData { + t.Errorf("Random sequence generator does not work correctly") + } +} diff --git a/internal/pkg/controller/controller_test.go b/internal/pkg/controller/controller_test.go index 28b4c33e..92780d89 100644 --- a/internal/pkg/controller/controller_test.go +++ b/internal/pkg/controller/controller_test.go @@ -1,68 +1,74 @@ package controller import ( + "os" "testing" "time" "github.com/sirupsen/logrus" - "github.com/stakater/Reloader/internal/pkg/helper" + "github.com/stakater/Reloader/internal/pkg/common" + "github.com/stakater/Reloader/internal/pkg/testutil" "github.com/stakater/Reloader/pkg/kube" - "k8s.io/api/extensions/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" ) var ( + client = getClient() + namespace = "test-reloader" configmapNamePrefix = "testconfigmap-reloader" secretNamePrefix = "testsecret-reloader" ) -// Creating a Controller to do a rolling upgrade upon updating the configmap or secret -func TestControllerForUpdatingConfigmapShouldUpdateDeployment(t *testing.T) { - client, err := kube.GetClient() - if err != nil { - logrus.Errorf("Unable to create Kubernetes client error = %v", err) - return - } - namespace := "test-reloader" - logrus.Infof("Step 1: Create namespace") - helper.CreateNamespace(namespace, client) - defer helper.DeleteNamespace(namespace, client) +func TestMain(m *testing.M) { - logrus.Infof("Step 2: Create controller") - controller, err := NewController(client, "configMaps", namespace) + logrus.Infof("Creating namespace %s", namespace) + testutil.CreateNamespace(namespace, client) + + logrus.Infof("Running Testcases") + retCode := m.Run() + + logrus.Infof("Deleting namespace %q.\n", namespace) + testutil.DeleteNamespace(namespace, client) + + 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 +} + +// Perform rolling upgrade on deployment and create env var upon updating the configmap +func TestControllerUpdatingConfigmapShouldCreateEnvInDeployment(t *testing.T) { + // Creating Controller + logrus.Infof("Creating controller") + controller, err := NewController(client, testutil.ConfigmapResourceType, namespace) if err != nil { logrus.Errorf("Unable to create NewController error = %v", err) return } stop := make(chan struct{}) defer close(stop) - logrus.Infof("Step 3: Start controller") + logrus.Infof("Starting controller") go controller.Run(1, stop) time.Sleep(10 * time.Second) - configmapName := configmapNamePrefix + "-update-" + helper.RandSeq(5) - configmapClient := client.CoreV1().ConfigMaps(namespace) - - logrus.Infof("Step 4: Create configmap") - _, err = configmapClient.Create(helper.GetConfigmap(namespace, configmapName, "www.google.com")) + // Creating configmap + configmapName := configmapNamePrefix + "-update-" + common.RandSeq(5) + configmapClient, err := testutil.CreateConfigMap(client, namespace, configmapName, "www.google.com") if err != nil { - t.Errorf("Error in configmap creation: %v", err) + t.Errorf("Error while creating the configmap %v", err) } - logrus.Infof("Created Configmap %q.\n", configmapName) - time.Sleep(10 * time.Second) - logrus.Infof("Step 5: Create Deployment") - deployment := createDeployment(configmapName, namespace, client) - - logrus.Infof("Step 6: Update configmap for first time") - logrus.Infof("Updating Configmap %q.\n", configmapName) - _, err = configmapClient.Get(configmapName, metav1.GetOptions{}) - if err != nil { - logrus.Errorf("Error while getting configmap %v", err) - } - _, updateErr := configmapClient.Update(helper.GetConfigmap(namespace, configmapName, "www.stakater.com")) + // Creating deployment + testutil.CreateDeployment(client, configmapName, namespace) + // Updating configmap for first time + updateErr := testutil.UpdateConfigMap(configmapClient, namespace, configmapName, "", "www.stakater.com") if updateErr != nil { err = controller.client.CoreV1().ConfigMaps(namespace).Delete(configmapName, &metav1.DeleteOptions{}) if err != nil { @@ -70,66 +76,169 @@ func TestControllerForUpdatingConfigmapShouldUpdateDeployment(t *testing.T) { } t.Errorf("Configmap was not updated") } - time.Sleep(10 * time.Second) - logrus.Infof("Step 7: Verify deployment update for first time") - shaData := helper.ConvertConfigmapToSHA(helper.GetConfigmap(namespace, configmapName, "www.stakater.com")) - updated := helper.VerifyDeploymentUpdate(client, namespace, configmapName, "_CONFIGMAP", shaData) + // Verifying deployment update + logrus.Infof("Verifying env var has been created") + shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "www.stakater.com") + updated := testutil.VerifyDeploymentUpdate(client, namespace, configmapName, common.ConfigmapEnvarPostfix, shaData, common.ConfigmapUpdateOnChangeAnnotation) 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(helper.GetConfigmap(namespace, configmapName, "aurorasolutions.io")) - time.Sleep(10 * time.Second) - - logrus.Infof("Step 9: Verify deployment update for second time") - 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") - } - time.Sleep(10 * time.Second) - - logrus.Infof("Step 10: Delete Deployment") - logrus.Infof("Deleting Deployment %q.\n", deployment.GetObjectMeta().GetName()) - deploymentError := controller.client.ExtensionsV1beta1().Deployments(namespace).Delete(configmapName, &metav1.DeleteOptions{}) - if deploymentError != nil { - logrus.Errorf("Error while deleting the configmap %v", deploymentError) + // Deleting deployment + err = testutil.DeleteDeployment(client, namespace, configmapName) + if err != nil { + logrus.Errorf("Error while deleting the deployment %v", err) } - logrus.Infof("Step 11: Delete Configmap") - logrus.Infof("Deleting Configmap %q.\n", configmapName) - err = controller.client.CoreV1().ConfigMaps(namespace).Delete(configmapName, &metav1.DeleteOptions{}) + // Deleting configmap + err = testutil.DeleteConfigMap(client, namespace, configmapName) if err != nil { logrus.Errorf("Error while deleting the configmap %v", err) } - time.Sleep(15 * time.Second) + time.Sleep(5 * time.Second) } -func createDeployment(deploymentName string, namespace string, client kubernetes.Interface) *v1beta1.Deployment { - deploymentClient := client.ExtensionsV1beta1().Deployments(namespace) - deployment := helper.GetDeployment(namespace, deploymentName) - deployment, err := deploymentClient.Create(deployment) +// Perform rolling upgrade on deployment and update env var upon updating the configmap +func TestControllerForUpdatingConfigmapShouldUpdateDeployment(t *testing.T) { + // Creating controller + logrus.Infof("Creating controller") + controller, err := NewController(client, testutil.ConfigmapResourceType, namespace) if err != nil { - logrus.Errorf("Error in deployment creation: %v", err) - } - logrus.Infof("Created Deployment %q.\n", deployment.GetObjectMeta().GetName()) - return deployment -} - -func TestControllerForUpdatingSecretShouldUpdateDeployment(t *testing.T) { - client, err := kube.GetClient() - if err != nil { - logrus.Errorf("Unable to create Kubernetes client error = %v", err) + logrus.Errorf("Unable to create NewController error = %v", err) return } - namespace := "test-reloader-secrets" - helper.CreateNamespace(namespace, client) - defer helper.DeleteNamespace(namespace, client) + stop := make(chan struct{}) + defer close(stop) + logrus.Infof("Starting controller") + go controller.Run(1, stop) + time.Sleep(10 * time.Second) - controller, err := NewController(client, "secrets", namespace) + // Creating secret + configmapName := configmapNamePrefix + "-update-" + common.RandSeq(5) + configmapClient, err := testutil.CreateConfigMap(client, namespace, configmapName, "www.google.com") + if err != nil { + t.Errorf("Error while creating the configmap %v", err) + } + + // Creating deployment + testutil.CreateDeployment(client, configmapName, namespace) + + // Updating configmap for first time + updateErr := testutil.UpdateConfigMap(configmapClient, namespace, configmapName, "", "www.stakater.com") + if updateErr != nil { + err = controller.client.CoreV1().ConfigMaps(namespace).Delete(configmapName, &metav1.DeleteOptions{}) + if err != nil { + logrus.Errorf("Error while deleting the configmap %v", err) + } + t.Errorf("Configmap was not updated") + } + + // Verifying deployment update + logrus.Infof("Verifying env var has been created") + shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "www.stakater.com") + updated := testutil.VerifyDeploymentUpdate(client, namespace, configmapName, common.ConfigmapEnvarPostfix, shaData, common.ConfigmapUpdateOnChangeAnnotation) + if !updated { + t.Errorf("Deployment was not updated") + } + time.Sleep(10 * time.Second) + + // Updating configmap for second time + updateErr = testutil.UpdateConfigMap(configmapClient, namespace, configmapName, "", "aurorasolutions.io") + if updateErr != nil { + err = controller.client.CoreV1().ConfigMaps(namespace).Delete(configmapName, &metav1.DeleteOptions{}) + if err != nil { + logrus.Errorf("Error while deleting the configmap %v", err) + } + t.Errorf("Configmap was not updated") + } + + // Verifying deployment update + logrus.Infof("Verifying env var has been updated") + shaData = testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "aurorasolutions.io") + updated = testutil.VerifyDeploymentUpdate(client, namespace, configmapName, common.ConfigmapEnvarPostfix, shaData, common.ConfigmapUpdateOnChangeAnnotation) + if !updated { + t.Errorf("Deployment was not updated") + } + time.Sleep(10 * time.Second) + + // Deleting deployment + err = testutil.DeleteDeployment(client, namespace, configmapName) + if err != nil { + logrus.Errorf("Error while deleting the deployment %v", err) + } + + // Deleting configmap + err = testutil.DeleteConfigMap(client, namespace, configmapName) + if err != nil { + logrus.Errorf("Error while deleting the configmap %v", err) + } + time.Sleep(5 * time.Second) +} + +// Do not Perform rolling upgrade on deployment and create env var upon updating the labels configmap +func TestControllerUpdatingConfigmapLabelsShouldNotCreateorUpdateEnvInDeployment(t *testing.T) { + // Creating Controller + logrus.Infof("Creating controller") + controller, err := NewController(client, testutil.ConfigmapResourceType, 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 controller.Run(1, stop) + time.Sleep(10 * time.Second) + + // Creating configmap + configmapName := configmapNamePrefix + "-update-" + common.RandSeq(5) + configmapClient, err := testutil.CreateConfigMap(client, namespace, configmapName, "www.google.com") + if err != nil { + t.Errorf("Error while creating the configmap %v", err) + } + + // Creating deployment + testutil.CreateDeployment(client, configmapName, namespace) + + // Updating configmap for first time + updateErr := testutil.UpdateConfigMap(configmapClient, namespace, configmapName, "test", "www.google.com") + if updateErr != nil { + err = controller.client.CoreV1().ConfigMaps(namespace).Delete(configmapName, &metav1.DeleteOptions{}) + if err != nil { + logrus.Errorf("Error while deleting the configmap %v", err) + } + t.Errorf("Configmap was not updated") + } + + // Verifying deployment update + logrus.Infof("Verifying env var has been created") + shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "www.google.com") + updated := testutil.VerifyDeploymentUpdate(client, namespace, configmapName, common.ConfigmapEnvarPostfix, shaData, common.ConfigmapUpdateOnChangeAnnotation) + if updated { + t.Errorf("Deployment should not be updated by changing label") + } + time.Sleep(10 * time.Second) + + // Deleting deployment + err = testutil.DeleteDeployment(client, namespace, configmapName) + if err != nil { + logrus.Errorf("Error while deleting the deployment %v", err) + } + + // Deleting configmap + err = testutil.DeleteConfigMap(client, namespace, configmapName) + if err != nil { + logrus.Errorf("Error while deleting the configmap %v", err) + } + time.Sleep(5 * time.Second) +} + +// Perform rolling upgrade on secret and create a env var upon updating the secret +func TestControllerUpdatingSecretShouldCreateEnvInDeployment(t *testing.T) { + // Creating controller + controller, err := NewController(client, testutil.SecretResourceType, namespace) if err != nil { logrus.Errorf("Unable to create NewController error = %v", err) return @@ -139,38 +248,174 @@ func TestControllerForUpdatingSecretShouldUpdateDeployment(t *testing.T) { go controller.Run(1, stop) time.Sleep(10 * time.Second) - secretName := secretNamePrefix + "-update-" + helper.RandSeq(5) - secretClient := client.CoreV1().Secrets(namespace) + // Creating secret + secretName := secretNamePrefix + "-update-" + common.RandSeq(5) data := "dGVzdFNlY3JldEVuY29kaW5nRm9yUmVsb2FkZXI=" - _, err = secretClient.Create(helper.GetSecret(namespace, secretName, data)) + secretClient, err := testutil.CreateSecret(client, namespace, secretName, data) if err != nil { - logrus.Errorf("Error in secret creation: %v", err) + t.Errorf("Error in secret creation: %v", err) } - logrus.Infof("Created Secret %q.\n", secretName) - time.Sleep(10 * time.Second) - logrus.Infof("Updating Secret %q.\n", secretName) - _, err = secretClient.Get(secretName, metav1.GetOptions{}) + // Creating deployment + _, err = testutil.CreateDeployment(client, secretName, namespace) if err != nil { - logrus.Errorf("Error while getting secret %v", err) + t.Errorf("Error in deployment creation: %v", err) } + + // Updating Secret data = "dGVzdFVwZGF0ZWRTZWNyZXRFbmNvZGluZ0ZvclJlbG9hZGVy" - _, updateErr := secretClient.Update(helper.GetSecret(namespace, secretName, data)) + err = testutil.UpdateSecret(secretClient, namespace, secretName, "", data) + if err != nil { + t.Errorf("Error while updating secret %v", err) + } - // TODO: Add functionality to verify reloader functionality here - - if updateErr != nil { - err := controller.client.CoreV1().Secrets(namespace).Delete(secretName, &metav1.DeleteOptions{}) - if err != nil { - logrus.Errorf("Error while deleting the secret %v", err) - } - logrus.Errorf("Error while updating the secret %v", err) + // Verifying Upgrade + logrus.Infof("Verifying env var has been created") + shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, data) + updated := testutil.VerifyDeploymentUpdate(client, namespace, secretName, common.SecretEnvarPostfix, shaData, common.SecretUpdateOnChangeAnnotation) + if !updated { + t.Errorf("Deployment was not updated") } time.Sleep(10 * time.Second) - logrus.Infof("Deleting Secret %q.\n", secretName) - err = controller.client.CoreV1().Secrets(namespace).Delete(secretName, &metav1.DeleteOptions{}) + + // Deleting Deployment + err = testutil.DeleteDeployment(client, namespace, secretName) + if err != nil { + logrus.Errorf("Error while deleting the deployment %v", err) + } + + //Deleting Secret + testutil.DeleteSecret(client, namespace, secretName) if err != nil { logrus.Errorf("Error while deleting the secret %v", err) } - time.Sleep(15 * time.Second) + time.Sleep(5 * time.Second) +} + +// Perform rolling upgrade on deployment and update env var upon updating the secret +func TestControllerUpdatingSecretShouldUpdateEnvInDeployment(t *testing.T) { + // Creating controller + controller, err := NewController(client, testutil.SecretResourceType, namespace) + if err != nil { + logrus.Errorf("Unable to create NewController error = %v", err) + return + } + stop := make(chan struct{}) + defer close(stop) + go controller.Run(1, stop) + time.Sleep(10 * time.Second) + + // Creating secret + secretName := secretNamePrefix + "-update-" + common.RandSeq(5) + data := "dGVzdFNlY3JldEVuY29kaW5nRm9yUmVsb2FkZXI=" + secretClient, err := testutil.CreateSecret(client, namespace, secretName, data) + if err != nil { + t.Errorf("Error in secret creation: %v", err) + } + + // Creating deployment + _, err = testutil.CreateDeployment(client, secretName, namespace) + if err != nil { + t.Errorf("Error in deployment creation: %v", err) + } + + // Updating Secret + data = "dGVzdFVwZGF0ZWRTZWNyZXRFbmNvZGluZ0ZvclJlbG9hZGVy" + err = testutil.UpdateSecret(secretClient, namespace, secretName, "", data) + if err != nil { + t.Errorf("Error while updating secret %v", err) + } + + // Verifying Upgrade + logrus.Infof("Verifying env var has been created") + shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, data) + updated := testutil.VerifyDeploymentUpdate(client, namespace, secretName, common.SecretEnvarPostfix, shaData, common.SecretUpdateOnChangeAnnotation) + if !updated { + t.Errorf("Deployment was not updated") + } + time.Sleep(10 * time.Second) + + // Updating Secret + data = "dGVzdFVwZGF0ZWRTZWNyZXRFbmNvZGluZ0ZvclJlbG9hZGVy" + err = testutil.UpdateSecret(secretClient, namespace, secretName, "", data) + if err != nil { + t.Errorf("Error while updating secret %v", err) + } + + // Verifying Upgrade + logrus.Infof("Verifying env var has been updated") + shaData = testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, data) + updated = testutil.VerifyDeploymentUpdate(client, namespace, secretName, common.SecretEnvarPostfix, shaData, common.SecretUpdateOnChangeAnnotation) + if !updated { + t.Errorf("Deployment was not updated") + } + time.Sleep(10 * time.Second) + + // Deleting Deployment + err = testutil.DeleteDeployment(client, namespace, secretName) + if err != nil { + logrus.Errorf("Error while deleting the deployment %v", err) + } + + //Deleting Secret + testutil.DeleteSecret(client, namespace, secretName) + if err != nil { + logrus.Errorf("Error while deleting the secret %v", err) + } + time.Sleep(5 * time.Second) +} + +// Do not Perform rolling upgrade on secret and create or update a env var upon updating the label in secret +func TestControllerUpdatingSecretLabelsShouldNotCreateorUpdateEnvInDeployment(t *testing.T) { + // Creating controller + controller, err := NewController(client, testutil.SecretResourceType, namespace) + if err != nil { + logrus.Errorf("Unable to create NewController error = %v", err) + return + } + stop := make(chan struct{}) + defer close(stop) + go controller.Run(1, stop) + time.Sleep(10 * time.Second) + + // Creating secret + secretName := secretNamePrefix + "-update-" + common.RandSeq(5) + data := "dGVzdFNlY3JldEVuY29kaW5nRm9yUmVsb2FkZXI=" + secretClient, err := testutil.CreateSecret(client, namespace, secretName, data) + if err != nil { + t.Errorf("Error in secret creation: %v", err) + } + + // Creating deployment + _, err = testutil.CreateDeployment(client, secretName, namespace) + if err != nil { + t.Errorf("Error in deployment creation: %v", err) + } + + err = testutil.UpdateSecret(secretClient, namespace, secretName, "test", data) + if err != nil { + t.Errorf("Error while updating secret %v", err) + } + + // Verifying Upgrade + logrus.Infof("Verifying env var has been created") + shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, data) + updated := testutil.VerifyDeploymentUpdate(client, namespace, secretName, common.SecretEnvarPostfix, shaData, common.SecretUpdateOnChangeAnnotation) + if updated { + t.Errorf("Deployment should not be updated by changing label in secret") + } + time.Sleep(10 * time.Second) + + // Deleting Deployment + err = testutil.DeleteDeployment(client, namespace, secretName) + if err != nil { + logrus.Errorf("Error while deleting the deployment %v", err) + } + + //Deleting Secret + testutil.DeleteSecret(client, namespace, secretName) + if err != nil { + logrus.Errorf("Error while deleting the secret %v", err) + } + time.Sleep(5 * time.Second) } diff --git a/internal/pkg/crypto/sha.go b/internal/pkg/crypto/sha.go new file mode 100644 index 00000000..043fc227 --- /dev/null +++ b/internal/pkg/crypto/sha.go @@ -0,0 +1,20 @@ +package crypto + +import ( + "crypto/sha1" + "fmt" + "io" + + "github.com/sirupsen/logrus" +) + +// GenerateSHA generates SHA from string +func GenerateSHA(data string) string { + hasher := sha1.New() + _, err := io.WriteString(hasher, data) + if err != nil { + logrus.Errorf("Unable to write data in hash writer %v", err) + } + sha := hasher.Sum(nil) + return fmt.Sprintf("%x", sha) +} diff --git a/internal/pkg/crypto/sha_test.go b/internal/pkg/crypto/sha_test.go new file mode 100644 index 00000000..d1bd50f2 --- /dev/null +++ b/internal/pkg/crypto/sha_test.go @@ -0,0 +1,15 @@ +package crypto + +import ( + "testing" +) + +// TestGenerateSHA generates the sha from given data and verifies whether it is correct or not +func TestGenerateSHA(t *testing.T) { + data := "www.stakater.com" + sha := GenerateSHA(data) + length := len(sha) + if length != 40 { + t.Errorf("Failed to generate SHA") + } +} diff --git a/internal/pkg/handler/created-handler.go b/internal/pkg/handler/create.go similarity index 100% rename from internal/pkg/handler/created-handler.go rename to internal/pkg/handler/create.go diff --git a/internal/pkg/handler/update.go b/internal/pkg/handler/update.go new file mode 100644 index 00000000..7e5ba0a2 --- /dev/null +++ b/internal/pkg/handler/update.go @@ -0,0 +1,226 @@ +package handler + +import ( + "sort" + "strings" + + "github.com/sirupsen/logrus" + "github.com/stakater/Reloader/internal/pkg/common" + "github.com/stakater/Reloader/internal/pkg/crypto" + "github.com/stakater/Reloader/pkg/kube" + "k8s.io/api/core/v1" + meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" +) + +// ResourceUpdatedHandler contains updated objects +type ResourceUpdatedHandler struct { + Resource interface{} + OldResource interface{} +} + +// Handle processes the updated resource +func (r ResourceUpdatedHandler) Handle() error { + if r.Resource == nil || r.OldResource == nil { + logrus.Errorf("Error in Handler") + } else { + logrus.Infof("Detected changes in object %s", r.Resource) + // process resource based on its type + rollingUpgrade(r, "deployments") + rollingUpgrade(r, "daemonsets") + rollingUpgrade(r, "statefulSets") + } + return nil +} + +func rollingUpgrade(r ResourceUpdatedHandler, rollingUpgradeType string) { + client, err := kube.GetClient() + if err != nil { + logrus.Fatalf("Unable to create Kubernetes client error = %v", err) + } + var namespace, resourceName, shaData, oldSHAdata, envarPostfix, annotation string + if _, ok := r.Resource.(*v1.ConfigMap); ok { + logrus.Infof("Performing 'Updated' action for resource of type 'configmap'") + namespace = r.Resource.(*v1.ConfigMap).Namespace + resourceName = r.Resource.(*v1.ConfigMap).Name + envarPostfix = common.ConfigmapEnvarPostfix + annotation = common.ConfigmapUpdateOnChangeAnnotation + shaData = getSHAfromConfigmapData(r.Resource.(*v1.ConfigMap).Data) + oldSHAdata = getSHAfromConfigmapData(r.OldResource.(*v1.ConfigMap).Data) + } else if _, ok := r.Resource.(*v1.Secret); ok { + logrus.Infof("Performing 'Updated' action for resource of type 'secret'") + namespace = r.Resource.(*v1.Secret).Namespace + resourceName = r.Resource.(*v1.Secret).Name + envarPostfix = common.SecretEnvarPostfix + annotation = common.SecretUpdateOnChangeAnnotation + shaData = getSHAfromSecretData(r.Resource.(*v1.Secret).Data) + oldSHAdata = getSHAfromSecretData(r.OldResource.(*v1.Secret).Data) + } else { + logrus.Warnf("Invalid resource: Resource should be 'Secret' or 'Configmap' but found, %v", r.Resource) + } + + if shaData != oldSHAdata { + if rollingUpgradeType == "deployments" { + RollingUpgradeDeployment(client, namespace, resourceName, shaData, envarPostfix, annotation) + } else if rollingUpgradeType == "daemonsets" { + RollingUpgradeDaemonSets(client, namespace, resourceName, shaData, envarPostfix, annotation) + } else if rollingUpgradeType == "statefulSets" { + RollingUpgradeStatefulSets(client, namespace, resourceName, shaData, envarPostfix, annotation) + } + } else { + logrus.Infof("Resource update will not happen because no data change detected") + } +} + +// RollingUpgradeDeployment upgrades the deployment if there is any change in configmap or secret data +func RollingUpgradeDeployment(client kubernetes.Interface, namespace string, resourceName string, shaData string, envarPostfix string, annotation string) error { + deployments, err := client.ExtensionsV1beta1().Deployments(namespace).List(meta_v1.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[annotation] + updated := performRollingUpgrade(containers, resourceName, annotationValue, shaData, envarPostfix) + if !updated { + logrus.Warnf("Rolling upgrade did not happen") + } else { + // update the deployment + _, err := client.ExtensionsV1beta1().Deployments(namespace).Update(&d) + if err != nil { + logrus.Errorf("Update deployment failed %v", err) + } else { + logrus.Infof("Updated Deployment %s", d.Name) + } + } + } + return nil +} + +// RollingUpgradeDaemonSets upgrades the daemonset if there is any change in configmap or secret data +func RollingUpgradeDaemonSets(client kubernetes.Interface, namespace string, resourceName string, shaData string, envarPostfix string, annotation string) error { + daemonSets, err := client.ExtensionsV1beta1().DaemonSets(namespace).List(meta_v1.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[annotation] + updated := performRollingUpgrade(containers, resourceName, annotationValue, shaData, envarPostfix) + if !updated { + logrus.Warnf("Rolling upgrade did not happen") + } else { + // update the daemonSet + _, err := client.ExtensionsV1beta1().DaemonSets(namespace).Update(&d) + if err != nil { + logrus.Errorf("Update daemonSet failed %v", err) + } else { + logrus.Infof("Updated daemonSet %s", d.Name) + } + } + } + return err +} + +// RollingUpgradeStatefulSets upgrades the statefulset if there is any change in configmap or secret data +func RollingUpgradeStatefulSets(client kubernetes.Interface, namespace string, resourceName string, shaData string, envarPostfix string, annotation string) error { + statefulSets, err := client.AppsV1beta1().StatefulSets(namespace).List(meta_v1.ListOptions{}) + if err != nil { + logrus.Errorf("Failed to list statefulSets %v", err) + } + + for _, s := range statefulSets.Items { + containers := s.Spec.Template.Spec.Containers + // match statefulSets with the correct annotation + annotationValue := s.ObjectMeta.Annotations[annotation] + updated := performRollingUpgrade(containers, resourceName, annotationValue, shaData, envarPostfix) + if !updated { + logrus.Warnf("Rolling upgrade did not happen") + } else { + // update the statefulSet + _, err := client.AppsV1beta1().StatefulSets(namespace).Update(&s) + if err != nil { + logrus.Errorf("Update statefulSet failed %v", err) + } else { + logrus.Infof("Updated statefulSet %s", s.Name) + } + } + } + return err +} + +func performRollingUpgrade(containers []v1.Container, resourceName string, annotationValue string, shaData string, envarPostfix string) bool { + updated := false + if annotationValue != "" { + values := strings.Split(annotationValue, ",") + for _, value := range values { + if value == resourceName { + updated = updateContainers(containers, value, shaData, envarPostfix) + break + } + } + } + return updated +} + +func updateContainers(containers []v1.Container, annotationValue string, shaData string, envarPostfix string) bool { + updated := false + envar := common.EnvVarPrefix + common.ConvertToEnvVarName(annotationValue) + envarPostfix + logrus.Infof("Generated environment variable: %s", envar) + for i := range containers { + envs := containers[i].Env + + //update if env var exists + updated = updateEnvVar(envs, envar, shaData) + + // if no existing env var exists lets create one + if !updated { + e := v1.EnvVar{ + Name: envar, + Value: shaData, + } + containers[i].Env = append(containers[i].Env, e) + updated = true + logrus.Infof("%s environment variable does not exist, creating a new env with value %s", envar, shaData) + } + } + return updated +} + +func updateEnvVar(envs []v1.EnvVar, envar string, shaData string) bool { + for j := range envs { + if envs[j].Name == envar { + logrus.Infof("%s environment variable found", envar) + if envs[j].Value != shaData { + logrus.Infof("Updating %s to %s", envar, shaData) + envs[j].Value = shaData + return true + } + } + } + return false +} + +func getSHAfromConfigmapData(data map[string]string) string { + logrus.Infof("Generating SHA for configmap data") + values := []string{} + for k, v := range data { + values = append(values, k+"="+v) + } + sort.Strings(values) + return crypto.GenerateSHA(strings.Join(values, ";")) +} + +func getSHAfromSecretData(data map[string][]byte) string { + logrus.Infof("Generating SHA for secret data") + values := []string{} + for k, v := range data { + values = append(values, k+"="+string(v[:])) + } + sort.Strings(values) + return crypto.GenerateSHA(strings.Join(values, ";")) +} diff --git a/internal/pkg/handler/update_test.go b/internal/pkg/handler/update_test.go new file mode 100644 index 00000000..45ec1e46 --- /dev/null +++ b/internal/pkg/handler/update_test.go @@ -0,0 +1,223 @@ +package handler + +import ( + "os" + "testing" + "time" + + "github.com/sirupsen/logrus" + "github.com/stakater/Reloader/internal/pkg/common" + "github.com/stakater/Reloader/internal/pkg/testutil" + "github.com/stakater/Reloader/pkg/kube" + "k8s.io/client-go/kubernetes" +) + +var ( + client = getClient() + namespace = "test-handler" + configmapName = "testconfigmap-handler-update-" + common.RandSeq(5) + secretName = "testsecret-handler-update-" + common.RandSeq(5) +) + +func TestMain(m *testing.M) { + + logrus.Infof("Creating namespace %s", namespace) + testutil.CreateNamespace(namespace, client) + + 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() { + // Creating configmap + _, err := testutil.CreateConfigMap(client, namespace, configmapName, "www.google.com") + if err != nil { + logrus.Errorf("Error in configmap creation: %v", err) + } + + // Creating secret + data := "dGVzdFNlY3JldEVuY29kaW5nRm9yUmVsb2FkZXI=" + _, err = testutil.CreateSecret(client, namespace, secretName, data) + if err != nil { + logrus.Errorf("Error in secret creation: %v", err) + } + + // Creating Deployment with configmap + testutil.CreateDeployment(client, configmapName, namespace) + if err != nil { + logrus.Errorf("Error in Deployment with configmap creation: %v", err) + } + + // Creating Deployment with secret + testutil.CreateDeployment(client, secretName, namespace) + if err != nil { + logrus.Errorf("Error in Deployment with secret creation: %v", err) + } + + // Creating Daemonset with configmap + testutil.CreateDaemonset(client, configmapName, namespace) + if err != nil { + logrus.Errorf("Error in Daemonset with configmap creation: %v", err) + } + + // Creating Daemonset with secret + testutil.CreateDaemonset(client, secretName, namespace) + if err != nil { + logrus.Errorf("Error in Daemonset with secret creation: %v", err) + } + + // Creating Statefulset with configmap + testutil.CreateStatefulset(client, configmapName, namespace) + if err != nil { + logrus.Errorf("Error in Statefulset with configmap creation: %v", err) + } + + // Creating Statefulset with secret + testutil.CreateStatefulset(client, secretName, namespace) + if err != nil { + logrus.Errorf("Error in Statefulset with secret creation: %v", err) + } + +} + +func teardown() { + // Deleting Deployment with configmap + deploymentError := testutil.DeleteDeployment(client, namespace, configmapName) + if deploymentError != nil { + logrus.Errorf("Error while deleting deployment with configmap %v", deploymentError) + } + + // Deleting Deployment with secret + deploymentError = testutil.DeleteDeployment(client, namespace, secretName) + if deploymentError != nil { + logrus.Errorf("Error while deleting deployment with secret %v", deploymentError) + } + + // Deleting Daemonset with configmap + daemonsetError := testutil.DeleteDaemonset(client, namespace, configmapName) + if daemonsetError != nil { + logrus.Errorf("Error while deleting daemonset with configmap %v", daemonsetError) + } + + // Deleting Deployment with secret + daemonsetError = testutil.DeleteDaemonset(client, namespace, secretName) + if daemonsetError != nil { + logrus.Errorf("Error while deleting daemonset with secret %v", daemonsetError) + } + + // Deleting Statefulset with configmap + statefulsetError := testutil.DeleteStatefulset(client, namespace, configmapName) + if statefulsetError != nil { + logrus.Errorf("Error while deleting statefulset with configmap %v", statefulsetError) + } + + // Deleting Deployment with secret + statefulsetError = testutil.DeleteStatefulset(client, namespace, secretName) + if statefulsetError != nil { + logrus.Errorf("Error while deleting statefulset with secret %v", statefulsetError) + } + + // Deleting Configmap + err := testutil.DeleteConfigMap(client, namespace, configmapName) + if err != nil { + logrus.Errorf("Error while deleting the configmap %v", err) + } + + // Deleting Secret + err = testutil.DeleteSecret(client, namespace, secretName) + if err != nil { + logrus.Errorf("Error while deleting the secret %v", err) + } + + // Deleting namespace + testutil.DeleteNamespace(namespace, client) + +} + +func TestRollingUpgradeForDeploymentWithConfigmap(t *testing.T) { + shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, configmapName, "www.stakater.com") + RollingUpgradeDeployment(client, namespace, configmapName, shaData, common.ConfigmapEnvarPostfix, common.ConfigmapUpdateOnChangeAnnotation) + time.Sleep(5 * time.Second) + + logrus.Infof("Verifying deployment update") + updated := testutil.VerifyDeploymentUpdate(client, namespace, configmapName, common.ConfigmapEnvarPostfix, shaData, common.ConfigmapUpdateOnChangeAnnotation) + if !updated { + t.Errorf("Deployment was not updated") + } +} + +func TestRollingUpgradeForDeploymentWithSecret(t *testing.T) { + shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, "dGVzdFVwZGF0ZWRTZWNyZXRFbmNvZGluZ0ZvclJlbG9hZGVy") + RollingUpgradeDeployment(client, namespace, secretName, shaData, common.SecretEnvarPostfix, common.SecretUpdateOnChangeAnnotation) + time.Sleep(5 * time.Second) + + logrus.Infof("Verifying deployment update") + updated := testutil.VerifyDeploymentUpdate(client, namespace, secretName, common.SecretEnvarPostfix, shaData, common.SecretUpdateOnChangeAnnotation) + if !updated { + t.Errorf("Deployment was not updated") + } +} + +func TestRollingUpgradeForDaemonsetWithConfigmap(t *testing.T) { + shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "www.facebook.com") + RollingUpgradeDaemonSets(client, namespace, configmapName, shaData, common.ConfigmapEnvarPostfix, common.ConfigmapUpdateOnChangeAnnotation) + time.Sleep(5 * time.Second) + + logrus.Infof("Verifying daemonset update") + updated := testutil.VerifyDaemonsetUpdate(client, namespace, configmapName, common.ConfigmapEnvarPostfix, shaData, common.ConfigmapUpdateOnChangeAnnotation) + if !updated { + t.Errorf("Daemonset was not updated") + } +} + +func TestRollingUpgradeForDaemonsetWithSecret(t *testing.T) { + shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, "d3d3LmZhY2Vib29rLmNvbQ==") + RollingUpgradeDaemonSets(client, namespace, secretName, shaData, common.SecretEnvarPostfix, common.SecretUpdateOnChangeAnnotation) + time.Sleep(5 * time.Second) + + logrus.Infof("Verifying daemonset update") + updated := testutil.VerifyDaemonsetUpdate(client, namespace, secretName, common.SecretEnvarPostfix, shaData, common.SecretUpdateOnChangeAnnotation) + if !updated { + t.Errorf("Daemonset was not updated") + } +} + +func TestRollingUpgradeForStatefulsetWithConfigmap(t *testing.T) { + shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "www.twitter.com") + RollingUpgradeStatefulSets(client, namespace, configmapName, shaData, common.ConfigmapEnvarPostfix, common.ConfigmapUpdateOnChangeAnnotation) + time.Sleep(5 * time.Second) + + logrus.Infof("Verifying statefulset update") + updated := testutil.VerifyStatefulsetUpdate(client, namespace, configmapName, common.ConfigmapEnvarPostfix, shaData, common.ConfigmapUpdateOnChangeAnnotation) + if !updated { + t.Errorf("Statefulset was not updated") + } +} + +func TestRollingUpgradeForStatefulsetWithSecret(t *testing.T) { + shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, "d3d3LnR3aXR0ZXIuY29t") + RollingUpgradeStatefulSets(client, namespace, secretName, shaData, common.SecretEnvarPostfix, common.SecretUpdateOnChangeAnnotation) + time.Sleep(5 * time.Second) + + logrus.Infof("Verifying statefulset update") + updated := testutil.VerifyStatefulsetUpdate(client, namespace, secretName, common.SecretEnvarPostfix, shaData, common.SecretUpdateOnChangeAnnotation) + if !updated { + t.Errorf("Statefulset was not updated") + } +} diff --git a/internal/pkg/handler/updated-handler.go b/internal/pkg/handler/updated-handler.go deleted file mode 100644 index 4ede66fd..00000000 --- a/internal/pkg/handler/updated-handler.go +++ /dev/null @@ -1,241 +0,0 @@ -package handler - -import ( - "strings" - - "github.com/sirupsen/logrus" - "github.com/stakater/Reloader/internal/pkg/helper" - "github.com/stakater/Reloader/pkg/kube" - "k8s.io/api/core/v1" - meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" -) - -const ( - configmapUpdateOnChangeAnnotation = "reloader.stakater.com/configmap.update-on-change" - // Adding separate annotation to differentiate between configmap and secret - secretUpdateOnChangeAnnotation = "reloader.stakater.com/secret.update-on-change" -) - -// ResourceUpdatedHandler contains updated objects -type ResourceUpdatedHandler struct { - Resource interface{} - OldResource interface{} -} - -// Handle processes the updated resource -func (r ResourceUpdatedHandler) Handle() error { - if r.Resource == nil || r.OldResource == nil { - logrus.Errorf("Error in Handler") - } else { - logrus.Infof("Detected changes in object %s", r.Resource) - // process resource based on its type - rollingUpgrade(r, "deployments") - rollingUpgrade(r, "daemonsets") - rollingUpgrade(r, "statefulSets") - } - return nil -} - -func rollingUpgrade(r ResourceUpdatedHandler, rollingUpgradeType string) { - client, err := kube.GetClient() - if err != nil { - logrus.Fatalf("Unable to create Kubernetes client error = %v", err) - } - var namespace, name, shaData, envNamePostfix string - if _, ok := r.Resource.(*v1.ConfigMap); ok { - logrus.Infof("Performing 'Updated' action for resource of type 'configmap'") - namespace = r.Resource.(*v1.ConfigMap).Namespace - name = r.Resource.(*v1.ConfigMap).Name - shaData = helper.ConvertConfigmapToSHA(r.Resource.(*v1.ConfigMap)) - envNamePostfix = "_CONFIGMAP" - } else if _, ok := r.Resource.(*v1.Secret); ok { - logrus.Infof("Performing 'Updated' action for resource of type 'secret'") - namespace = r.Resource.(*v1.Secret).Namespace - name = r.Resource.(*v1.Secret).Name - shaData = helper.ConvertSecretToSHA(r.Resource.(*v1.Secret)) - envNamePostfix = "_SECRET" - } else { - logrus.Warnf("Invalid resource: Resource should be 'Secret' or 'Configmap' but found, %v", r.Resource) - } - - if rollingUpgradeType == "deployments" { - RollingUpgradeForDeployment(client, namespace, name, shaData, envNamePostfix) - } else if rollingUpgradeType == "daemonsets" { - RollingUpgradeForDaemonSets(client, namespace, name, shaData, envNamePostfix) - } else if rollingUpgradeType == "statefulSets" { - RollingUpgradeForStatefulSets(client, namespace, name, shaData, envNamePostfix) - } -} - -// 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.Errorf("Failed to list deployments %v", err) - } - var updateOnChangeAnnotation string - if envName == "_CONFIGMAP" { - updateOnChangeAnnotation = configmapUpdateOnChangeAnnotation - } else if envName == "_SECRET" { - updateOnChangeAnnotation = secretUpdateOnChangeAnnotation - } - for _, d := range deployments.Items { - containers := d.Spec.Template.Spec.Containers - // match deployments with the correct annotation - annotationValue := d.ObjectMeta.Annotations[updateOnChangeAnnotation] - - if annotationValue != "" { - values := strings.Split(annotationValue, ",") - matches := false - for _, value := range values { - if value == name { - matches = true - break - } - } - if matches { - updated := updateContainers(containers, name, shaData, envName) - - if !updated { - logrus.Warnf("Rolling upgrade did not happen") - } else { - // update the deployment - _, err := client.ExtensionsV1beta1().Deployments(namespace).Update(&d) - if err != nil { - logrus.Errorf("Update deployment failed %v", err) - } else { - logrus.Infof("Updated Deployment %s", d.Name) - } - } - } - } - } - return nil -} - -// 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.Errorf("Failed to list daemonSets %v", err) - } - - var updateOnChangeAnnotation string - if envName == "_CONFIGMAP" { - updateOnChangeAnnotation = configmapUpdateOnChangeAnnotation - } else if envName == "_SECRET" { - updateOnChangeAnnotation = secretUpdateOnChangeAnnotation - } - for _, d := range daemonSets.Items { - containers := d.Spec.Template.Spec.Containers - // match daemonSets with the correct annotation - annotationValue := d.ObjectMeta.Annotations[updateOnChangeAnnotation] - - if annotationValue != "" { - values := strings.Split(annotationValue, ",") - matches := false - for _, value := range values { - if value == name { - matches = true - break - } - } - if matches { - updated := updateContainers(containers, name, shaData, envName) - - if !updated { - logrus.Warnf("Rolling upgrade did not happen") - } else { - // update the daemonSet - _, err := client.ExtensionsV1beta1().DaemonSets(namespace).Update(&d) - if err != nil { - logrus.Errorf("Update daemonSet failed %v", err) - } else { - logrus.Infof("Updated daemonSet %s", d.Name) - } - } - } - } - } - return nil -} - -// 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.Errorf("Failed to list statefulSets %v", err) - } - var updateOnChangeAnnotation string - if envName == "_CONFIGMAP" { - updateOnChangeAnnotation = configmapUpdateOnChangeAnnotation - } else if envName == "_SECRET" { - updateOnChangeAnnotation = secretUpdateOnChangeAnnotation - } - for _, d := range statefulSets.Items { - containers := d.Spec.Template.Spec.Containers - // match statefulSets with the correct annotation - annotationValue := d.ObjectMeta.Annotations[updateOnChangeAnnotation] - - if annotationValue != "" { - values := strings.Split(annotationValue, ",") - matches := false - for _, value := range values { - if value == name { - matches = true - break - } - } - if matches { - updated := updateContainers(containers, name, shaData, envName) - - if !updated { - logrus.Warnf("Rolling upgrade did not happen") - } else { - // update the statefulSet - _, err := client.AppsV1beta1().StatefulSets(namespace).Update(&d) - if err != nil { - logrus.Errorf("Update statefulSet failed %v", err) - } else { - logrus.Infof("Updated statefulSet %s", d.Name) - } - } - } - } - } - return nil -} - -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) - - for i := range containers { - envs := containers[i].Env - matched := false - for j := range envs { - if envs[j].Name == envar { - matched = true - logrus.Infof("%s environment variable found", envar) - if envs[j].Value != shaData { - logrus.Infof("Updating %s to %s", envar, shaData) - envs[j].Value = shaData - updated = true - } - } - } - // if no existing env var exists lets create one - if !matched { - e := v1.EnvVar{ - Name: envar, - 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, shaData) - } - } - return updated -} diff --git a/internal/pkg/handlerTester/updated-handler_test.go b/internal/pkg/handlerTester/updated-handler_test.go deleted file mode 100644 index 1beb6efd..00000000 --- a/internal/pkg/handlerTester/updated-handler_test.go +++ /dev/null @@ -1,261 +0,0 @@ -package handlerTester - -import ( - "os" - "testing" - "time" - - "github.com/sirupsen/logrus" - "github.com/stakater/Reloader/internal/pkg/controller" - "github.com/stakater/Reloader/internal/pkg/handler" - "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/helper.go b/internal/pkg/helper/helper.go deleted file mode 100644 index 41c40d22..00000000 --- a/internal/pkg/helper/helper.go +++ /dev/null @@ -1,68 +0,0 @@ -package helper - -import ( - "bytes" - "crypto/sha1" - "fmt" - "io" - "sort" - "strings" - - "github.com/sirupsen/logrus" - "k8s.io/api/core/v1" -) - -// ConvertToEnvVarName converts the given text into a usable env var -// removing any special chars with '_' and transforming text to upper case -func ConvertToEnvVarName(text string) string { - var buffer bytes.Buffer - upper := strings.ToUpper(text) - lastCharValid := false - for i := 0; i < len(upper); i++ { - ch := upper[i] - if (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') { - buffer.WriteString(string(ch)) - lastCharValid = true - } else { - if lastCharValid { - buffer.WriteString("_") - } - lastCharValid = false - } - } - return buffer.String() -} - -// ConvertConfigmapToSHA generates SHA for configmap data -func ConvertConfigmapToSHA(cm *v1.ConfigMap) string { - logrus.Infof("Generating SHA for configmap data") - values := []string{} - for k, v := range cm.Data { - values = append(values, k+"="+v) - } - sort.Strings(values) - sha := GenerateSHA(strings.Join(values, ";")) - logrus.Infof("SHA for configmap data: %s", sha) - return sha -} - -// ConvertSecretToSHA generates SHA for secret data -func ConvertSecretToSHA(se *v1.Secret) string { - logrus.Infof("Generating SHA for secret data") - values := []string{} - for k, v := range se.Data { - values = append(values, k+"="+string(v[:])) - } - sort.Strings(values) - sha := GenerateSHA(strings.Join(values, ";")) - logrus.Infof("SHA for secret data: %s", sha) - return sha -} - -// GenerateSHA generates SHA from string -func GenerateSHA(data string) string { - hasher := sha1.New() - io.WriteString(hasher, data) - sha := hasher.Sum(nil) - return fmt.Sprintf("%x", sha) -} diff --git a/internal/pkg/helper/testUtils.go b/internal/pkg/helper/testUtils.go deleted file mode 100644 index 82710d19..00000000 --- a/internal/pkg/helper/testUtils.go +++ /dev/null @@ -1,310 +0,0 @@ -package helper - -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 "" -} diff --git a/internal/pkg/testutil/kube.go b/internal/pkg/testutil/kube.go new file mode 100644 index 00000000..75026ac2 --- /dev/null +++ b/internal/pkg/testutil/kube.go @@ -0,0 +1,447 @@ +package testutil + +import ( + "sort" + "strings" + "time" + + "github.com/sirupsen/logrus" + "github.com/stakater/Reloader/internal/pkg/common" + "github.com/stakater/Reloader/internal/pkg/crypto" + 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" + core_v1 "k8s.io/client-go/kubernetes/typed/core/v1" +) + +var ( + // ConfigmapResourceType is a resource type which controller watches for changes + ConfigmapResourceType = "configMaps" + // SecretResourceType is a resource type which controller watches for changes + SecretResourceType = "secrets" +) + +// 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{ + common.ConfigmapUpdateOnChangeAnnotation: deploymentName, + common.SecretUpdateOnChangeAnnotation: 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{ + common.ConfigmapUpdateOnChangeAnnotation: daemonsetName, + common.SecretUpdateOnChangeAnnotation: 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{ + common.ConfigmapUpdateOnChangeAnnotation: statefulsetName, + common.SecretUpdateOnChangeAnnotation: 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}, + } +} + +// GetConfigmapWithUpdatedLabel provides configmap for testing +func GetConfigmapWithUpdatedLabel(namespace string, configmapName string, testLabel string, testData string) *v1.ConfigMap { + return &v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: configmapName, + Namespace: namespace, + Labels: map[string]string{"firstLabel": testLabel}, + }, + 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)}, + } +} + +// GetSecretWithUpdatedLabel provides secret for testing +func GetSecretWithUpdatedLabel(namespace string, secretName string, label string, data string) *v1.Secret { + return &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: secretName, + Namespace: namespace, + Labels: map[string]string{"firstLabel": label}, + }, + 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, envarPostfix string, shaData string, annotation 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[annotation] + if annotationValue != "" { + values := strings.Split(annotationValue, ",") + matches := false + for _, value := range values { + if value == name { + matches = true + break + } + } + if matches { + envName := common.EnvVarPrefix + common.ConvertToEnvVarName(annotationValue) + envarPostfix + updated := getResourceSHA(containers, envName) + if updated == shaData { + 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, annotation 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[annotation] + if annotationValue != "" { + values := strings.Split(annotationValue, ",") + matches := false + for _, value := range values { + if value == name { + matches = true + break + } + } + if matches { + envName := common.EnvVarPrefix + common.ConvertToEnvVarName(annotationValue) + resourceType + updated := getResourceSHA(containers, envName) + + if updated == shaData { + 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, annotation 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[annotation] + if annotationValue != "" { + values := strings.Split(annotationValue, ",") + matches := false + for _, value := range values { + if value == name { + matches = true + break + } + } + if matches { + envName := common.EnvVarPrefix + common.ConvertToEnvVarName(annotationValue) + resourceType + updated := getResourceSHA(containers, envName) + + if updated == shaData { + 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 "" +} + +//ConvertResourceToSHA generates SHA from secret or configmap data +func ConvertResourceToSHA(resourceType string, namespace string, resourceName string, data string) string { + values := []string{} + logrus.Infof("Generating SHA for secret data") + if resourceType == SecretResourceType { + secret := GetSecret(namespace, resourceName, data) + for k, v := range secret.Data { + values = append(values, k+"="+string(v[:])) + } + } else if resourceType == ConfigmapResourceType { + configmap := GetConfigmap(namespace, resourceName, data) + for k, v := range configmap.Data { + values = append(values, k+"="+string(v[:])) + } + } + sort.Strings(values) + return crypto.GenerateSHA(strings.Join(values, ";")) +} + +// CreateConfigMap creates a configmap in given namespace and returns the ConfigMapInterface +func CreateConfigMap(client kubernetes.Interface, namespace string, configmapName string, data string) (core_v1.ConfigMapInterface, error) { + logrus.Infof("Creating configmap") + configmapClient := client.CoreV1().ConfigMaps(namespace) + _, err := configmapClient.Create(GetConfigmap(namespace, configmapName, data)) + time.Sleep(10 * time.Second) + return configmapClient, err +} + +// CreateSecret creates a secret in given namespace and returns the SecretInterface +func CreateSecret(client kubernetes.Interface, namespace string, secretName string, data string) (core_v1.SecretInterface, error) { + logrus.Infof("Creating secret") + secretClient := client.CoreV1().Secrets(namespace) + _, err := secretClient.Create(GetSecret(namespace, secretName, data)) + time.Sleep(10 * time.Second) + return secretClient, err +} + +// CreateDeployment creates a deployment in given namespace and returns the Deployment +func CreateDeployment(client kubernetes.Interface, deploymentName string, namespace string) (*v1beta1.Deployment, error) { + logrus.Infof("Creating Deployment") + deploymentClient := client.ExtensionsV1beta1().Deployments(namespace) + deployment, err := deploymentClient.Create(GetDeployment(namespace, deploymentName)) + time.Sleep(5 * time.Second) + return deployment, err +} + +// CreateDaemonset creates a deployment in given namespace and returns the DaemonSet +func CreateDaemonset(client kubernetes.Interface, daemonsetName string, namespace string) (*v1beta1.DaemonSet, error) { + logrus.Infof("Creating Daemonset") + daemonsetClient := client.ExtensionsV1beta1().DaemonSets(namespace) + daemonset, err := daemonsetClient.Create(GetDaemonset(namespace, daemonsetName)) + time.Sleep(5 * time.Second) + return daemonset, err +} + +// CreateStatefulset creates a deployment in given namespace and returns the StatefulSet +func CreateStatefulset(client kubernetes.Interface, statefulsetName string, namespace string) (*v1_beta1.StatefulSet, error) { + logrus.Infof("Creating Statefulset") + statefulsetClient := client.AppsV1beta1().StatefulSets(namespace) + statefulset, err := statefulsetClient.Create(GetStatefulset(namespace, statefulsetName)) + time.Sleep(5 * time.Second) + return statefulset, err +} + +// DeleteDeployment creates a deployment in given namespace and returns the error if any +func DeleteDeployment(client kubernetes.Interface, namespace string, deploymentName string) error { + logrus.Infof("Deleting Deployment") + deploymentError := client.ExtensionsV1beta1().Deployments(namespace).Delete(deploymentName, &metav1.DeleteOptions{}) + time.Sleep(5 * time.Second) + return deploymentError +} + +// DeleteDaemonset creates a daemonset in given namespace and returns the error if any +func DeleteDaemonset(client kubernetes.Interface, namespace string, daemonsetName string) error { + logrus.Infof("Deleting Daemonset %s", daemonsetName) + daemonsetError := client.ExtensionsV1beta1().DaemonSets(namespace).Delete(daemonsetName, &metav1.DeleteOptions{}) + time.Sleep(5 * time.Second) + return daemonsetError +} + +// DeleteStatefulset creates a statefulset in given namespace and returns the error if any +func DeleteStatefulset(client kubernetes.Interface, namespace string, statefulsetName string) error { + logrus.Infof("Deleting Statefulset %s", statefulsetName) + statefulsetError := client.AppsV1beta1().StatefulSets(namespace).Delete(statefulsetName, &metav1.DeleteOptions{}) + time.Sleep(5 * time.Second) + return statefulsetError +} + +// UpdateConfigMap updates a configmap in given namespace and returns the error if any +func UpdateConfigMap(configmapClient core_v1.ConfigMapInterface, namespace string, configmapName string, label string, data string) error { + logrus.Infof("Updating configmap %q.\n", configmapName) + var configmap *v1.ConfigMap + if label != "" { + configmap = GetConfigmapWithUpdatedLabel(namespace, configmapName, label, data) + } else { + configmap = GetConfigmap(namespace, configmapName, data) + } + _, updateErr := configmapClient.Update(configmap) + time.Sleep(5 * time.Second) + return updateErr +} + +// UpdateSecret updates a secret in given namespace and returns the error if any +func UpdateSecret(secretClient core_v1.SecretInterface, namespace string, secretName string, label string, data string) error { + logrus.Infof("Updating secret %q.\n", secretName) + var secret *v1.Secret + if label != "" { + secret = GetSecretWithUpdatedLabel(namespace, secretName, label, data) + } else { + secret = GetSecret(namespace, secretName, data) + } + _, updateErr := secretClient.Update(secret) + time.Sleep(5 * time.Second) + return updateErr +} + +// DeleteConfigMap deletes a configmap in given namespace and returns the error if any +func DeleteConfigMap(client kubernetes.Interface, namespace string, configmapName string) error { + logrus.Infof("Deleting configmap %q.\n", configmapName) + err := client.CoreV1().ConfigMaps(namespace).Delete(configmapName, &metav1.DeleteOptions{}) + time.Sleep(5 * time.Second) + return err +} + +// DeleteSecret deletes a secret in given namespace and returns the error if any +func DeleteSecret(client kubernetes.Interface, namespace string, secretName string) error { + logrus.Infof("Deleting secret %q.\n", secretName) + err := client.CoreV1().Secrets(namespace).Delete(secretName, &metav1.DeleteOptions{}) + time.Sleep(5 * time.Second) + return err +} From 034d2dcd93e5fcd6c90f9d89fc757f398aaa3cfd Mon Sep 17 00:00:00 2001 From: faizanahmad055 Date: Mon, 23 Jul 2018 16:10:53 +0500 Subject: [PATCH 12/21] Implement golangci review comments on PR-2 --- internal/pkg/controller/controller_test.go | 21 ++++-- internal/pkg/handler/update.go | 77 +++++++++++----------- internal/pkg/handler/update_test.go | 42 ++++++++---- internal/pkg/testutil/kube.go | 2 +- 4 files changed, 85 insertions(+), 57 deletions(-) diff --git a/internal/pkg/controller/controller_test.go b/internal/pkg/controller/controller_test.go index 92780d89..9ca560af 100644 --- a/internal/pkg/controller/controller_test.go +++ b/internal/pkg/controller/controller_test.go @@ -65,7 +65,10 @@ func TestControllerUpdatingConfigmapShouldCreateEnvInDeployment(t *testing.T) { } // Creating deployment - testutil.CreateDeployment(client, configmapName, namespace) + _, err = testutil.CreateDeployment(client, configmapName, namespace) + if err != nil { + t.Errorf("Error in deployment creation: %v", err) + } // Updating configmap for first time updateErr := testutil.UpdateConfigMap(configmapClient, namespace, configmapName, "", "www.stakater.com") @@ -123,7 +126,10 @@ func TestControllerForUpdatingConfigmapShouldUpdateDeployment(t *testing.T) { } // Creating deployment - testutil.CreateDeployment(client, configmapName, namespace) + _, err = testutil.CreateDeployment(client, configmapName, namespace) + if err != nil { + t.Errorf("Error in deployment creation: %v", err) + } // Updating configmap for first time updateErr := testutil.UpdateConfigMap(configmapClient, namespace, configmapName, "", "www.stakater.com") @@ -200,7 +206,10 @@ func TestControllerUpdatingConfigmapLabelsShouldNotCreateorUpdateEnvInDeployment } // Creating deployment - testutil.CreateDeployment(client, configmapName, namespace) + _, err = testutil.CreateDeployment(client, configmapName, namespace) + if err != nil { + t.Errorf("Error in deployment creation: %v", err) + } // Updating configmap for first time updateErr := testutil.UpdateConfigMap(configmapClient, namespace, configmapName, "test", "www.google.com") @@ -285,7 +294,7 @@ func TestControllerUpdatingSecretShouldCreateEnvInDeployment(t *testing.T) { } //Deleting Secret - testutil.DeleteSecret(client, namespace, secretName) + err = testutil.DeleteSecret(client, namespace, secretName) if err != nil { logrus.Errorf("Error while deleting the secret %v", err) } @@ -358,7 +367,7 @@ func TestControllerUpdatingSecretShouldUpdateEnvInDeployment(t *testing.T) { } //Deleting Secret - testutil.DeleteSecret(client, namespace, secretName) + err = testutil.DeleteSecret(client, namespace, secretName) if err != nil { logrus.Errorf("Error while deleting the secret %v", err) } @@ -413,7 +422,7 @@ func TestControllerUpdatingSecretLabelsShouldNotCreateorUpdateEnvInDeployment(t } //Deleting Secret - testutil.DeleteSecret(client, namespace, secretName) + err = testutil.DeleteSecret(client, namespace, secretName) if err != nil { logrus.Errorf("Error while deleting the secret %v", err) } diff --git a/internal/pkg/handler/update.go b/internal/pkg/handler/update.go index 7e5ba0a2..8690ab1f 100644 --- a/internal/pkg/handler/update.go +++ b/internal/pkg/handler/update.go @@ -38,34 +38,38 @@ func rollingUpgrade(r ResourceUpdatedHandler, rollingUpgradeType string) { if err != nil { logrus.Fatalf("Unable to create Kubernetes client error = %v", err) } - var namespace, resourceName, shaData, oldSHAdata, envarPostfix, annotation string - if _, ok := r.Resource.(*v1.ConfigMap); ok { - logrus.Infof("Performing 'Updated' action for resource of type 'configmap'") - namespace = r.Resource.(*v1.ConfigMap).Namespace - resourceName = r.Resource.(*v1.ConfigMap).Name - envarPostfix = common.ConfigmapEnvarPostfix - annotation = common.ConfigmapUpdateOnChangeAnnotation - shaData = getSHAfromConfigmapData(r.Resource.(*v1.ConfigMap).Data) - oldSHAdata = getSHAfromConfigmapData(r.OldResource.(*v1.ConfigMap).Data) - } else if _, ok := r.Resource.(*v1.Secret); ok { - logrus.Infof("Performing 'Updated' action for resource of type 'secret'") - namespace = r.Resource.(*v1.Secret).Namespace - resourceName = r.Resource.(*v1.Secret).Name - envarPostfix = common.SecretEnvarPostfix - annotation = common.SecretUpdateOnChangeAnnotation - shaData = getSHAfromSecretData(r.Resource.(*v1.Secret).Data) - oldSHAdata = getSHAfromSecretData(r.OldResource.(*v1.Secret).Data) - } else { - logrus.Warnf("Invalid resource: Resource should be 'Secret' or 'Configmap' but found, %v", r.Resource) - } + var shaData, oldSHAdata string + shaData = getSHAfromData(r.Resource) + oldSHAdata = getSHAfromData(r.OldResource) if shaData != oldSHAdata { + var namespace, resourceName, envarPostfix, annotation string + if _, ok := r.Resource.(*v1.ConfigMap); ok { + logrus.Infof("Performing 'Updated' action for resource of type 'configmap'") + namespace = r.Resource.(*v1.ConfigMap).Namespace + resourceName = r.Resource.(*v1.ConfigMap).Name + envarPostfix = common.ConfigmapEnvarPostfix + annotation = common.ConfigmapUpdateOnChangeAnnotation + } else if _, ok := r.Resource.(*v1.Secret); ok { + logrus.Infof("Performing 'Updated' action for resource of type 'secret'") + namespace = r.Resource.(*v1.Secret).Namespace + resourceName = r.Resource.(*v1.Secret).Name + envarPostfix = common.SecretEnvarPostfix + annotation = common.SecretUpdateOnChangeAnnotation + } else { + logrus.Warnf("Invalid resource: Resource should be 'Secret' or 'Configmap' but found, %v", r.Resource) + } + if rollingUpgradeType == "deployments" { - RollingUpgradeDeployment(client, namespace, resourceName, shaData, envarPostfix, annotation) + err = RollingUpgradeDeployment(client, namespace, resourceName, shaData, envarPostfix, annotation) } else if rollingUpgradeType == "daemonsets" { - RollingUpgradeDaemonSets(client, namespace, resourceName, shaData, envarPostfix, annotation) + err = RollingUpgradeDaemonSets(client, namespace, resourceName, shaData, envarPostfix, annotation) } else if rollingUpgradeType == "statefulSets" { - RollingUpgradeStatefulSets(client, namespace, resourceName, shaData, envarPostfix, annotation) + err = RollingUpgradeStatefulSets(client, namespace, resourceName, shaData, envarPostfix, annotation) + } + + if err != nil { + logrus.Errorf("Rolling upgrade failed for %s of resource type %s", resourceName, rollingUpgradeType) } } else { logrus.Infof("Resource update will not happen because no data change detected") @@ -205,22 +209,19 @@ func updateEnvVar(envs []v1.EnvVar, envar string, shaData string) bool { return false } -func getSHAfromConfigmapData(data map[string]string) string { - logrus.Infof("Generating SHA for configmap data") +func getSHAfromData(resource interface{}) string { values := []string{} - for k, v := range data { - values = append(values, k+"="+v) + if _, ok := resource.(*v1.ConfigMap); ok { + logrus.Infof("Generating SHA for configmap data") + for k, v := range resource.(*v1.ConfigMap).Data { + values = append(values, k+"="+v) + } + } else if _, ok := resource.(*v1.Secret); ok{ + logrus.Infof("Generating SHA for secret data") + for k, v := range resource.(*v1.Secret).Data { + values = append(values, k+"="+string(v[:])) + } } sort.Strings(values) return crypto.GenerateSHA(strings.Join(values, ";")) -} - -func getSHAfromSecretData(data map[string][]byte) string { - logrus.Infof("Generating SHA for secret data") - values := []string{} - for k, v := range data { - values = append(values, k+"="+string(v[:])) - } - sort.Strings(values) - return crypto.GenerateSHA(strings.Join(values, ";")) -} +} \ No newline at end of file diff --git a/internal/pkg/handler/update_test.go b/internal/pkg/handler/update_test.go index 45ec1e46..a64d173e 100644 --- a/internal/pkg/handler/update_test.go +++ b/internal/pkg/handler/update_test.go @@ -59,37 +59,37 @@ func setup() { } // Creating Deployment with configmap - testutil.CreateDeployment(client, configmapName, namespace) + _, err = testutil.CreateDeployment(client, configmapName, namespace) if err != nil { logrus.Errorf("Error in Deployment with configmap creation: %v", err) } // Creating Deployment with secret - testutil.CreateDeployment(client, secretName, namespace) + _, err = testutil.CreateDeployment(client, secretName, namespace) if err != nil { logrus.Errorf("Error in Deployment with secret creation: %v", err) } // Creating Daemonset with configmap - testutil.CreateDaemonset(client, configmapName, namespace) + _, err = testutil.CreateDaemonset(client, configmapName, namespace) if err != nil { logrus.Errorf("Error in Daemonset with configmap creation: %v", err) } // Creating Daemonset with secret - testutil.CreateDaemonset(client, secretName, namespace) + _, err = testutil.CreateDaemonset(client, secretName, namespace) if err != nil { logrus.Errorf("Error in Daemonset with secret creation: %v", err) } // Creating Statefulset with configmap - testutil.CreateStatefulset(client, configmapName, namespace) + _, err = testutil.CreateStatefulset(client, configmapName, namespace) if err != nil { logrus.Errorf("Error in Statefulset with configmap creation: %v", err) } // Creating Statefulset with secret - testutil.CreateStatefulset(client, secretName, namespace) + _, err = testutil.CreateStatefulset(client, secretName, namespace) if err != nil { logrus.Errorf("Error in Statefulset with secret creation: %v", err) } @@ -152,8 +152,11 @@ func teardown() { func TestRollingUpgradeForDeploymentWithConfigmap(t *testing.T) { shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, configmapName, "www.stakater.com") - RollingUpgradeDeployment(client, namespace, configmapName, shaData, common.ConfigmapEnvarPostfix, common.ConfigmapUpdateOnChangeAnnotation) + err := RollingUpgradeDeployment(client, namespace, configmapName, shaData, common.ConfigmapEnvarPostfix, common.ConfigmapUpdateOnChangeAnnotation) time.Sleep(5 * time.Second) + if err != nil { + t.Errorf("Rolling upgrade failed for Deployment with configmap") + } logrus.Infof("Verifying deployment update") updated := testutil.VerifyDeploymentUpdate(client, namespace, configmapName, common.ConfigmapEnvarPostfix, shaData, common.ConfigmapUpdateOnChangeAnnotation) @@ -164,8 +167,11 @@ func TestRollingUpgradeForDeploymentWithConfigmap(t *testing.T) { func TestRollingUpgradeForDeploymentWithSecret(t *testing.T) { shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, "dGVzdFVwZGF0ZWRTZWNyZXRFbmNvZGluZ0ZvclJlbG9hZGVy") - RollingUpgradeDeployment(client, namespace, secretName, shaData, common.SecretEnvarPostfix, common.SecretUpdateOnChangeAnnotation) + err := RollingUpgradeDeployment(client, namespace, secretName, shaData, common.SecretEnvarPostfix, common.SecretUpdateOnChangeAnnotation) time.Sleep(5 * time.Second) + if err != nil { + t.Errorf("Rolling upgrade failed for Deployment with Secret") + } logrus.Infof("Verifying deployment update") updated := testutil.VerifyDeploymentUpdate(client, namespace, secretName, common.SecretEnvarPostfix, shaData, common.SecretUpdateOnChangeAnnotation) @@ -176,8 +182,11 @@ func TestRollingUpgradeForDeploymentWithSecret(t *testing.T) { func TestRollingUpgradeForDaemonsetWithConfigmap(t *testing.T) { shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "www.facebook.com") - RollingUpgradeDaemonSets(client, namespace, configmapName, shaData, common.ConfigmapEnvarPostfix, common.ConfigmapUpdateOnChangeAnnotation) + err := RollingUpgradeDaemonSets(client, namespace, configmapName, shaData, common.ConfigmapEnvarPostfix, common.ConfigmapUpdateOnChangeAnnotation) time.Sleep(5 * time.Second) + if err != nil { + t.Errorf("Rolling upgrade failed for DaemonSet with configmap") + } logrus.Infof("Verifying daemonset update") updated := testutil.VerifyDaemonsetUpdate(client, namespace, configmapName, common.ConfigmapEnvarPostfix, shaData, common.ConfigmapUpdateOnChangeAnnotation) @@ -188,8 +197,11 @@ func TestRollingUpgradeForDaemonsetWithConfigmap(t *testing.T) { func TestRollingUpgradeForDaemonsetWithSecret(t *testing.T) { shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, "d3d3LmZhY2Vib29rLmNvbQ==") - RollingUpgradeDaemonSets(client, namespace, secretName, shaData, common.SecretEnvarPostfix, common.SecretUpdateOnChangeAnnotation) + err := RollingUpgradeDaemonSets(client, namespace, secretName, shaData, common.SecretEnvarPostfix, common.SecretUpdateOnChangeAnnotation) time.Sleep(5 * time.Second) + if err != nil { + t.Errorf("Rolling upgrade failed for DaemonSet with secret") + } logrus.Infof("Verifying daemonset update") updated := testutil.VerifyDaemonsetUpdate(client, namespace, secretName, common.SecretEnvarPostfix, shaData, common.SecretUpdateOnChangeAnnotation) @@ -200,8 +212,11 @@ func TestRollingUpgradeForDaemonsetWithSecret(t *testing.T) { func TestRollingUpgradeForStatefulsetWithConfigmap(t *testing.T) { shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "www.twitter.com") - RollingUpgradeStatefulSets(client, namespace, configmapName, shaData, common.ConfigmapEnvarPostfix, common.ConfigmapUpdateOnChangeAnnotation) + err := RollingUpgradeStatefulSets(client, namespace, configmapName, shaData, common.ConfigmapEnvarPostfix, common.ConfigmapUpdateOnChangeAnnotation) time.Sleep(5 * time.Second) + if err != nil { + t.Errorf("Rolling upgrade failed for StatefulSet with configmap") + } logrus.Infof("Verifying statefulset update") updated := testutil.VerifyStatefulsetUpdate(client, namespace, configmapName, common.ConfigmapEnvarPostfix, shaData, common.ConfigmapUpdateOnChangeAnnotation) @@ -212,8 +227,11 @@ func TestRollingUpgradeForStatefulsetWithConfigmap(t *testing.T) { func TestRollingUpgradeForStatefulsetWithSecret(t *testing.T) { shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, "d3d3LnR3aXR0ZXIuY29t") - RollingUpgradeStatefulSets(client, namespace, secretName, shaData, common.SecretEnvarPostfix, common.SecretUpdateOnChangeAnnotation) + err := RollingUpgradeStatefulSets(client, namespace, secretName, shaData, common.SecretEnvarPostfix, common.SecretUpdateOnChangeAnnotation) time.Sleep(5 * time.Second) + if err != nil { + t.Errorf("Rolling upgrade failed for StatefulSet with secret") + } logrus.Infof("Verifying statefulset update") updated := testutil.VerifyStatefulsetUpdate(client, namespace, secretName, common.SecretEnvarPostfix, shaData, common.SecretUpdateOnChangeAnnotation) diff --git a/internal/pkg/testutil/kube.go b/internal/pkg/testutil/kube.go index 75026ac2..7abd836f 100644 --- a/internal/pkg/testutil/kube.go +++ b/internal/pkg/testutil/kube.go @@ -326,7 +326,7 @@ func ConvertResourceToSHA(resourceType string, namespace string, resourceName st } else if resourceType == ConfigmapResourceType { configmap := GetConfigmap(namespace, resourceName, data) for k, v := range configmap.Data { - values = append(values, k+"="+string(v[:])) + values = append(values, k+"="+v) } } sort.Strings(values) From effc40cab1af53dcec98d18d9eb4f668c92c1bf5 Mon Sep 17 00:00:00 2001 From: faizanahmad055 Date: Mon, 23 Jul 2018 18:31:33 +0500 Subject: [PATCH 13/21] Added Daemonset and statefulset e2e test --- internal/pkg/controller/controller_test.go | 678 +++++++++++++++++++-- internal/pkg/handler/update.go | 6 +- internal/pkg/handler/update_test.go | 84 +-- internal/pkg/testutil/kube.go | 48 +- 4 files changed, 703 insertions(+), 113 deletions(-) diff --git a/internal/pkg/controller/controller_test.go b/internal/pkg/controller/controller_test.go index 9ca560af..d417e7e7 100644 --- a/internal/pkg/controller/controller_test.go +++ b/internal/pkg/controller/controller_test.go @@ -18,6 +18,9 @@ var ( namespace = "test-reloader" configmapNamePrefix = "testconfigmap-reloader" secretNamePrefix = "testsecret-reloader" + data = "dGVzdFNlY3JldEVuY29kaW5nRm9yUmVsb2FkZXI=" + newData = "dGVzdE5ld1NlY3JldEVuY29kaW5nRm9yUmVsb2FkZXI=" + updatedData = "dGVzdFVwZGF0ZWRTZWNyZXRFbmNvZGluZ0ZvclJlbG9hZGVy" ) func TestMain(m *testing.M) { @@ -55,7 +58,7 @@ func TestControllerUpdatingConfigmapShouldCreateEnvInDeployment(t *testing.T) { defer close(stop) logrus.Infof("Starting controller") go controller.Run(1, stop) - time.Sleep(10 * time.Second) + time.Sleep(5 * time.Second) // Creating configmap configmapName := configmapNamePrefix + "-update-" + common.RandSeq(5) @@ -87,7 +90,7 @@ func TestControllerUpdatingConfigmapShouldCreateEnvInDeployment(t *testing.T) { if !updated { t.Errorf("Deployment was not updated") } - time.Sleep(10 * time.Second) + time.Sleep(5 * time.Second) // Deleting deployment err = testutil.DeleteDeployment(client, namespace, configmapName) @@ -116,7 +119,7 @@ func TestControllerForUpdatingConfigmapShouldUpdateDeployment(t *testing.T) { defer close(stop) logrus.Infof("Starting controller") go controller.Run(1, stop) - time.Sleep(10 * time.Second) + time.Sleep(5 * time.Second) // Creating secret configmapName := configmapNamePrefix + "-update-" + common.RandSeq(5) @@ -141,15 +144,6 @@ func TestControllerForUpdatingConfigmapShouldUpdateDeployment(t *testing.T) { t.Errorf("Configmap was not updated") } - // Verifying deployment update - logrus.Infof("Verifying env var has been created") - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "www.stakater.com") - updated := testutil.VerifyDeploymentUpdate(client, namespace, configmapName, common.ConfigmapEnvarPostfix, shaData, common.ConfigmapUpdateOnChangeAnnotation) - if !updated { - t.Errorf("Deployment was not updated") - } - time.Sleep(10 * time.Second) - // Updating configmap for second time updateErr = testutil.UpdateConfigMap(configmapClient, namespace, configmapName, "", "aurorasolutions.io") if updateErr != nil { @@ -162,12 +156,12 @@ func TestControllerForUpdatingConfigmapShouldUpdateDeployment(t *testing.T) { // Verifying deployment update logrus.Infof("Verifying env var has been updated") - shaData = testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "aurorasolutions.io") - updated = testutil.VerifyDeploymentUpdate(client, namespace, configmapName, common.ConfigmapEnvarPostfix, shaData, common.ConfigmapUpdateOnChangeAnnotation) + shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "aurorasolutions.io") + updated := testutil.VerifyDeploymentUpdate(client, namespace, configmapName, common.ConfigmapEnvarPostfix, shaData, common.ConfigmapUpdateOnChangeAnnotation) if !updated { t.Errorf("Deployment was not updated") } - time.Sleep(10 * time.Second) + time.Sleep(5 * time.Second) // Deleting deployment err = testutil.DeleteDeployment(client, namespace, configmapName) @@ -196,7 +190,7 @@ func TestControllerUpdatingConfigmapLabelsShouldNotCreateorUpdateEnvInDeployment defer close(stop) logrus.Infof("Starting controller") go controller.Run(1, stop) - time.Sleep(10 * time.Second) + time.Sleep(5 * time.Second) // Creating configmap configmapName := configmapNamePrefix + "-update-" + common.RandSeq(5) @@ -228,7 +222,7 @@ func TestControllerUpdatingConfigmapLabelsShouldNotCreateorUpdateEnvInDeployment if updated { t.Errorf("Deployment should not be updated by changing label") } - time.Sleep(10 * time.Second) + time.Sleep(5 * time.Second) // Deleting deployment err = testutil.DeleteDeployment(client, namespace, configmapName) @@ -255,11 +249,10 @@ func TestControllerUpdatingSecretShouldCreateEnvInDeployment(t *testing.T) { stop := make(chan struct{}) defer close(stop) go controller.Run(1, stop) - time.Sleep(10 * time.Second) + time.Sleep(5 * time.Second) // Creating secret secretName := secretNamePrefix + "-update-" + common.RandSeq(5) - data := "dGVzdFNlY3JldEVuY29kaW5nRm9yUmVsb2FkZXI=" secretClient, err := testutil.CreateSecret(client, namespace, secretName, data) if err != nil { t.Errorf("Error in secret creation: %v", err) @@ -272,20 +265,19 @@ func TestControllerUpdatingSecretShouldCreateEnvInDeployment(t *testing.T) { } // Updating Secret - data = "dGVzdFVwZGF0ZWRTZWNyZXRFbmNvZGluZ0ZvclJlbG9hZGVy" - err = testutil.UpdateSecret(secretClient, namespace, secretName, "", data) + err = testutil.UpdateSecret(secretClient, namespace, secretName, "", newData) if err != nil { t.Errorf("Error while updating secret %v", err) } // Verifying Upgrade logrus.Infof("Verifying env var has been created") - shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, data) + shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, newData) updated := testutil.VerifyDeploymentUpdate(client, namespace, secretName, common.SecretEnvarPostfix, shaData, common.SecretUpdateOnChangeAnnotation) if !updated { t.Errorf("Deployment was not updated") } - time.Sleep(10 * time.Second) + //time.Sleep(5 * time.Second) // Deleting Deployment err = testutil.DeleteDeployment(client, namespace, secretName) @@ -312,11 +304,10 @@ func TestControllerUpdatingSecretShouldUpdateEnvInDeployment(t *testing.T) { stop := make(chan struct{}) defer close(stop) go controller.Run(1, stop) - time.Sleep(10 * time.Second) + time.Sleep(5 * time.Second) // Creating secret secretName := secretNamePrefix + "-update-" + common.RandSeq(5) - data := "dGVzdFNlY3JldEVuY29kaW5nRm9yUmVsb2FkZXI=" secretClient, err := testutil.CreateSecret(client, namespace, secretName, data) if err != nil { t.Errorf("Error in secret creation: %v", err) @@ -329,36 +320,25 @@ func TestControllerUpdatingSecretShouldUpdateEnvInDeployment(t *testing.T) { } // Updating Secret - data = "dGVzdFVwZGF0ZWRTZWNyZXRFbmNvZGluZ0ZvclJlbG9hZGVy" - err = testutil.UpdateSecret(secretClient, namespace, secretName, "", data) + err = testutil.UpdateSecret(secretClient, namespace, secretName, "", newData) if err != nil { t.Errorf("Error while updating secret %v", err) } - // Verifying Upgrade - logrus.Infof("Verifying env var has been created") - shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, data) - updated := testutil.VerifyDeploymentUpdate(client, namespace, secretName, common.SecretEnvarPostfix, shaData, common.SecretUpdateOnChangeAnnotation) - if !updated { - t.Errorf("Deployment was not updated") - } - time.Sleep(10 * time.Second) - // Updating Secret - data = "dGVzdFVwZGF0ZWRTZWNyZXRFbmNvZGluZ0ZvclJlbG9hZGVy" - err = testutil.UpdateSecret(secretClient, namespace, secretName, "", data) + err = testutil.UpdateSecret(secretClient, namespace, secretName, "", updatedData) if err != nil { t.Errorf("Error while updating secret %v", err) } // Verifying Upgrade logrus.Infof("Verifying env var has been updated") - shaData = testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, data) - updated = testutil.VerifyDeploymentUpdate(client, namespace, secretName, common.SecretEnvarPostfix, shaData, common.SecretUpdateOnChangeAnnotation) + shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, updatedData) + updated := testutil.VerifyDeploymentUpdate(client, namespace, secretName, common.SecretEnvarPostfix, shaData, common.SecretUpdateOnChangeAnnotation) if !updated { t.Errorf("Deployment was not updated") } - time.Sleep(10 * time.Second) + //time.Sleep(5 * time.Second) // Deleting Deployment err = testutil.DeleteDeployment(client, namespace, secretName) @@ -385,11 +365,10 @@ func TestControllerUpdatingSecretLabelsShouldNotCreateorUpdateEnvInDeployment(t stop := make(chan struct{}) defer close(stop) go controller.Run(1, stop) - time.Sleep(10 * time.Second) + time.Sleep(5 * time.Second) // Creating secret secretName := secretNamePrefix + "-update-" + common.RandSeq(5) - data := "dGVzdFNlY3JldEVuY29kaW5nRm9yUmVsb2FkZXI=" secretClient, err := testutil.CreateSecret(client, namespace, secretName, data) if err != nil { t.Errorf("Error in secret creation: %v", err) @@ -413,7 +392,7 @@ func TestControllerUpdatingSecretLabelsShouldNotCreateorUpdateEnvInDeployment(t if updated { t.Errorf("Deployment should not be updated by changing label in secret") } - time.Sleep(10 * time.Second) + //time.Sleep(5 * time.Second) // Deleting Deployment err = testutil.DeleteDeployment(client, namespace, secretName) @@ -428,3 +407,614 @@ func TestControllerUpdatingSecretLabelsShouldNotCreateorUpdateEnvInDeployment(t } time.Sleep(5 * time.Second) } + +// Perform rolling upgrade on DaemonSet and create env var upon updating the configmap +func TestControllerUpdatingConfigmapShouldCreateEnvInDaemonSet(t *testing.T) { + // Creating Controller + logrus.Infof("Creating controller") + controller, err := NewController(client, testutil.ConfigmapResourceType, 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 controller.Run(1, stop) + time.Sleep(5 * time.Second) + + // Creating configmap + configmapName := configmapNamePrefix + "-update-" + common.RandSeq(5) + configmapClient, err := testutil.CreateConfigMap(client, namespace, configmapName, "www.google.com") + if err != nil { + t.Errorf("Error while creating the configmap %v", err) + } + + // Creating DaemonSet + _, err = testutil.CreateDaemonSet(client, configmapName, namespace) + if err != nil { + t.Errorf("Error in DaemonSet creation: %v", err) + } + + // Updating configmap for first time + updateErr := testutil.UpdateConfigMap(configmapClient, namespace, configmapName, "", "www.stakater.com") + if updateErr != nil { + err = controller.client.CoreV1().ConfigMaps(namespace).Delete(configmapName, &metav1.DeleteOptions{}) + if err != nil { + logrus.Errorf("Error while deleting the configmap %v", err) + } + t.Errorf("Configmap was not updated") + } + + // Verifying DaemonSet update + logrus.Infof("Verifying env var has been created") + shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "www.stakater.com") + updated := testutil.VerifyDaemonSetUpdate(client, namespace, configmapName, common.ConfigmapEnvarPostfix, shaData, common.ConfigmapUpdateOnChangeAnnotation) + if !updated { + t.Errorf("DaemonSet was not updated") + } + time.Sleep(5 * time.Second) + + // Deleting DaemonSet + err = testutil.DeleteDaemonSet(client, namespace, configmapName) + if err != nil { + logrus.Errorf("Error while deleting the DaemonSet %v", err) + } + + // Deleting configmap + err = testutil.DeleteConfigMap(client, namespace, configmapName) + if err != nil { + logrus.Errorf("Error while deleting the configmap %v", err) + } + time.Sleep(5 * time.Second) +} + +// Perform rolling upgrade on DaemonSet and update env var upon updating the configmap +func TestControllerForUpdatingConfigmapShouldUpdateDaemonSet(t *testing.T) { + // Creating controller + logrus.Infof("Creating controller") + controller, err := NewController(client, testutil.ConfigmapResourceType, 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 controller.Run(1, stop) + time.Sleep(5 * time.Second) + + // Creating secret + configmapName := configmapNamePrefix + "-update-" + common.RandSeq(5) + configmapClient, err := testutil.CreateConfigMap(client, namespace, configmapName, "www.google.com") + if err != nil { + t.Errorf("Error while creating the configmap %v", err) + } + + // Creating DaemonSet + _, err = testutil.CreateDaemonSet(client, configmapName, namespace) + if err != nil { + t.Errorf("Error in DaemonSet creation: %v", err) + } + + // Updating configmap for first time + updateErr := testutil.UpdateConfigMap(configmapClient, namespace, configmapName, "", "www.stakater.com") + if updateErr != nil { + err = controller.client.CoreV1().ConfigMaps(namespace).Delete(configmapName, &metav1.DeleteOptions{}) + if err != nil { + logrus.Errorf("Error while deleting the configmap %v", err) + } + t.Errorf("Configmap was not updated") + } + + // Updating configmap for second time + updateErr = testutil.UpdateConfigMap(configmapClient, namespace, configmapName, "", "aurorasolutions.io") + if updateErr != nil { + err = controller.client.CoreV1().ConfigMaps(namespace).Delete(configmapName, &metav1.DeleteOptions{}) + if err != nil { + logrus.Errorf("Error while deleting the configmap %v", err) + } + t.Errorf("Configmap was not updated") + } + + // Verifying DaemonSet update + logrus.Infof("Verifying env var has been updated") + shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "aurorasolutions.io") + updated := testutil.VerifyDaemonSetUpdate(client, namespace, configmapName, common.ConfigmapEnvarPostfix, shaData, common.ConfigmapUpdateOnChangeAnnotation) + if !updated { + t.Errorf("DaemonSet was not updated") + } + time.Sleep(5 * time.Second) + + // Deleting DaemonSet + err = testutil.DeleteDaemonSet(client, namespace, configmapName) + if err != nil { + logrus.Errorf("Error while deleting the DaemonSet %v", err) + } + + // Deleting configmap + err = testutil.DeleteConfigMap(client, namespace, configmapName) + if err != nil { + logrus.Errorf("Error while deleting the configmap %v", err) + } + time.Sleep(5 * time.Second) +} + +// Perform rolling upgrade on secret and create a env var upon updating the secret +func TestControllerUpdatingSecretShouldCreateEnvInDaemonSet(t *testing.T) { + // Creating controller + controller, err := NewController(client, testutil.SecretResourceType, namespace) + if err != nil { + logrus.Errorf("Unable to create NewController error = %v", err) + return + } + stop := make(chan struct{}) + defer close(stop) + go controller.Run(1, stop) + time.Sleep(5 * time.Second) + + // Creating secret + secretName := secretNamePrefix + "-update-" + common.RandSeq(5) + secretClient, err := testutil.CreateSecret(client, namespace, secretName, data) + if err != nil { + t.Errorf("Error in secret creation: %v", err) + } + + // Creating DaemonSet + _, err = testutil.CreateDaemonSet(client, secretName, namespace) + if err != nil { + t.Errorf("Error in DaemonSet creation: %v", err) + } + + // Updating Secret + err = testutil.UpdateSecret(secretClient, namespace, secretName, "", newData) + if err != nil { + t.Errorf("Error while updating secret %v", err) + } + + // Verifying Upgrade + logrus.Infof("Verifying env var has been created") + shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, newData) + updated := testutil.VerifyDaemonSetUpdate(client, namespace, secretName, common.SecretEnvarPostfix, shaData, common.SecretUpdateOnChangeAnnotation) + if !updated { + t.Errorf("DaemonSet was not updated") + } + //time.Sleep(5 * time.Second) + + // Deleting DaemonSet + err = testutil.DeleteDaemonSet(client, namespace, secretName) + if err != nil { + logrus.Errorf("Error while deleting the DaemonSet %v", err) + } + + //Deleting Secret + err = testutil.DeleteSecret(client, namespace, secretName) + if err != nil { + logrus.Errorf("Error while deleting the secret %v", err) + } + time.Sleep(5 * time.Second) +} + +// Perform rolling upgrade on DaemonSet and update env var upon updating the secret +func TestControllerUpdatingSecretShouldUpdateEnvInDaemonSet(t *testing.T) { + // Creating controller + controller, err := NewController(client, testutil.SecretResourceType, namespace) + if err != nil { + logrus.Errorf("Unable to create NewController error = %v", err) + return + } + stop := make(chan struct{}) + defer close(stop) + go controller.Run(1, stop) + time.Sleep(5 * time.Second) + + // Creating secret + secretName := secretNamePrefix + "-update-" + common.RandSeq(5) + secretClient, err := testutil.CreateSecret(client, namespace, secretName, data) + if err != nil { + t.Errorf("Error in secret creation: %v", err) + } + + // Creating DaemonSet + _, err = testutil.CreateDaemonSet(client, secretName, namespace) + if err != nil { + t.Errorf("Error in DaemonSet creation: %v", err) + } + + // Updating Secret + err = testutil.UpdateSecret(secretClient, namespace, secretName, "", newData) + if err != nil { + t.Errorf("Error while updating secret %v", err) + } + + // Updating Secret + err = testutil.UpdateSecret(secretClient, namespace, secretName, "", updatedData) + if err != nil { + t.Errorf("Error while updating secret %v", err) + } + + // Verifying Upgrade + logrus.Infof("Verifying env var has been updated") + shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, updatedData) + updated := testutil.VerifyDaemonSetUpdate(client, namespace, secretName, common.SecretEnvarPostfix, shaData, common.SecretUpdateOnChangeAnnotation) + if !updated { + t.Errorf("DaemonSet was not updated") + } + //time.Sleep(5 * time.Second) + + // Deleting DaemonSet + err = testutil.DeleteDaemonSet(client, namespace, secretName) + if err != nil { + logrus.Errorf("Error while deleting the DaemonSet %v", err) + } + + //Deleting Secret + err = testutil.DeleteSecret(client, namespace, secretName) + if err != nil { + logrus.Errorf("Error while deleting the secret %v", err) + } + time.Sleep(5 * time.Second) +} + +// Do not Perform rolling upgrade on secret and create or update a env var upon updating the label in secret +func TestControllerUpdatingSecretLabelsShouldNotCreateorUpdateEnvInDaemonSet(t *testing.T) { + // Creating controller + controller, err := NewController(client, testutil.SecretResourceType, namespace) + if err != nil { + logrus.Errorf("Unable to create NewController error = %v", err) + return + } + stop := make(chan struct{}) + defer close(stop) + go controller.Run(1, stop) + time.Sleep(5 * time.Second) + + // Creating secret + secretName := secretNamePrefix + "-update-" + common.RandSeq(5) + secretClient, err := testutil.CreateSecret(client, namespace, secretName, data) + if err != nil { + t.Errorf("Error in secret creation: %v", err) + } + + // Creating DaemonSet + _, err = testutil.CreateDaemonSet(client, secretName, namespace) + if err != nil { + t.Errorf("Error in DaemonSet creation: %v", err) + } + + err = testutil.UpdateSecret(secretClient, namespace, secretName, "test", data) + if err != nil { + t.Errorf("Error while updating secret %v", err) + } + + // Verifying Upgrade + logrus.Infof("Verifying env var has been created") + shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, data) + updated := testutil.VerifyDaemonSetUpdate(client, namespace, secretName, common.SecretEnvarPostfix, shaData, common.SecretUpdateOnChangeAnnotation) + if updated { + t.Errorf("DaemonSet should not be updated by changing label in secret") + } + //time.Sleep(5 * time.Second) + + // Deleting DaemonSet + err = testutil.DeleteDaemonSet(client, namespace, secretName) + if err != nil { + logrus.Errorf("Error while deleting the DaemonSet %v", err) + } + + //Deleting Secret + err = testutil.DeleteSecret(client, namespace, secretName) + if err != nil { + logrus.Errorf("Error while deleting the secret %v", err) + } + time.Sleep(5 * time.Second) +} + +// Perform rolling upgrade on StatefulSet and create env var upon updating the configmap +func TestControllerUpdatingConfigmapShouldCreateEnvInStatefulSet(t *testing.T) { + // Creating Controller + logrus.Infof("Creating controller") + controller, err := NewController(client, testutil.ConfigmapResourceType, 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 controller.Run(1, stop) + time.Sleep(5 * time.Second) + + // Creating configmap + configmapName := configmapNamePrefix + "-update-" + common.RandSeq(5) + configmapClient, err := testutil.CreateConfigMap(client, namespace, configmapName, "www.google.com") + if err != nil { + t.Errorf("Error while creating the configmap %v", err) + } + + // Creating StatefulSet + _, err = testutil.CreateStatefulSet(client, configmapName, namespace) + if err != nil { + t.Errorf("Error in StatefulSet creation: %v", err) + } + + // Updating configmap for first time + updateErr := testutil.UpdateConfigMap(configmapClient, namespace, configmapName, "", "www.stakater.com") + if updateErr != nil { + err = controller.client.CoreV1().ConfigMaps(namespace).Delete(configmapName, &metav1.DeleteOptions{}) + if err != nil { + logrus.Errorf("Error while deleting the configmap %v", err) + } + t.Errorf("Configmap was not updated") + } + + // Verifying StatefulSet update + logrus.Infof("Verifying env var has been created") + shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "www.stakater.com") + updated := testutil.VerifyStatefulSetUpdate(client, namespace, configmapName, common.ConfigmapEnvarPostfix, shaData, common.ConfigmapUpdateOnChangeAnnotation) + if !updated { + t.Errorf("StatefulSet was not updated") + } + time.Sleep(5 * time.Second) + + // Deleting StatefulSet + err = testutil.DeleteStatefulSet(client, namespace, configmapName) + if err != nil { + logrus.Errorf("Error while deleting the StatefulSet %v", err) + } + + // Deleting configmap + err = testutil.DeleteConfigMap(client, namespace, configmapName) + if err != nil { + logrus.Errorf("Error while deleting the configmap %v", err) + } + time.Sleep(5 * time.Second) +} + +// Perform rolling upgrade on StatefulSet and update env var upon updating the configmap +func TestControllerForUpdatingConfigmapShouldUpdateStatefulSet(t *testing.T) { + // Creating controller + logrus.Infof("Creating controller") + controller, err := NewController(client, testutil.ConfigmapResourceType, 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 controller.Run(1, stop) + time.Sleep(5 * time.Second) + + // Creating secret + configmapName := configmapNamePrefix + "-update-" + common.RandSeq(5) + configmapClient, err := testutil.CreateConfigMap(client, namespace, configmapName, "www.google.com") + if err != nil { + t.Errorf("Error while creating the configmap %v", err) + } + + // Creating StatefulSet + _, err = testutil.CreateStatefulSet(client, configmapName, namespace) + if err != nil { + t.Errorf("Error in StatefulSet creation: %v", err) + } + + // Updating configmap for first time + updateErr := testutil.UpdateConfigMap(configmapClient, namespace, configmapName, "", "www.stakater.com") + if updateErr != nil { + err = controller.client.CoreV1().ConfigMaps(namespace).Delete(configmapName, &metav1.DeleteOptions{}) + if err != nil { + logrus.Errorf("Error while deleting the configmap %v", err) + } + t.Errorf("Configmap was not updated") + } + + // Updating configmap for second time + updateErr = testutil.UpdateConfigMap(configmapClient, namespace, configmapName, "", "aurorasolutions.io") + if updateErr != nil { + err = controller.client.CoreV1().ConfigMaps(namespace).Delete(configmapName, &metav1.DeleteOptions{}) + if err != nil { + logrus.Errorf("Error while deleting the configmap %v", err) + } + t.Errorf("Configmap was not updated") + } + + // Verifying StatefulSet update + logrus.Infof("Verifying env var has been updated") + shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "aurorasolutions.io") + updated := testutil.VerifyStatefulSetUpdate(client, namespace, configmapName, common.ConfigmapEnvarPostfix, shaData, common.ConfigmapUpdateOnChangeAnnotation) + if !updated { + t.Errorf("StatefulSet was not updated") + } + time.Sleep(5 * time.Second) + + // Deleting StatefulSet + err = testutil.DeleteStatefulSet(client, namespace, configmapName) + if err != nil { + logrus.Errorf("Error while deleting the StatefulSet %v", err) + } + + // Deleting configmap + err = testutil.DeleteConfigMap(client, namespace, configmapName) + if err != nil { + logrus.Errorf("Error while deleting the configmap %v", err) + } + time.Sleep(5 * time.Second) +} + +// Do not Perform rolling upgrade on StatefulSet and create env var upon updating the labels configmap +func TestControllerUpdatingConfigmapLabelsShouldNotCreateorUpdateEnvInStatefulSet(t *testing.T) { + // Creating Controller + logrus.Infof("Creating controller") + controller, err := NewController(client, testutil.ConfigmapResourceType, 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 controller.Run(1, stop) + time.Sleep(5 * time.Second) + + // Creating configmap + configmapName := configmapNamePrefix + "-update-" + common.RandSeq(5) + configmapClient, err := testutil.CreateConfigMap(client, namespace, configmapName, "www.google.com") + if err != nil { + t.Errorf("Error while creating the configmap %v", err) + } + + // Creating StatefulSet + _, err = testutil.CreateStatefulSet(client, configmapName, namespace) + if err != nil { + t.Errorf("Error in StatefulSet creation: %v", err) + } + + // Updating configmap for first time + updateErr := testutil.UpdateConfigMap(configmapClient, namespace, configmapName, "test", "www.google.com") + if updateErr != nil { + err = controller.client.CoreV1().ConfigMaps(namespace).Delete(configmapName, &metav1.DeleteOptions{}) + if err != nil { + logrus.Errorf("Error while deleting the configmap %v", err) + } + t.Errorf("Configmap was not updated") + } + + // Verifying StatefulSet update + logrus.Infof("Verifying env var has been created") + shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "www.google.com") + updated := testutil.VerifyStatefulSetUpdate(client, namespace, configmapName, common.ConfigmapEnvarPostfix, shaData, common.ConfigmapUpdateOnChangeAnnotation) + if updated { + t.Errorf("StatefulSet should not be updated by changing label") + } + time.Sleep(5 * time.Second) + + // Deleting StatefulSet + err = testutil.DeleteStatefulSet(client, namespace, configmapName) + if err != nil { + logrus.Errorf("Error while deleting the StatefulSet %v", err) + } + + // Deleting configmap + err = testutil.DeleteConfigMap(client, namespace, configmapName) + if err != nil { + logrus.Errorf("Error while deleting the configmap %v", err) + } + time.Sleep(5 * time.Second) +} + +// Perform rolling upgrade on secret and create a env var upon updating the secret +func TestControllerUpdatingSecretShouldCreateEnvInStatefulSet(t *testing.T) { + // Creating controller + controller, err := NewController(client, testutil.SecretResourceType, namespace) + if err != nil { + logrus.Errorf("Unable to create NewController error = %v", err) + return + } + stop := make(chan struct{}) + defer close(stop) + go controller.Run(1, stop) + time.Sleep(5 * time.Second) + + // Creating secret + secretName := secretNamePrefix + "-update-" + common.RandSeq(5) + secretClient, err := testutil.CreateSecret(client, namespace, secretName, data) + if err != nil { + t.Errorf("Error in secret creation: %v", err) + } + + // Creating StatefulSet + _, err = testutil.CreateStatefulSet(client, secretName, namespace) + if err != nil { + t.Errorf("Error in StatefulSet creation: %v", err) + } + + // Updating Secret + err = testutil.UpdateSecret(secretClient, namespace, secretName, "", newData) + if err != nil { + t.Errorf("Error while updating secret %v", err) + } + + // Verifying Upgrade + logrus.Infof("Verifying env var has been created") + shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, newData) + updated := testutil.VerifyStatefulSetUpdate(client, namespace, secretName, common.SecretEnvarPostfix, shaData, common.SecretUpdateOnChangeAnnotation) + if !updated { + t.Errorf("StatefulSet was not updated") + } + //time.Sleep(5 * time.Second) + + // Deleting StatefulSet + err = testutil.DeleteStatefulSet(client, namespace, secretName) + if err != nil { + logrus.Errorf("Error while deleting the StatefulSet %v", err) + } + + //Deleting Secret + err = testutil.DeleteSecret(client, namespace, secretName) + if err != nil { + logrus.Errorf("Error while deleting the secret %v", err) + } + time.Sleep(5 * time.Second) +} + +// Perform rolling upgrade on StatefulSet and update env var upon updating the secret +func TestControllerUpdatingSecretShouldUpdateEnvInStatefulSet(t *testing.T) { + // Creating controller + controller, err := NewController(client, testutil.SecretResourceType, namespace) + if err != nil { + logrus.Errorf("Unable to create NewController error = %v", err) + return + } + stop := make(chan struct{}) + defer close(stop) + go controller.Run(1, stop) + time.Sleep(5 * time.Second) + + // Creating secret + secretName := secretNamePrefix + "-update-" + common.RandSeq(5) + secretClient, err := testutil.CreateSecret(client, namespace, secretName, data) + if err != nil { + t.Errorf("Error in secret creation: %v", err) + } + + // Creating StatefulSet + _, err = testutil.CreateStatefulSet(client, secretName, namespace) + if err != nil { + t.Errorf("Error in StatefulSet creation: %v", err) + } + + // Updating Secret + err = testutil.UpdateSecret(secretClient, namespace, secretName, "", newData) + if err != nil { + t.Errorf("Error while updating secret %v", err) + } + + // Updating Secret + err = testutil.UpdateSecret(secretClient, namespace, secretName, "", updatedData) + if err != nil { + t.Errorf("Error while updating secret %v", err) + } + + // Verifying Upgrade + logrus.Infof("Verifying env var has been updated") + shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, updatedData) + updated := testutil.VerifyStatefulSetUpdate(client, namespace, secretName, common.SecretEnvarPostfix, shaData, common.SecretUpdateOnChangeAnnotation) + if !updated { + t.Errorf("StatefulSet was not updated") + } + //time.Sleep(5 * time.Second) + + // Deleting StatefulSet + err = testutil.DeleteStatefulSet(client, namespace, secretName) + if err != nil { + logrus.Errorf("Error while deleting the StatefulSet %v", err) + } + + //Deleting Secret + err = testutil.DeleteSecret(client, namespace, secretName) + if err != nil { + logrus.Errorf("Error while deleting the secret %v", err) + } + time.Sleep(5 * time.Second) +} diff --git a/internal/pkg/handler/update.go b/internal/pkg/handler/update.go index 8690ab1f..f0c694e4 100644 --- a/internal/pkg/handler/update.go +++ b/internal/pkg/handler/update.go @@ -59,7 +59,7 @@ func rollingUpgrade(r ResourceUpdatedHandler, rollingUpgradeType string) { } else { logrus.Warnf("Invalid resource: Resource should be 'Secret' or 'Configmap' but found, %v", r.Resource) } - + if rollingUpgradeType == "deployments" { err = RollingUpgradeDeployment(client, namespace, resourceName, shaData, envarPostfix, annotation) } else if rollingUpgradeType == "daemonsets" { @@ -216,7 +216,7 @@ func getSHAfromData(resource interface{}) string { for k, v := range resource.(*v1.ConfigMap).Data { values = append(values, k+"="+v) } - } else if _, ok := resource.(*v1.Secret); ok{ + } else if _, ok := resource.(*v1.Secret); ok { logrus.Infof("Generating SHA for secret data") for k, v := range resource.(*v1.Secret).Data { values = append(values, k+"="+string(v[:])) @@ -224,4 +224,4 @@ func getSHAfromData(resource interface{}) string { } sort.Strings(values) return crypto.GenerateSHA(strings.Join(values, ";")) -} \ No newline at end of file +} diff --git a/internal/pkg/handler/update_test.go b/internal/pkg/handler/update_test.go index a64d173e..7cc5461f 100644 --- a/internal/pkg/handler/update_test.go +++ b/internal/pkg/handler/update_test.go @@ -70,28 +70,28 @@ func setup() { logrus.Errorf("Error in Deployment with secret creation: %v", err) } - // Creating Daemonset with configmap - _, err = testutil.CreateDaemonset(client, configmapName, namespace) + // Creating DaemonSet with configmap + _, err = testutil.CreateDaemonSet(client, configmapName, namespace) if err != nil { - logrus.Errorf("Error in Daemonset with configmap creation: %v", err) + logrus.Errorf("Error in DaemonSet with configmap creation: %v", err) } - // Creating Daemonset with secret - _, err = testutil.CreateDaemonset(client, secretName, namespace) + // Creating DaemonSet with secret + _, err = testutil.CreateDaemonSet(client, secretName, namespace) if err != nil { - logrus.Errorf("Error in Daemonset with secret creation: %v", err) + logrus.Errorf("Error in DaemonSet with secret creation: %v", err) } - // Creating Statefulset with configmap - _, err = testutil.CreateStatefulset(client, configmapName, namespace) + // Creating StatefulSet with configmap + _, err = testutil.CreateStatefulSet(client, configmapName, namespace) if err != nil { - logrus.Errorf("Error in Statefulset with configmap creation: %v", err) + logrus.Errorf("Error in StatefulSet with configmap creation: %v", err) } - // Creating Statefulset with secret - _, err = testutil.CreateStatefulset(client, secretName, namespace) + // Creating StatefulSet with secret + _, err = testutil.CreateStatefulSet(client, secretName, namespace) if err != nil { - logrus.Errorf("Error in Statefulset with secret creation: %v", err) + logrus.Errorf("Error in StatefulSet with secret creation: %v", err) } } @@ -109,28 +109,28 @@ func teardown() { logrus.Errorf("Error while deleting deployment with secret %v", deploymentError) } - // Deleting Daemonset with configmap - daemonsetError := testutil.DeleteDaemonset(client, namespace, configmapName) - if daemonsetError != nil { - logrus.Errorf("Error while deleting daemonset with configmap %v", daemonsetError) + // Deleting DaemonSet with configmap + daemonSetError := testutil.DeleteDaemonSet(client, namespace, configmapName) + if daemonSetError != nil { + logrus.Errorf("Error while deleting daemonSet with configmap %v", daemonSetError) } // Deleting Deployment with secret - daemonsetError = testutil.DeleteDaemonset(client, namespace, secretName) - if daemonsetError != nil { - logrus.Errorf("Error while deleting daemonset with secret %v", daemonsetError) + daemonSetError = testutil.DeleteDaemonSet(client, namespace, secretName) + if daemonSetError != nil { + logrus.Errorf("Error while deleting daemonSet with secret %v", daemonSetError) } - // Deleting Statefulset with configmap - statefulsetError := testutil.DeleteStatefulset(client, namespace, configmapName) - if statefulsetError != nil { - logrus.Errorf("Error while deleting statefulset with configmap %v", statefulsetError) + // Deleting StatefulSet with configmap + statefulSetError := testutil.DeleteStatefulSet(client, namespace, configmapName) + if statefulSetError != nil { + logrus.Errorf("Error while deleting statefulSet with configmap %v", statefulSetError) } // Deleting Deployment with secret - statefulsetError = testutil.DeleteStatefulset(client, namespace, secretName) - if statefulsetError != nil { - logrus.Errorf("Error while deleting statefulset with secret %v", statefulsetError) + statefulSetError = testutil.DeleteStatefulSet(client, namespace, secretName) + if statefulSetError != nil { + logrus.Errorf("Error while deleting statefulSet with secret %v", statefulSetError) } // Deleting Configmap @@ -180,7 +180,7 @@ func TestRollingUpgradeForDeploymentWithSecret(t *testing.T) { } } -func TestRollingUpgradeForDaemonsetWithConfigmap(t *testing.T) { +func TestRollingUpgradeForDaemonSetWithConfigmap(t *testing.T) { shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "www.facebook.com") err := RollingUpgradeDaemonSets(client, namespace, configmapName, shaData, common.ConfigmapEnvarPostfix, common.ConfigmapUpdateOnChangeAnnotation) time.Sleep(5 * time.Second) @@ -188,14 +188,14 @@ func TestRollingUpgradeForDaemonsetWithConfigmap(t *testing.T) { t.Errorf("Rolling upgrade failed for DaemonSet with configmap") } - logrus.Infof("Verifying daemonset update") - updated := testutil.VerifyDaemonsetUpdate(client, namespace, configmapName, common.ConfigmapEnvarPostfix, shaData, common.ConfigmapUpdateOnChangeAnnotation) + logrus.Infof("Verifying daemonSet update") + updated := testutil.VerifyDaemonSetUpdate(client, namespace, configmapName, common.ConfigmapEnvarPostfix, shaData, common.ConfigmapUpdateOnChangeAnnotation) if !updated { - t.Errorf("Daemonset was not updated") + t.Errorf("DaemonSet was not updated") } } -func TestRollingUpgradeForDaemonsetWithSecret(t *testing.T) { +func TestRollingUpgradeForDaemonSetWithSecret(t *testing.T) { shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, "d3d3LmZhY2Vib29rLmNvbQ==") err := RollingUpgradeDaemonSets(client, namespace, secretName, shaData, common.SecretEnvarPostfix, common.SecretUpdateOnChangeAnnotation) time.Sleep(5 * time.Second) @@ -203,14 +203,14 @@ func TestRollingUpgradeForDaemonsetWithSecret(t *testing.T) { t.Errorf("Rolling upgrade failed for DaemonSet with secret") } - logrus.Infof("Verifying daemonset update") - updated := testutil.VerifyDaemonsetUpdate(client, namespace, secretName, common.SecretEnvarPostfix, shaData, common.SecretUpdateOnChangeAnnotation) + logrus.Infof("Verifying daemonSet update") + updated := testutil.VerifyDaemonSetUpdate(client, namespace, secretName, common.SecretEnvarPostfix, shaData, common.SecretUpdateOnChangeAnnotation) if !updated { - t.Errorf("Daemonset was not updated") + t.Errorf("DaemonSet was not updated") } } -func TestRollingUpgradeForStatefulsetWithConfigmap(t *testing.T) { +func TestRollingUpgradeForStatefulSetWithConfigmap(t *testing.T) { shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "www.twitter.com") err := RollingUpgradeStatefulSets(client, namespace, configmapName, shaData, common.ConfigmapEnvarPostfix, common.ConfigmapUpdateOnChangeAnnotation) time.Sleep(5 * time.Second) @@ -218,14 +218,14 @@ func TestRollingUpgradeForStatefulsetWithConfigmap(t *testing.T) { t.Errorf("Rolling upgrade failed for StatefulSet with configmap") } - logrus.Infof("Verifying statefulset update") - updated := testutil.VerifyStatefulsetUpdate(client, namespace, configmapName, common.ConfigmapEnvarPostfix, shaData, common.ConfigmapUpdateOnChangeAnnotation) + logrus.Infof("Verifying statefulSet update") + updated := testutil.VerifyStatefulSetUpdate(client, namespace, configmapName, common.ConfigmapEnvarPostfix, shaData, common.ConfigmapUpdateOnChangeAnnotation) if !updated { - t.Errorf("Statefulset was not updated") + t.Errorf("StatefulSet was not updated") } } -func TestRollingUpgradeForStatefulsetWithSecret(t *testing.T) { +func TestRollingUpgradeForStatefulSetWithSecret(t *testing.T) { shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, "d3d3LnR3aXR0ZXIuY29t") err := RollingUpgradeStatefulSets(client, namespace, secretName, shaData, common.SecretEnvarPostfix, common.SecretUpdateOnChangeAnnotation) time.Sleep(5 * time.Second) @@ -233,9 +233,9 @@ func TestRollingUpgradeForStatefulsetWithSecret(t *testing.T) { t.Errorf("Rolling upgrade failed for StatefulSet with secret") } - logrus.Infof("Verifying statefulset update") - updated := testutil.VerifyStatefulsetUpdate(client, namespace, secretName, common.SecretEnvarPostfix, shaData, common.SecretUpdateOnChangeAnnotation) + logrus.Infof("Verifying statefulSet update") + updated := testutil.VerifyStatefulSetUpdate(client, namespace, secretName, common.SecretEnvarPostfix, shaData, common.SecretUpdateOnChangeAnnotation) if !updated { - t.Errorf("Statefulset was not updated") + t.Errorf("StatefulSet was not updated") } } diff --git a/internal/pkg/testutil/kube.go b/internal/pkg/testutil/kube.go index 7abd836f..b17a88ea 100644 --- a/internal/pkg/testutil/kube.go +++ b/internal/pkg/testutil/kube.go @@ -83,8 +83,8 @@ func GetDeployment(namespace string, deploymentName string) *v1beta1.Deployment } } -// GetDaemonset provides daemonset for testing -func GetDaemonset(namespace string, daemonsetName string) *v1beta1.DaemonSet { +// GetDaemonSet provides daemonset for testing +func GetDaemonSet(namespace string, daemonsetName string) *v1beta1.DaemonSet { return &v1beta1.DaemonSet{ ObjectMeta: metav1.ObjectMeta{ Name: daemonsetName, @@ -121,8 +121,8 @@ func GetDaemonset(namespace string, daemonsetName string) *v1beta1.DaemonSet { } } -// GetStatefulset provides statefulset for testing -func GetStatefulset(namespace string, statefulsetName string) *v1_beta1.StatefulSet { +// GetStatefulSet provides statefulset for testing +func GetStatefulSet(namespace string, statefulsetName string) *v1_beta1.StatefulSet { return &v1_beta1.StatefulSet{ ObjectMeta: metav1.ObjectMeta{ Name: statefulsetName, @@ -238,8 +238,8 @@ func VerifyDeploymentUpdate(client kubernetes.Interface, namespace string, name 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, annotation string) bool { +// 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, annotation string) bool { daemonsets, err := client.ExtensionsV1beta1().DaemonSets(namespace).List(metav1.ListOptions{}) if err != nil { logrus.Errorf("Failed to list daemonsets %v", err) @@ -270,8 +270,8 @@ func VerifyDaemonsetUpdate(client kubernetes.Interface, namespace string, name s 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, annotation string) bool { +// 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, annotation string) bool { statefulsets, err := client.AppsV1beta1().StatefulSets(namespace).List(metav1.ListOptions{}) if err != nil { logrus.Errorf("Failed to list statefulsets %v", err) @@ -338,7 +338,7 @@ func CreateConfigMap(client kubernetes.Interface, namespace string, configmapNam logrus.Infof("Creating configmap") configmapClient := client.CoreV1().ConfigMaps(namespace) _, err := configmapClient.Create(GetConfigmap(namespace, configmapName, data)) - time.Sleep(10 * time.Second) + time.Sleep(5 * time.Second) return configmapClient, err } @@ -347,7 +347,7 @@ func CreateSecret(client kubernetes.Interface, namespace string, secretName stri logrus.Infof("Creating secret") secretClient := client.CoreV1().Secrets(namespace) _, err := secretClient.Create(GetSecret(namespace, secretName, data)) - time.Sleep(10 * time.Second) + time.Sleep(5 * time.Second) return secretClient, err } @@ -360,20 +360,20 @@ func CreateDeployment(client kubernetes.Interface, deploymentName string, namesp return deployment, err } -// CreateDaemonset creates a deployment in given namespace and returns the DaemonSet -func CreateDaemonset(client kubernetes.Interface, daemonsetName string, namespace string) (*v1beta1.DaemonSet, error) { - logrus.Infof("Creating Daemonset") +// CreateDaemonSet creates a deployment in given namespace and returns the DaemonSet +func CreateDaemonSet(client kubernetes.Interface, daemonsetName string, namespace string) (*v1beta1.DaemonSet, error) { + logrus.Infof("Creating DaemonSet") daemonsetClient := client.ExtensionsV1beta1().DaemonSets(namespace) - daemonset, err := daemonsetClient.Create(GetDaemonset(namespace, daemonsetName)) + daemonset, err := daemonsetClient.Create(GetDaemonSet(namespace, daemonsetName)) time.Sleep(5 * time.Second) return daemonset, err } -// CreateStatefulset creates a deployment in given namespace and returns the StatefulSet -func CreateStatefulset(client kubernetes.Interface, statefulsetName string, namespace string) (*v1_beta1.StatefulSet, error) { - logrus.Infof("Creating Statefulset") +// CreateStatefulSet creates a deployment in given namespace and returns the StatefulSet +func CreateStatefulSet(client kubernetes.Interface, statefulsetName string, namespace string) (*v1_beta1.StatefulSet, error) { + logrus.Infof("Creating StatefulSet") statefulsetClient := client.AppsV1beta1().StatefulSets(namespace) - statefulset, err := statefulsetClient.Create(GetStatefulset(namespace, statefulsetName)) + statefulset, err := statefulsetClient.Create(GetStatefulSet(namespace, statefulsetName)) time.Sleep(5 * time.Second) return statefulset, err } @@ -386,17 +386,17 @@ func DeleteDeployment(client kubernetes.Interface, namespace string, deploymentN return deploymentError } -// DeleteDaemonset creates a daemonset in given namespace and returns the error if any -func DeleteDaemonset(client kubernetes.Interface, namespace string, daemonsetName string) error { - logrus.Infof("Deleting Daemonset %s", daemonsetName) +// DeleteDaemonSet creates a daemonset in given namespace and returns the error if any +func DeleteDaemonSet(client kubernetes.Interface, namespace string, daemonsetName string) error { + logrus.Infof("Deleting DaemonSet %s", daemonsetName) daemonsetError := client.ExtensionsV1beta1().DaemonSets(namespace).Delete(daemonsetName, &metav1.DeleteOptions{}) time.Sleep(5 * time.Second) return daemonsetError } -// DeleteStatefulset creates a statefulset in given namespace and returns the error if any -func DeleteStatefulset(client kubernetes.Interface, namespace string, statefulsetName string) error { - logrus.Infof("Deleting Statefulset %s", statefulsetName) +// DeleteStatefulSet creates a statefulset in given namespace and returns the error if any +func DeleteStatefulSet(client kubernetes.Interface, namespace string, statefulsetName string) error { + logrus.Infof("Deleting StatefulSet %s", statefulsetName) statefulsetError := client.AppsV1beta1().StatefulSets(namespace).Delete(statefulsetName, &metav1.DeleteOptions{}) time.Sleep(5 * time.Second) return statefulsetError From c7d4a0aa9d968d7edbe68973aa817f3b70f74ed6 Mon Sep 17 00:00:00 2001 From: faizanahmad055 Date: Mon, 23 Jul 2018 19:00:05 +0500 Subject: [PATCH 14/21] Fix controller e2e test cases --- internal/pkg/controller/controller_test.go | 250 ++------------------- 1 file changed, 14 insertions(+), 236 deletions(-) diff --git a/internal/pkg/controller/controller_test.go b/internal/pkg/controller/controller_test.go index d417e7e7..55a1ba57 100644 --- a/internal/pkg/controller/controller_test.go +++ b/internal/pkg/controller/controller_test.go @@ -9,7 +9,6 @@ import ( "github.com/stakater/Reloader/internal/pkg/common" "github.com/stakater/Reloader/internal/pkg/testutil" "github.com/stakater/Reloader/pkg/kube" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" ) @@ -28,6 +27,20 @@ func TestMain(m *testing.M) { logrus.Infof("Creating namespace %s", namespace) testutil.CreateNamespace(namespace, client) + logrus.Infof("Creating controller") + for k := range kube.ResourceMap { + c, err := NewController(client, k, namespace) + if err != nil { + logrus.Fatalf("%s", err) + } + + // Now let's start the controller + stop := make(chan struct{}) + defer close(stop) + go c.Run(1, stop) + } + time.Sleep(5 * time.Second) + logrus.Infof("Running Testcases") retCode := m.Run() @@ -47,18 +60,6 @@ func getClient() *kubernetes.Clientset { // Perform rolling upgrade on deployment and create env var upon updating the configmap func TestControllerUpdatingConfigmapShouldCreateEnvInDeployment(t *testing.T) { - // Creating Controller - logrus.Infof("Creating controller") - controller, err := NewController(client, testutil.ConfigmapResourceType, 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 controller.Run(1, stop) - time.Sleep(5 * time.Second) // Creating configmap configmapName := configmapNamePrefix + "-update-" + common.RandSeq(5) @@ -76,10 +77,6 @@ func TestControllerUpdatingConfigmapShouldCreateEnvInDeployment(t *testing.T) { // Updating configmap for first time updateErr := testutil.UpdateConfigMap(configmapClient, namespace, configmapName, "", "www.stakater.com") if updateErr != nil { - err = controller.client.CoreV1().ConfigMaps(namespace).Delete(configmapName, &metav1.DeleteOptions{}) - if err != nil { - logrus.Errorf("Error while deleting the configmap %v", err) - } t.Errorf("Configmap was not updated") } @@ -108,19 +105,6 @@ func TestControllerUpdatingConfigmapShouldCreateEnvInDeployment(t *testing.T) { // Perform rolling upgrade on deployment and update env var upon updating the configmap func TestControllerForUpdatingConfigmapShouldUpdateDeployment(t *testing.T) { - // Creating controller - logrus.Infof("Creating controller") - controller, err := NewController(client, testutil.ConfigmapResourceType, 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 controller.Run(1, stop) - time.Sleep(5 * time.Second) - // Creating secret configmapName := configmapNamePrefix + "-update-" + common.RandSeq(5) configmapClient, err := testutil.CreateConfigMap(client, namespace, configmapName, "www.google.com") @@ -137,20 +121,12 @@ func TestControllerForUpdatingConfigmapShouldUpdateDeployment(t *testing.T) { // Updating configmap for first time updateErr := testutil.UpdateConfigMap(configmapClient, namespace, configmapName, "", "www.stakater.com") if updateErr != nil { - err = controller.client.CoreV1().ConfigMaps(namespace).Delete(configmapName, &metav1.DeleteOptions{}) - if err != nil { - logrus.Errorf("Error while deleting the configmap %v", err) - } t.Errorf("Configmap was not updated") } // Updating configmap for second time updateErr = testutil.UpdateConfigMap(configmapClient, namespace, configmapName, "", "aurorasolutions.io") if updateErr != nil { - err = controller.client.CoreV1().ConfigMaps(namespace).Delete(configmapName, &metav1.DeleteOptions{}) - if err != nil { - logrus.Errorf("Error while deleting the configmap %v", err) - } t.Errorf("Configmap was not updated") } @@ -179,19 +155,6 @@ func TestControllerForUpdatingConfigmapShouldUpdateDeployment(t *testing.T) { // Do not Perform rolling upgrade on deployment and create env var upon updating the labels configmap func TestControllerUpdatingConfigmapLabelsShouldNotCreateorUpdateEnvInDeployment(t *testing.T) { - // Creating Controller - logrus.Infof("Creating controller") - controller, err := NewController(client, testutil.ConfigmapResourceType, 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 controller.Run(1, stop) - time.Sleep(5 * time.Second) - // Creating configmap configmapName := configmapNamePrefix + "-update-" + common.RandSeq(5) configmapClient, err := testutil.CreateConfigMap(client, namespace, configmapName, "www.google.com") @@ -208,10 +171,6 @@ func TestControllerUpdatingConfigmapLabelsShouldNotCreateorUpdateEnvInDeployment // Updating configmap for first time updateErr := testutil.UpdateConfigMap(configmapClient, namespace, configmapName, "test", "www.google.com") if updateErr != nil { - err = controller.client.CoreV1().ConfigMaps(namespace).Delete(configmapName, &metav1.DeleteOptions{}) - if err != nil { - logrus.Errorf("Error while deleting the configmap %v", err) - } t.Errorf("Configmap was not updated") } @@ -240,17 +199,6 @@ func TestControllerUpdatingConfigmapLabelsShouldNotCreateorUpdateEnvInDeployment // Perform rolling upgrade on secret and create a env var upon updating the secret func TestControllerUpdatingSecretShouldCreateEnvInDeployment(t *testing.T) { - // Creating controller - controller, err := NewController(client, testutil.SecretResourceType, namespace) - if err != nil { - logrus.Errorf("Unable to create NewController error = %v", err) - return - } - stop := make(chan struct{}) - defer close(stop) - go controller.Run(1, stop) - time.Sleep(5 * time.Second) - // Creating secret secretName := secretNamePrefix + "-update-" + common.RandSeq(5) secretClient, err := testutil.CreateSecret(client, namespace, secretName, data) @@ -295,17 +243,6 @@ func TestControllerUpdatingSecretShouldCreateEnvInDeployment(t *testing.T) { // Perform rolling upgrade on deployment and update env var upon updating the secret func TestControllerUpdatingSecretShouldUpdateEnvInDeployment(t *testing.T) { - // Creating controller - controller, err := NewController(client, testutil.SecretResourceType, namespace) - if err != nil { - logrus.Errorf("Unable to create NewController error = %v", err) - return - } - stop := make(chan struct{}) - defer close(stop) - go controller.Run(1, stop) - time.Sleep(5 * time.Second) - // Creating secret secretName := secretNamePrefix + "-update-" + common.RandSeq(5) secretClient, err := testutil.CreateSecret(client, namespace, secretName, data) @@ -356,17 +293,6 @@ func TestControllerUpdatingSecretShouldUpdateEnvInDeployment(t *testing.T) { // Do not Perform rolling upgrade on secret and create or update a env var upon updating the label in secret func TestControllerUpdatingSecretLabelsShouldNotCreateorUpdateEnvInDeployment(t *testing.T) { - // Creating controller - controller, err := NewController(client, testutil.SecretResourceType, namespace) - if err != nil { - logrus.Errorf("Unable to create NewController error = %v", err) - return - } - stop := make(chan struct{}) - defer close(stop) - go controller.Run(1, stop) - time.Sleep(5 * time.Second) - // Creating secret secretName := secretNamePrefix + "-update-" + common.RandSeq(5) secretClient, err := testutil.CreateSecret(client, namespace, secretName, data) @@ -410,19 +336,6 @@ func TestControllerUpdatingSecretLabelsShouldNotCreateorUpdateEnvInDeployment(t // Perform rolling upgrade on DaemonSet and create env var upon updating the configmap func TestControllerUpdatingConfigmapShouldCreateEnvInDaemonSet(t *testing.T) { - // Creating Controller - logrus.Infof("Creating controller") - controller, err := NewController(client, testutil.ConfigmapResourceType, 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 controller.Run(1, stop) - time.Sleep(5 * time.Second) - // Creating configmap configmapName := configmapNamePrefix + "-update-" + common.RandSeq(5) configmapClient, err := testutil.CreateConfigMap(client, namespace, configmapName, "www.google.com") @@ -439,10 +352,6 @@ func TestControllerUpdatingConfigmapShouldCreateEnvInDaemonSet(t *testing.T) { // Updating configmap for first time updateErr := testutil.UpdateConfigMap(configmapClient, namespace, configmapName, "", "www.stakater.com") if updateErr != nil { - err = controller.client.CoreV1().ConfigMaps(namespace).Delete(configmapName, &metav1.DeleteOptions{}) - if err != nil { - logrus.Errorf("Error while deleting the configmap %v", err) - } t.Errorf("Configmap was not updated") } @@ -471,19 +380,6 @@ func TestControllerUpdatingConfigmapShouldCreateEnvInDaemonSet(t *testing.T) { // Perform rolling upgrade on DaemonSet and update env var upon updating the configmap func TestControllerForUpdatingConfigmapShouldUpdateDaemonSet(t *testing.T) { - // Creating controller - logrus.Infof("Creating controller") - controller, err := NewController(client, testutil.ConfigmapResourceType, 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 controller.Run(1, stop) - time.Sleep(5 * time.Second) - // Creating secret configmapName := configmapNamePrefix + "-update-" + common.RandSeq(5) configmapClient, err := testutil.CreateConfigMap(client, namespace, configmapName, "www.google.com") @@ -500,20 +396,12 @@ func TestControllerForUpdatingConfigmapShouldUpdateDaemonSet(t *testing.T) { // Updating configmap for first time updateErr := testutil.UpdateConfigMap(configmapClient, namespace, configmapName, "", "www.stakater.com") if updateErr != nil { - err = controller.client.CoreV1().ConfigMaps(namespace).Delete(configmapName, &metav1.DeleteOptions{}) - if err != nil { - logrus.Errorf("Error while deleting the configmap %v", err) - } t.Errorf("Configmap was not updated") } // Updating configmap for second time updateErr = testutil.UpdateConfigMap(configmapClient, namespace, configmapName, "", "aurorasolutions.io") if updateErr != nil { - err = controller.client.CoreV1().ConfigMaps(namespace).Delete(configmapName, &metav1.DeleteOptions{}) - if err != nil { - logrus.Errorf("Error while deleting the configmap %v", err) - } t.Errorf("Configmap was not updated") } @@ -542,17 +430,6 @@ func TestControllerForUpdatingConfigmapShouldUpdateDaemonSet(t *testing.T) { // Perform rolling upgrade on secret and create a env var upon updating the secret func TestControllerUpdatingSecretShouldCreateEnvInDaemonSet(t *testing.T) { - // Creating controller - controller, err := NewController(client, testutil.SecretResourceType, namespace) - if err != nil { - logrus.Errorf("Unable to create NewController error = %v", err) - return - } - stop := make(chan struct{}) - defer close(stop) - go controller.Run(1, stop) - time.Sleep(5 * time.Second) - // Creating secret secretName := secretNamePrefix + "-update-" + common.RandSeq(5) secretClient, err := testutil.CreateSecret(client, namespace, secretName, data) @@ -597,17 +474,6 @@ func TestControllerUpdatingSecretShouldCreateEnvInDaemonSet(t *testing.T) { // Perform rolling upgrade on DaemonSet and update env var upon updating the secret func TestControllerUpdatingSecretShouldUpdateEnvInDaemonSet(t *testing.T) { - // Creating controller - controller, err := NewController(client, testutil.SecretResourceType, namespace) - if err != nil { - logrus.Errorf("Unable to create NewController error = %v", err) - return - } - stop := make(chan struct{}) - defer close(stop) - go controller.Run(1, stop) - time.Sleep(5 * time.Second) - // Creating secret secretName := secretNamePrefix + "-update-" + common.RandSeq(5) secretClient, err := testutil.CreateSecret(client, namespace, secretName, data) @@ -658,17 +524,6 @@ func TestControllerUpdatingSecretShouldUpdateEnvInDaemonSet(t *testing.T) { // Do not Perform rolling upgrade on secret and create or update a env var upon updating the label in secret func TestControllerUpdatingSecretLabelsShouldNotCreateorUpdateEnvInDaemonSet(t *testing.T) { - // Creating controller - controller, err := NewController(client, testutil.SecretResourceType, namespace) - if err != nil { - logrus.Errorf("Unable to create NewController error = %v", err) - return - } - stop := make(chan struct{}) - defer close(stop) - go controller.Run(1, stop) - time.Sleep(5 * time.Second) - // Creating secret secretName := secretNamePrefix + "-update-" + common.RandSeq(5) secretClient, err := testutil.CreateSecret(client, namespace, secretName, data) @@ -712,19 +567,6 @@ func TestControllerUpdatingSecretLabelsShouldNotCreateorUpdateEnvInDaemonSet(t * // Perform rolling upgrade on StatefulSet and create env var upon updating the configmap func TestControllerUpdatingConfigmapShouldCreateEnvInStatefulSet(t *testing.T) { - // Creating Controller - logrus.Infof("Creating controller") - controller, err := NewController(client, testutil.ConfigmapResourceType, 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 controller.Run(1, stop) - time.Sleep(5 * time.Second) - // Creating configmap configmapName := configmapNamePrefix + "-update-" + common.RandSeq(5) configmapClient, err := testutil.CreateConfigMap(client, namespace, configmapName, "www.google.com") @@ -741,10 +583,6 @@ func TestControllerUpdatingConfigmapShouldCreateEnvInStatefulSet(t *testing.T) { // Updating configmap for first time updateErr := testutil.UpdateConfigMap(configmapClient, namespace, configmapName, "", "www.stakater.com") if updateErr != nil { - err = controller.client.CoreV1().ConfigMaps(namespace).Delete(configmapName, &metav1.DeleteOptions{}) - if err != nil { - logrus.Errorf("Error while deleting the configmap %v", err) - } t.Errorf("Configmap was not updated") } @@ -773,19 +611,6 @@ func TestControllerUpdatingConfigmapShouldCreateEnvInStatefulSet(t *testing.T) { // Perform rolling upgrade on StatefulSet and update env var upon updating the configmap func TestControllerForUpdatingConfigmapShouldUpdateStatefulSet(t *testing.T) { - // Creating controller - logrus.Infof("Creating controller") - controller, err := NewController(client, testutil.ConfigmapResourceType, 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 controller.Run(1, stop) - time.Sleep(5 * time.Second) - // Creating secret configmapName := configmapNamePrefix + "-update-" + common.RandSeq(5) configmapClient, err := testutil.CreateConfigMap(client, namespace, configmapName, "www.google.com") @@ -802,20 +627,12 @@ func TestControllerForUpdatingConfigmapShouldUpdateStatefulSet(t *testing.T) { // Updating configmap for first time updateErr := testutil.UpdateConfigMap(configmapClient, namespace, configmapName, "", "www.stakater.com") if updateErr != nil { - err = controller.client.CoreV1().ConfigMaps(namespace).Delete(configmapName, &metav1.DeleteOptions{}) - if err != nil { - logrus.Errorf("Error while deleting the configmap %v", err) - } t.Errorf("Configmap was not updated") } // Updating configmap for second time updateErr = testutil.UpdateConfigMap(configmapClient, namespace, configmapName, "", "aurorasolutions.io") if updateErr != nil { - err = controller.client.CoreV1().ConfigMaps(namespace).Delete(configmapName, &metav1.DeleteOptions{}) - if err != nil { - logrus.Errorf("Error while deleting the configmap %v", err) - } t.Errorf("Configmap was not updated") } @@ -844,19 +661,6 @@ func TestControllerForUpdatingConfigmapShouldUpdateStatefulSet(t *testing.T) { // Do not Perform rolling upgrade on StatefulSet and create env var upon updating the labels configmap func TestControllerUpdatingConfigmapLabelsShouldNotCreateorUpdateEnvInStatefulSet(t *testing.T) { - // Creating Controller - logrus.Infof("Creating controller") - controller, err := NewController(client, testutil.ConfigmapResourceType, 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 controller.Run(1, stop) - time.Sleep(5 * time.Second) - // Creating configmap configmapName := configmapNamePrefix + "-update-" + common.RandSeq(5) configmapClient, err := testutil.CreateConfigMap(client, namespace, configmapName, "www.google.com") @@ -873,10 +677,6 @@ func TestControllerUpdatingConfigmapLabelsShouldNotCreateorUpdateEnvInStatefulSe // Updating configmap for first time updateErr := testutil.UpdateConfigMap(configmapClient, namespace, configmapName, "test", "www.google.com") if updateErr != nil { - err = controller.client.CoreV1().ConfigMaps(namespace).Delete(configmapName, &metav1.DeleteOptions{}) - if err != nil { - logrus.Errorf("Error while deleting the configmap %v", err) - } t.Errorf("Configmap was not updated") } @@ -905,17 +705,6 @@ func TestControllerUpdatingConfigmapLabelsShouldNotCreateorUpdateEnvInStatefulSe // Perform rolling upgrade on secret and create a env var upon updating the secret func TestControllerUpdatingSecretShouldCreateEnvInStatefulSet(t *testing.T) { - // Creating controller - controller, err := NewController(client, testutil.SecretResourceType, namespace) - if err != nil { - logrus.Errorf("Unable to create NewController error = %v", err) - return - } - stop := make(chan struct{}) - defer close(stop) - go controller.Run(1, stop) - time.Sleep(5 * time.Second) - // Creating secret secretName := secretNamePrefix + "-update-" + common.RandSeq(5) secretClient, err := testutil.CreateSecret(client, namespace, secretName, data) @@ -960,17 +749,6 @@ func TestControllerUpdatingSecretShouldCreateEnvInStatefulSet(t *testing.T) { // Perform rolling upgrade on StatefulSet and update env var upon updating the secret func TestControllerUpdatingSecretShouldUpdateEnvInStatefulSet(t *testing.T) { - // Creating controller - controller, err := NewController(client, testutil.SecretResourceType, namespace) - if err != nil { - logrus.Errorf("Unable to create NewController error = %v", err) - return - } - stop := make(chan struct{}) - defer close(stop) - go controller.Run(1, stop) - time.Sleep(5 * time.Second) - // Creating secret secretName := secretNamePrefix + "-update-" + common.RandSeq(5) secretClient, err := testutil.CreateSecret(client, namespace, secretName, data) From 61a2af1782c1678a6885635572da9b9d4658d084 Mon Sep 17 00:00:00 2001 From: faizanahmad055 Date: Mon, 23 Jul 2018 19:20:52 +0500 Subject: [PATCH 15/21] Remove SHA data log --- internal/pkg/handler/update.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/pkg/handler/update.go b/internal/pkg/handler/update.go index f0c694e4..51f16fe2 100644 --- a/internal/pkg/handler/update.go +++ b/internal/pkg/handler/update.go @@ -189,7 +189,7 @@ func updateContainers(containers []v1.Container, annotationValue string, shaData } containers[i].Env = append(containers[i].Env, e) updated = true - logrus.Infof("%s environment variable does not exist, creating a new env with value %s", envar, shaData) + logrus.Infof("%s environment variable does not exist, creating a new envVar", envar) } } return updated From 5b467b731c5d51c01d4cc3402a64b87dadcdebe6 Mon Sep 17 00:00:00 2001 From: faizanahmad055 Date: Tue, 24 Jul 2018 17:45:48 +0500 Subject: [PATCH 16/21] Revamp perform rolling method --- README.md | 4 +- .../chart/reloader/templates/rbac.yaml | 1 - internal/pkg/common/common.go | 13 - internal/pkg/constants/annotations.go | 8 + internal/pkg/constants/constants.go | 10 + internal/pkg/controller/controller.go | 5 +- internal/pkg/controller/controller_test.go | 33 +- internal/pkg/handler/update.go | 311 ++++++++++-------- internal/pkg/handler/update_test.go | 102 +++++- internal/pkg/testutil/kube.go | 19 +- internal/pkg/util/interface.go | 38 +++ 11 files changed, 356 insertions(+), 188 deletions(-) create mode 100644 internal/pkg/constants/annotations.go create mode 100644 internal/pkg/constants/constants.go create mode 100644 internal/pkg/util/interface.go diff --git a/README.md b/README.md index 725a6ba1..bb34881c 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ For a `Deployment` called `foo` have a `ConfigMap` called `foo`. Then add this a ```yaml metadata: annotations: - reloader.stakater.com/configmap.update-on-change: "foo" + configmap.reloader.stakater.com/reload: "foo" ``` OR @@ -31,7 +31,7 @@ For a `Deployment` called `foo` have a `Secret` called `foo`. Then add this anno ```yaml metadata: annotations: - reloader.stakater.com/secret.update-on-change: "foo" + secret.reloader.stakater.com/reload: "foo" ``` Then, providing `Reloader` is running, whenever you edit the `ConfigMap` or `Secret` called `foo` the Reloader will update the `Deployment` by adding the environment variable: diff --git a/deployments/kubernetes/chart/reloader/templates/rbac.yaml b/deployments/kubernetes/chart/reloader/templates/rbac.yaml index 88c2cbb5..cc0b0d3e 100644 --- a/deployments/kubernetes/chart/reloader/templates/rbac.yaml +++ b/deployments/kubernetes/chart/reloader/templates/rbac.yaml @@ -37,7 +37,6 @@ rules: - get - update - patch - - watch --- apiVersion: rbac.authorization.k8s.io/v1beta1 kind: RoleBinding diff --git a/internal/pkg/common/common.go b/internal/pkg/common/common.go index 2030d233..3f52f1d8 100644 --- a/internal/pkg/common/common.go +++ b/internal/pkg/common/common.go @@ -11,19 +11,6 @@ var ( letters = []rune("abcdefghijklmnopqrstuvwxyz") ) -const ( - // ConfigmapUpdateOnChangeAnnotation is an annotation to detect changes in configmaps - ConfigmapUpdateOnChangeAnnotation = "configmap.reloader.stakater.com/reload" - // SecretUpdateOnChangeAnnotation is an annotation to detect changes in secrets - SecretUpdateOnChangeAnnotation = "secret.reloader.stakater.com/reload" - // ConfigmapEnvarPostfix is a postfix for configmap envVar - ConfigmapEnvarPostfix = "_CONFIGMAP" - // SecretEnvarPostfix is a postfix for secret envVar - SecretEnvarPostfix = "_SECRET" - // EnvVarPrefix is a Prefix for environment variable - EnvVarPrefix = "STAKATER_" -) - // ConvertToEnvVarName converts the given text into a usable env var // removing any special chars with '_' and transforming text to upper case func ConvertToEnvVarName(text string) string { diff --git a/internal/pkg/constants/annotations.go b/internal/pkg/constants/annotations.go new file mode 100644 index 00000000..df6eb8dd --- /dev/null +++ b/internal/pkg/constants/annotations.go @@ -0,0 +1,8 @@ +package constants + +const ( + // ConfigmapUpdateOnChangeAnnotation is an annotation to detect changes in configmaps + ConfigmapUpdateOnChangeAnnotation = "configmap.reloader.stakater.com/reload" + // SecretUpdateOnChangeAnnotation is an annotation to detect changes in secrets + SecretUpdateOnChangeAnnotation = "secret.reloader.stakater.com/reload" +) \ No newline at end of file diff --git a/internal/pkg/constants/constants.go b/internal/pkg/constants/constants.go new file mode 100644 index 00000000..efb53927 --- /dev/null +++ b/internal/pkg/constants/constants.go @@ -0,0 +1,10 @@ +package constants + +const ( + // ConfigmapEnvarPostfix is a postfix for configmap envVar + ConfigmapEnvarPostfix = "_CONFIGMAP" + // SecretEnvarPostfix is a postfix for secret envVar + SecretEnvarPostfix = "_SECRET" + // EnvVarPrefix is a Prefix for environment variable + EnvVarPrefix = "STAKATER_" +) \ No newline at end of file diff --git a/internal/pkg/controller/controller.go b/internal/pkg/controller/controller.go index 2a1f72ae..0a84011d 100644 --- a/internal/pkg/controller/controller.go +++ b/internal/pkg/controller/controller.go @@ -9,7 +9,6 @@ import ( "github.com/stakater/Reloader/pkg/kube" "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/util/runtime" - errorHandler "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/cache" @@ -73,7 +72,7 @@ func (c *Controller) Delete(old interface{}) { func (c *Controller) Run(threadiness int, stopCh chan struct{}) { logrus.Infof("Starting Controller") - defer errorHandler.HandleCrash() + defer runtime.HandleCrash() // Let the workers stop when we are done defer c.queue.ShutDown() @@ -82,7 +81,7 @@ func (c *Controller) Run(threadiness int, stopCh chan struct{}) { // Wait for all involved caches to be synced, before processing items from the queue is started if !cache.WaitForCacheSync(stopCh, c.informer.HasSynced) { - errorHandler.HandleError(fmt.Errorf("Timed out waiting for caches to sync")) + runtime.HandleError(fmt.Errorf("Timed out waiting for caches to sync")) return } diff --git a/internal/pkg/controller/controller_test.go b/internal/pkg/controller/controller_test.go index 55a1ba57..3903b360 100644 --- a/internal/pkg/controller/controller_test.go +++ b/internal/pkg/controller/controller_test.go @@ -7,6 +7,7 @@ import ( "github.com/sirupsen/logrus" "github.com/stakater/Reloader/internal/pkg/common" + "github.com/stakater/Reloader/internal/pkg/constants" "github.com/stakater/Reloader/internal/pkg/testutil" "github.com/stakater/Reloader/pkg/kube" "k8s.io/client-go/kubernetes" @@ -83,7 +84,7 @@ func TestControllerUpdatingConfigmapShouldCreateEnvInDeployment(t *testing.T) { // Verifying deployment update logrus.Infof("Verifying env var has been created") shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "www.stakater.com") - updated := testutil.VerifyDeploymentUpdate(client, namespace, configmapName, common.ConfigmapEnvarPostfix, shaData, common.ConfigmapUpdateOnChangeAnnotation) + updated := testutil.VerifyDeploymentUpdate(client, namespace, configmapName, constants.ConfigmapEnvarPostfix, shaData, constants.ConfigmapUpdateOnChangeAnnotation) if !updated { t.Errorf("Deployment was not updated") } @@ -133,7 +134,7 @@ func TestControllerForUpdatingConfigmapShouldUpdateDeployment(t *testing.T) { // Verifying deployment update logrus.Infof("Verifying env var has been updated") shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "aurorasolutions.io") - updated := testutil.VerifyDeploymentUpdate(client, namespace, configmapName, common.ConfigmapEnvarPostfix, shaData, common.ConfigmapUpdateOnChangeAnnotation) + updated := testutil.VerifyDeploymentUpdate(client, namespace, configmapName, constants.ConfigmapEnvarPostfix, shaData, constants.ConfigmapUpdateOnChangeAnnotation) if !updated { t.Errorf("Deployment was not updated") } @@ -177,7 +178,7 @@ func TestControllerUpdatingConfigmapLabelsShouldNotCreateorUpdateEnvInDeployment // Verifying deployment update logrus.Infof("Verifying env var has been created") shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "www.google.com") - updated := testutil.VerifyDeploymentUpdate(client, namespace, configmapName, common.ConfigmapEnvarPostfix, shaData, common.ConfigmapUpdateOnChangeAnnotation) + updated := testutil.VerifyDeploymentUpdate(client, namespace, configmapName, constants.ConfigmapEnvarPostfix, shaData, constants.ConfigmapUpdateOnChangeAnnotation) if updated { t.Errorf("Deployment should not be updated by changing label") } @@ -221,7 +222,7 @@ func TestControllerUpdatingSecretShouldCreateEnvInDeployment(t *testing.T) { // Verifying Upgrade logrus.Infof("Verifying env var has been created") shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, newData) - updated := testutil.VerifyDeploymentUpdate(client, namespace, secretName, common.SecretEnvarPostfix, shaData, common.SecretUpdateOnChangeAnnotation) + updated := testutil.VerifyDeploymentUpdate(client, namespace, secretName, constants.SecretEnvarPostfix, shaData, constants.SecretUpdateOnChangeAnnotation) if !updated { t.Errorf("Deployment was not updated") } @@ -271,7 +272,7 @@ func TestControllerUpdatingSecretShouldUpdateEnvInDeployment(t *testing.T) { // Verifying Upgrade logrus.Infof("Verifying env var has been updated") shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, updatedData) - updated := testutil.VerifyDeploymentUpdate(client, namespace, secretName, common.SecretEnvarPostfix, shaData, common.SecretUpdateOnChangeAnnotation) + updated := testutil.VerifyDeploymentUpdate(client, namespace, secretName, constants.SecretEnvarPostfix, shaData, constants.SecretUpdateOnChangeAnnotation) if !updated { t.Errorf("Deployment was not updated") } @@ -314,7 +315,7 @@ func TestControllerUpdatingSecretLabelsShouldNotCreateorUpdateEnvInDeployment(t // Verifying Upgrade logrus.Infof("Verifying env var has been created") shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, data) - updated := testutil.VerifyDeploymentUpdate(client, namespace, secretName, common.SecretEnvarPostfix, shaData, common.SecretUpdateOnChangeAnnotation) + updated := testutil.VerifyDeploymentUpdate(client, namespace, secretName, constants.SecretEnvarPostfix, shaData, constants.SecretUpdateOnChangeAnnotation) if updated { t.Errorf("Deployment should not be updated by changing label in secret") } @@ -358,7 +359,7 @@ func TestControllerUpdatingConfigmapShouldCreateEnvInDaemonSet(t *testing.T) { // Verifying DaemonSet update logrus.Infof("Verifying env var has been created") shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "www.stakater.com") - updated := testutil.VerifyDaemonSetUpdate(client, namespace, configmapName, common.ConfigmapEnvarPostfix, shaData, common.ConfigmapUpdateOnChangeAnnotation) + updated := testutil.VerifyDaemonSetUpdate(client, namespace, configmapName, constants.ConfigmapEnvarPostfix, shaData, constants.ConfigmapUpdateOnChangeAnnotation) if !updated { t.Errorf("DaemonSet was not updated") } @@ -408,7 +409,7 @@ func TestControllerForUpdatingConfigmapShouldUpdateDaemonSet(t *testing.T) { // Verifying DaemonSet update logrus.Infof("Verifying env var has been updated") shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "aurorasolutions.io") - updated := testutil.VerifyDaemonSetUpdate(client, namespace, configmapName, common.ConfigmapEnvarPostfix, shaData, common.ConfigmapUpdateOnChangeAnnotation) + updated := testutil.VerifyDaemonSetUpdate(client, namespace, configmapName, constants.ConfigmapEnvarPostfix, shaData, constants.ConfigmapUpdateOnChangeAnnotation) if !updated { t.Errorf("DaemonSet was not updated") } @@ -452,7 +453,7 @@ func TestControllerUpdatingSecretShouldCreateEnvInDaemonSet(t *testing.T) { // Verifying Upgrade logrus.Infof("Verifying env var has been created") shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, newData) - updated := testutil.VerifyDaemonSetUpdate(client, namespace, secretName, common.SecretEnvarPostfix, shaData, common.SecretUpdateOnChangeAnnotation) + updated := testutil.VerifyDaemonSetUpdate(client, namespace, secretName, constants.SecretEnvarPostfix, shaData, constants.SecretUpdateOnChangeAnnotation) if !updated { t.Errorf("DaemonSet was not updated") } @@ -502,7 +503,7 @@ func TestControllerUpdatingSecretShouldUpdateEnvInDaemonSet(t *testing.T) { // Verifying Upgrade logrus.Infof("Verifying env var has been updated") shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, updatedData) - updated := testutil.VerifyDaemonSetUpdate(client, namespace, secretName, common.SecretEnvarPostfix, shaData, common.SecretUpdateOnChangeAnnotation) + updated := testutil.VerifyDaemonSetUpdate(client, namespace, secretName, constants.SecretEnvarPostfix, shaData, constants.SecretUpdateOnChangeAnnotation) if !updated { t.Errorf("DaemonSet was not updated") } @@ -545,7 +546,7 @@ func TestControllerUpdatingSecretLabelsShouldNotCreateorUpdateEnvInDaemonSet(t * // Verifying Upgrade logrus.Infof("Verifying env var has been created") shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, data) - updated := testutil.VerifyDaemonSetUpdate(client, namespace, secretName, common.SecretEnvarPostfix, shaData, common.SecretUpdateOnChangeAnnotation) + updated := testutil.VerifyDaemonSetUpdate(client, namespace, secretName, constants.SecretEnvarPostfix, shaData, constants.SecretUpdateOnChangeAnnotation) if updated { t.Errorf("DaemonSet should not be updated by changing label in secret") } @@ -589,7 +590,7 @@ func TestControllerUpdatingConfigmapShouldCreateEnvInStatefulSet(t *testing.T) { // Verifying StatefulSet update logrus.Infof("Verifying env var has been created") shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "www.stakater.com") - updated := testutil.VerifyStatefulSetUpdate(client, namespace, configmapName, common.ConfigmapEnvarPostfix, shaData, common.ConfigmapUpdateOnChangeAnnotation) + updated := testutil.VerifyStatefulSetUpdate(client, namespace, configmapName, constants.ConfigmapEnvarPostfix, shaData, constants.ConfigmapUpdateOnChangeAnnotation) if !updated { t.Errorf("StatefulSet was not updated") } @@ -639,7 +640,7 @@ func TestControllerForUpdatingConfigmapShouldUpdateStatefulSet(t *testing.T) { // Verifying StatefulSet update logrus.Infof("Verifying env var has been updated") shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "aurorasolutions.io") - updated := testutil.VerifyStatefulSetUpdate(client, namespace, configmapName, common.ConfigmapEnvarPostfix, shaData, common.ConfigmapUpdateOnChangeAnnotation) + updated := testutil.VerifyStatefulSetUpdate(client, namespace, configmapName, constants.ConfigmapEnvarPostfix, shaData, constants.ConfigmapUpdateOnChangeAnnotation) if !updated { t.Errorf("StatefulSet was not updated") } @@ -683,7 +684,7 @@ func TestControllerUpdatingConfigmapLabelsShouldNotCreateorUpdateEnvInStatefulSe // Verifying StatefulSet update logrus.Infof("Verifying env var has been created") shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "www.google.com") - updated := testutil.VerifyStatefulSetUpdate(client, namespace, configmapName, common.ConfigmapEnvarPostfix, shaData, common.ConfigmapUpdateOnChangeAnnotation) + updated := testutil.VerifyStatefulSetUpdate(client, namespace, configmapName, constants.ConfigmapEnvarPostfix, shaData, constants.ConfigmapUpdateOnChangeAnnotation) if updated { t.Errorf("StatefulSet should not be updated by changing label") } @@ -727,7 +728,7 @@ func TestControllerUpdatingSecretShouldCreateEnvInStatefulSet(t *testing.T) { // Verifying Upgrade logrus.Infof("Verifying env var has been created") shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, newData) - updated := testutil.VerifyStatefulSetUpdate(client, namespace, secretName, common.SecretEnvarPostfix, shaData, common.SecretUpdateOnChangeAnnotation) + updated := testutil.VerifyStatefulSetUpdate(client, namespace, secretName, constants.SecretEnvarPostfix, shaData, constants.SecretUpdateOnChangeAnnotation) if !updated { t.Errorf("StatefulSet was not updated") } @@ -777,7 +778,7 @@ func TestControllerUpdatingSecretShouldUpdateEnvInStatefulSet(t *testing.T) { // Verifying Upgrade logrus.Infof("Verifying env var has been updated") shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, updatedData) - updated := testutil.VerifyStatefulSetUpdate(client, namespace, secretName, common.SecretEnvarPostfix, shaData, common.SecretUpdateOnChangeAnnotation) + updated := testutil.VerifyStatefulSetUpdate(client, namespace, secretName, constants.SecretEnvarPostfix, shaData, constants.SecretUpdateOnChangeAnnotation) if !updated { t.Errorf("StatefulSet was not updated") } diff --git a/internal/pkg/handler/update.go b/internal/pkg/handler/update.go index 51f16fe2..78fb3c7b 100644 --- a/internal/pkg/handler/update.go +++ b/internal/pkg/handler/update.go @@ -6,9 +6,13 @@ import ( "github.com/sirupsen/logrus" "github.com/stakater/Reloader/internal/pkg/common" + "github.com/stakater/Reloader/internal/pkg/constants" "github.com/stakater/Reloader/internal/pkg/crypto" + "github.com/stakater/Reloader/internal/pkg/util" "github.com/stakater/Reloader/pkg/kube" + apps_v1beta1 "k8s.io/api/apps/v1beta1" "k8s.io/api/core/v1" + "k8s.io/api/extensions/v1beta1" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" ) @@ -19,6 +23,30 @@ type ResourceUpdatedHandler struct { OldResource interface{} } +//Config contains rolling upgrade configuration parameters +type Config struct { + namespace string + resourceName string + annotation string + shaValue string +} + +//ItemsFunc is a generic function to return a specific resource array in given namespace +type ItemsFunc func(kubernetes.Interface, string) []interface{} + +//ContainersFunc is a generic func to return containers +type ContainersFunc func(interface{}) []v1.Container + +//UpdateFunc performs the resource update +type UpdateFunc func(kubernetes.Interface, string, interface{}) error + +//RollingUpgradeFuncs contains generic functions to perform rolling upgrade +type RollingUpgradeFuncs struct { + ItemsFunc ItemsFunc + ContainersFunc ContainersFunc + UpdateFunc UpdateFunc +} + // Handle processes the updated resource func (r ResourceUpdatedHandler) Handle() error { if r.Resource == nil || r.OldResource == nil { @@ -26,154 +54,173 @@ func (r ResourceUpdatedHandler) Handle() error { } else { logrus.Infof("Detected changes in object %s", r.Resource) // process resource based on its type - rollingUpgrade(r, "deployments") - rollingUpgrade(r, "daemonsets") - rollingUpgrade(r, "statefulSets") + rollingUpgrade(r, RollingUpgradeFuncs{ + ItemsFunc: GetDeploymentItems, + ContainersFunc: GetDeploymentContainers, + UpdateFunc: UpdateDeployment, + }) + rollingUpgrade(r, RollingUpgradeFuncs{ + ItemsFunc: GetDaemonSetItems, + ContainersFunc: GetDaemonSetContainers, + UpdateFunc: UpdateDaemonSet, + }) + rollingUpgrade(r, RollingUpgradeFuncs{ + ItemsFunc: GetStatefulSetItems, + ContainersFunc: GetStatefulsetContainers, + UpdateFunc: UpdateStatefulset, + }) } return nil } -func rollingUpgrade(r ResourceUpdatedHandler, rollingUpgradeType string) { +// GetDeploymentItems returns the deployments in given namespace +func GetDeploymentItems(client kubernetes.Interface, namespace string) []interface{} { + deployments, err := client.ExtensionsV1beta1().Deployments(namespace).List(meta_v1.ListOptions{}) + if err != nil { + logrus.Errorf("Failed to list deployments %v", err) + } + return util.InterfaceSlice(deployments.Items) +} + +// GetDaemonSetItems returns the daemonSet in given namespace +func GetDaemonSetItems(client kubernetes.Interface, namespace string) []interface{} { + daemonSets, err := client.ExtensionsV1beta1().DaemonSets(namespace).List(meta_v1.ListOptions{}) + if err != nil { + logrus.Errorf("Failed to list daemonSets %v", err) + } + return util.InterfaceSlice(daemonSets.Items) +} + +// GetStatefulSetItems returns the statefulSet in given namespace +func GetStatefulSetItems(client kubernetes.Interface, namespace string) []interface{} { + statefulSets, err := client.AppsV1beta1().StatefulSets(namespace).List(meta_v1.ListOptions{}) + if err != nil { + logrus.Errorf("Failed to list statefulSets %v", err) + } + return util.InterfaceSlice(statefulSets.Items) +} + +// GetDeploymentContainers returns the containers of given deployment +func GetDeploymentContainers(item interface{}) []v1.Container { + return item.(v1beta1.Deployment).Spec.Template.Spec.Containers +} + +// GetDaemonSetContainers returns the containers of given daemonset +func GetDaemonSetContainers(item interface{}) []v1.Container { + return item.(v1beta1.DaemonSet).Spec.Template.Spec.Containers +} + +// GetStatefulsetContainers returns the containers of given statefulSet +func GetStatefulsetContainers(item interface{}) []v1.Container { + return item.(apps_v1beta1.StatefulSet).Spec.Template.Spec.Containers +} + +// UpdateDeployment performs rolling upgrade on deployment +func UpdateDeployment(client kubernetes.Interface, namespace string, resource interface{}) error { + deployment := resource.(v1beta1.Deployment) + _, err := client.ExtensionsV1beta1().Deployments(namespace).Update(&deployment) + return err +} + +// UpdateDaemonSet performs rolling upgrade on daemonSet +func UpdateDaemonSet(client kubernetes.Interface, namespace string, resource interface{}) error { + daemonSet := resource.(v1beta1.DaemonSet) + _, err := client.ExtensionsV1beta1().DaemonSets(namespace).Update(&daemonSet) + return err +} + +// UpdateStatefulset performs rolling upgrade on statefulSet +func UpdateStatefulset(client kubernetes.Interface, namespace string, resource interface{}) error { + statefulSet := resource.(apps_v1beta1.StatefulSet) + _, err := client.AppsV1beta1().StatefulSets(namespace).Update(&statefulSet) + return err +} + +func rollingUpgrade(r ResourceUpdatedHandler, upgradeFuncs RollingUpgradeFuncs) { client, err := kube.GetClient() if err != nil { logrus.Fatalf("Unable to create Kubernetes client error = %v", err) } - var shaData, oldSHAdata string - shaData = getSHAfromData(r.Resource) - oldSHAdata = getSHAfromData(r.OldResource) - if shaData != oldSHAdata { - var namespace, resourceName, envarPostfix, annotation string - if _, ok := r.Resource.(*v1.ConfigMap); ok { - logrus.Infof("Performing 'Updated' action for resource of type 'configmap'") - namespace = r.Resource.(*v1.ConfigMap).Namespace - resourceName = r.Resource.(*v1.ConfigMap).Name - envarPostfix = common.ConfigmapEnvarPostfix - annotation = common.ConfigmapUpdateOnChangeAnnotation - } else if _, ok := r.Resource.(*v1.Secret); ok { - logrus.Infof("Performing 'Updated' action for resource of type 'secret'") - namespace = r.Resource.(*v1.Secret).Namespace - resourceName = r.Resource.(*v1.Secret).Name - envarPostfix = common.SecretEnvarPostfix - annotation = common.SecretUpdateOnChangeAnnotation - } else { - logrus.Warnf("Invalid resource: Resource should be 'Secret' or 'Configmap' but found, %v", r.Resource) - } - - if rollingUpgradeType == "deployments" { - err = RollingUpgradeDeployment(client, namespace, resourceName, shaData, envarPostfix, annotation) - } else if rollingUpgradeType == "daemonsets" { - err = RollingUpgradeDaemonSets(client, namespace, resourceName, shaData, envarPostfix, annotation) - } else if rollingUpgradeType == "statefulSets" { - err = RollingUpgradeStatefulSets(client, namespace, resourceName, shaData, envarPostfix, annotation) - } + config, envVarPostfix, oldSHAData := getConfig(r) + if config.shaValue != oldSHAData { + err = PerformRollingUpgrade(client, config, envVarPostfix, upgradeFuncs) if err != nil { - logrus.Errorf("Rolling upgrade failed for %s of resource type %s", resourceName, rollingUpgradeType) + logrus.Fatalf("Rolling upgrade failed with error = %v", err) } } else { - logrus.Infof("Resource update will not happen because no data change detected") + logrus.Infof("Rolling upgrade will not happend because no actual change in data has been detected") } } -// RollingUpgradeDeployment upgrades the deployment if there is any change in configmap or secret data -func RollingUpgradeDeployment(client kubernetes.Interface, namespace string, resourceName string, shaData string, envarPostfix string, annotation string) error { - deployments, err := client.ExtensionsV1beta1().Deployments(namespace).List(meta_v1.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[annotation] - updated := performRollingUpgrade(containers, resourceName, annotationValue, shaData, envarPostfix) - if !updated { - logrus.Warnf("Rolling upgrade did not happen") - } else { - // update the deployment - _, err := client.ExtensionsV1beta1().Deployments(namespace).Update(&d) - if err != nil { - logrus.Errorf("Update deployment failed %v", err) - } else { - logrus.Infof("Updated Deployment %s", d.Name) - } +func getConfig(r ResourceUpdatedHandler) (Config, string, string) { + var shaData, oldSHAData, envVarPostfix string + var config Config + if _, ok := r.Resource.(*v1.ConfigMap); ok { + logrus.Infof("Performing 'Updated' action for resource of type 'configmap'") + configmap := r.Resource.(*v1.ConfigMap) + shaData = getSHAfromConfigmap(configmap.Data) + oldSHAData = getSHAfromConfigmap(r.OldResource.(*v1.ConfigMap).Data) + config = Config{ + namespace: configmap.Namespace, + resourceName: configmap.Name, + annotation: constants.ConfigmapUpdateOnChangeAnnotation, + shaValue: shaData, } + envVarPostfix = constants.ConfigmapEnvarPostfix + } else if _, ok := r.Resource.(*v1.Secret); ok { + logrus.Infof("Performing 'Updated' action for resource of type 'secret'") + secret := r.Resource.(*v1.Secret) + shaData = getSHAfromSecret(secret.Data) + oldSHAData = getSHAfromSecret(r.OldResource.(*v1.Secret).Data) + config = Config{ + namespace: secret.Namespace, + resourceName: secret.Name, + annotation: constants.SecretUpdateOnChangeAnnotation, + shaValue: shaData, + } + envVarPostfix = constants.SecretEnvarPostfix + } else { + logrus.Warnf("Invalid resource: Resource should be 'Secret' or 'Configmap' but found, %v", r.Resource) } - return nil + return config, envVarPostfix, oldSHAData } -// RollingUpgradeDaemonSets upgrades the daemonset if there is any change in configmap or secret data -func RollingUpgradeDaemonSets(client kubernetes.Interface, namespace string, resourceName string, shaData string, envarPostfix string, annotation string) error { - daemonSets, err := client.ExtensionsV1beta1().DaemonSets(namespace).List(meta_v1.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[annotation] - updated := performRollingUpgrade(containers, resourceName, annotationValue, shaData, envarPostfix) - if !updated { - logrus.Warnf("Rolling upgrade did not happen") - } else { - // update the daemonSet - _, err := client.ExtensionsV1beta1().DaemonSets(namespace).Update(&d) - if err != nil { - logrus.Errorf("Update daemonSet failed %v", err) - } else { - logrus.Infof("Updated daemonSet %s", d.Name) +// PerformRollingUpgrade upgrades the deployment if there is any change in configmap or secret data +func PerformRollingUpgrade(client kubernetes.Interface, config Config, envarPostfix string, upgradeFuncs RollingUpgradeFuncs) error { + items := upgradeFuncs.ItemsFunc(client, config.namespace) + var err error + for _, i := range items { + containers := upgradeFuncs.ContainersFunc(i) + // find correct annotation and update the resource + annotationValue := util.ToObjectMeta(i).Annotations[config.annotation] + if annotationValue != "" { + values := strings.Split(annotationValue, ",") + for _, value := range values { + if value == config.resourceName { + updated := updateContainers(containers, value, config.shaValue, envarPostfix) + if !updated { + logrus.Warnf("Rolling upgrade did not happen") + } else { + err = upgradeFuncs.UpdateFunc(client, config.namespace, i) + if err != nil { + logrus.Errorf("Update deployment failed %v", err) + } else { + logrus.Infof("Updated Deployment %s", config.resourceName) + } + break + } + } } } } return err } -// RollingUpgradeStatefulSets upgrades the statefulset if there is any change in configmap or secret data -func RollingUpgradeStatefulSets(client kubernetes.Interface, namespace string, resourceName string, shaData string, envarPostfix string, annotation string) error { - statefulSets, err := client.AppsV1beta1().StatefulSets(namespace).List(meta_v1.ListOptions{}) - if err != nil { - logrus.Errorf("Failed to list statefulSets %v", err) - } - - for _, s := range statefulSets.Items { - containers := s.Spec.Template.Spec.Containers - // match statefulSets with the correct annotation - annotationValue := s.ObjectMeta.Annotations[annotation] - updated := performRollingUpgrade(containers, resourceName, annotationValue, shaData, envarPostfix) - if !updated { - logrus.Warnf("Rolling upgrade did not happen") - } else { - // update the statefulSet - _, err := client.AppsV1beta1().StatefulSets(namespace).Update(&s) - if err != nil { - logrus.Errorf("Update statefulSet failed %v", err) - } else { - logrus.Infof("Updated statefulSet %s", s.Name) - } - } - } - return err -} - -func performRollingUpgrade(containers []v1.Container, resourceName string, annotationValue string, shaData string, envarPostfix string) bool { - updated := false - if annotationValue != "" { - values := strings.Split(annotationValue, ",") - for _, value := range values { - if value == resourceName { - updated = updateContainers(containers, value, shaData, envarPostfix) - break - } - } - } - return updated -} - func updateContainers(containers []v1.Container, annotationValue string, shaData string, envarPostfix string) bool { updated := false - envar := common.EnvVarPrefix + common.ConvertToEnvVarName(annotationValue) + envarPostfix + envar := constants.EnvVarPrefix + common.ConvertToEnvVarName(annotationValue) + envarPostfix logrus.Infof("Generated environment variable: %s", envar) for i := range containers { envs := containers[i].Env @@ -209,18 +256,20 @@ func updateEnvVar(envs []v1.EnvVar, envar string, shaData string) bool { return false } -func getSHAfromData(resource interface{}) string { +func getSHAfromConfigmap(data map[string]string) string { values := []string{} - if _, ok := resource.(*v1.ConfigMap); ok { - logrus.Infof("Generating SHA for configmap data") - for k, v := range resource.(*v1.ConfigMap).Data { - values = append(values, k+"="+v) - } - } else if _, ok := resource.(*v1.Secret); ok { - logrus.Infof("Generating SHA for secret data") - for k, v := range resource.(*v1.Secret).Data { - values = append(values, k+"="+string(v[:])) - } + for k, v := range data { + values = append(values, k+"="+v) + } + sort.Strings(values) + return crypto.GenerateSHA(strings.Join(values, ";")) +} + +func getSHAfromSecret(data map[string][]byte) string { + values := []string{} + logrus.Infof("Generating SHA for secret data") + for k, v := range data { + values = append(values, k+"="+string(v[:])) } sort.Strings(values) return crypto.GenerateSHA(strings.Join(values, ";")) diff --git a/internal/pkg/handler/update_test.go b/internal/pkg/handler/update_test.go index 7cc5461f..7f54fa09 100644 --- a/internal/pkg/handler/update_test.go +++ b/internal/pkg/handler/update_test.go @@ -7,6 +7,7 @@ import ( "github.com/sirupsen/logrus" "github.com/stakater/Reloader/internal/pkg/common" + "github.com/stakater/Reloader/internal/pkg/constants" "github.com/stakater/Reloader/internal/pkg/testutil" "github.com/stakater/Reloader/pkg/kube" "k8s.io/client-go/kubernetes" @@ -152,14 +153,26 @@ func teardown() { func TestRollingUpgradeForDeploymentWithConfigmap(t *testing.T) { shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, configmapName, "www.stakater.com") - err := RollingUpgradeDeployment(client, namespace, configmapName, shaData, common.ConfigmapEnvarPostfix, common.ConfigmapUpdateOnChangeAnnotation) + config := Config{ + namespace: namespace, + resourceName: configmapName, + shaValue: shaData, + annotation: constants.ConfigmapUpdateOnChangeAnnotation, + } + deploymentFuncs := RollingUpgradeFuncs{ + ItemsFunc: GetDeploymentItems, + ContainersFunc: GetDeploymentContainers, + UpdateFunc: UpdateDeployment, + } + + err := PerformRollingUpgrade(client, config, constants.ConfigmapEnvarPostfix, deploymentFuncs) time.Sleep(5 * time.Second) if err != nil { - t.Errorf("Rolling upgrade failed for Deployment with configmap") + t.Errorf("Rolling upgrade failed for Deployment with Configmap") } logrus.Infof("Verifying deployment update") - updated := testutil.VerifyDeploymentUpdate(client, namespace, configmapName, common.ConfigmapEnvarPostfix, shaData, common.ConfigmapUpdateOnChangeAnnotation) + updated := testutil.VerifyDeploymentUpdate(client, namespace, configmapName, constants.ConfigmapEnvarPostfix, shaData, constants.ConfigmapUpdateOnChangeAnnotation) if !updated { t.Errorf("Deployment was not updated") } @@ -167,14 +180,26 @@ func TestRollingUpgradeForDeploymentWithConfigmap(t *testing.T) { func TestRollingUpgradeForDeploymentWithSecret(t *testing.T) { shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, "dGVzdFVwZGF0ZWRTZWNyZXRFbmNvZGluZ0ZvclJlbG9hZGVy") - err := RollingUpgradeDeployment(client, namespace, secretName, shaData, common.SecretEnvarPostfix, common.SecretUpdateOnChangeAnnotation) + config := Config{ + namespace: namespace, + resourceName: secretName, + shaValue: shaData, + annotation: constants.SecretUpdateOnChangeAnnotation, + } + deploymentFuncs := RollingUpgradeFuncs{ + ItemsFunc: GetDeploymentItems, + ContainersFunc: GetDeploymentContainers, + UpdateFunc: UpdateDeployment, + } + + err := PerformRollingUpgrade(client, config, constants.SecretEnvarPostfix, deploymentFuncs) time.Sleep(5 * time.Second) if err != nil { t.Errorf("Rolling upgrade failed for Deployment with Secret") } logrus.Infof("Verifying deployment update") - updated := testutil.VerifyDeploymentUpdate(client, namespace, secretName, common.SecretEnvarPostfix, shaData, common.SecretUpdateOnChangeAnnotation) + updated := testutil.VerifyDeploymentUpdate(client, namespace, secretName, constants.SecretEnvarPostfix, shaData, constants.SecretUpdateOnChangeAnnotation) if !updated { t.Errorf("Deployment was not updated") } @@ -182,14 +207,26 @@ func TestRollingUpgradeForDeploymentWithSecret(t *testing.T) { func TestRollingUpgradeForDaemonSetWithConfigmap(t *testing.T) { shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "www.facebook.com") - err := RollingUpgradeDaemonSets(client, namespace, configmapName, shaData, common.ConfigmapEnvarPostfix, common.ConfigmapUpdateOnChangeAnnotation) + config := Config{ + namespace: namespace, + resourceName: configmapName, + shaValue: shaData, + annotation: constants.ConfigmapUpdateOnChangeAnnotation, + } + daemonSetFuncs := RollingUpgradeFuncs{ + ItemsFunc: GetDaemonSetItems, + ContainersFunc: GetDaemonSetContainers, + UpdateFunc: UpdateDaemonSet, + } + + err := PerformRollingUpgrade(client, config, constants.ConfigmapEnvarPostfix, daemonSetFuncs) time.Sleep(5 * time.Second) if err != nil { t.Errorf("Rolling upgrade failed for DaemonSet with configmap") } logrus.Infof("Verifying daemonSet update") - updated := testutil.VerifyDaemonSetUpdate(client, namespace, configmapName, common.ConfigmapEnvarPostfix, shaData, common.ConfigmapUpdateOnChangeAnnotation) + updated := testutil.VerifyDaemonSetUpdate(client, namespace, configmapName, constants.ConfigmapEnvarPostfix, shaData, constants.ConfigmapUpdateOnChangeAnnotation) if !updated { t.Errorf("DaemonSet was not updated") } @@ -197,14 +234,27 @@ func TestRollingUpgradeForDaemonSetWithConfigmap(t *testing.T) { func TestRollingUpgradeForDaemonSetWithSecret(t *testing.T) { shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, "d3d3LmZhY2Vib29rLmNvbQ==") - err := RollingUpgradeDaemonSets(client, namespace, secretName, shaData, common.SecretEnvarPostfix, common.SecretUpdateOnChangeAnnotation) + + config := Config{ + namespace: namespace, + resourceName: secretName, + shaValue: shaData, + annotation: constants.SecretUpdateOnChangeAnnotation, + } + daemonSetFuncs := RollingUpgradeFuncs{ + ItemsFunc: GetDaemonSetItems, + ContainersFunc: GetDaemonSetContainers, + UpdateFunc: UpdateDaemonSet, + } + + err := PerformRollingUpgrade(client, config, constants.SecretEnvarPostfix, daemonSetFuncs) time.Sleep(5 * time.Second) if err != nil { t.Errorf("Rolling upgrade failed for DaemonSet with secret") } logrus.Infof("Verifying daemonSet update") - updated := testutil.VerifyDaemonSetUpdate(client, namespace, secretName, common.SecretEnvarPostfix, shaData, common.SecretUpdateOnChangeAnnotation) + updated := testutil.VerifyDaemonSetUpdate(client, namespace, secretName, constants.SecretEnvarPostfix, shaData, constants.SecretUpdateOnChangeAnnotation) if !updated { t.Errorf("DaemonSet was not updated") } @@ -212,14 +262,27 @@ func TestRollingUpgradeForDaemonSetWithSecret(t *testing.T) { func TestRollingUpgradeForStatefulSetWithConfigmap(t *testing.T) { shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "www.twitter.com") - err := RollingUpgradeStatefulSets(client, namespace, configmapName, shaData, common.ConfigmapEnvarPostfix, common.ConfigmapUpdateOnChangeAnnotation) + + config := Config{ + namespace: namespace, + resourceName: configmapName, + shaValue: shaData, + annotation: constants.ConfigmapUpdateOnChangeAnnotation, + } + statefulSetFuncs := RollingUpgradeFuncs{ + ItemsFunc: GetStatefulSetItems, + ContainersFunc: GetStatefulsetContainers, + UpdateFunc: UpdateStatefulset, + } + + err := PerformRollingUpgrade(client, config, constants.ConfigmapEnvarPostfix, statefulSetFuncs) time.Sleep(5 * time.Second) if err != nil { t.Errorf("Rolling upgrade failed for StatefulSet with configmap") } logrus.Infof("Verifying statefulSet update") - updated := testutil.VerifyStatefulSetUpdate(client, namespace, configmapName, common.ConfigmapEnvarPostfix, shaData, common.ConfigmapUpdateOnChangeAnnotation) + updated := testutil.VerifyStatefulSetUpdate(client, namespace, configmapName, constants.ConfigmapEnvarPostfix, shaData, constants.ConfigmapUpdateOnChangeAnnotation) if !updated { t.Errorf("StatefulSet was not updated") } @@ -227,14 +290,27 @@ func TestRollingUpgradeForStatefulSetWithConfigmap(t *testing.T) { func TestRollingUpgradeForStatefulSetWithSecret(t *testing.T) { shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, "d3d3LnR3aXR0ZXIuY29t") - err := RollingUpgradeStatefulSets(client, namespace, secretName, shaData, common.SecretEnvarPostfix, common.SecretUpdateOnChangeAnnotation) + + config := Config{ + namespace: namespace, + resourceName: secretName, + shaValue: shaData, + annotation: constants.SecretUpdateOnChangeAnnotation, + } + statefulSetFuncs := RollingUpgradeFuncs{ + ItemsFunc: GetStatefulSetItems, + ContainersFunc: GetStatefulsetContainers, + UpdateFunc: UpdateStatefulset, + } + + err := PerformRollingUpgrade(client, config, constants.SecretEnvarPostfix, statefulSetFuncs) time.Sleep(5 * time.Second) if err != nil { t.Errorf("Rolling upgrade failed for StatefulSet with secret") } logrus.Infof("Verifying statefulSet update") - updated := testutil.VerifyStatefulSetUpdate(client, namespace, secretName, common.SecretEnvarPostfix, shaData, common.SecretUpdateOnChangeAnnotation) + updated := testutil.VerifyStatefulSetUpdate(client, namespace, secretName, constants.SecretEnvarPostfix, shaData, constants.SecretUpdateOnChangeAnnotation) if !updated { t.Errorf("StatefulSet was not updated") } diff --git a/internal/pkg/testutil/kube.go b/internal/pkg/testutil/kube.go index b17a88ea..4fea00f7 100644 --- a/internal/pkg/testutil/kube.go +++ b/internal/pkg/testutil/kube.go @@ -7,6 +7,7 @@ import ( "github.com/sirupsen/logrus" "github.com/stakater/Reloader/internal/pkg/common" + "github.com/stakater/Reloader/internal/pkg/constants" "github.com/stakater/Reloader/internal/pkg/crypto" v1_beta1 "k8s.io/api/apps/v1beta1" "k8s.io/api/core/v1" @@ -52,8 +53,8 @@ func GetDeployment(namespace string, deploymentName string) *v1beta1.Deployment Namespace: namespace, Labels: map[string]string{"firstLabel": "temp"}, Annotations: map[string]string{ - common.ConfigmapUpdateOnChangeAnnotation: deploymentName, - common.SecretUpdateOnChangeAnnotation: deploymentName}, + constants.ConfigmapUpdateOnChangeAnnotation: deploymentName, + constants.SecretUpdateOnChangeAnnotation: deploymentName}, }, Spec: v1beta1.DeploymentSpec{ Replicas: &replicaset, @@ -91,8 +92,8 @@ func GetDaemonSet(namespace string, daemonsetName string) *v1beta1.DaemonSet { Namespace: namespace, Labels: map[string]string{"firstLabel": "temp"}, Annotations: map[string]string{ - common.ConfigmapUpdateOnChangeAnnotation: daemonsetName, - common.SecretUpdateOnChangeAnnotation: daemonsetName}, + constants.ConfigmapUpdateOnChangeAnnotation: daemonsetName, + constants.SecretUpdateOnChangeAnnotation: daemonsetName}, }, Spec: v1beta1.DaemonSetSpec{ UpdateStrategy: v1beta1.DaemonSetUpdateStrategy{ @@ -129,8 +130,8 @@ func GetStatefulSet(namespace string, statefulsetName string) *v1_beta1.Stateful Namespace: namespace, Labels: map[string]string{"firstLabel": "temp"}, Annotations: map[string]string{ - common.ConfigmapUpdateOnChangeAnnotation: statefulsetName, - common.SecretUpdateOnChangeAnnotation: statefulsetName}, + constants.ConfigmapUpdateOnChangeAnnotation: statefulsetName, + constants.SecretUpdateOnChangeAnnotation: statefulsetName}, }, Spec: v1_beta1.StatefulSetSpec{ UpdateStrategy: v1_beta1.StatefulSetUpdateStrategy{ @@ -227,7 +228,7 @@ func VerifyDeploymentUpdate(client kubernetes.Interface, namespace string, name } } if matches { - envName := common.EnvVarPrefix + common.ConvertToEnvVarName(annotationValue) + envarPostfix + envName := constants.EnvVarPrefix + common.ConvertToEnvVarName(annotationValue) + envarPostfix updated := getResourceSHA(containers, envName) if updated == shaData { return true @@ -258,7 +259,7 @@ func VerifyDaemonSetUpdate(client kubernetes.Interface, namespace string, name s } } if matches { - envName := common.EnvVarPrefix + common.ConvertToEnvVarName(annotationValue) + resourceType + envName := constants.EnvVarPrefix + common.ConvertToEnvVarName(annotationValue) + resourceType updated := getResourceSHA(containers, envName) if updated == shaData { @@ -290,7 +291,7 @@ func VerifyStatefulSetUpdate(client kubernetes.Interface, namespace string, name } } if matches { - envName := common.EnvVarPrefix + common.ConvertToEnvVarName(annotationValue) + resourceType + envName := constants.EnvVarPrefix + common.ConvertToEnvVarName(annotationValue) + resourceType updated := getResourceSHA(containers, envName) if updated == shaData { diff --git a/internal/pkg/util/interface.go b/internal/pkg/util/interface.go new file mode 100644 index 00000000..e4f318a9 --- /dev/null +++ b/internal/pkg/util/interface.go @@ -0,0 +1,38 @@ +package util + +import ( + "reflect" + + "github.com/sirupsen/logrus" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// InterfaceSlice converts an interface to an interface array +func InterfaceSlice(slice interface{}) []interface{} { + s := reflect.ValueOf(slice) + if s.Kind() != reflect.Slice { + logrus.Errorf("InterfaceSlice() given a non-slice type") + } + + ret := make([]interface{}, s.Len()) + + for i := 0; i < s.Len(); i++ { + ret[i] = s.Index(i).Interface() + } + + return ret +} + +type ObjectMeta struct { + metav1.ObjectMeta +} + +func ToObjectMeta(kubernetesObject interface{}) ObjectMeta { + objectValue := reflect.ValueOf(kubernetesObject) + fieldName := reflect.TypeOf((*metav1.ObjectMeta)(nil)).Elem().Name() + field := objectValue.FieldByName(fieldName).Interface().(metav1.ObjectMeta) + + return ObjectMeta{ + ObjectMeta: field, + } +} From 2cac0cc71393c487298ac7dd5daadf6d61649fe6 Mon Sep 17 00:00:00 2001 From: faizanahmad055 Date: Tue, 24 Jul 2018 21:05:50 +0500 Subject: [PATCH 17/21] Implement PR-2 review comments --- internal/pkg/callbacks/rolling_upgrade.go | 108 +++++++ internal/pkg/constants/annotations.go | 2 +- internal/pkg/constants/constants.go | 10 +- internal/pkg/controller/controller.go | 1 - internal/pkg/controller/controller_test.go | 301 +++++++++++++----- internal/pkg/crypto/sha_test.go | 6 +- internal/pkg/handler/update.go | 192 ++++------- internal/pkg/handler/update_test.go | 157 +++++---- internal/pkg/testutil/kube.go | 150 +++------ internal/pkg/util/config.go | 9 + .../pkg/{common/common.go => util/util.go} | 18 +- .../common_test.go => util/util_test.go} | 10 +- 12 files changed, 529 insertions(+), 435 deletions(-) create mode 100644 internal/pkg/callbacks/rolling_upgrade.go create mode 100644 internal/pkg/util/config.go rename internal/pkg/{common/common.go => util/util.go} (66%) rename internal/pkg/{common/common_test.go => util/util_test.go} (56%) diff --git a/internal/pkg/callbacks/rolling_upgrade.go b/internal/pkg/callbacks/rolling_upgrade.go new file mode 100644 index 00000000..b44beef0 --- /dev/null +++ b/internal/pkg/callbacks/rolling_upgrade.go @@ -0,0 +1,108 @@ +package callbacks + +import ( + "github.com/sirupsen/logrus" + "github.com/stakater/Reloader/internal/pkg/util" + apps_v1beta1 "k8s.io/api/apps/v1beta1" + "k8s.io/api/core/v1" + "k8s.io/api/extensions/v1beta1" + meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" +) + +//ItemsFunc is a generic function to return a specific resource array in given namespace +type ItemsFunc func(kubernetes.Interface, string) []interface{} + +//ContainersFunc is a generic func to return containers +type ContainersFunc func(interface{}) []v1.Container + +//UpdateFunc performs the resource update +type UpdateFunc func(kubernetes.Interface, string, interface{}) error + +type ResourceTypeFunc func() string + +//RollingUpgradeFuncs contains generic functions to perform rolling upgrade +type RollingUpgradeFuncs struct { + ItemsFunc ItemsFunc + ContainersFunc ContainersFunc + UpdateFunc UpdateFunc + ResourceTypeFunc ResourceTypeFunc +} + +// GetDeploymentItems returns the deployments in given namespace +func GetDeploymentItems(client kubernetes.Interface, namespace string) []interface{} { + deployments, err := client.ExtensionsV1beta1().Deployments(namespace).List(meta_v1.ListOptions{}) + if err != nil { + logrus.Errorf("Failed to list deployments %v", err) + } + return util.InterfaceSlice(deployments.Items) +} + +// GetDaemonSetItems returns the daemonSet in given namespace +func GetDaemonSetItems(client kubernetes.Interface, namespace string) []interface{} { + daemonSets, err := client.ExtensionsV1beta1().DaemonSets(namespace).List(meta_v1.ListOptions{}) + if err != nil { + logrus.Errorf("Failed to list daemonSets %v", err) + } + return util.InterfaceSlice(daemonSets.Items) +} + +// GetStatefulSetItems returns the statefulSet in given namespace +func GetStatefulSetItems(client kubernetes.Interface, namespace string) []interface{} { + statefulSets, err := client.AppsV1beta1().StatefulSets(namespace).List(meta_v1.ListOptions{}) + if err != nil { + logrus.Errorf("Failed to list statefulSets %v", err) + } + return util.InterfaceSlice(statefulSets.Items) +} + +// GetDeploymentContainers returns the containers of given deployment +func GetDeploymentContainers(item interface{}) []v1.Container { + return item.(v1beta1.Deployment).Spec.Template.Spec.Containers +} + +// GetDaemonSetContainers returns the containers of given daemonset +func GetDaemonSetContainers(item interface{}) []v1.Container { + return item.(v1beta1.DaemonSet).Spec.Template.Spec.Containers +} + +// GetStatefulsetContainers returns the containers of given statefulSet +func GetStatefulsetContainers(item interface{}) []v1.Container { + return item.(apps_v1beta1.StatefulSet).Spec.Template.Spec.Containers +} + +// GetDeploymentTypeName returns Deployment resource type +func GetDeploymentTypeName() string { + return "Deployment" +} + +// GetDaemonSetTypeName returns DaemonSet resource type +func GetDaemonSetTypeName() string { + return "DaemonSet" +} + +// GetStatefulSetTypeName returns StatefulSet resource type +func GetStatefulSetTypeName() string { + return "StatefulSet" +} + +// UpdateDeployment performs rolling upgrade on deployment +func UpdateDeployment(client kubernetes.Interface, namespace string, resource interface{}) error { + deployment := resource.(v1beta1.Deployment) + _, err := client.ExtensionsV1beta1().Deployments(namespace).Update(&deployment) + return err +} + +// UpdateDaemonSet performs rolling upgrade on daemonSet +func UpdateDaemonSet(client kubernetes.Interface, namespace string, resource interface{}) error { + daemonSet := resource.(v1beta1.DaemonSet) + _, err := client.ExtensionsV1beta1().DaemonSets(namespace).Update(&daemonSet) + return err +} + +// UpdateStatefulset performs rolling upgrade on statefulSet +func UpdateStatefulset(client kubernetes.Interface, namespace string, resource interface{}) error { + statefulSet := resource.(apps_v1beta1.StatefulSet) + _, err := client.AppsV1beta1().StatefulSets(namespace).Update(&statefulSet) + return err +} diff --git a/internal/pkg/constants/annotations.go b/internal/pkg/constants/annotations.go index df6eb8dd..d06cc5a4 100644 --- a/internal/pkg/constants/annotations.go +++ b/internal/pkg/constants/annotations.go @@ -5,4 +5,4 @@ const ( ConfigmapUpdateOnChangeAnnotation = "configmap.reloader.stakater.com/reload" // SecretUpdateOnChangeAnnotation is an annotation to detect changes in secrets SecretUpdateOnChangeAnnotation = "secret.reloader.stakater.com/reload" -) \ No newline at end of file +) diff --git a/internal/pkg/constants/constants.go b/internal/pkg/constants/constants.go index efb53927..d01792df 100644 --- a/internal/pkg/constants/constants.go +++ b/internal/pkg/constants/constants.go @@ -1,10 +1,10 @@ package constants const ( - // ConfigmapEnvarPostfix is a postfix for configmap envVar - ConfigmapEnvarPostfix = "_CONFIGMAP" - // SecretEnvarPostfix is a postfix for secret envVar - SecretEnvarPostfix = "_SECRET" + // ConfigmapEnvVarPostfix is a postfix for configmap envVar + ConfigmapEnvVarPostfix = "_CONFIGMAP" + // SecretEnvVarPostfix is a postfix for secret envVar + SecretEnvVarPostfix = "_SECRET" // EnvVarPrefix is a Prefix for environment variable EnvVarPrefix = "STAKATER_" -) \ No newline at end of file +) diff --git a/internal/pkg/controller/controller.go b/internal/pkg/controller/controller.go index 0a84011d..8bb5ff59 100644 --- a/internal/pkg/controller/controller.go +++ b/internal/pkg/controller/controller.go @@ -64,7 +64,6 @@ func (c *Controller) Update(old interface{}, new interface{}) { // Delete function to add an object to the queue in case of deleting a resource func (c *Controller) Delete(old interface{}) { - // TODO Added this function for future usecase logrus.Infof("Resource deletion has been detected but no further implementation found to take action") } diff --git a/internal/pkg/controller/controller_test.go b/internal/pkg/controller/controller_test.go index 3903b360..ef860f66 100644 --- a/internal/pkg/controller/controller_test.go +++ b/internal/pkg/controller/controller_test.go @@ -6,15 +6,15 @@ import ( "time" "github.com/sirupsen/logrus" - "github.com/stakater/Reloader/internal/pkg/common" + "github.com/stakater/Reloader/internal/pkg/callbacks" "github.com/stakater/Reloader/internal/pkg/constants" "github.com/stakater/Reloader/internal/pkg/testutil" + "github.com/stakater/Reloader/internal/pkg/util" "github.com/stakater/Reloader/pkg/kube" - "k8s.io/client-go/kubernetes" ) var ( - client = getClient() + client = testutil.GetClient() namespace = "test-reloader" configmapNamePrefix = "testconfigmap-reloader" secretNamePrefix = "testsecret-reloader" @@ -51,19 +51,11 @@ func TestMain(m *testing.M) { 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 -} - // Perform rolling upgrade on deployment and create env var upon updating the configmap func TestControllerUpdatingConfigmapShouldCreateEnvInDeployment(t *testing.T) { // Creating configmap - configmapName := configmapNamePrefix + "-update-" + common.RandSeq(5) + configmapName := configmapNamePrefix + "-update-" + testutil.RandSeq(5) configmapClient, err := testutil.CreateConfigMap(client, namespace, configmapName, "www.google.com") if err != nil { t.Errorf("Error while creating the configmap %v", err) @@ -84,7 +76,19 @@ func TestControllerUpdatingConfigmapShouldCreateEnvInDeployment(t *testing.T) { // Verifying deployment update logrus.Infof("Verifying env var has been created") shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "www.stakater.com") - updated := testutil.VerifyDeploymentUpdate(client, namespace, configmapName, constants.ConfigmapEnvarPostfix, shaData, constants.ConfigmapUpdateOnChangeAnnotation) + config := util.Config{ + Namespace: namespace, + ResourceName: configmapName, + SHAValue: shaData, + Annotation: constants.ConfigmapUpdateOnChangeAnnotation, + } + deploymentFuncs := callbacks.RollingUpgradeFuncs{ + ItemsFunc: callbacks.GetDeploymentItems, + ContainersFunc: callbacks.GetDeploymentContainers, + UpdateFunc: callbacks.UpdateDeployment, + ResourceTypeFunc: callbacks.GetDeploymentTypeName, + } + updated := testutil.VerifyResourceUpdate(client, config, constants.ConfigmapEnvVarPostfix, deploymentFuncs) if !updated { t.Errorf("Deployment was not updated") } @@ -107,7 +111,7 @@ func TestControllerUpdatingConfigmapShouldCreateEnvInDeployment(t *testing.T) { // Perform rolling upgrade on deployment and update env var upon updating the configmap func TestControllerForUpdatingConfigmapShouldUpdateDeployment(t *testing.T) { // Creating secret - configmapName := configmapNamePrefix + "-update-" + common.RandSeq(5) + configmapName := configmapNamePrefix + "-update-" + testutil.RandSeq(5) configmapClient, err := testutil.CreateConfigMap(client, namespace, configmapName, "www.google.com") if err != nil { t.Errorf("Error while creating the configmap %v", err) @@ -134,7 +138,21 @@ func TestControllerForUpdatingConfigmapShouldUpdateDeployment(t *testing.T) { // Verifying deployment update logrus.Infof("Verifying env var has been updated") shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "aurorasolutions.io") - updated := testutil.VerifyDeploymentUpdate(client, namespace, configmapName, constants.ConfigmapEnvarPostfix, shaData, constants.ConfigmapUpdateOnChangeAnnotation) + config := util.Config{ + Namespace: namespace, + ResourceName: configmapName, + SHAValue: shaData, + Annotation: constants.ConfigmapUpdateOnChangeAnnotation, + } + + deploymentFuncs := callbacks.RollingUpgradeFuncs{ + ItemsFunc: callbacks.GetDeploymentItems, + ContainersFunc: callbacks.GetDeploymentContainers, + UpdateFunc: callbacks.UpdateDeployment, + ResourceTypeFunc: callbacks.GetDeploymentTypeName, + } + + updated := testutil.VerifyResourceUpdate(client, config, constants.ConfigmapEnvVarPostfix, deploymentFuncs) if !updated { t.Errorf("Deployment was not updated") } @@ -157,7 +175,7 @@ func TestControllerForUpdatingConfigmapShouldUpdateDeployment(t *testing.T) { // Do not Perform rolling upgrade on deployment and create env var upon updating the labels configmap func TestControllerUpdatingConfigmapLabelsShouldNotCreateorUpdateEnvInDeployment(t *testing.T) { // Creating configmap - configmapName := configmapNamePrefix + "-update-" + common.RandSeq(5) + configmapName := configmapNamePrefix + "-update-" + testutil.RandSeq(5) configmapClient, err := testutil.CreateConfigMap(client, namespace, configmapName, "www.google.com") if err != nil { t.Errorf("Error while creating the configmap %v", err) @@ -178,7 +196,19 @@ func TestControllerUpdatingConfigmapLabelsShouldNotCreateorUpdateEnvInDeployment // Verifying deployment update logrus.Infof("Verifying env var has been created") shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "www.google.com") - updated := testutil.VerifyDeploymentUpdate(client, namespace, configmapName, constants.ConfigmapEnvarPostfix, shaData, constants.ConfigmapUpdateOnChangeAnnotation) + config := util.Config{ + Namespace: namespace, + ResourceName: configmapName, + SHAValue: shaData, + Annotation: constants.ConfigmapUpdateOnChangeAnnotation, + } + deploymentFuncs := callbacks.RollingUpgradeFuncs{ + ItemsFunc: callbacks.GetDeploymentItems, + ContainersFunc: callbacks.GetDeploymentContainers, + UpdateFunc: callbacks.UpdateDeployment, + ResourceTypeFunc: callbacks.GetDeploymentTypeName, + } + updated := testutil.VerifyResourceUpdate(client, config, constants.ConfigmapEnvVarPostfix, deploymentFuncs) if updated { t.Errorf("Deployment should not be updated by changing label") } @@ -201,7 +231,7 @@ func TestControllerUpdatingConfigmapLabelsShouldNotCreateorUpdateEnvInDeployment // Perform rolling upgrade on secret and create a env var upon updating the secret func TestControllerUpdatingSecretShouldCreateEnvInDeployment(t *testing.T) { // Creating secret - secretName := secretNamePrefix + "-update-" + common.RandSeq(5) + secretName := secretNamePrefix + "-update-" + testutil.RandSeq(5) secretClient, err := testutil.CreateSecret(client, namespace, secretName, data) if err != nil { t.Errorf("Error in secret creation: %v", err) @@ -222,7 +252,19 @@ func TestControllerUpdatingSecretShouldCreateEnvInDeployment(t *testing.T) { // Verifying Upgrade logrus.Infof("Verifying env var has been created") shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, newData) - updated := testutil.VerifyDeploymentUpdate(client, namespace, secretName, constants.SecretEnvarPostfix, shaData, constants.SecretUpdateOnChangeAnnotation) + config := util.Config{ + Namespace: namespace, + ResourceName: secretName, + SHAValue: shaData, + Annotation: constants.SecretUpdateOnChangeAnnotation, + } + deploymentFuncs := callbacks.RollingUpgradeFuncs{ + ItemsFunc: callbacks.GetDeploymentItems, + ContainersFunc: callbacks.GetDeploymentContainers, + UpdateFunc: callbacks.UpdateDeployment, + ResourceTypeFunc: callbacks.GetDeploymentTypeName, + } + updated := testutil.VerifyResourceUpdate(client, config, constants.SecretEnvVarPostfix, deploymentFuncs) if !updated { t.Errorf("Deployment was not updated") } @@ -245,7 +287,7 @@ func TestControllerUpdatingSecretShouldCreateEnvInDeployment(t *testing.T) { // Perform rolling upgrade on deployment and update env var upon updating the secret func TestControllerUpdatingSecretShouldUpdateEnvInDeployment(t *testing.T) { // Creating secret - secretName := secretNamePrefix + "-update-" + common.RandSeq(5) + secretName := secretNamePrefix + "-update-" + testutil.RandSeq(5) secretClient, err := testutil.CreateSecret(client, namespace, secretName, data) if err != nil { t.Errorf("Error in secret creation: %v", err) @@ -272,7 +314,19 @@ func TestControllerUpdatingSecretShouldUpdateEnvInDeployment(t *testing.T) { // Verifying Upgrade logrus.Infof("Verifying env var has been updated") shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, updatedData) - updated := testutil.VerifyDeploymentUpdate(client, namespace, secretName, constants.SecretEnvarPostfix, shaData, constants.SecretUpdateOnChangeAnnotation) + config := util.Config{ + Namespace: namespace, + ResourceName: secretName, + SHAValue: shaData, + Annotation: constants.SecretUpdateOnChangeAnnotation, + } + deploymentFuncs := callbacks.RollingUpgradeFuncs{ + ItemsFunc: callbacks.GetDeploymentItems, + ContainersFunc: callbacks.GetDeploymentContainers, + UpdateFunc: callbacks.UpdateDeployment, + ResourceTypeFunc: callbacks.GetDeploymentTypeName, + } + updated := testutil.VerifyResourceUpdate(client, config, constants.SecretEnvVarPostfix, deploymentFuncs) if !updated { t.Errorf("Deployment was not updated") } @@ -295,7 +349,7 @@ func TestControllerUpdatingSecretShouldUpdateEnvInDeployment(t *testing.T) { // Do not Perform rolling upgrade on secret and create or update a env var upon updating the label in secret func TestControllerUpdatingSecretLabelsShouldNotCreateorUpdateEnvInDeployment(t *testing.T) { // Creating secret - secretName := secretNamePrefix + "-update-" + common.RandSeq(5) + secretName := secretNamePrefix + "-update-" + testutil.RandSeq(5) secretClient, err := testutil.CreateSecret(client, namespace, secretName, data) if err != nil { t.Errorf("Error in secret creation: %v", err) @@ -315,7 +369,19 @@ func TestControllerUpdatingSecretLabelsShouldNotCreateorUpdateEnvInDeployment(t // Verifying Upgrade logrus.Infof("Verifying env var has been created") shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, data) - updated := testutil.VerifyDeploymentUpdate(client, namespace, secretName, constants.SecretEnvarPostfix, shaData, constants.SecretUpdateOnChangeAnnotation) + config := util.Config{ + Namespace: namespace, + ResourceName: secretName, + SHAValue: shaData, + Annotation: constants.SecretUpdateOnChangeAnnotation, + } + deploymentFuncs := callbacks.RollingUpgradeFuncs{ + ItemsFunc: callbacks.GetDeploymentItems, + ContainersFunc: callbacks.GetDeploymentContainers, + UpdateFunc: callbacks.UpdateDeployment, + ResourceTypeFunc: callbacks.GetDeploymentTypeName, + } + updated := testutil.VerifyResourceUpdate(client, config, constants.SecretEnvVarPostfix, deploymentFuncs) if updated { t.Errorf("Deployment should not be updated by changing label in secret") } @@ -338,7 +404,7 @@ func TestControllerUpdatingSecretLabelsShouldNotCreateorUpdateEnvInDeployment(t // Perform rolling upgrade on DaemonSet and create env var upon updating the configmap func TestControllerUpdatingConfigmapShouldCreateEnvInDaemonSet(t *testing.T) { // Creating configmap - configmapName := configmapNamePrefix + "-update-" + common.RandSeq(5) + configmapName := configmapNamePrefix + "-update-" + testutil.RandSeq(5) configmapClient, err := testutil.CreateConfigMap(client, namespace, configmapName, "www.google.com") if err != nil { t.Errorf("Error while creating the configmap %v", err) @@ -359,7 +425,19 @@ func TestControllerUpdatingConfigmapShouldCreateEnvInDaemonSet(t *testing.T) { // Verifying DaemonSet update logrus.Infof("Verifying env var has been created") shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "www.stakater.com") - updated := testutil.VerifyDaemonSetUpdate(client, namespace, configmapName, constants.ConfigmapEnvarPostfix, shaData, constants.ConfigmapUpdateOnChangeAnnotation) + config := util.Config{ + Namespace: namespace, + ResourceName: configmapName, + SHAValue: shaData, + Annotation: constants.ConfigmapUpdateOnChangeAnnotation, + } + daemonSetFuncs := callbacks.RollingUpgradeFuncs{ + ItemsFunc: callbacks.GetDaemonSetItems, + ContainersFunc: callbacks.GetDaemonSetContainers, + UpdateFunc: callbacks.UpdateDaemonSet, + ResourceTypeFunc: callbacks.GetDaemonSetTypeName, + } + updated := testutil.VerifyResourceUpdate(client, config, constants.ConfigmapEnvVarPostfix, daemonSetFuncs) if !updated { t.Errorf("DaemonSet was not updated") } @@ -382,7 +460,7 @@ func TestControllerUpdatingConfigmapShouldCreateEnvInDaemonSet(t *testing.T) { // Perform rolling upgrade on DaemonSet and update env var upon updating the configmap func TestControllerForUpdatingConfigmapShouldUpdateDaemonSet(t *testing.T) { // Creating secret - configmapName := configmapNamePrefix + "-update-" + common.RandSeq(5) + configmapName := configmapNamePrefix + "-update-" + testutil.RandSeq(5) configmapClient, err := testutil.CreateConfigMap(client, namespace, configmapName, "www.google.com") if err != nil { t.Errorf("Error while creating the configmap %v", err) @@ -409,7 +487,19 @@ func TestControllerForUpdatingConfigmapShouldUpdateDaemonSet(t *testing.T) { // Verifying DaemonSet update logrus.Infof("Verifying env var has been updated") shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "aurorasolutions.io") - updated := testutil.VerifyDaemonSetUpdate(client, namespace, configmapName, constants.ConfigmapEnvarPostfix, shaData, constants.ConfigmapUpdateOnChangeAnnotation) + config := util.Config{ + Namespace: namespace, + ResourceName: configmapName, + SHAValue: shaData, + Annotation: constants.ConfigmapUpdateOnChangeAnnotation, + } + daemonSetFuncs := callbacks.RollingUpgradeFuncs{ + ItemsFunc: callbacks.GetDaemonSetItems, + ContainersFunc: callbacks.GetDaemonSetContainers, + UpdateFunc: callbacks.UpdateDaemonSet, + ResourceTypeFunc: callbacks.GetDaemonSetTypeName, + } + updated := testutil.VerifyResourceUpdate(client, config, constants.ConfigmapEnvVarPostfix, daemonSetFuncs) if !updated { t.Errorf("DaemonSet was not updated") } @@ -432,7 +522,7 @@ func TestControllerForUpdatingConfigmapShouldUpdateDaemonSet(t *testing.T) { // Perform rolling upgrade on secret and create a env var upon updating the secret func TestControllerUpdatingSecretShouldCreateEnvInDaemonSet(t *testing.T) { // Creating secret - secretName := secretNamePrefix + "-update-" + common.RandSeq(5) + secretName := secretNamePrefix + "-update-" + testutil.RandSeq(5) secretClient, err := testutil.CreateSecret(client, namespace, secretName, data) if err != nil { t.Errorf("Error in secret creation: %v", err) @@ -453,7 +543,19 @@ func TestControllerUpdatingSecretShouldCreateEnvInDaemonSet(t *testing.T) { // Verifying Upgrade logrus.Infof("Verifying env var has been created") shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, newData) - updated := testutil.VerifyDaemonSetUpdate(client, namespace, secretName, constants.SecretEnvarPostfix, shaData, constants.SecretUpdateOnChangeAnnotation) + config := util.Config{ + Namespace: namespace, + ResourceName: secretName, + SHAValue: shaData, + Annotation: constants.SecretUpdateOnChangeAnnotation, + } + daemonSetFuncs := callbacks.RollingUpgradeFuncs{ + ItemsFunc: callbacks.GetDaemonSetItems, + ContainersFunc: callbacks.GetDaemonSetContainers, + UpdateFunc: callbacks.UpdateDaemonSet, + ResourceTypeFunc: callbacks.GetDaemonSetTypeName, + } + updated := testutil.VerifyResourceUpdate(client, config, constants.SecretEnvVarPostfix, daemonSetFuncs) if !updated { t.Errorf("DaemonSet was not updated") } @@ -476,7 +578,7 @@ func TestControllerUpdatingSecretShouldCreateEnvInDaemonSet(t *testing.T) { // Perform rolling upgrade on DaemonSet and update env var upon updating the secret func TestControllerUpdatingSecretShouldUpdateEnvInDaemonSet(t *testing.T) { // Creating secret - secretName := secretNamePrefix + "-update-" + common.RandSeq(5) + secretName := secretNamePrefix + "-update-" + testutil.RandSeq(5) secretClient, err := testutil.CreateSecret(client, namespace, secretName, data) if err != nil { t.Errorf("Error in secret creation: %v", err) @@ -493,6 +595,7 @@ func TestControllerUpdatingSecretShouldUpdateEnvInDaemonSet(t *testing.T) { if err != nil { t.Errorf("Error while updating secret %v", err) } + time.Sleep(5 * time.Second) // Updating Secret err = testutil.UpdateSecret(secretClient, namespace, secretName, "", updatedData) @@ -503,7 +606,19 @@ func TestControllerUpdatingSecretShouldUpdateEnvInDaemonSet(t *testing.T) { // Verifying Upgrade logrus.Infof("Verifying env var has been updated") shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, updatedData) - updated := testutil.VerifyDaemonSetUpdate(client, namespace, secretName, constants.SecretEnvarPostfix, shaData, constants.SecretUpdateOnChangeAnnotation) + config := util.Config{ + Namespace: namespace, + ResourceName: secretName, + SHAValue: shaData, + Annotation: constants.SecretUpdateOnChangeAnnotation, + } + daemonSetFuncs := callbacks.RollingUpgradeFuncs{ + ItemsFunc: callbacks.GetDaemonSetItems, + ContainersFunc: callbacks.GetDaemonSetContainers, + UpdateFunc: callbacks.UpdateDaemonSet, + ResourceTypeFunc: callbacks.GetDaemonSetTypeName, + } + updated := testutil.VerifyResourceUpdate(client, config, constants.SecretEnvVarPostfix, daemonSetFuncs) if !updated { t.Errorf("DaemonSet was not updated") } @@ -526,7 +641,7 @@ func TestControllerUpdatingSecretShouldUpdateEnvInDaemonSet(t *testing.T) { // Do not Perform rolling upgrade on secret and create or update a env var upon updating the label in secret func TestControllerUpdatingSecretLabelsShouldNotCreateorUpdateEnvInDaemonSet(t *testing.T) { // Creating secret - secretName := secretNamePrefix + "-update-" + common.RandSeq(5) + secretName := secretNamePrefix + "-update-" + testutil.RandSeq(5) secretClient, err := testutil.CreateSecret(client, namespace, secretName, data) if err != nil { t.Errorf("Error in secret creation: %v", err) @@ -546,7 +661,19 @@ func TestControllerUpdatingSecretLabelsShouldNotCreateorUpdateEnvInDaemonSet(t * // Verifying Upgrade logrus.Infof("Verifying env var has been created") shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, data) - updated := testutil.VerifyDaemonSetUpdate(client, namespace, secretName, constants.SecretEnvarPostfix, shaData, constants.SecretUpdateOnChangeAnnotation) + config := util.Config{ + Namespace: namespace, + ResourceName: secretName, + SHAValue: shaData, + Annotation: constants.SecretUpdateOnChangeAnnotation, + } + daemonSetFuncs := callbacks.RollingUpgradeFuncs{ + ItemsFunc: callbacks.GetDaemonSetItems, + ContainersFunc: callbacks.GetDaemonSetContainers, + UpdateFunc: callbacks.UpdateDaemonSet, + ResourceTypeFunc: callbacks.GetDaemonSetTypeName, + } + updated := testutil.VerifyResourceUpdate(client, config, constants.SecretEnvVarPostfix, daemonSetFuncs) if updated { t.Errorf("DaemonSet should not be updated by changing label in secret") } @@ -569,7 +696,7 @@ func TestControllerUpdatingSecretLabelsShouldNotCreateorUpdateEnvInDaemonSet(t * // Perform rolling upgrade on StatefulSet and create env var upon updating the configmap func TestControllerUpdatingConfigmapShouldCreateEnvInStatefulSet(t *testing.T) { // Creating configmap - configmapName := configmapNamePrefix + "-update-" + common.RandSeq(5) + configmapName := configmapNamePrefix + "-update-" + testutil.RandSeq(5) configmapClient, err := testutil.CreateConfigMap(client, namespace, configmapName, "www.google.com") if err != nil { t.Errorf("Error while creating the configmap %v", err) @@ -590,7 +717,19 @@ func TestControllerUpdatingConfigmapShouldCreateEnvInStatefulSet(t *testing.T) { // Verifying StatefulSet update logrus.Infof("Verifying env var has been created") shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "www.stakater.com") - updated := testutil.VerifyStatefulSetUpdate(client, namespace, configmapName, constants.ConfigmapEnvarPostfix, shaData, constants.ConfigmapUpdateOnChangeAnnotation) + config := util.Config{ + Namespace: namespace, + ResourceName: configmapName, + SHAValue: shaData, + Annotation: constants.ConfigmapUpdateOnChangeAnnotation, + } + statefulSetFuncs := callbacks.RollingUpgradeFuncs{ + ItemsFunc: callbacks.GetStatefulSetItems, + ContainersFunc: callbacks.GetStatefulsetContainers, + UpdateFunc: callbacks.UpdateStatefulset, + ResourceTypeFunc: callbacks.GetStatefulSetTypeName, + } + updated := testutil.VerifyResourceUpdate(client, config, constants.ConfigmapEnvVarPostfix, statefulSetFuncs) if !updated { t.Errorf("StatefulSet was not updated") } @@ -613,7 +752,7 @@ func TestControllerUpdatingConfigmapShouldCreateEnvInStatefulSet(t *testing.T) { // Perform rolling upgrade on StatefulSet and update env var upon updating the configmap func TestControllerForUpdatingConfigmapShouldUpdateStatefulSet(t *testing.T) { // Creating secret - configmapName := configmapNamePrefix + "-update-" + common.RandSeq(5) + configmapName := configmapNamePrefix + "-update-" + testutil.RandSeq(5) configmapClient, err := testutil.CreateConfigMap(client, namespace, configmapName, "www.google.com") if err != nil { t.Errorf("Error while creating the configmap %v", err) @@ -640,7 +779,19 @@ func TestControllerForUpdatingConfigmapShouldUpdateStatefulSet(t *testing.T) { // Verifying StatefulSet update logrus.Infof("Verifying env var has been updated") shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "aurorasolutions.io") - updated := testutil.VerifyStatefulSetUpdate(client, namespace, configmapName, constants.ConfigmapEnvarPostfix, shaData, constants.ConfigmapUpdateOnChangeAnnotation) + config := util.Config{ + Namespace: namespace, + ResourceName: configmapName, + SHAValue: shaData, + Annotation: constants.ConfigmapUpdateOnChangeAnnotation, + } + statefulSetFuncs := callbacks.RollingUpgradeFuncs{ + ItemsFunc: callbacks.GetStatefulSetItems, + ContainersFunc: callbacks.GetStatefulsetContainers, + UpdateFunc: callbacks.UpdateStatefulset, + ResourceTypeFunc: callbacks.GetStatefulSetTypeName, + } + updated := testutil.VerifyResourceUpdate(client, config, constants.ConfigmapEnvVarPostfix, statefulSetFuncs) if !updated { t.Errorf("StatefulSet was not updated") } @@ -660,54 +811,10 @@ func TestControllerForUpdatingConfigmapShouldUpdateStatefulSet(t *testing.T) { time.Sleep(5 * time.Second) } -// Do not Perform rolling upgrade on StatefulSet and create env var upon updating the labels configmap -func TestControllerUpdatingConfigmapLabelsShouldNotCreateorUpdateEnvInStatefulSet(t *testing.T) { - // Creating configmap - configmapName := configmapNamePrefix + "-update-" + common.RandSeq(5) - configmapClient, err := testutil.CreateConfigMap(client, namespace, configmapName, "www.google.com") - if err != nil { - t.Errorf("Error while creating the configmap %v", err) - } - - // Creating StatefulSet - _, err = testutil.CreateStatefulSet(client, configmapName, namespace) - if err != nil { - t.Errorf("Error in StatefulSet creation: %v", err) - } - - // Updating configmap for first time - updateErr := testutil.UpdateConfigMap(configmapClient, namespace, configmapName, "test", "www.google.com") - if updateErr != nil { - t.Errorf("Configmap was not updated") - } - - // Verifying StatefulSet update - logrus.Infof("Verifying env var has been created") - shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "www.google.com") - updated := testutil.VerifyStatefulSetUpdate(client, namespace, configmapName, constants.ConfigmapEnvarPostfix, shaData, constants.ConfigmapUpdateOnChangeAnnotation) - if updated { - t.Errorf("StatefulSet should not be updated by changing label") - } - time.Sleep(5 * time.Second) - - // Deleting StatefulSet - err = testutil.DeleteStatefulSet(client, namespace, configmapName) - if err != nil { - logrus.Errorf("Error while deleting the StatefulSet %v", err) - } - - // Deleting configmap - err = testutil.DeleteConfigMap(client, namespace, configmapName) - if err != nil { - logrus.Errorf("Error while deleting the configmap %v", err) - } - time.Sleep(5 * time.Second) -} - // Perform rolling upgrade on secret and create a env var upon updating the secret func TestControllerUpdatingSecretShouldCreateEnvInStatefulSet(t *testing.T) { // Creating secret - secretName := secretNamePrefix + "-update-" + common.RandSeq(5) + secretName := secretNamePrefix + "-update-" + testutil.RandSeq(5) secretClient, err := testutil.CreateSecret(client, namespace, secretName, data) if err != nil { t.Errorf("Error in secret creation: %v", err) @@ -728,7 +835,19 @@ func TestControllerUpdatingSecretShouldCreateEnvInStatefulSet(t *testing.T) { // Verifying Upgrade logrus.Infof("Verifying env var has been created") shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, newData) - updated := testutil.VerifyStatefulSetUpdate(client, namespace, secretName, constants.SecretEnvarPostfix, shaData, constants.SecretUpdateOnChangeAnnotation) + config := util.Config{ + Namespace: namespace, + ResourceName: secretName, + SHAValue: shaData, + Annotation: constants.SecretUpdateOnChangeAnnotation, + } + statefulSetFuncs := callbacks.RollingUpgradeFuncs{ + ItemsFunc: callbacks.GetStatefulSetItems, + ContainersFunc: callbacks.GetStatefulsetContainers, + UpdateFunc: callbacks.UpdateStatefulset, + ResourceTypeFunc: callbacks.GetStatefulSetTypeName, + } + updated := testutil.VerifyResourceUpdate(client, config, constants.SecretEnvVarPostfix, statefulSetFuncs) if !updated { t.Errorf("StatefulSet was not updated") } @@ -751,7 +870,7 @@ func TestControllerUpdatingSecretShouldCreateEnvInStatefulSet(t *testing.T) { // Perform rolling upgrade on StatefulSet and update env var upon updating the secret func TestControllerUpdatingSecretShouldUpdateEnvInStatefulSet(t *testing.T) { // Creating secret - secretName := secretNamePrefix + "-update-" + common.RandSeq(5) + secretName := secretNamePrefix + "-update-" + testutil.RandSeq(5) secretClient, err := testutil.CreateSecret(client, namespace, secretName, data) if err != nil { t.Errorf("Error in secret creation: %v", err) @@ -778,7 +897,19 @@ func TestControllerUpdatingSecretShouldUpdateEnvInStatefulSet(t *testing.T) { // Verifying Upgrade logrus.Infof("Verifying env var has been updated") shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, updatedData) - updated := testutil.VerifyStatefulSetUpdate(client, namespace, secretName, constants.SecretEnvarPostfix, shaData, constants.SecretUpdateOnChangeAnnotation) + config := util.Config{ + Namespace: namespace, + ResourceName: secretName, + SHAValue: shaData, + Annotation: constants.SecretUpdateOnChangeAnnotation, + } + statefulSetFuncs := callbacks.RollingUpgradeFuncs{ + ItemsFunc: callbacks.GetStatefulSetItems, + ContainersFunc: callbacks.GetStatefulsetContainers, + UpdateFunc: callbacks.UpdateStatefulset, + ResourceTypeFunc: callbacks.GetStatefulSetTypeName, + } + updated := testutil.VerifyResourceUpdate(client, config, constants.SecretEnvVarPostfix, statefulSetFuncs) if !updated { t.Errorf("StatefulSet was not updated") } diff --git a/internal/pkg/crypto/sha_test.go b/internal/pkg/crypto/sha_test.go index d1bd50f2..60d5af63 100644 --- a/internal/pkg/crypto/sha_test.go +++ b/internal/pkg/crypto/sha_test.go @@ -7,9 +7,9 @@ import ( // TestGenerateSHA generates the sha from given data and verifies whether it is correct or not func TestGenerateSHA(t *testing.T) { data := "www.stakater.com" - sha := GenerateSHA(data) - length := len(sha) - if length != 40 { + sha := "abd4ed82fb04548388a6cf3c339fd9dc84d275df" + result := GenerateSHA(data) + if result != sha { t.Errorf("Failed to generate SHA") } } diff --git a/internal/pkg/handler/update.go b/internal/pkg/handler/update.go index 78fb3c7b..01dc04bf 100644 --- a/internal/pkg/handler/update.go +++ b/internal/pkg/handler/update.go @@ -5,15 +5,12 @@ import ( "strings" "github.com/sirupsen/logrus" - "github.com/stakater/Reloader/internal/pkg/common" + "github.com/stakater/Reloader/internal/pkg/callbacks" "github.com/stakater/Reloader/internal/pkg/constants" "github.com/stakater/Reloader/internal/pkg/crypto" "github.com/stakater/Reloader/internal/pkg/util" "github.com/stakater/Reloader/pkg/kube" - apps_v1beta1 "k8s.io/api/apps/v1beta1" "k8s.io/api/core/v1" - "k8s.io/api/extensions/v1beta1" - meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" ) @@ -23,30 +20,6 @@ type ResourceUpdatedHandler struct { OldResource interface{} } -//Config contains rolling upgrade configuration parameters -type Config struct { - namespace string - resourceName string - annotation string - shaValue string -} - -//ItemsFunc is a generic function to return a specific resource array in given namespace -type ItemsFunc func(kubernetes.Interface, string) []interface{} - -//ContainersFunc is a generic func to return containers -type ContainersFunc func(interface{}) []v1.Container - -//UpdateFunc performs the resource update -type UpdateFunc func(kubernetes.Interface, string, interface{}) error - -//RollingUpgradeFuncs contains generic functions to perform rolling upgrade -type RollingUpgradeFuncs struct { - ItemsFunc ItemsFunc - ContainersFunc ContainersFunc - UpdateFunc UpdateFunc -} - // Handle processes the updated resource func (r ResourceUpdatedHandler) Handle() error { if r.Resource == nil || r.OldResource == nil { @@ -54,89 +27,29 @@ func (r ResourceUpdatedHandler) Handle() error { } else { logrus.Infof("Detected changes in object %s", r.Resource) // process resource based on its type - rollingUpgrade(r, RollingUpgradeFuncs{ - ItemsFunc: GetDeploymentItems, - ContainersFunc: GetDeploymentContainers, - UpdateFunc: UpdateDeployment, + rollingUpgrade(r, callbacks.RollingUpgradeFuncs{ + ItemsFunc: callbacks.GetDeploymentItems, + ContainersFunc: callbacks.GetDeploymentContainers, + UpdateFunc: callbacks.UpdateDeployment, + ResourceTypeFunc: callbacks.GetDeploymentTypeName, }) - rollingUpgrade(r, RollingUpgradeFuncs{ - ItemsFunc: GetDaemonSetItems, - ContainersFunc: GetDaemonSetContainers, - UpdateFunc: UpdateDaemonSet, + rollingUpgrade(r, callbacks.RollingUpgradeFuncs{ + ItemsFunc: callbacks.GetDaemonSetItems, + ContainersFunc: callbacks.GetDaemonSetContainers, + UpdateFunc: callbacks.UpdateDaemonSet, + ResourceTypeFunc: callbacks.GetDaemonSetTypeName, }) - rollingUpgrade(r, RollingUpgradeFuncs{ - ItemsFunc: GetStatefulSetItems, - ContainersFunc: GetStatefulsetContainers, - UpdateFunc: UpdateStatefulset, + rollingUpgrade(r, callbacks.RollingUpgradeFuncs{ + ItemsFunc: callbacks.GetStatefulSetItems, + ContainersFunc: callbacks.GetStatefulsetContainers, + UpdateFunc: callbacks.UpdateStatefulset, + ResourceTypeFunc: callbacks.GetStatefulSetTypeName, }) } return nil } -// GetDeploymentItems returns the deployments in given namespace -func GetDeploymentItems(client kubernetes.Interface, namespace string) []interface{} { - deployments, err := client.ExtensionsV1beta1().Deployments(namespace).List(meta_v1.ListOptions{}) - if err != nil { - logrus.Errorf("Failed to list deployments %v", err) - } - return util.InterfaceSlice(deployments.Items) -} - -// GetDaemonSetItems returns the daemonSet in given namespace -func GetDaemonSetItems(client kubernetes.Interface, namespace string) []interface{} { - daemonSets, err := client.ExtensionsV1beta1().DaemonSets(namespace).List(meta_v1.ListOptions{}) - if err != nil { - logrus.Errorf("Failed to list daemonSets %v", err) - } - return util.InterfaceSlice(daemonSets.Items) -} - -// GetStatefulSetItems returns the statefulSet in given namespace -func GetStatefulSetItems(client kubernetes.Interface, namespace string) []interface{} { - statefulSets, err := client.AppsV1beta1().StatefulSets(namespace).List(meta_v1.ListOptions{}) - if err != nil { - logrus.Errorf("Failed to list statefulSets %v", err) - } - return util.InterfaceSlice(statefulSets.Items) -} - -// GetDeploymentContainers returns the containers of given deployment -func GetDeploymentContainers(item interface{}) []v1.Container { - return item.(v1beta1.Deployment).Spec.Template.Spec.Containers -} - -// GetDaemonSetContainers returns the containers of given daemonset -func GetDaemonSetContainers(item interface{}) []v1.Container { - return item.(v1beta1.DaemonSet).Spec.Template.Spec.Containers -} - -// GetStatefulsetContainers returns the containers of given statefulSet -func GetStatefulsetContainers(item interface{}) []v1.Container { - return item.(apps_v1beta1.StatefulSet).Spec.Template.Spec.Containers -} - -// UpdateDeployment performs rolling upgrade on deployment -func UpdateDeployment(client kubernetes.Interface, namespace string, resource interface{}) error { - deployment := resource.(v1beta1.Deployment) - _, err := client.ExtensionsV1beta1().Deployments(namespace).Update(&deployment) - return err -} - -// UpdateDaemonSet performs rolling upgrade on daemonSet -func UpdateDaemonSet(client kubernetes.Interface, namespace string, resource interface{}) error { - daemonSet := resource.(v1beta1.DaemonSet) - _, err := client.ExtensionsV1beta1().DaemonSets(namespace).Update(&daemonSet) - return err -} - -// UpdateStatefulset performs rolling upgrade on statefulSet -func UpdateStatefulset(client kubernetes.Interface, namespace string, resource interface{}) error { - statefulSet := resource.(apps_v1beta1.StatefulSet) - _, err := client.AppsV1beta1().StatefulSets(namespace).Update(&statefulSet) - return err -} - -func rollingUpgrade(r ResourceUpdatedHandler, upgradeFuncs RollingUpgradeFuncs) { +func rollingUpgrade(r ResourceUpdatedHandler, upgradeFuncs callbacks.RollingUpgradeFuncs) { client, err := kube.GetClient() if err != nil { logrus.Fatalf("Unable to create Kubernetes client error = %v", err) @@ -144,7 +57,7 @@ func rollingUpgrade(r ResourceUpdatedHandler, upgradeFuncs RollingUpgradeFuncs) config, envVarPostfix, oldSHAData := getConfig(r) - if config.shaValue != oldSHAData { + if config.SHAValue != oldSHAData { err = PerformRollingUpgrade(client, config, envVarPostfix, upgradeFuncs) if err != nil { logrus.Fatalf("Rolling upgrade failed with error = %v", err) @@ -154,60 +67,66 @@ func rollingUpgrade(r ResourceUpdatedHandler, upgradeFuncs RollingUpgradeFuncs) } } -func getConfig(r ResourceUpdatedHandler) (Config, string, string) { - var shaData, oldSHAData, envVarPostfix string - var config Config +func getConfig(r ResourceUpdatedHandler) (util.Config, string, string) { + var oldSHAData, envVarPostfix string + var config util.Config if _, ok := r.Resource.(*v1.ConfigMap); ok { logrus.Infof("Performing 'Updated' action for resource of type 'configmap'") - configmap := r.Resource.(*v1.ConfigMap) - shaData = getSHAfromConfigmap(configmap.Data) oldSHAData = getSHAfromConfigmap(r.OldResource.(*v1.ConfigMap).Data) - config = Config{ - namespace: configmap.Namespace, - resourceName: configmap.Name, - annotation: constants.ConfigmapUpdateOnChangeAnnotation, - shaValue: shaData, - } - envVarPostfix = constants.ConfigmapEnvarPostfix + config = getConfigmapConfig(r) + envVarPostfix = constants.ConfigmapEnvVarPostfix } else if _, ok := r.Resource.(*v1.Secret); ok { logrus.Infof("Performing 'Updated' action for resource of type 'secret'") - secret := r.Resource.(*v1.Secret) - shaData = getSHAfromSecret(secret.Data) oldSHAData = getSHAfromSecret(r.OldResource.(*v1.Secret).Data) - config = Config{ - namespace: secret.Namespace, - resourceName: secret.Name, - annotation: constants.SecretUpdateOnChangeAnnotation, - shaValue: shaData, - } - envVarPostfix = constants.SecretEnvarPostfix + config = getSecretConfig(r) + envVarPostfix = constants.SecretEnvVarPostfix } else { logrus.Warnf("Invalid resource: Resource should be 'Secret' or 'Configmap' but found, %v", r.Resource) } return config, envVarPostfix, oldSHAData } +func getConfigmapConfig(r ResourceUpdatedHandler) util.Config { + configmap := r.Resource.(*v1.ConfigMap) + return util.Config{ + Namespace: configmap.Namespace, + ResourceName: configmap.Name, + Annotation: constants.ConfigmapUpdateOnChangeAnnotation, + SHAValue: getSHAfromConfigmap(configmap.Data), + } +} + +func getSecretConfig(r ResourceUpdatedHandler) util.Config { + secret := r.Resource.(*v1.Secret) + return util.Config{ + Namespace: secret.Namespace, + ResourceName: secret.Name, + Annotation: constants.SecretUpdateOnChangeAnnotation, + SHAValue: getSHAfromSecret(secret.Data), + } +} + // PerformRollingUpgrade upgrades the deployment if there is any change in configmap or secret data -func PerformRollingUpgrade(client kubernetes.Interface, config Config, envarPostfix string, upgradeFuncs RollingUpgradeFuncs) error { - items := upgradeFuncs.ItemsFunc(client, config.namespace) +func PerformRollingUpgrade(client kubernetes.Interface, config util.Config, envarPostfix string, upgradeFuncs callbacks.RollingUpgradeFuncs) error { + items := upgradeFuncs.ItemsFunc(client, config.Namespace) var err error for _, i := range items { containers := upgradeFuncs.ContainersFunc(i) // find correct annotation and update the resource - annotationValue := util.ToObjectMeta(i).Annotations[config.annotation] + annotationValue := util.ToObjectMeta(i).Annotations[config.Annotation] if annotationValue != "" { values := strings.Split(annotationValue, ",") for _, value := range values { - if value == config.resourceName { - updated := updateContainers(containers, value, config.shaValue, envarPostfix) + if value == config.ResourceName { + updated := updateContainers(containers, value, config.SHAValue, envarPostfix) if !updated { logrus.Warnf("Rolling upgrade did not happen") } else { - err = upgradeFuncs.UpdateFunc(client, config.namespace, i) + err = upgradeFuncs.UpdateFunc(client, config.Namespace, i) if err != nil { - logrus.Errorf("Update deployment failed %v", err) + logrus.Errorf("Update %s failed %v", upgradeFuncs.ResourceTypeFunc, err) } else { - logrus.Infof("Updated Deployment %s", config.resourceName) + logrus.Infof("Updated %s of type %s", config.ResourceName, upgradeFuncs.ResourceTypeFunc) } break } @@ -220,7 +139,7 @@ func PerformRollingUpgrade(client kubernetes.Interface, config Config, envarPost func updateContainers(containers []v1.Container, annotationValue string, shaData string, envarPostfix string) bool { updated := false - envar := constants.EnvVarPrefix + common.ConvertToEnvVarName(annotationValue) + envarPostfix + envar := constants.EnvVarPrefix + util.ConvertToEnvVarName(annotationValue) + envarPostfix logrus.Infof("Generated environment variable: %s", envar) for i := range containers { envs := containers[i].Env @@ -247,7 +166,7 @@ func updateEnvVar(envs []v1.EnvVar, envar string, shaData string) bool { if envs[j].Name == envar { logrus.Infof("%s environment variable found", envar) if envs[j].Value != shaData { - logrus.Infof("Updating %s to %s", envar, shaData) + logrus.Infof("Updating %s", envar) envs[j].Value = shaData return true } @@ -267,7 +186,6 @@ func getSHAfromConfigmap(data map[string]string) string { func getSHAfromSecret(data map[string][]byte) string { values := []string{} - logrus.Infof("Generating SHA for secret data") for k, v := range data { values = append(values, k+"="+string(v[:])) } diff --git a/internal/pkg/handler/update_test.go b/internal/pkg/handler/update_test.go index 7f54fa09..e37d8daa 100644 --- a/internal/pkg/handler/update_test.go +++ b/internal/pkg/handler/update_test.go @@ -6,18 +6,17 @@ import ( "time" "github.com/sirupsen/logrus" - "github.com/stakater/Reloader/internal/pkg/common" + "github.com/stakater/Reloader/internal/pkg/callbacks" "github.com/stakater/Reloader/internal/pkg/constants" "github.com/stakater/Reloader/internal/pkg/testutil" - "github.com/stakater/Reloader/pkg/kube" - "k8s.io/client-go/kubernetes" + "github.com/stakater/Reloader/internal/pkg/util" ) var ( - client = getClient() + client = testutil.GetClient() namespace = "test-handler" - configmapName = "testconfigmap-handler-update-" + common.RandSeq(5) - secretName = "testsecret-handler-update-" + common.RandSeq(5) + configmapName = "testconfigmap-handler-" + testutil.RandSeq(5) + secretName = "testsecret-handler-" + testutil.RandSeq(5) ) func TestMain(m *testing.M) { @@ -37,14 +36,6 @@ func TestMain(m *testing.M) { 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() { // Creating configmap _, err := testutil.CreateConfigMap(client, namespace, configmapName, "www.google.com") @@ -153,26 +144,27 @@ func teardown() { func TestRollingUpgradeForDeploymentWithConfigmap(t *testing.T) { shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, configmapName, "www.stakater.com") - config := Config{ - namespace: namespace, - resourceName: configmapName, - shaValue: shaData, - annotation: constants.ConfigmapUpdateOnChangeAnnotation, + config := util.Config{ + Namespace: namespace, + ResourceName: configmapName, + SHAValue: shaData, + Annotation: constants.ConfigmapUpdateOnChangeAnnotation, } - deploymentFuncs := RollingUpgradeFuncs{ - ItemsFunc: GetDeploymentItems, - ContainersFunc: GetDeploymentContainers, - UpdateFunc: UpdateDeployment, + deploymentFuncs := callbacks.RollingUpgradeFuncs{ + ItemsFunc: callbacks.GetDeploymentItems, + ContainersFunc: callbacks.GetDeploymentContainers, + UpdateFunc: callbacks.UpdateDeployment, + ResourceTypeFunc: callbacks.GetDeploymentTypeName, } - err := PerformRollingUpgrade(client, config, constants.ConfigmapEnvarPostfix, deploymentFuncs) + err := PerformRollingUpgrade(client, config, constants.ConfigmapEnvVarPostfix, deploymentFuncs) time.Sleep(5 * time.Second) if err != nil { t.Errorf("Rolling upgrade failed for Deployment with Configmap") } logrus.Infof("Verifying deployment update") - updated := testutil.VerifyDeploymentUpdate(client, namespace, configmapName, constants.ConfigmapEnvarPostfix, shaData, constants.ConfigmapUpdateOnChangeAnnotation) + updated := testutil.VerifyResourceUpdate(client, config, constants.ConfigmapEnvVarPostfix, deploymentFuncs) if !updated { t.Errorf("Deployment was not updated") } @@ -180,26 +172,27 @@ func TestRollingUpgradeForDeploymentWithConfigmap(t *testing.T) { func TestRollingUpgradeForDeploymentWithSecret(t *testing.T) { shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, "dGVzdFVwZGF0ZWRTZWNyZXRFbmNvZGluZ0ZvclJlbG9hZGVy") - config := Config{ - namespace: namespace, - resourceName: secretName, - shaValue: shaData, - annotation: constants.SecretUpdateOnChangeAnnotation, + config := util.Config{ + Namespace: namespace, + ResourceName: secretName, + SHAValue: shaData, + Annotation: constants.SecretUpdateOnChangeAnnotation, } - deploymentFuncs := RollingUpgradeFuncs{ - ItemsFunc: GetDeploymentItems, - ContainersFunc: GetDeploymentContainers, - UpdateFunc: UpdateDeployment, + deploymentFuncs := callbacks.RollingUpgradeFuncs{ + ItemsFunc: callbacks.GetDeploymentItems, + ContainersFunc: callbacks.GetDeploymentContainers, + UpdateFunc: callbacks.UpdateDeployment, + ResourceTypeFunc: callbacks.GetDeploymentTypeName, } - err := PerformRollingUpgrade(client, config, constants.SecretEnvarPostfix, deploymentFuncs) + err := PerformRollingUpgrade(client, config, constants.SecretEnvVarPostfix, deploymentFuncs) time.Sleep(5 * time.Second) if err != nil { t.Errorf("Rolling upgrade failed for Deployment with Secret") } logrus.Infof("Verifying deployment update") - updated := testutil.VerifyDeploymentUpdate(client, namespace, secretName, constants.SecretEnvarPostfix, shaData, constants.SecretUpdateOnChangeAnnotation) + updated := testutil.VerifyResourceUpdate(client, config, constants.SecretEnvVarPostfix, deploymentFuncs) if !updated { t.Errorf("Deployment was not updated") } @@ -207,26 +200,27 @@ func TestRollingUpgradeForDeploymentWithSecret(t *testing.T) { func TestRollingUpgradeForDaemonSetWithConfigmap(t *testing.T) { shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "www.facebook.com") - config := Config{ - namespace: namespace, - resourceName: configmapName, - shaValue: shaData, - annotation: constants.ConfigmapUpdateOnChangeAnnotation, + config := util.Config{ + Namespace: namespace, + ResourceName: configmapName, + SHAValue: shaData, + Annotation: constants.ConfigmapUpdateOnChangeAnnotation, } - daemonSetFuncs := RollingUpgradeFuncs{ - ItemsFunc: GetDaemonSetItems, - ContainersFunc: GetDaemonSetContainers, - UpdateFunc: UpdateDaemonSet, + daemonSetFuncs := callbacks.RollingUpgradeFuncs{ + ItemsFunc: callbacks.GetDaemonSetItems, + ContainersFunc: callbacks.GetDaemonSetContainers, + UpdateFunc: callbacks.UpdateDaemonSet, + ResourceTypeFunc: callbacks.GetDaemonSetTypeName, } - err := PerformRollingUpgrade(client, config, constants.ConfigmapEnvarPostfix, daemonSetFuncs) + err := PerformRollingUpgrade(client, config, constants.ConfigmapEnvVarPostfix, daemonSetFuncs) time.Sleep(5 * time.Second) if err != nil { t.Errorf("Rolling upgrade failed for DaemonSet with configmap") } logrus.Infof("Verifying daemonSet update") - updated := testutil.VerifyDaemonSetUpdate(client, namespace, configmapName, constants.ConfigmapEnvarPostfix, shaData, constants.ConfigmapUpdateOnChangeAnnotation) + updated := testutil.VerifyResourceUpdate(client, config, constants.ConfigmapEnvVarPostfix, daemonSetFuncs) if !updated { t.Errorf("DaemonSet was not updated") } @@ -235,26 +229,27 @@ func TestRollingUpgradeForDaemonSetWithConfigmap(t *testing.T) { func TestRollingUpgradeForDaemonSetWithSecret(t *testing.T) { shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, "d3d3LmZhY2Vib29rLmNvbQ==") - config := Config{ - namespace: namespace, - resourceName: secretName, - shaValue: shaData, - annotation: constants.SecretUpdateOnChangeAnnotation, + config := util.Config{ + Namespace: namespace, + ResourceName: secretName, + SHAValue: shaData, + Annotation: constants.SecretUpdateOnChangeAnnotation, } - daemonSetFuncs := RollingUpgradeFuncs{ - ItemsFunc: GetDaemonSetItems, - ContainersFunc: GetDaemonSetContainers, - UpdateFunc: UpdateDaemonSet, + daemonSetFuncs := callbacks.RollingUpgradeFuncs{ + ItemsFunc: callbacks.GetDaemonSetItems, + ContainersFunc: callbacks.GetDaemonSetContainers, + UpdateFunc: callbacks.UpdateDaemonSet, + ResourceTypeFunc: callbacks.GetDaemonSetTypeName, } - err := PerformRollingUpgrade(client, config, constants.SecretEnvarPostfix, daemonSetFuncs) + err := PerformRollingUpgrade(client, config, constants.SecretEnvVarPostfix, daemonSetFuncs) time.Sleep(5 * time.Second) if err != nil { t.Errorf("Rolling upgrade failed for DaemonSet with secret") } logrus.Infof("Verifying daemonSet update") - updated := testutil.VerifyDaemonSetUpdate(client, namespace, secretName, constants.SecretEnvarPostfix, shaData, constants.SecretUpdateOnChangeAnnotation) + updated := testutil.VerifyResourceUpdate(client, config, constants.SecretEnvVarPostfix, daemonSetFuncs) if !updated { t.Errorf("DaemonSet was not updated") } @@ -263,26 +258,27 @@ func TestRollingUpgradeForDaemonSetWithSecret(t *testing.T) { func TestRollingUpgradeForStatefulSetWithConfigmap(t *testing.T) { shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, namespace, configmapName, "www.twitter.com") - config := Config{ - namespace: namespace, - resourceName: configmapName, - shaValue: shaData, - annotation: constants.ConfigmapUpdateOnChangeAnnotation, + config := util.Config{ + Namespace: namespace, + ResourceName: configmapName, + SHAValue: shaData, + Annotation: constants.ConfigmapUpdateOnChangeAnnotation, } - statefulSetFuncs := RollingUpgradeFuncs{ - ItemsFunc: GetStatefulSetItems, - ContainersFunc: GetStatefulsetContainers, - UpdateFunc: UpdateStatefulset, + statefulSetFuncs := callbacks.RollingUpgradeFuncs{ + ItemsFunc: callbacks.GetStatefulSetItems, + ContainersFunc: callbacks.GetStatefulsetContainers, + UpdateFunc: callbacks.UpdateStatefulset, + ResourceTypeFunc: callbacks.GetStatefulSetTypeName, } - err := PerformRollingUpgrade(client, config, constants.ConfigmapEnvarPostfix, statefulSetFuncs) + err := PerformRollingUpgrade(client, config, constants.ConfigmapEnvVarPostfix, statefulSetFuncs) time.Sleep(5 * time.Second) if err != nil { t.Errorf("Rolling upgrade failed for StatefulSet with configmap") } logrus.Infof("Verifying statefulSet update") - updated := testutil.VerifyStatefulSetUpdate(client, namespace, configmapName, constants.ConfigmapEnvarPostfix, shaData, constants.ConfigmapUpdateOnChangeAnnotation) + updated := testutil.VerifyResourceUpdate(client, config, constants.ConfigmapEnvVarPostfix, statefulSetFuncs) if !updated { t.Errorf("StatefulSet was not updated") } @@ -291,26 +287,27 @@ func TestRollingUpgradeForStatefulSetWithConfigmap(t *testing.T) { func TestRollingUpgradeForStatefulSetWithSecret(t *testing.T) { shaData := testutil.ConvertResourceToSHA(testutil.SecretResourceType, namespace, secretName, "d3d3LnR3aXR0ZXIuY29t") - config := Config{ - namespace: namespace, - resourceName: secretName, - shaValue: shaData, - annotation: constants.SecretUpdateOnChangeAnnotation, + config := util.Config{ + Namespace: namespace, + ResourceName: secretName, + SHAValue: shaData, + Annotation: constants.SecretUpdateOnChangeAnnotation, } - statefulSetFuncs := RollingUpgradeFuncs{ - ItemsFunc: GetStatefulSetItems, - ContainersFunc: GetStatefulsetContainers, - UpdateFunc: UpdateStatefulset, + statefulSetFuncs := callbacks.RollingUpgradeFuncs{ + ItemsFunc: callbacks.GetStatefulSetItems, + ContainersFunc: callbacks.GetStatefulsetContainers, + UpdateFunc: callbacks.UpdateStatefulset, + ResourceTypeFunc: callbacks.GetStatefulSetTypeName, } - err := PerformRollingUpgrade(client, config, constants.SecretEnvarPostfix, statefulSetFuncs) + err := PerformRollingUpgrade(client, config, constants.SecretEnvVarPostfix, statefulSetFuncs) time.Sleep(5 * time.Second) if err != nil { t.Errorf("Rolling upgrade failed for StatefulSet with secret") } logrus.Infof("Verifying statefulSet update") - updated := testutil.VerifyStatefulSetUpdate(client, namespace, secretName, constants.SecretEnvarPostfix, shaData, constants.SecretUpdateOnChangeAnnotation) + updated := testutil.VerifyResourceUpdate(client, config, constants.SecretEnvVarPostfix, statefulSetFuncs) if !updated { t.Errorf("StatefulSet was not updated") } diff --git a/internal/pkg/testutil/kube.go b/internal/pkg/testutil/kube.go index 4fea00f7..040f92cc 100644 --- a/internal/pkg/testutil/kube.go +++ b/internal/pkg/testutil/kube.go @@ -1,14 +1,17 @@ package testutil import ( + "math/rand" "sort" "strings" "time" "github.com/sirupsen/logrus" - "github.com/stakater/Reloader/internal/pkg/common" + "github.com/stakater/Reloader/internal/pkg/callbacks" "github.com/stakater/Reloader/internal/pkg/constants" "github.com/stakater/Reloader/internal/pkg/crypto" + "github.com/stakater/Reloader/internal/pkg/util" + "github.com/stakater/Reloader/pkg/kube" v1_beta1 "k8s.io/api/apps/v1beta1" "k8s.io/api/core/v1" "k8s.io/api/extensions/v1beta1" @@ -18,12 +21,21 @@ import ( ) var ( + letters = []rune("abcdefghijklmnopqrstuvwxyz") // ConfigmapResourceType is a resource type which controller watches for changes ConfigmapResourceType = "configMaps" // SecretResourceType is a resource type which controller watches for changes SecretResourceType = "secrets" ) +func GetClient() *kubernetes.Clientset { + newClient, err := kube.GetClient() + if err != nil { + logrus.Fatalf("Unable to create Kubernetes client error = %v", err) + } + return newClient +} + // CreateNamespace creates namespace for testing func CreateNamespace(namespace string, client kubernetes.Interface) { _, err := client.CoreV1().Namespaces().Create(&v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: namespace}}) @@ -208,102 +220,8 @@ func GetSecretWithUpdatedLabel(namespace string, secretName string, label string } } -// VerifyDeploymentUpdate verifies whether deployment has been updated with environment variable or not -func VerifyDeploymentUpdate(client kubernetes.Interface, namespace string, name string, envarPostfix string, shaData string, annotation 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[annotation] - if annotationValue != "" { - values := strings.Split(annotationValue, ",") - matches := false - for _, value := range values { - if value == name { - matches = true - break - } - } - if matches { - envName := constants.EnvVarPrefix + common.ConvertToEnvVarName(annotationValue) + envarPostfix - updated := getResourceSHA(containers, envName) - if updated == shaData { - 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, annotation 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[annotation] - if annotationValue != "" { - values := strings.Split(annotationValue, ",") - matches := false - for _, value := range values { - if value == name { - matches = true - break - } - } - if matches { - envName := constants.EnvVarPrefix + common.ConvertToEnvVarName(annotationValue) + resourceType - updated := getResourceSHA(containers, envName) - - if updated == shaData { - 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, annotation 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[annotation] - if annotationValue != "" { - values := strings.Split(annotationValue, ",") - matches := false - for _, value := range values { - if value == name { - matches = true - break - } - } - if matches { - envName := constants.EnvVarPrefix + common.ConvertToEnvVarName(annotationValue) + resourceType - updated := getResourceSHA(containers, envName) - - if updated == shaData { - return true - } - } - } - } - return false -} - -func getResourceSHA(containers []v1.Container, envar string) string { +// GetResourceSHA returns the SHA value of given environment variable +func GetResourceSHA(containers []v1.Container, envar string) string { for i := range containers { envs := containers[i].Env for j := range envs { @@ -446,3 +364,41 @@ func DeleteSecret(client kubernetes.Interface, namespace string, secretName stri time.Sleep(5 * time.Second) return err } + +// 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) +} + +func VerifyResourceUpdate(client kubernetes.Interface, config util.Config, envVarPostfix string, upgradeFuncs callbacks.RollingUpgradeFuncs) bool { + items := upgradeFuncs.ItemsFunc(client, config.Namespace) + for _, i := range items { + containers := upgradeFuncs.ContainersFunc(i) + // match statefulsets with the correct annotation + annotationValue := util.ToObjectMeta(i).Annotations[config.Annotation] + if annotationValue != "" { + values := strings.Split(annotationValue, ",") + matches := false + for _, value := range values { + if value == config.ResourceName { + matches = true + break + } + } + if matches { + envName := constants.EnvVarPrefix + util.ConvertToEnvVarName(annotationValue) + envVarPostfix + updated := GetResourceSHA(containers, envName) + + if updated == config.SHAValue { + return true + } + } + } + } + return false +} diff --git a/internal/pkg/util/config.go b/internal/pkg/util/config.go new file mode 100644 index 00000000..19577d35 --- /dev/null +++ b/internal/pkg/util/config.go @@ -0,0 +1,9 @@ +package util + +//Config contains rolling upgrade configuration parameters +type Config struct { + Namespace string + ResourceName string + Annotation string + SHAValue string +} diff --git a/internal/pkg/common/common.go b/internal/pkg/util/util.go similarity index 66% rename from internal/pkg/common/common.go rename to internal/pkg/util/util.go index 3f52f1d8..3368a24c 100644 --- a/internal/pkg/common/common.go +++ b/internal/pkg/util/util.go @@ -1,14 +1,8 @@ -package common +package util import ( "bytes" - "math/rand" "strings" - "time" -) - -var ( - letters = []rune("abcdefghijklmnopqrstuvwxyz") ) // ConvertToEnvVarName converts the given text into a usable env var @@ -31,13 +25,3 @@ func ConvertToEnvVarName(text string) string { } return buffer.String() } - -// 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) -} diff --git a/internal/pkg/common/common_test.go b/internal/pkg/util/util_test.go similarity index 56% rename from internal/pkg/common/common_test.go rename to internal/pkg/util/util_test.go index 7216cddd..d635fb42 100644 --- a/internal/pkg/common/common_test.go +++ b/internal/pkg/util/util_test.go @@ -1,4 +1,4 @@ -package common +package util import ( "testing" @@ -11,11 +11,3 @@ func TestConvertToEnvVarName(t *testing.T) { t.Errorf("Failed to convert data into environment variable") } } - -func TestRandSeq(t *testing.T) { - data := RandSeq(5) - newData := RandSeq(5) - if data == newData { - t.Errorf("Random sequence generator does not work correctly") - } -} From e11e1744cda0d304b6d1421ebd4a430487763b15 Mon Sep 17 00:00:00 2001 From: faizanahmad055 Date: Tue, 24 Jul 2018 21:17:56 +0500 Subject: [PATCH 18/21] Use fake client for update tests --- glide.lock | 269 ++++++++++++++++++++++++++++ internal/pkg/handler/update_test.go | 3 +- 2 files changed, 271 insertions(+), 1 deletion(-) create mode 100644 glide.lock diff --git a/glide.lock b/glide.lock new file mode 100644 index 00000000..bc2e3f20 --- /dev/null +++ b/glide.lock @@ -0,0 +1,269 @@ +hash: b6fe060028bdb1249ba2413746476c2550b267eeab3c166c36a86e000a8dd354 +updated: 2018-07-24T21:12:43.027181463+05:00 +imports: +- name: github.com/davecgh/go-spew + version: 782f4967f2dc4564575ca782fe2d04090b5faca8 + subpackages: + - spew +- name: github.com/emicklei/go-restful + version: ff4f55a206334ef123e4f79bbf348980da81ca46 + subpackages: + - log +- name: github.com/emicklei/go-restful-swagger12 + version: dcef7f55730566d41eae5db10e7d6981829720f6 +- name: github.com/ghodss/yaml + version: 73d445a93680fa1a78ae23a5839bad48f32ba1ee +- name: github.com/go-openapi/jsonpointer + version: 46af16f9f7b149af66e5d1bd010e3574dc06de98 +- name: github.com/go-openapi/jsonreference + version: 13c6e3589ad90f49bd3e3bbe2c2cb3d7a4142272 +- name: github.com/go-openapi/spec + version: 6aced65f8501fe1217321abf0749d354824ba2ff +- name: github.com/go-openapi/swag + version: 1d0bd113de87027671077d3c71eb3ac5d7dbba72 +- name: github.com/gogo/protobuf + version: c0656edd0d9eab7c66d1eb0c568f9039345796f7 + subpackages: + - proto + - sortkeys +- name: github.com/golang/glog + version: 44145f04b68cf362d9c4df2182967c2275eaefed +- name: github.com/golang/protobuf + version: 4bd1920723d7b7c925de087aa32e2187708897f7 + subpackages: + - proto + - ptypes + - ptypes/any + - ptypes/duration + - ptypes/timestamp +- name: github.com/google/btree + version: 7d79101e329e5a3adf994758c578dab82b90c017 +- name: github.com/google/gofuzz + version: 44d81051d367757e1c7c6a5a86423ece9afcf63c +- name: github.com/googleapis/gnostic + version: 0c5108395e2debce0d731cf0287ddf7242066aba + subpackages: + - OpenAPIv2 + - compiler + - extensions +- name: github.com/gregjones/httpcache + version: 787624de3eb7bd915c329cba748687a3b22666a6 + subpackages: + - diskcache +- name: github.com/hashicorp/golang-lru + version: a0d98a5f288019575c6d1f4bb1573fef2d1fcdc4 + subpackages: + - simplelru +- name: github.com/howeyc/gopass + version: bf9dde6d0d2c004a008c27aaee91170c786f6db8 +- name: github.com/imdario/mergo + version: 6633656539c1639d9d78127b7d47c622b5d7b6dc +- name: github.com/inconshreveable/mousetrap + version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75 +- name: github.com/json-iterator/go + version: 36b14963da70d11297d313183d7e6388c8510e1e +- name: github.com/juju/ratelimit + version: 5b9ff866471762aa2ab2dced63c9fb6f53921342 +- name: github.com/mailru/easyjson + version: d5b7844b561a7bc640052f1b935f7b800330d7e0 + subpackages: + - buffer + - jlexer + - jwriter +- name: github.com/peterbourgon/diskv + version: 5f041e8faa004a95c88a202771f4cc3e991971e6 +- name: github.com/PuerkitoBio/purell + version: 8a290539e2e8629dbc4e6bad948158f790ec31f4 +- name: github.com/PuerkitoBio/urlesc + version: 5bd2802263f21d8788851d5305584c82a5c75d7e +- name: github.com/sirupsen/logrus + version: c155da19408a8799da419ed3eeb0cb5db0ad5dbc +- name: github.com/spf13/cobra + version: ef82de70bb3f60c65fb8eebacbb2d122ef517385 +- name: github.com/spf13/pflag + version: 583c0c0531f06d5278b7d917446061adc344b5cd +- name: golang.org/x/crypto + version: 81e90905daefcd6fd217b62423c0908922eadb30 + subpackages: + - ssh/terminal +- name: golang.org/x/net + version: 1c05540f6879653db88113bc4a2b70aec4bd491f + subpackages: + - context + - http2 + - http2/hpack + - idna + - lex/httplex +- name: golang.org/x/sys + version: 7ddbeae9ae08c6a06a59597f0c9edbc5ff2444ce + subpackages: + - unix + - windows +- name: golang.org/x/text + version: b19bf474d317b857955b12035d2c5acb57ce8b01 + subpackages: + - cases + - internal + - internal/tag + - language + - runes + - secure/bidirule + - secure/precis + - transform + - unicode/bidi + - unicode/norm + - width +- name: gopkg.in/inf.v0 + version: 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4 +- name: gopkg.in/yaml.v2 + version: 53feefa2559fb8dfa8d81baad31be332c97d6c77 +- name: k8s.io/api + version: fe29995db37613b9c5b2a647544cf627bfa8d299 + subpackages: + - admissionregistration/v1alpha1 + - apps/v1beta1 + - apps/v1beta2 + - authentication/v1 + - authentication/v1beta1 + - authorization/v1 + - authorization/v1beta1 + - autoscaling/v1 + - autoscaling/v2beta1 + - batch/v1 + - batch/v1beta1 + - batch/v2alpha1 + - certificates/v1beta1 + - core/v1 + - extensions/v1beta1 + - networking/v1 + - policy/v1beta1 + - rbac/v1 + - rbac/v1alpha1 + - rbac/v1beta1 + - scheduling/v1alpha1 + - settings/v1alpha1 + - storage/v1 + - storage/v1beta1 +- name: k8s.io/apimachinery + version: 019ae5ada31de202164b118aee88ee2d14075c31 + subpackages: + - pkg/api/equality + - pkg/api/errors + - pkg/api/meta + - pkg/api/resource + - pkg/apis/meta/internalversion + - pkg/apis/meta/v1 + - pkg/apis/meta/v1/unstructured + - pkg/apis/meta/v1alpha1 + - pkg/conversion + - pkg/conversion/queryparams + - pkg/conversion/unstructured + - pkg/fields + - pkg/labels + - pkg/runtime + - pkg/runtime/schema + - pkg/runtime/serializer + - pkg/runtime/serializer/json + - pkg/runtime/serializer/protobuf + - pkg/runtime/serializer/recognizer + - pkg/runtime/serializer/streaming + - pkg/runtime/serializer/versioning + - pkg/selection + - pkg/types + - pkg/util/cache + - pkg/util/clock + - pkg/util/diff + - pkg/util/errors + - pkg/util/framer + - pkg/util/intstr + - pkg/util/json + - pkg/util/net + - pkg/util/runtime + - pkg/util/sets + - pkg/util/validation + - pkg/util/validation/field + - pkg/util/wait + - pkg/util/yaml + - pkg/version + - pkg/watch + - third_party/forked/golang/reflect +- name: k8s.io/client-go + version: 35874c597fed17ca62cd197e516d7d5ff9a2958c + subpackages: + - discovery + - discovery/fake + - kubernetes + - kubernetes/fake + - kubernetes/scheme + - kubernetes/typed/admissionregistration/v1alpha1 + - kubernetes/typed/admissionregistration/v1alpha1/fake + - kubernetes/typed/apps/v1beta1 + - kubernetes/typed/apps/v1beta1/fake + - kubernetes/typed/apps/v1beta2 + - kubernetes/typed/apps/v1beta2/fake + - kubernetes/typed/authentication/v1 + - kubernetes/typed/authentication/v1/fake + - kubernetes/typed/authentication/v1beta1 + - kubernetes/typed/authentication/v1beta1/fake + - kubernetes/typed/authorization/v1 + - kubernetes/typed/authorization/v1/fake + - kubernetes/typed/authorization/v1beta1 + - kubernetes/typed/authorization/v1beta1/fake + - kubernetes/typed/autoscaling/v1 + - kubernetes/typed/autoscaling/v1/fake + - kubernetes/typed/autoscaling/v2beta1 + - kubernetes/typed/autoscaling/v2beta1/fake + - kubernetes/typed/batch/v1 + - kubernetes/typed/batch/v1/fake + - kubernetes/typed/batch/v1beta1 + - kubernetes/typed/batch/v1beta1/fake + - kubernetes/typed/batch/v2alpha1 + - kubernetes/typed/batch/v2alpha1/fake + - kubernetes/typed/certificates/v1beta1 + - kubernetes/typed/certificates/v1beta1/fake + - kubernetes/typed/core/v1 + - kubernetes/typed/core/v1/fake + - kubernetes/typed/extensions/v1beta1 + - kubernetes/typed/extensions/v1beta1/fake + - kubernetes/typed/networking/v1 + - kubernetes/typed/networking/v1/fake + - kubernetes/typed/policy/v1beta1 + - kubernetes/typed/policy/v1beta1/fake + - kubernetes/typed/rbac/v1 + - kubernetes/typed/rbac/v1/fake + - kubernetes/typed/rbac/v1alpha1 + - kubernetes/typed/rbac/v1alpha1/fake + - kubernetes/typed/rbac/v1beta1 + - kubernetes/typed/rbac/v1beta1/fake + - kubernetes/typed/scheduling/v1alpha1 + - kubernetes/typed/scheduling/v1alpha1/fake + - kubernetes/typed/settings/v1alpha1 + - kubernetes/typed/settings/v1alpha1/fake + - kubernetes/typed/storage/v1 + - kubernetes/typed/storage/v1/fake + - kubernetes/typed/storage/v1beta1 + - kubernetes/typed/storage/v1beta1/fake + - pkg/version + - rest + - rest/watch + - testing + - tools/auth + - tools/cache + - tools/clientcmd + - tools/clientcmd/api + - tools/clientcmd/api/latest + - tools/clientcmd/api/v1 + - tools/metrics + - tools/pager + - tools/reference + - transport + - util/cert + - util/flowcontrol + - util/homedir + - util/integer + - util/workqueue +- name: k8s.io/kube-openapi + version: 868f2f29720b192240e18284659231b440f9cda5 + subpackages: + - pkg/common +testImports: [] diff --git a/internal/pkg/handler/update_test.go b/internal/pkg/handler/update_test.go index e37d8daa..4c077b76 100644 --- a/internal/pkg/handler/update_test.go +++ b/internal/pkg/handler/update_test.go @@ -10,10 +10,11 @@ import ( "github.com/stakater/Reloader/internal/pkg/constants" "github.com/stakater/Reloader/internal/pkg/testutil" "github.com/stakater/Reloader/internal/pkg/util" + testclient "k8s.io/client-go/kubernetes/fake" ) var ( - client = testutil.GetClient() + client = testclient.NewSimpleClientset() namespace = "test-handler" configmapName = "testconfigmap-handler-" + testutil.RandSeq(5) secretName = "testsecret-handler-" + testutil.RandSeq(5) From c8c0f98c1d526915ce113d952f84c79886aab6f6 Mon Sep 17 00:00:00 2001 From: faizanahmad055 Date: Thu, 26 Jul 2018 12:59:10 +0500 Subject: [PATCH 19/21] Implement PR-2 review comments --- internal/pkg/callbacks/rolling_upgrade.go | 25 +---- internal/pkg/controller/controller_test.go | 120 ++++++++++----------- internal/pkg/handler/update.go | 28 ++--- internal/pkg/handler/update_test.go | 48 ++++----- 4 files changed, 102 insertions(+), 119 deletions(-) diff --git a/internal/pkg/callbacks/rolling_upgrade.go b/internal/pkg/callbacks/rolling_upgrade.go index b44beef0..ba395097 100644 --- a/internal/pkg/callbacks/rolling_upgrade.go +++ b/internal/pkg/callbacks/rolling_upgrade.go @@ -19,14 +19,12 @@ type ContainersFunc func(interface{}) []v1.Container //UpdateFunc performs the resource update type UpdateFunc func(kubernetes.Interface, string, interface{}) error -type ResourceTypeFunc func() string - //RollingUpgradeFuncs contains generic functions to perform rolling upgrade type RollingUpgradeFuncs struct { - ItemsFunc ItemsFunc - ContainersFunc ContainersFunc - UpdateFunc UpdateFunc - ResourceTypeFunc ResourceTypeFunc + ItemsFunc ItemsFunc + ContainersFunc ContainersFunc + UpdateFunc UpdateFunc + ResourceType string } // GetDeploymentItems returns the deployments in given namespace @@ -71,21 +69,6 @@ func GetStatefulsetContainers(item interface{}) []v1.Container { return item.(apps_v1beta1.StatefulSet).Spec.Template.Spec.Containers } -// GetDeploymentTypeName returns Deployment resource type -func GetDeploymentTypeName() string { - return "Deployment" -} - -// GetDaemonSetTypeName returns DaemonSet resource type -func GetDaemonSetTypeName() string { - return "DaemonSet" -} - -// GetStatefulSetTypeName returns StatefulSet resource type -func GetStatefulSetTypeName() string { - return "StatefulSet" -} - // UpdateDeployment performs rolling upgrade on deployment func UpdateDeployment(client kubernetes.Interface, namespace string, resource interface{}) error { deployment := resource.(v1beta1.Deployment) diff --git a/internal/pkg/controller/controller_test.go b/internal/pkg/controller/controller_test.go index ef860f66..3fdd4b86 100644 --- a/internal/pkg/controller/controller_test.go +++ b/internal/pkg/controller/controller_test.go @@ -83,10 +83,10 @@ func TestControllerUpdatingConfigmapShouldCreateEnvInDeployment(t *testing.T) { Annotation: constants.ConfigmapUpdateOnChangeAnnotation, } deploymentFuncs := callbacks.RollingUpgradeFuncs{ - ItemsFunc: callbacks.GetDeploymentItems, - ContainersFunc: callbacks.GetDeploymentContainers, - UpdateFunc: callbacks.UpdateDeployment, - ResourceTypeFunc: callbacks.GetDeploymentTypeName, + ItemsFunc: callbacks.GetDeploymentItems, + ContainersFunc: callbacks.GetDeploymentContainers, + UpdateFunc: callbacks.UpdateDeployment, + ResourceType: "Deployment", } updated := testutil.VerifyResourceUpdate(client, config, constants.ConfigmapEnvVarPostfix, deploymentFuncs) if !updated { @@ -146,10 +146,10 @@ func TestControllerForUpdatingConfigmapShouldUpdateDeployment(t *testing.T) { } deploymentFuncs := callbacks.RollingUpgradeFuncs{ - ItemsFunc: callbacks.GetDeploymentItems, - ContainersFunc: callbacks.GetDeploymentContainers, - UpdateFunc: callbacks.UpdateDeployment, - ResourceTypeFunc: callbacks.GetDeploymentTypeName, + ItemsFunc: callbacks.GetDeploymentItems, + ContainersFunc: callbacks.GetDeploymentContainers, + UpdateFunc: callbacks.UpdateDeployment, + ResourceType: "Deployment", } updated := testutil.VerifyResourceUpdate(client, config, constants.ConfigmapEnvVarPostfix, deploymentFuncs) @@ -203,10 +203,10 @@ func TestControllerUpdatingConfigmapLabelsShouldNotCreateorUpdateEnvInDeployment Annotation: constants.ConfigmapUpdateOnChangeAnnotation, } deploymentFuncs := callbacks.RollingUpgradeFuncs{ - ItemsFunc: callbacks.GetDeploymentItems, - ContainersFunc: callbacks.GetDeploymentContainers, - UpdateFunc: callbacks.UpdateDeployment, - ResourceTypeFunc: callbacks.GetDeploymentTypeName, + ItemsFunc: callbacks.GetDeploymentItems, + ContainersFunc: callbacks.GetDeploymentContainers, + UpdateFunc: callbacks.UpdateDeployment, + ResourceType: "Deployment", } updated := testutil.VerifyResourceUpdate(client, config, constants.ConfigmapEnvVarPostfix, deploymentFuncs) if updated { @@ -259,10 +259,10 @@ func TestControllerUpdatingSecretShouldCreateEnvInDeployment(t *testing.T) { Annotation: constants.SecretUpdateOnChangeAnnotation, } deploymentFuncs := callbacks.RollingUpgradeFuncs{ - ItemsFunc: callbacks.GetDeploymentItems, - ContainersFunc: callbacks.GetDeploymentContainers, - UpdateFunc: callbacks.UpdateDeployment, - ResourceTypeFunc: callbacks.GetDeploymentTypeName, + ItemsFunc: callbacks.GetDeploymentItems, + ContainersFunc: callbacks.GetDeploymentContainers, + UpdateFunc: callbacks.UpdateDeployment, + ResourceType: "Deployment", } updated := testutil.VerifyResourceUpdate(client, config, constants.SecretEnvVarPostfix, deploymentFuncs) if !updated { @@ -321,10 +321,10 @@ func TestControllerUpdatingSecretShouldUpdateEnvInDeployment(t *testing.T) { Annotation: constants.SecretUpdateOnChangeAnnotation, } deploymentFuncs := callbacks.RollingUpgradeFuncs{ - ItemsFunc: callbacks.GetDeploymentItems, - ContainersFunc: callbacks.GetDeploymentContainers, - UpdateFunc: callbacks.UpdateDeployment, - ResourceTypeFunc: callbacks.GetDeploymentTypeName, + ItemsFunc: callbacks.GetDeploymentItems, + ContainersFunc: callbacks.GetDeploymentContainers, + UpdateFunc: callbacks.UpdateDeployment, + ResourceType: "Deployment", } updated := testutil.VerifyResourceUpdate(client, config, constants.SecretEnvVarPostfix, deploymentFuncs) if !updated { @@ -376,10 +376,10 @@ func TestControllerUpdatingSecretLabelsShouldNotCreateorUpdateEnvInDeployment(t Annotation: constants.SecretUpdateOnChangeAnnotation, } deploymentFuncs := callbacks.RollingUpgradeFuncs{ - ItemsFunc: callbacks.GetDeploymentItems, - ContainersFunc: callbacks.GetDeploymentContainers, - UpdateFunc: callbacks.UpdateDeployment, - ResourceTypeFunc: callbacks.GetDeploymentTypeName, + ItemsFunc: callbacks.GetDeploymentItems, + ContainersFunc: callbacks.GetDeploymentContainers, + UpdateFunc: callbacks.UpdateDeployment, + ResourceType: "Deployment", } updated := testutil.VerifyResourceUpdate(client, config, constants.SecretEnvVarPostfix, deploymentFuncs) if updated { @@ -432,10 +432,10 @@ func TestControllerUpdatingConfigmapShouldCreateEnvInDaemonSet(t *testing.T) { Annotation: constants.ConfigmapUpdateOnChangeAnnotation, } daemonSetFuncs := callbacks.RollingUpgradeFuncs{ - ItemsFunc: callbacks.GetDaemonSetItems, - ContainersFunc: callbacks.GetDaemonSetContainers, - UpdateFunc: callbacks.UpdateDaemonSet, - ResourceTypeFunc: callbacks.GetDaemonSetTypeName, + ItemsFunc: callbacks.GetDaemonSetItems, + ContainersFunc: callbacks.GetDaemonSetContainers, + UpdateFunc: callbacks.UpdateDaemonSet, + ResourceType: "DaemonSet", } updated := testutil.VerifyResourceUpdate(client, config, constants.ConfigmapEnvVarPostfix, daemonSetFuncs) if !updated { @@ -494,10 +494,10 @@ func TestControllerForUpdatingConfigmapShouldUpdateDaemonSet(t *testing.T) { Annotation: constants.ConfigmapUpdateOnChangeAnnotation, } daemonSetFuncs := callbacks.RollingUpgradeFuncs{ - ItemsFunc: callbacks.GetDaemonSetItems, - ContainersFunc: callbacks.GetDaemonSetContainers, - UpdateFunc: callbacks.UpdateDaemonSet, - ResourceTypeFunc: callbacks.GetDaemonSetTypeName, + ItemsFunc: callbacks.GetDaemonSetItems, + ContainersFunc: callbacks.GetDaemonSetContainers, + UpdateFunc: callbacks.UpdateDaemonSet, + ResourceType: "DaemonSet", } updated := testutil.VerifyResourceUpdate(client, config, constants.ConfigmapEnvVarPostfix, daemonSetFuncs) if !updated { @@ -550,10 +550,10 @@ func TestControllerUpdatingSecretShouldCreateEnvInDaemonSet(t *testing.T) { Annotation: constants.SecretUpdateOnChangeAnnotation, } daemonSetFuncs := callbacks.RollingUpgradeFuncs{ - ItemsFunc: callbacks.GetDaemonSetItems, - ContainersFunc: callbacks.GetDaemonSetContainers, - UpdateFunc: callbacks.UpdateDaemonSet, - ResourceTypeFunc: callbacks.GetDaemonSetTypeName, + ItemsFunc: callbacks.GetDaemonSetItems, + ContainersFunc: callbacks.GetDaemonSetContainers, + UpdateFunc: callbacks.UpdateDaemonSet, + ResourceType: "DaemonSet", } updated := testutil.VerifyResourceUpdate(client, config, constants.SecretEnvVarPostfix, daemonSetFuncs) if !updated { @@ -613,10 +613,10 @@ func TestControllerUpdatingSecretShouldUpdateEnvInDaemonSet(t *testing.T) { Annotation: constants.SecretUpdateOnChangeAnnotation, } daemonSetFuncs := callbacks.RollingUpgradeFuncs{ - ItemsFunc: callbacks.GetDaemonSetItems, - ContainersFunc: callbacks.GetDaemonSetContainers, - UpdateFunc: callbacks.UpdateDaemonSet, - ResourceTypeFunc: callbacks.GetDaemonSetTypeName, + ItemsFunc: callbacks.GetDaemonSetItems, + ContainersFunc: callbacks.GetDaemonSetContainers, + UpdateFunc: callbacks.UpdateDaemonSet, + ResourceType: "DaemonSet", } updated := testutil.VerifyResourceUpdate(client, config, constants.SecretEnvVarPostfix, daemonSetFuncs) if !updated { @@ -668,10 +668,10 @@ func TestControllerUpdatingSecretLabelsShouldNotCreateorUpdateEnvInDaemonSet(t * Annotation: constants.SecretUpdateOnChangeAnnotation, } daemonSetFuncs := callbacks.RollingUpgradeFuncs{ - ItemsFunc: callbacks.GetDaemonSetItems, - ContainersFunc: callbacks.GetDaemonSetContainers, - UpdateFunc: callbacks.UpdateDaemonSet, - ResourceTypeFunc: callbacks.GetDaemonSetTypeName, + ItemsFunc: callbacks.GetDaemonSetItems, + ContainersFunc: callbacks.GetDaemonSetContainers, + UpdateFunc: callbacks.UpdateDaemonSet, + ResourceType: "DaemonSet", } updated := testutil.VerifyResourceUpdate(client, config, constants.SecretEnvVarPostfix, daemonSetFuncs) if updated { @@ -724,10 +724,10 @@ func TestControllerUpdatingConfigmapShouldCreateEnvInStatefulSet(t *testing.T) { Annotation: constants.ConfigmapUpdateOnChangeAnnotation, } statefulSetFuncs := callbacks.RollingUpgradeFuncs{ - ItemsFunc: callbacks.GetStatefulSetItems, - ContainersFunc: callbacks.GetStatefulsetContainers, - UpdateFunc: callbacks.UpdateStatefulset, - ResourceTypeFunc: callbacks.GetStatefulSetTypeName, + ItemsFunc: callbacks.GetStatefulSetItems, + ContainersFunc: callbacks.GetStatefulsetContainers, + UpdateFunc: callbacks.UpdateStatefulset, + ResourceType: "StatefulSet", } updated := testutil.VerifyResourceUpdate(client, config, constants.ConfigmapEnvVarPostfix, statefulSetFuncs) if !updated { @@ -786,10 +786,10 @@ func TestControllerForUpdatingConfigmapShouldUpdateStatefulSet(t *testing.T) { Annotation: constants.ConfigmapUpdateOnChangeAnnotation, } statefulSetFuncs := callbacks.RollingUpgradeFuncs{ - ItemsFunc: callbacks.GetStatefulSetItems, - ContainersFunc: callbacks.GetStatefulsetContainers, - UpdateFunc: callbacks.UpdateStatefulset, - ResourceTypeFunc: callbacks.GetStatefulSetTypeName, + ItemsFunc: callbacks.GetStatefulSetItems, + ContainersFunc: callbacks.GetStatefulsetContainers, + UpdateFunc: callbacks.UpdateStatefulset, + ResourceType: "StatefulSet", } updated := testutil.VerifyResourceUpdate(client, config, constants.ConfigmapEnvVarPostfix, statefulSetFuncs) if !updated { @@ -842,10 +842,10 @@ func TestControllerUpdatingSecretShouldCreateEnvInStatefulSet(t *testing.T) { Annotation: constants.SecretUpdateOnChangeAnnotation, } statefulSetFuncs := callbacks.RollingUpgradeFuncs{ - ItemsFunc: callbacks.GetStatefulSetItems, - ContainersFunc: callbacks.GetStatefulsetContainers, - UpdateFunc: callbacks.UpdateStatefulset, - ResourceTypeFunc: callbacks.GetStatefulSetTypeName, + ItemsFunc: callbacks.GetStatefulSetItems, + ContainersFunc: callbacks.GetStatefulsetContainers, + UpdateFunc: callbacks.UpdateStatefulset, + ResourceType: "StatefulSet", } updated := testutil.VerifyResourceUpdate(client, config, constants.SecretEnvVarPostfix, statefulSetFuncs) if !updated { @@ -904,10 +904,10 @@ func TestControllerUpdatingSecretShouldUpdateEnvInStatefulSet(t *testing.T) { Annotation: constants.SecretUpdateOnChangeAnnotation, } statefulSetFuncs := callbacks.RollingUpgradeFuncs{ - ItemsFunc: callbacks.GetStatefulSetItems, - ContainersFunc: callbacks.GetStatefulsetContainers, - UpdateFunc: callbacks.UpdateStatefulset, - ResourceTypeFunc: callbacks.GetStatefulSetTypeName, + ItemsFunc: callbacks.GetStatefulSetItems, + ContainersFunc: callbacks.GetStatefulsetContainers, + UpdateFunc: callbacks.UpdateStatefulset, + ResourceType: "StatefulSet", } updated := testutil.VerifyResourceUpdate(client, config, constants.SecretEnvVarPostfix, statefulSetFuncs) if !updated { diff --git a/internal/pkg/handler/update.go b/internal/pkg/handler/update.go index 01dc04bf..d8bda1b4 100644 --- a/internal/pkg/handler/update.go +++ b/internal/pkg/handler/update.go @@ -28,22 +28,22 @@ func (r ResourceUpdatedHandler) Handle() error { logrus.Infof("Detected changes in object %s", r.Resource) // process resource based on its type rollingUpgrade(r, callbacks.RollingUpgradeFuncs{ - ItemsFunc: callbacks.GetDeploymentItems, - ContainersFunc: callbacks.GetDeploymentContainers, - UpdateFunc: callbacks.UpdateDeployment, - ResourceTypeFunc: callbacks.GetDeploymentTypeName, + ItemsFunc: callbacks.GetDeploymentItems, + ContainersFunc: callbacks.GetDeploymentContainers, + UpdateFunc: callbacks.UpdateDeployment, + ResourceType: "Deployment", }) rollingUpgrade(r, callbacks.RollingUpgradeFuncs{ - ItemsFunc: callbacks.GetDaemonSetItems, - ContainersFunc: callbacks.GetDaemonSetContainers, - UpdateFunc: callbacks.UpdateDaemonSet, - ResourceTypeFunc: callbacks.GetDaemonSetTypeName, + ItemsFunc: callbacks.GetDaemonSetItems, + ContainersFunc: callbacks.GetDaemonSetContainers, + UpdateFunc: callbacks.UpdateDaemonSet, + ResourceType: "DaemonSet", }) rollingUpgrade(r, callbacks.RollingUpgradeFuncs{ - ItemsFunc: callbacks.GetStatefulSetItems, - ContainersFunc: callbacks.GetStatefulsetContainers, - UpdateFunc: callbacks.UpdateStatefulset, - ResourceTypeFunc: callbacks.GetStatefulSetTypeName, + ItemsFunc: callbacks.GetStatefulSetItems, + ContainersFunc: callbacks.GetStatefulsetContainers, + UpdateFunc: callbacks.UpdateStatefulset, + ResourceType: "StatefulSet", }) } return nil @@ -124,9 +124,9 @@ func PerformRollingUpgrade(client kubernetes.Interface, config util.Config, enva } else { err = upgradeFuncs.UpdateFunc(client, config.Namespace, i) if err != nil { - logrus.Errorf("Update %s failed %v", upgradeFuncs.ResourceTypeFunc, err) + logrus.Errorf("Update %s failed %v", upgradeFuncs.ResourceType, err) } else { - logrus.Infof("Updated %s of type %s", config.ResourceName, upgradeFuncs.ResourceTypeFunc) + logrus.Infof("Updated %s of type %s", config.ResourceName, upgradeFuncs.ResourceType) } break } diff --git a/internal/pkg/handler/update_test.go b/internal/pkg/handler/update_test.go index 4c077b76..2e9c6f1c 100644 --- a/internal/pkg/handler/update_test.go +++ b/internal/pkg/handler/update_test.go @@ -152,10 +152,10 @@ func TestRollingUpgradeForDeploymentWithConfigmap(t *testing.T) { Annotation: constants.ConfigmapUpdateOnChangeAnnotation, } deploymentFuncs := callbacks.RollingUpgradeFuncs{ - ItemsFunc: callbacks.GetDeploymentItems, - ContainersFunc: callbacks.GetDeploymentContainers, - UpdateFunc: callbacks.UpdateDeployment, - ResourceTypeFunc: callbacks.GetDeploymentTypeName, + ItemsFunc: callbacks.GetDeploymentItems, + ContainersFunc: callbacks.GetDeploymentContainers, + UpdateFunc: callbacks.UpdateDeployment, + ResourceType: "Deployment", } err := PerformRollingUpgrade(client, config, constants.ConfigmapEnvVarPostfix, deploymentFuncs) @@ -180,10 +180,10 @@ func TestRollingUpgradeForDeploymentWithSecret(t *testing.T) { Annotation: constants.SecretUpdateOnChangeAnnotation, } deploymentFuncs := callbacks.RollingUpgradeFuncs{ - ItemsFunc: callbacks.GetDeploymentItems, - ContainersFunc: callbacks.GetDeploymentContainers, - UpdateFunc: callbacks.UpdateDeployment, - ResourceTypeFunc: callbacks.GetDeploymentTypeName, + ItemsFunc: callbacks.GetDeploymentItems, + ContainersFunc: callbacks.GetDeploymentContainers, + UpdateFunc: callbacks.UpdateDeployment, + ResourceType: "Deployment", } err := PerformRollingUpgrade(client, config, constants.SecretEnvVarPostfix, deploymentFuncs) @@ -208,10 +208,10 @@ func TestRollingUpgradeForDaemonSetWithConfigmap(t *testing.T) { Annotation: constants.ConfigmapUpdateOnChangeAnnotation, } daemonSetFuncs := callbacks.RollingUpgradeFuncs{ - ItemsFunc: callbacks.GetDaemonSetItems, - ContainersFunc: callbacks.GetDaemonSetContainers, - UpdateFunc: callbacks.UpdateDaemonSet, - ResourceTypeFunc: callbacks.GetDaemonSetTypeName, + ItemsFunc: callbacks.GetDaemonSetItems, + ContainersFunc: callbacks.GetDaemonSetContainers, + UpdateFunc: callbacks.UpdateDaemonSet, + ResourceType: "DaemonSet", } err := PerformRollingUpgrade(client, config, constants.ConfigmapEnvVarPostfix, daemonSetFuncs) @@ -237,10 +237,10 @@ func TestRollingUpgradeForDaemonSetWithSecret(t *testing.T) { Annotation: constants.SecretUpdateOnChangeAnnotation, } daemonSetFuncs := callbacks.RollingUpgradeFuncs{ - ItemsFunc: callbacks.GetDaemonSetItems, - ContainersFunc: callbacks.GetDaemonSetContainers, - UpdateFunc: callbacks.UpdateDaemonSet, - ResourceTypeFunc: callbacks.GetDaemonSetTypeName, + ItemsFunc: callbacks.GetDaemonSetItems, + ContainersFunc: callbacks.GetDaemonSetContainers, + UpdateFunc: callbacks.UpdateDaemonSet, + ResourceType: "DaemonSet", } err := PerformRollingUpgrade(client, config, constants.SecretEnvVarPostfix, daemonSetFuncs) @@ -266,10 +266,10 @@ func TestRollingUpgradeForStatefulSetWithConfigmap(t *testing.T) { Annotation: constants.ConfigmapUpdateOnChangeAnnotation, } statefulSetFuncs := callbacks.RollingUpgradeFuncs{ - ItemsFunc: callbacks.GetStatefulSetItems, - ContainersFunc: callbacks.GetStatefulsetContainers, - UpdateFunc: callbacks.UpdateStatefulset, - ResourceTypeFunc: callbacks.GetStatefulSetTypeName, + ItemsFunc: callbacks.GetStatefulSetItems, + ContainersFunc: callbacks.GetStatefulsetContainers, + UpdateFunc: callbacks.UpdateStatefulset, + ResourceType: "StatefulSet", } err := PerformRollingUpgrade(client, config, constants.ConfigmapEnvVarPostfix, statefulSetFuncs) @@ -295,10 +295,10 @@ func TestRollingUpgradeForStatefulSetWithSecret(t *testing.T) { Annotation: constants.SecretUpdateOnChangeAnnotation, } statefulSetFuncs := callbacks.RollingUpgradeFuncs{ - ItemsFunc: callbacks.GetStatefulSetItems, - ContainersFunc: callbacks.GetStatefulsetContainers, - UpdateFunc: callbacks.UpdateStatefulset, - ResourceTypeFunc: callbacks.GetStatefulSetTypeName, + ItemsFunc: callbacks.GetStatefulSetItems, + ContainersFunc: callbacks.GetStatefulsetContainers, + UpdateFunc: callbacks.UpdateStatefulset, + ResourceType: "StatefulSet", } err := PerformRollingUpgrade(client, config, constants.SecretEnvVarPostfix, statefulSetFuncs) From 0454d185107cd657535af8a01843bbe74d0c5f1d Mon Sep 17 00:00:00 2001 From: faizanahmad055 Date: Thu, 26 Jul 2018 13:28:26 +0500 Subject: [PATCH 20/21] Fix manifest and .version --- .version | 2 +- deployments/kubernetes/manifests/rbac.yaml | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/.version b/.version index 8acdd82b..8a9ecc2e 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -0.0.1 +0.0.1 \ No newline at end of file diff --git a/deployments/kubernetes/manifests/rbac.yaml b/deployments/kubernetes/manifests/rbac.yaml index e56b0f6a..5654b85a 100644 --- a/deployments/kubernetes/manifests/rbac.yaml +++ b/deployments/kubernetes/manifests/rbac.yaml @@ -36,6 +36,19 @@ rules: - list - get - watch + - apiGroups: + - "" + - "extensions" + - "apps" + resources: + - deployments + - daemonsets + - statefulsets + verbs: + - list + - get + - update + - patch --- apiVersion: rbac.authorization.k8s.io/v1beta1 kind: RoleBinding From dd2b3de46d402a12e0a2c9b67f87f50c5e7a38b7 Mon Sep 17 00:00:00 2001 From: faizanahmad055 Date: Thu, 26 Jul 2018 14:43:04 +0500 Subject: [PATCH 21/21] Update fabric8 pipeline library version --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 8d76442b..10e7e595 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,5 +1,5 @@ #!/usr/bin/groovy -@Library('github.com/stakater/fabric8-pipeline-library@v2.4.0') +@Library('github.com/stakater/fabric8-pipeline-library@v2.5.1') def dummy