From 46bd1f13c20eac08d8d8946ca37ad8ae904b4516 Mon Sep 17 00:00:00 2001 From: srinivashegde86 Date: Mon, 18 Mar 2019 10:41:46 -0700 Subject: [PATCH] Add some more common test componets in pkg/test (#321) --- test/clients.go | 36 +++++++++++++++-- test/crd.go | 95 +++++++++++++++++++++++++++++++++++++++++++++ test/e2e_flags.go | 15 +++++++ test/kube_checks.go | 31 ++++++++++++++- 4 files changed, 172 insertions(+), 5 deletions(-) create mode 100644 test/crd.go diff --git a/test/clients.go b/test/clients.go index 297dd9c11..edae5e27c 100644 --- a/test/clients.go +++ b/test/clients.go @@ -19,8 +19,12 @@ limitations under the License. package test import ( + "fmt" + "strings" + "github.com/knative/pkg/test/logging" "github.com/knative/pkg/test/spoof" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" k8styped "k8s.io/client-go/kubernetes/typed/core/v1" @@ -67,7 +71,7 @@ func BuildClientConfig(kubeConfigPath string, clusterName string) (*rest.Config, // UpdateConfigMap updates the config map for specified @name with values func (client *KubeClient) UpdateConfigMap(name string, configName string, values map[string]string) error { - configMap, err := client.getConfigMap(name).Get(configName, metav1.GetOptions{}) + configMap, err := client.GetConfigMap(name).Get(configName, metav1.GetOptions{}) if err != nil { return err } @@ -76,11 +80,35 @@ func (client *KubeClient) UpdateConfigMap(name string, configName string, values configMap.Data[key] = value } - _, err = client.getConfigMap(name).Update(configMap) + _, err = client.GetConfigMap(name).Update(configMap) return err } -// getConfigMap gets the knative serving config map. -func (client *KubeClient) getConfigMap(name string) k8styped.ConfigMapInterface { +// GetConfigMap gets the knative serving config map. +func (client *KubeClient) GetConfigMap(name string) k8styped.ConfigMapInterface { return client.Kube.CoreV1().ConfigMaps(name) } + +// CreatePod will create a Pod +func (client *KubeClient) CreatePod(pod *corev1.Pod) (*corev1.Pod, error) { + pods := client.Kube.CoreV1().Pods(pod.GetNamespace()) + return pods.Create(pod) +} + +// PodLogs returns Pod logs for given Pod and Container +func (client *KubeClient) PodLogs(podName, containerName string) ([]byte, error) { + pods := client.Kube.CoreV1().Pods(Flags.Namespace) + podList, err := pods.List(metav1.ListOptions{}) + if err != nil { + return nil, err + } + for _, pod := range podList.Items { + if strings.Contains(pod.Name, podName) { + result := pods.GetLogs(pod.Name, &corev1.PodLogOptions{ + Container: containerName, + }).Do() + return result.Raw() + } + } + return nil, fmt.Errorf("Could not find logs for %s/%s", podName, containerName) +} diff --git a/test/crd.go b/test/crd.go new file mode 100644 index 000000000..ac74d09a7 --- /dev/null +++ b/test/crd.go @@ -0,0 +1,95 @@ +/* +Copyright 2019 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// This file contains functions that construct boilerplate CRD definitions. + +package test + +import ( + corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const ( + nginxPort = 80 + nginxName = "nginx" + nginxImage = "nginx:1.7.9" +) + +// ServiceAccount returns ServiceAccount object in given namespace +func ServiceAccount(name string, namespace string) *corev1.ServiceAccount { + return &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + } +} + +// ClusterRoleBinding returns ClusterRoleBinding for given subject and role +func ClusterRoleBinding(name string, namespace string, serviceAccount string, role string) *rbacv1.ClusterRoleBinding { + return &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + Subjects: []rbacv1.Subject{ + { + Kind: "ServiceAccount", + Name: serviceAccount, + Namespace: namespace, + }, + }, + RoleRef: rbacv1.RoleRef{ + Kind: "ClusterRole", + Name: role, + APIGroup: "rbac.authorization.k8s.io", + }, + } +} + +// CoreV1ObjectReference returns a corev1.ObjectReference for the given name, kind and apiversion +func CoreV1ObjectReference(kind, apiversion, name string) *corev1.ObjectReference { + return &corev1.ObjectReference{ + Kind: kind, + APIVersion: apiversion, + Name: name, + } +} + +// NginxPod returns nginx pod defined in given namespace +func NginxPod(namespace string) *corev1.Pod { + return &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: nginxName, + Namespace: namespace, + Annotations: map[string]string{"sidecar.istio.io/inject": "true"}, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: nginxName, + Image: nginxImage, + Ports: []corev1.ContainerPort{ + { + ContainerPort: nginxPort, + }, + }, + }, + }, + }, + } +} diff --git a/test/e2e_flags.go b/test/e2e_flags.go index 4b49f9b9e..674c4d69f 100644 --- a/test/e2e_flags.go +++ b/test/e2e_flags.go @@ -21,6 +21,8 @@ package test import ( "flag" + "fmt" + "os" "os/user" "path" ) @@ -37,6 +39,8 @@ type EnvironmentFlags struct { IngressEndpoint string // Host to use for ingress endpoint LogVerbose bool // Enable verbose logging EmitMetrics bool // Emit metrics + DockerRepo string // Docker repo (defaults to $KO_DOCKER_REPO) + Tag string // Tag for test images } func initializeFlags() *EnvironmentFlags { @@ -63,5 +67,16 @@ func initializeFlags() *EnvironmentFlags { flag.BoolVar(&f.EmitMetrics, "emitmetrics", false, "Set this flag to true if you would like tests to emit metrics, e.g. latency of resources being realized in the system.") + defaultRepo := os.Getenv("KO_DOCKER_REPO") + flag.StringVar(&f.DockerRepo, "dockerrepo", defaultRepo, + "Provide the uri of the docker repo you have uploaded the test image to using `uploadtestimage.sh`. Defaults to $KO_DOCKER_REPO") + + flag.StringVar(&f.Tag, "tag", "e2e", "Provide the version tag for the test images.") + return &f } + +// ImagePath is a helper function to prefix image name with repo and suffix with tag +func ImagePath(name string) string { + return fmt.Sprintf("%s/%s:%s", Flags.DockerRepo, name, Flags.Tag) +} diff --git a/test/kube_checks.go b/test/kube_checks.go index 9888eb6e7..381f07815 100644 --- a/test/kube_checks.go +++ b/test/kube_checks.go @@ -22,6 +22,7 @@ package test import ( "context" "fmt" + "strings" "time" "github.com/knative/pkg/test/logging" @@ -35,6 +36,7 @@ import ( const ( interval = 1 * time.Second podTimeout = 8 * time.Minute + logTimeout = 1 * time.Minute ) // WaitForDeploymentState polls the status of the Deployment called name @@ -78,9 +80,36 @@ func GetConfigMap(client *KubeClient, namespace string) k8styped.ConfigMapInterf return client.Kube.CoreV1().ConfigMaps(namespace) } -// Returns a func that evaluates if a deployment has scaled to 0 pods +// DeploymentScaledToZeroFunc returns a func that evaluates if a deployment has scaled to 0 pods func DeploymentScaledToZeroFunc() func(d *apiv1beta1.Deployment) (bool, error) { return func(d *apiv1beta1.Deployment) (bool, error) { return d.Status.ReadyReplicas == 0, nil } } + +// WaitForLogContent waits until logs for given Pod/Container include the given content. +// If the content is not present within timeout it returns error. +func WaitForLogContent(client *KubeClient, podName, containerName, content string) error { + return wait.PollImmediate(interval, logTimeout, func() (bool, error) { + logs, err := client.PodLogs(podName, containerName) + if err != nil { + return true, err + } + return strings.Contains(string(logs), content), nil + }) +} + +// WaitForAllPodsRunning waits for all the pods to be in running state +func WaitForAllPodsRunning(client *KubeClient, namespace string) error { + return WaitForPodListState(client, PodsRunning, "PodsAreRunning", namespace) +} + +// PodsRunning will check the status conditions of the pod list and return true all pods are Running +func PodsRunning(podList *corev1.PodList) (bool, error) { + for _, pod := range podList.Items { + if pod.Status.Phase != corev1.PodRunning && pod.Status.Phase != corev1.PodSucceeded { + return false, nil + } + } + return true, nil +}