Files
Reloader/internal/pkg/testutil/kube.go
2018-07-23 15:24:52 +05:00

448 lines
15 KiB
Go

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
}