Add helper class and complete integration test

This commit is contained in:
faizanahmad055
2018-07-18 16:19:37 +05:00
parent a3f8f30a6f
commit a317555db9
3 changed files with 168 additions and 73 deletions
+93 -11
View File
@@ -2,10 +2,12 @@ package controller
import (
"math/rand"
"strings"
"testing"
"time"
"github.com/sirupsen/logrus"
helper "github.com/stakater/Reloader/internal/pkg/helper"
"github.com/stakater/Reloader/pkg/kube"
"k8s.io/api/core/v1"
"k8s.io/api/extensions/v1beta1"
@@ -14,9 +16,11 @@ import (
)
var (
configmapNamePrefix = "testconfigmap-reloader"
secretNamePrefix = "testsecret-reloader"
letters = []rune("abcdefghijklmnopqrstuvwxyz")
configmapNamePrefix = "testconfigmap-reloader"
secretNamePrefix = "testsecret-reloader"
letters = []rune("abcdefghijklmnopqrstuvwxyz")
configmapUpdateOnChangeAnnotation = "reloader.stakater.com/configmap.update-on-change"
secretUpdateOnChangeAnnotation = "reloader.stakater.com/secret.update-on-change"
)
func randSeq(n int) string {
@@ -36,9 +40,11 @@ func TestControllerForUpdatingConfigmapShouldUpdateDeployment(t *testing.T) {
return
}
namespace := "test-reloader"
logrus.Infof("Step 1: Create namespace")
createNamespace(t, namespace, client)
defer deleteNamespace(t, namespace, client)
logrus.Infof("Step 2: Create controller")
controller, err := NewController(client, "configMaps", namespace)
if err != nil {
logrus.Errorf("Unable to create NewController error = %v", err)
@@ -46,41 +52,68 @@ func TestControllerForUpdatingConfigmapShouldUpdateDeployment(t *testing.T) {
}
stop := make(chan struct{})
defer close(stop)
logrus.Infof("Step 3: Start controller")
go controller.Run(1, stop)
time.Sleep(10 * time.Second)
configmapName := configmapNamePrefix + "-update-" + randSeq(5)
configmapClient := client.CoreV1().ConfigMaps(namespace)
logrus.Infof("Step 4: Create configmap")
_, err = configmapClient.Create(initConfigmap(namespace, configmapName))
if err != nil {
logrus.Fatalf("Fatal error in configmap creation: %v", err)
}
logrus.Infof("Created Configmap %q.\n", configmapName)
time.Sleep(10 * time.Second)
deployment := createDeployement(configmapName, namespace, client)
logrus.Infof("Step 5: Create Deployment")
deployment := createDeployment(configmapName, namespace, client)
logrus.Infof("Step 6: Update configmap for first time")
logrus.Infof("Updating Configmap %q.\n", configmapName)
_, err = configmapClient.Get(configmapName, metav1.GetOptions{})
if err != nil {
logrus.Errorf("Error while getting configmap %v", err)
}
_, updateErr := configmapClient.Update(updateConfigmap(namespace, configmapName))
// TODO: Add functionality to verify reloader functionality here
_, updateErr := configmapClient.Update(updateConfigmap(namespace, configmapName, "www.stakater.com"))
if updateErr != nil {
err = controller.client.CoreV1().ConfigMaps(namespace).Delete(configmapName, &metav1.DeleteOptions{})
if err != nil {
logrus.Errorf("Error while deleting the configmap %v", err)
}
logrus.Fatalf("Fatal error in configmap update: %v", updateErr)
t.Errorf("Configmap was not updated")
}
time.Sleep(10 * time.Second)
logrus.Infof("Step 7: Verify deployment update for first time")
updated := verifyDeploymentUpdate(client, namespace, configmapName, "_CONFIGMAP", "www.stakater.com")
if !updated {
t.Errorf("Deployment was not updated")
}
time.Sleep(10 * time.Second)
logrus.Infof("Step 8: Update configmap for Second time")
_, updateErr = configmapClient.Update(updateConfigmap(namespace, configmapName, "aurorasolutions.io"))
time.Sleep(10 * time.Second)
logrus.Infof("Step 9: Verify deployment update for second time")
updated = verifyDeploymentUpdate(client, namespace, configmapName, "_CONFIGMAP", "aurorasolutions.io")
if !updated {
t.Errorf("Deployment was not updated")
}
time.Sleep(10 * time.Second)
logrus.Infof("Step 10: Delete Deployment")
logrus.Infof("Deleting Deployment %q.\n", deployment.GetObjectMeta().GetName())
deploymentError := controller.client.ExtensionsV1beta1().Deployments(namespace).Delete(configmapName, &metav1.DeleteOptions{})
if deploymentError != nil {
logrus.Fatalf("Error while deleting the configmap %v", deploymentError)
}
logrus.Infof("Step 11: Delete Configmap")
logrus.Infof("Deleting Configmap %q.\n", configmapName)
err = controller.client.CoreV1().ConfigMaps(namespace).Delete(configmapName, &metav1.DeleteOptions{})
if err != nil {
@@ -89,7 +122,55 @@ func TestControllerForUpdatingConfigmapShouldUpdateDeployment(t *testing.T) {
time.Sleep(15 * time.Second)
}
func createDeployement(deploymentName string, namespace string, client kubernetes.Interface) *v1beta1.Deployment {
func verifyDeploymentUpdate(client kubernetes.Interface, namespace string, name string, resourceType string, change string) bool {
deployments, err := client.ExtensionsV1beta1().Deployments(namespace).List(metav1.ListOptions{})
if err != nil {
logrus.Errorf("Failed to list deployments %v", err)
}
for _, d := range deployments.Items {
containers := d.Spec.Template.Spec.Containers
// match deployments with the correct annotation
annotationValue := d.ObjectMeta.Annotations[configmapUpdateOnChangeAnnotation]
if annotationValue != "" {
values := strings.Split(annotationValue, ",")
matches := false
for _, value := range values {
if value == name {
matches = true
break
}
}
if matches {
sshData := helper.ConvertConfigmapToSHA(updateConfigmap(namespace, name, change))
envName := "STAKATER_" + helper.ConvertToEnvVarName(annotationValue) + resourceType
updated := getResourceSsh(containers, envName)
logrus.Infof("sshData %s", sshData)
logrus.Infof("updated %s", updated)
if updated != sshData {
return false
} else {
return true
}
}
}
}
return false
}
func getResourceSsh(containers []v1.Container, envar string) string {
for i := range containers {
envs := containers[i].Env
for j := range envs {
if envs[j].Name == envar {
return envs[j].Value
}
}
}
return ""
}
func createDeployment(deploymentName string, namespace string, client kubernetes.Interface) *v1beta1.Deployment {
deploymentClient := client.ExtensionsV1beta1().Deployments(namespace)
deployment := initDeployment(namespace, deploymentName)
deployment, err := deploymentClient.Create(deployment)
@@ -223,6 +304,7 @@ func createNamespace(t *testing.T, namespace string, client kubernetes.Interface
}
func deleteNamespace(t *testing.T, namespace string, client kubernetes.Interface) {
logrus.Infof("Step 12: Delete Namespace")
err := client.CoreV1().Namespaces().Delete(namespace, &metav1.DeleteOptions{})
if err != nil {
t.Error("Failed to delete namespace that was created for testing", err)
@@ -231,14 +313,14 @@ func deleteNamespace(t *testing.T, namespace string, client kubernetes.Interface
}
}
func updateConfigmap(namespace string, configmapName string) *v1.ConfigMap {
func updateConfigmap(namespace string, configmapName string, testData string) *v1.ConfigMap {
return &v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: configmapName,
Namespace: namespace,
Labels: map[string]string{"firstLabel": "temp"},
},
Data: map[string]string{"test.url": "www.stakater.com"},
Data: map[string]string{"test.url": testData},
}
}
+7 -62
View File
@@ -1,13 +1,10 @@
package handler
import (
"bytes"
"crypto/sha1"
"io"
"sort"
"strings"
"github.com/sirupsen/logrus"
helper "github.com/stakater/Reloader/internal/pkg/helper"
"github.com/stakater/Reloader/pkg/kube"
"k8s.io/api/core/v1"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -16,7 +13,7 @@ import (
const (
configmapUpdateOnChangeAnnotation = "reloader.stakater.com/configmap.update-on-change"
// Adding seperate annotation to differentiate between configmap and secret
// Adding separate annotation to differentiate between configmap and secret
secretUpdateOnChangeAnnotation = "reloader.stakater.com/secret.update-on-change"
)
@@ -59,12 +56,12 @@ func rollingUpgrade(r ResourceUpdatedHandler, resourceType string, rollingUpgrad
if resourceType == "configmaps" {
namespace = r.Resource.(*v1.ConfigMap).Namespace
name = r.Resource.(*v1.ConfigMap).Name
sshData = convertConfigmapToSHA(r.Resource.(*v1.ConfigMap))
sshData = helper.ConvertConfigmapToSHA(r.Resource.(*v1.ConfigMap))
envName = "_CONFIGMAP"
} else if resourceType == "secrets" {
namespace = r.Resource.(*v1.Secret).Namespace
name = r.Resource.(*v1.Secret).Name
sshData = convertSecretToSHA(r.Resource.(*v1.Secret))
sshData = helper.ConvertSecretToSHA(r.Resource.(*v1.Secret))
envName = "_SECRET"
}
@@ -211,7 +208,7 @@ func rollingUpgradeForStatefulSets(client kubernetes.Interface, r ResourceUpdate
func updateContainers(containers []v1.Container, annotationValue string, sshData string, resourceType string) bool {
updated := false
envar := "STAKATER_" + convertToEnvVarName(annotationValue) + resourceType
envar := "STAKATER_" + helper.ConvertToEnvVarName(annotationValue) + resourceType
logrus.Infof("Generated environment variable: %s", envar)
for i := range containers {
@@ -220,7 +217,7 @@ func updateContainers(containers []v1.Container, annotationValue string, sshData
for j := range envs {
if envs[j].Name == envar {
matched = true
logrus.Infof("%s environment variable found")
logrus.Infof("%s environment variable found", envar)
if envs[j].Value != sshData {
logrus.Infof("Updating %s to %s", envar, sshData)
envs[j].Value = sshData
@@ -236,60 +233,8 @@ func updateContainers(containers []v1.Container, annotationValue string, sshData
}
containers[i].Env = append(containers[i].Env, e)
updated = true
logrus.Infof("%s environment variable does not found so creating a new one")
logrus.Infof("%s environment variable does not found, creating a new env with value %s", envar, sshData)
}
}
return updated
}
// convertToEnvVarName converts the given text into a usable env var
// removing any special chars with '_' and transforming text to upper case
func convertToEnvVarName(text string) string {
var buffer bytes.Buffer
upper := strings.ToUpper(text)
lastCharValid := false
for i := 0; i < len(upper); i++ {
ch := upper[i]
if (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') {
buffer.WriteString(string(ch))
lastCharValid = true
} else {
if lastCharValid {
buffer.WriteString("_")
}
lastCharValid = false
}
}
return buffer.String()
}
func convertConfigmapToSHA(cm *v1.ConfigMap) string {
logrus.Infof("Generating SHA for configmap data")
values := []string{}
for k, v := range cm.Data {
values = append(values, k+"="+v)
}
sort.Strings(values)
sha := generateSHA(strings.Join(values, ";"))
logrus.Infof("SHA for configmap data: %x", sha)
return sha
}
func convertSecretToSHA(se *v1.Secret) string {
logrus.Infof("Generating SHA for secret data")
values := []string{}
for k, v := range se.Data {
values = append(values, k+"="+string(v[:]))
}
sort.Strings(values)
sha := generateSHA(strings.Join(values, ";"))
logrus.Infof("SHA for secret data: %x", sha)
return sha
}
func generateSHA(data string) string {
hasher := sha1.New()
io.WriteString(hasher, data)
sha := hasher.Sum(nil)
return string(sha[:])
}
+68
View File
@@ -0,0 +1,68 @@
package handler
import (
"bytes"
"crypto/sha1"
"fmt"
"io"
"sort"
"strings"
"github.com/sirupsen/logrus"
"k8s.io/api/core/v1"
)
// ConvertToEnvVarName converts the given text into a usable env var
// removing any special chars with '_' and transforming text to upper case
func ConvertToEnvVarName(text string) string {
var buffer bytes.Buffer
upper := strings.ToUpper(text)
lastCharValid := false
for i := 0; i < len(upper); i++ {
ch := upper[i]
if (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') {
buffer.WriteString(string(ch))
lastCharValid = true
} else {
if lastCharValid {
buffer.WriteString("_")
}
lastCharValid = false
}
}
return buffer.String()
}
// ConvertConfigmapToSHA generates SHA for configmap data
func ConvertConfigmapToSHA(cm *v1.ConfigMap) string {
logrus.Infof("Generating SHA for configmap data")
values := []string{}
for k, v := range cm.Data {
values = append(values, k+"="+v)
}
sort.Strings(values)
sha := GenerateSHA(strings.Join(values, ";"))
logrus.Infof("SHA for configmap data: %s", sha)
return sha
}
// ConvertSecretToSHA generates SHA for secret data
func ConvertSecretToSHA(se *v1.Secret) string {
logrus.Infof("Generating SHA for secret data")
values := []string{}
for k, v := range se.Data {
values = append(values, k+"="+string(v[:]))
}
sort.Strings(values)
sha := GenerateSHA(strings.Join(values, ";"))
logrus.Infof("SHA for secret data: %s", sha)
return sha
}
// GenerateSHA generates SHA from string
func GenerateSHA(data string) string {
hasher := sha1.New()
io.WriteString(hasher, data)
sha := hasher.Sum(nil)
return fmt.Sprintf("%x", sha)
}