Implement PR-2 review comments

This commit is contained in:
faizanahmad055
2018-07-23 15:24:52 +05:00
parent 5befb4d6eb
commit 81c7b3ef25
13 changed files with 1351 additions and 978 deletions
+56
View File
@@ -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)
}
+21
View File
@@ -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")
}
}
+343 -98
View File
@@ -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)
}
+20
View File
@@ -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)
}
+15
View File
@@ -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")
}
}
+226
View File
@@ -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, ";"))
}
+223
View File
@@ -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")
}
}
-241
View File
@@ -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
}
@@ -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")
}
}
-68
View File
@@ -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)
}
-310
View File
@@ -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 ""
}
+447
View File
@@ -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
}