From 2cac0cc71393c487298ac7dd5daadf6d61649fe6 Mon Sep 17 00:00:00 2001 From: faizanahmad055 Date: Tue, 24 Jul 2018 21:05:50 +0500 Subject: [PATCH] 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 0000000..b44beef --- /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 df6eb8d..d06cc5a 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 efb5392..d01792d 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 0a84011..8bb5ff5 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 3903b36..ef860f6 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 d1bd50f..60d5af6 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 78fb3c7..01dc04b 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 7f54fa0..e37d8da 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 4fea00f..040f92c 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 0000000..19577d3 --- /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 3f52f1d..3368a24 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 7216cdd..d635fb4 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") - } -}