mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-11-04 07:49:35 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			544 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			544 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/*
 | 
						|
Copyright 2019 The Kubernetes 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.
 | 
						|
*/
 | 
						|
 | 
						|
package pod
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
	"fmt"
 | 
						|
	"strconv"
 | 
						|
	"strings"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/onsi/ginkgo"
 | 
						|
	"github.com/onsi/gomega"
 | 
						|
 | 
						|
	v1 "k8s.io/api/core/v1"
 | 
						|
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
						|
	"k8s.io/apimachinery/pkg/labels"
 | 
						|
	"k8s.io/apimachinery/pkg/types"
 | 
						|
	"k8s.io/apimachinery/pkg/util/wait"
 | 
						|
	clientset "k8s.io/client-go/kubernetes"
 | 
						|
	podutil "k8s.io/kubernetes/pkg/api/v1/pod"
 | 
						|
	"k8s.io/kubernetes/pkg/client/conditions"
 | 
						|
	kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
 | 
						|
	e2elog "k8s.io/kubernetes/test/e2e/framework/log"
 | 
						|
	testutils "k8s.io/kubernetes/test/utils"
 | 
						|
	imageutils "k8s.io/kubernetes/test/utils/image"
 | 
						|
)
 | 
						|
 | 
						|
// TODO: Move to its own subpkg.
 | 
						|
// expectNoError checks if "err" is set, and if so, fails assertion while logging the error.
 | 
						|
func expectNoError(err error, explain ...interface{}) {
 | 
						|
	expectNoErrorWithOffset(1, err, explain...)
 | 
						|
}
 | 
						|
 | 
						|
// TODO: Move to its own subpkg.
 | 
						|
// expectNoErrorWithOffset checks if "err" is set, and if so, fails assertion while logging the error at "offset" levels above its caller
 | 
						|
// (for example, for call chain f -> g -> expectNoErrorWithOffset(1, ...) error would be logged for "f").
 | 
						|
func expectNoErrorWithOffset(offset int, err error, explain ...interface{}) {
 | 
						|
	if err != nil {
 | 
						|
		e2elog.Logf("Unexpected error occurred: %v", err)
 | 
						|
	}
 | 
						|
	gomega.ExpectWithOffset(1+offset, err).NotTo(gomega.HaveOccurred(), explain...)
 | 
						|
}
 | 
						|
 | 
						|
func isElementOf(podUID types.UID, pods *v1.PodList) bool {
 | 
						|
	for _, pod := range pods.Items {
 | 
						|
		if pod.UID == podUID {
 | 
						|
			return true
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return false
 | 
						|
}
 | 
						|
 | 
						|
// ProxyResponseChecker is a context for checking pods responses by issuing GETs to them (via the API
 | 
						|
// proxy) and verifying that they answer with their own pod name.
 | 
						|
type ProxyResponseChecker struct {
 | 
						|
	c              clientset.Interface
 | 
						|
	ns             string
 | 
						|
	label          labels.Selector
 | 
						|
	controllerName string
 | 
						|
	respondName    bool // Whether the pod should respond with its own name.
 | 
						|
	pods           *v1.PodList
 | 
						|
}
 | 
						|
 | 
						|
// NewProxyResponseChecker returns a context for checking pods responses.
 | 
						|
func NewProxyResponseChecker(c clientset.Interface, ns string, label labels.Selector, controllerName string, respondName bool, pods *v1.PodList) ProxyResponseChecker {
 | 
						|
	return ProxyResponseChecker{c, ns, label, controllerName, respondName, pods}
 | 
						|
}
 | 
						|
 | 
						|
// CheckAllResponses issues GETs to all pods in the context and verify they
 | 
						|
// reply with their own pod name.
 | 
						|
func (r ProxyResponseChecker) CheckAllResponses() (done bool, err error) {
 | 
						|
	successes := 0
 | 
						|
	options := metav1.ListOptions{LabelSelector: r.label.String()}
 | 
						|
	currentPods, err := r.c.CoreV1().Pods(r.ns).List(options)
 | 
						|
	expectNoError(err, "Failed to get list of currentPods in namespace: %s", r.ns)
 | 
						|
	for i, pod := range r.pods.Items {
 | 
						|
		// Check that the replica list remains unchanged, otherwise we have problems.
 | 
						|
		if !isElementOf(pod.UID, currentPods) {
 | 
						|
			return false, fmt.Errorf("pod with UID %s is no longer a member of the replica set.  Must have been restarted for some reason.  Current replica set: %v", pod.UID, currentPods)
 | 
						|
		}
 | 
						|
 | 
						|
		ctx, cancel := context.WithTimeout(context.Background(), singleCallTimeout)
 | 
						|
		defer cancel()
 | 
						|
 | 
						|
		body, err := r.c.CoreV1().RESTClient().Get().
 | 
						|
			Context(ctx).
 | 
						|
			Namespace(r.ns).
 | 
						|
			Resource("pods").
 | 
						|
			SubResource("proxy").
 | 
						|
			Name(string(pod.Name)).
 | 
						|
			Do().
 | 
						|
			Raw()
 | 
						|
 | 
						|
		if err != nil {
 | 
						|
			if ctx.Err() != nil {
 | 
						|
				// We may encounter errors here because of a race between the pod readiness and apiserver
 | 
						|
				// proxy. So, we log the error and retry if this occurs.
 | 
						|
				e2elog.Logf("Controller %s: Failed to Get from replica %d [%s]: %v\n pod status: %#v", r.controllerName, i+1, pod.Name, err, pod.Status)
 | 
						|
				return false, nil
 | 
						|
			}
 | 
						|
			e2elog.Logf("Controller %s: Failed to GET from replica %d [%s]: %v\npod status: %#v", r.controllerName, i+1, pod.Name, err, pod.Status)
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		// The response checker expects the pod's name unless !respondName, in
 | 
						|
		// which case it just checks for a non-empty response.
 | 
						|
		got := string(body)
 | 
						|
		what := ""
 | 
						|
		if r.respondName {
 | 
						|
			what = "expected"
 | 
						|
			want := pod.Name
 | 
						|
			if got != want {
 | 
						|
				e2elog.Logf("Controller %s: Replica %d [%s] expected response %q but got %q",
 | 
						|
					r.controllerName, i+1, pod.Name, want, got)
 | 
						|
				continue
 | 
						|
			}
 | 
						|
		} else {
 | 
						|
			what = "non-empty"
 | 
						|
			if len(got) == 0 {
 | 
						|
				e2elog.Logf("Controller %s: Replica %d [%s] expected non-empty response",
 | 
						|
					r.controllerName, i+1, pod.Name)
 | 
						|
				continue
 | 
						|
			}
 | 
						|
		}
 | 
						|
		successes++
 | 
						|
		e2elog.Logf("Controller %s: Got %s result from replica %d [%s]: %q, %d of %d required successes so far",
 | 
						|
			r.controllerName, what, i+1, pod.Name, got, successes, len(r.pods.Items))
 | 
						|
	}
 | 
						|
	if successes < len(r.pods.Items) {
 | 
						|
		return false, nil
 | 
						|
	}
 | 
						|
	return true, nil
 | 
						|
}
 | 
						|
 | 
						|
func podRunning(c clientset.Interface, podName, namespace string) wait.ConditionFunc {
 | 
						|
	return func() (bool, error) {
 | 
						|
		pod, err := c.CoreV1().Pods(namespace).Get(podName, metav1.GetOptions{})
 | 
						|
		if err != nil {
 | 
						|
			return false, err
 | 
						|
		}
 | 
						|
		switch pod.Status.Phase {
 | 
						|
		case v1.PodRunning:
 | 
						|
			return true, nil
 | 
						|
		case v1.PodFailed, v1.PodSucceeded:
 | 
						|
			return false, conditions.ErrPodCompleted
 | 
						|
		}
 | 
						|
		return false, nil
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func podCompleted(c clientset.Interface, podName, namespace string) wait.ConditionFunc {
 | 
						|
	return func() (bool, error) {
 | 
						|
		pod, err := c.CoreV1().Pods(namespace).Get(podName, metav1.GetOptions{})
 | 
						|
		if err != nil {
 | 
						|
			return false, err
 | 
						|
		}
 | 
						|
		switch pod.Status.Phase {
 | 
						|
		case v1.PodFailed, v1.PodSucceeded:
 | 
						|
			return true, nil
 | 
						|
		}
 | 
						|
		return false, nil
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func podRunningAndReady(c clientset.Interface, podName, namespace string) wait.ConditionFunc {
 | 
						|
	return func() (bool, error) {
 | 
						|
		pod, err := c.CoreV1().Pods(namespace).Get(podName, metav1.GetOptions{})
 | 
						|
		if err != nil {
 | 
						|
			return false, err
 | 
						|
		}
 | 
						|
		switch pod.Status.Phase {
 | 
						|
		case v1.PodFailed, v1.PodSucceeded:
 | 
						|
			return false, conditions.ErrPodCompleted
 | 
						|
		case v1.PodRunning:
 | 
						|
			return podutil.IsPodReady(pod), nil
 | 
						|
		}
 | 
						|
		return false, nil
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func podNotPending(c clientset.Interface, podName, namespace string) wait.ConditionFunc {
 | 
						|
	return func() (bool, error) {
 | 
						|
		pod, err := c.CoreV1().Pods(namespace).Get(podName, metav1.GetOptions{})
 | 
						|
		if err != nil {
 | 
						|
			return false, err
 | 
						|
		}
 | 
						|
		switch pod.Status.Phase {
 | 
						|
		case v1.PodPending:
 | 
						|
			return false, nil
 | 
						|
		default:
 | 
						|
			return true, nil
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// PodsCreated returns a pod list matched by the given name.
 | 
						|
func PodsCreated(c clientset.Interface, ns, name string, replicas int32) (*v1.PodList, error) {
 | 
						|
	label := labels.SelectorFromSet(labels.Set(map[string]string{"name": name}))
 | 
						|
	return PodsCreatedByLabel(c, ns, name, replicas, label)
 | 
						|
}
 | 
						|
 | 
						|
// PodsCreatedByLabel returns a created pod list matched by the given label.
 | 
						|
func PodsCreatedByLabel(c clientset.Interface, ns, name string, replicas int32, label labels.Selector) (*v1.PodList, error) {
 | 
						|
	timeout := 2 * time.Minute
 | 
						|
	for start := time.Now(); time.Since(start) < timeout; time.Sleep(5 * time.Second) {
 | 
						|
		options := metav1.ListOptions{LabelSelector: label.String()}
 | 
						|
 | 
						|
		// List the pods, making sure we observe all the replicas.
 | 
						|
		pods, err := c.CoreV1().Pods(ns).List(options)
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
 | 
						|
		created := []v1.Pod{}
 | 
						|
		for _, pod := range pods.Items {
 | 
						|
			if pod.DeletionTimestamp != nil {
 | 
						|
				continue
 | 
						|
			}
 | 
						|
			created = append(created, pod)
 | 
						|
		}
 | 
						|
		e2elog.Logf("Pod name %s: Found %d pods out of %d", name, len(created), replicas)
 | 
						|
 | 
						|
		if int32(len(created)) == replicas {
 | 
						|
			pods.Items = created
 | 
						|
			return pods, nil
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return nil, fmt.Errorf("Pod name %s: Gave up waiting %v for %d pods to come up", name, timeout, replicas)
 | 
						|
}
 | 
						|
 | 
						|
// VerifyPods checks if the specified pod is responding.
 | 
						|
func VerifyPods(c clientset.Interface, ns, name string, wantName bool, replicas int32) error {
 | 
						|
	return podRunningMaybeResponding(c, ns, name, wantName, replicas, true)
 | 
						|
}
 | 
						|
 | 
						|
// VerifyPodsRunning checks if the specified pod is running.
 | 
						|
func VerifyPodsRunning(c clientset.Interface, ns, name string, wantName bool, replicas int32) error {
 | 
						|
	return podRunningMaybeResponding(c, ns, name, wantName, replicas, false)
 | 
						|
}
 | 
						|
 | 
						|
func podRunningMaybeResponding(c clientset.Interface, ns, name string, wantName bool, replicas int32, checkResponding bool) error {
 | 
						|
	pods, err := PodsCreated(c, ns, name, replicas)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	e := podsRunning(c, pods)
 | 
						|
	if len(e) > 0 {
 | 
						|
		return fmt.Errorf("failed to wait for pods running: %v", e)
 | 
						|
	}
 | 
						|
	if checkResponding {
 | 
						|
		err = PodsResponding(c, ns, name, wantName, pods)
 | 
						|
		if err != nil {
 | 
						|
			return fmt.Errorf("failed to wait for pods responding: %v", err)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func podsRunning(c clientset.Interface, pods *v1.PodList) []error {
 | 
						|
	// Wait for the pods to enter the running state. Waiting loops until the pods
 | 
						|
	// are running so non-running pods cause a timeout for this test.
 | 
						|
	ginkgo.By("ensuring each pod is running")
 | 
						|
	e := []error{}
 | 
						|
	errorChan := make(chan error)
 | 
						|
 | 
						|
	for _, pod := range pods.Items {
 | 
						|
		go func(p v1.Pod) {
 | 
						|
			errorChan <- WaitForPodRunningInNamespace(c, &p)
 | 
						|
		}(pod)
 | 
						|
	}
 | 
						|
 | 
						|
	for range pods.Items {
 | 
						|
		err := <-errorChan
 | 
						|
		if err != nil {
 | 
						|
			e = append(e, err)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return e
 | 
						|
}
 | 
						|
 | 
						|
// LogPodStates logs basic info of provided pods for debugging.
 | 
						|
func LogPodStates(pods []v1.Pod) {
 | 
						|
	// Find maximum widths for pod, node, and phase strings for column printing.
 | 
						|
	maxPodW, maxNodeW, maxPhaseW, maxGraceW := len("POD"), len("NODE"), len("PHASE"), len("GRACE")
 | 
						|
	for i := range pods {
 | 
						|
		pod := &pods[i]
 | 
						|
		if len(pod.ObjectMeta.Name) > maxPodW {
 | 
						|
			maxPodW = len(pod.ObjectMeta.Name)
 | 
						|
		}
 | 
						|
		if len(pod.Spec.NodeName) > maxNodeW {
 | 
						|
			maxNodeW = len(pod.Spec.NodeName)
 | 
						|
		}
 | 
						|
		if len(pod.Status.Phase) > maxPhaseW {
 | 
						|
			maxPhaseW = len(pod.Status.Phase)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	// Increase widths by one to separate by a single space.
 | 
						|
	maxPodW++
 | 
						|
	maxNodeW++
 | 
						|
	maxPhaseW++
 | 
						|
	maxGraceW++
 | 
						|
 | 
						|
	// Log pod info. * does space padding, - makes them left-aligned.
 | 
						|
	e2elog.Logf("%-[1]*[2]s %-[3]*[4]s %-[5]*[6]s %-[7]*[8]s %[9]s",
 | 
						|
		maxPodW, "POD", maxNodeW, "NODE", maxPhaseW, "PHASE", maxGraceW, "GRACE", "CONDITIONS")
 | 
						|
	for _, pod := range pods {
 | 
						|
		grace := ""
 | 
						|
		if pod.DeletionGracePeriodSeconds != nil {
 | 
						|
			grace = fmt.Sprintf("%ds", *pod.DeletionGracePeriodSeconds)
 | 
						|
		}
 | 
						|
		e2elog.Logf("%-[1]*[2]s %-[3]*[4]s %-[5]*[6]s %-[7]*[8]s %[9]s",
 | 
						|
			maxPodW, pod.ObjectMeta.Name, maxNodeW, pod.Spec.NodeName, maxPhaseW, pod.Status.Phase, maxGraceW, grace, pod.Status.Conditions)
 | 
						|
	}
 | 
						|
	e2elog.Logf("") // Final empty line helps for readability.
 | 
						|
}
 | 
						|
 | 
						|
// logPodTerminationMessages logs termination messages for failing pods.  It's a short snippet (much smaller than full logs), but it often shows
 | 
						|
// why pods crashed and since it is in the API, it's fast to retrieve.
 | 
						|
func logPodTerminationMessages(pods []v1.Pod) {
 | 
						|
	for _, pod := range pods {
 | 
						|
		for _, status := range pod.Status.InitContainerStatuses {
 | 
						|
			if status.LastTerminationState.Terminated != nil && len(status.LastTerminationState.Terminated.Message) > 0 {
 | 
						|
				e2elog.Logf("%s[%s].initContainer[%s]=%s", pod.Name, pod.Namespace, status.Name, status.LastTerminationState.Terminated.Message)
 | 
						|
			}
 | 
						|
		}
 | 
						|
		for _, status := range pod.Status.ContainerStatuses {
 | 
						|
			if status.LastTerminationState.Terminated != nil && len(status.LastTerminationState.Terminated.Message) > 0 {
 | 
						|
				e2elog.Logf("%s[%s].container[%s]=%s", pod.Name, pod.Namespace, status.Name, status.LastTerminationState.Terminated.Message)
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// DumpAllPodInfoForNamespace logs all pod information for a given namespace.
 | 
						|
func DumpAllPodInfoForNamespace(c clientset.Interface, namespace string) {
 | 
						|
	pods, err := c.CoreV1().Pods(namespace).List(metav1.ListOptions{})
 | 
						|
	if err != nil {
 | 
						|
		e2elog.Logf("unable to fetch pod debug info: %v", err)
 | 
						|
	}
 | 
						|
	LogPodStates(pods.Items)
 | 
						|
	logPodTerminationMessages(pods.Items)
 | 
						|
}
 | 
						|
 | 
						|
// FilterNonRestartablePods filters out pods that will never get recreated if
 | 
						|
// deleted after termination.
 | 
						|
func FilterNonRestartablePods(pods []*v1.Pod) []*v1.Pod {
 | 
						|
	var results []*v1.Pod
 | 
						|
	for _, p := range pods {
 | 
						|
		if isNotRestartAlwaysMirrorPod(p) {
 | 
						|
			// Mirror pods with restart policy == Never will not get
 | 
						|
			// recreated if they are deleted after the pods have
 | 
						|
			// terminated. For now, we discount such pods.
 | 
						|
			// https://github.com/kubernetes/kubernetes/issues/34003
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		results = append(results, p)
 | 
						|
	}
 | 
						|
	return results
 | 
						|
}
 | 
						|
 | 
						|
func isNotRestartAlwaysMirrorPod(p *v1.Pod) bool {
 | 
						|
	if !kubetypes.IsMirrorPod(p) {
 | 
						|
		return false
 | 
						|
	}
 | 
						|
	return p.Spec.RestartPolicy != v1.RestartPolicyAlways
 | 
						|
}
 | 
						|
 | 
						|
// NewExecPodSpec returns the pod spec of hostexec pod
 | 
						|
func NewExecPodSpec(ns, name string, hostNetwork bool) *v1.Pod {
 | 
						|
	immediate := int64(0)
 | 
						|
	pod := &v1.Pod{
 | 
						|
		ObjectMeta: metav1.ObjectMeta{
 | 
						|
			Name:      name,
 | 
						|
			Namespace: ns,
 | 
						|
		},
 | 
						|
		Spec: v1.PodSpec{
 | 
						|
			Containers: []v1.Container{
 | 
						|
				{
 | 
						|
					Name:            "agnhost",
 | 
						|
					Image:           imageutils.GetE2EImage(imageutils.Agnhost),
 | 
						|
					ImagePullPolicy: v1.PullIfNotPresent,
 | 
						|
				},
 | 
						|
			},
 | 
						|
			HostNetwork:                   hostNetwork,
 | 
						|
			SecurityContext:               &v1.PodSecurityContext{},
 | 
						|
			TerminationGracePeriodSeconds: &immediate,
 | 
						|
		},
 | 
						|
	}
 | 
						|
	return pod
 | 
						|
}
 | 
						|
 | 
						|
// newExecPodSpec returns the pod spec of exec pod
 | 
						|
func newExecPodSpec(ns, generateName string) *v1.Pod {
 | 
						|
	immediate := int64(0)
 | 
						|
	pod := &v1.Pod{
 | 
						|
		ObjectMeta: metav1.ObjectMeta{
 | 
						|
			GenerateName: generateName,
 | 
						|
			Namespace:    ns,
 | 
						|
		},
 | 
						|
		Spec: v1.PodSpec{
 | 
						|
			TerminationGracePeriodSeconds: &immediate,
 | 
						|
			Containers: []v1.Container{
 | 
						|
				{
 | 
						|
					Name:  "agnhost-pause",
 | 
						|
					Image: imageutils.GetE2EImage(imageutils.Agnhost),
 | 
						|
					Args:  []string{"pause"},
 | 
						|
				},
 | 
						|
			},
 | 
						|
		},
 | 
						|
	}
 | 
						|
	return pod
 | 
						|
}
 | 
						|
 | 
						|
// CreateExecPodOrFail creates a agnhost pause pod used as a vessel for kubectl exec commands.
 | 
						|
// Pod name is uniquely generated.
 | 
						|
func CreateExecPodOrFail(client clientset.Interface, ns, generateName string, tweak func(*v1.Pod)) *v1.Pod {
 | 
						|
	e2elog.Logf("Creating new exec pod")
 | 
						|
	pod := newExecPodSpec(ns, generateName)
 | 
						|
	if tweak != nil {
 | 
						|
		tweak(pod)
 | 
						|
	}
 | 
						|
	execPod, err := client.CoreV1().Pods(ns).Create(pod)
 | 
						|
	expectNoError(err, "failed to create new exec pod in namespace: %s", ns)
 | 
						|
	err = wait.PollImmediate(poll, 5*time.Minute, func() (bool, error) {
 | 
						|
		retrievedPod, err := client.CoreV1().Pods(execPod.Namespace).Get(execPod.Name, metav1.GetOptions{})
 | 
						|
		if err != nil {
 | 
						|
			if testutils.IsRetryableAPIError(err) {
 | 
						|
				return false, nil
 | 
						|
			}
 | 
						|
			return false, err
 | 
						|
		}
 | 
						|
		return retrievedPod.Status.Phase == v1.PodRunning, nil
 | 
						|
	})
 | 
						|
	expectNoError(err)
 | 
						|
	return execPod
 | 
						|
}
 | 
						|
 | 
						|
// CheckPodsRunningReady returns whether all pods whose names are listed in
 | 
						|
// podNames in namespace ns are running and ready, using c and waiting at most
 | 
						|
// timeout.
 | 
						|
func CheckPodsRunningReady(c clientset.Interface, ns string, podNames []string, timeout time.Duration) bool {
 | 
						|
	return checkPodsCondition(c, ns, podNames, timeout, testutils.PodRunningReady, "running and ready")
 | 
						|
}
 | 
						|
 | 
						|
// CheckPodsRunningReadyOrSucceeded returns whether all pods whose names are
 | 
						|
// listed in podNames in namespace ns are running and ready, or succeeded; use
 | 
						|
// c and waiting at most timeout.
 | 
						|
func CheckPodsRunningReadyOrSucceeded(c clientset.Interface, ns string, podNames []string, timeout time.Duration) bool {
 | 
						|
	return checkPodsCondition(c, ns, podNames, timeout, testutils.PodRunningReadyOrSucceeded, "running and ready, or succeeded")
 | 
						|
}
 | 
						|
 | 
						|
// checkPodsCondition returns whether all pods whose names are listed in podNames
 | 
						|
// in namespace ns are in the condition, using c and waiting at most timeout.
 | 
						|
func checkPodsCondition(c clientset.Interface, ns string, podNames []string, timeout time.Duration, condition podCondition, desc string) bool {
 | 
						|
	np := len(podNames)
 | 
						|
	e2elog.Logf("Waiting up to %v for %d pods to be %s: %s", timeout, np, desc, podNames)
 | 
						|
	type waitPodResult struct {
 | 
						|
		success bool
 | 
						|
		podName string
 | 
						|
	}
 | 
						|
	result := make(chan waitPodResult, len(podNames))
 | 
						|
	for _, podName := range podNames {
 | 
						|
		// Launch off pod readiness checkers.
 | 
						|
		go func(name string) {
 | 
						|
			err := WaitForPodCondition(c, ns, name, desc, timeout, condition)
 | 
						|
			result <- waitPodResult{err == nil, name}
 | 
						|
		}(podName)
 | 
						|
	}
 | 
						|
	// Wait for them all to finish.
 | 
						|
	success := true
 | 
						|
	for range podNames {
 | 
						|
		res := <-result
 | 
						|
		if !res.success {
 | 
						|
			e2elog.Logf("Pod %[1]s failed to be %[2]s.", res.podName, desc)
 | 
						|
			success = false
 | 
						|
		}
 | 
						|
	}
 | 
						|
	e2elog.Logf("Wanted all %d pods to be %s. Result: %t. Pods: %v", np, desc, success, podNames)
 | 
						|
	return success
 | 
						|
}
 | 
						|
 | 
						|
// GetPodLogs returns the logs of the specified container (namespace/pod/container).
 | 
						|
// TODO(random-liu): Change this to be a member function of the framework.
 | 
						|
func GetPodLogs(c clientset.Interface, namespace, podName, containerName string) (string, error) {
 | 
						|
	return getPodLogsInternal(c, namespace, podName, containerName, false)
 | 
						|
}
 | 
						|
 | 
						|
// GetPreviousPodLogs returns the logs of the previous instance of the
 | 
						|
// specified container (namespace/pod/container).
 | 
						|
func GetPreviousPodLogs(c clientset.Interface, namespace, podName, containerName string) (string, error) {
 | 
						|
	return getPodLogsInternal(c, namespace, podName, containerName, true)
 | 
						|
}
 | 
						|
 | 
						|
// utility function for gomega Eventually
 | 
						|
func getPodLogsInternal(c clientset.Interface, namespace, podName, containerName string, previous bool) (string, error) {
 | 
						|
	logs, err := c.CoreV1().RESTClient().Get().
 | 
						|
		Resource("pods").
 | 
						|
		Namespace(namespace).
 | 
						|
		Name(podName).SubResource("log").
 | 
						|
		Param("container", containerName).
 | 
						|
		Param("previous", strconv.FormatBool(previous)).
 | 
						|
		Do().
 | 
						|
		Raw()
 | 
						|
	if err != nil {
 | 
						|
		return "", err
 | 
						|
	}
 | 
						|
	if err == nil && strings.Contains(string(logs), "Internal Error") {
 | 
						|
		return "", fmt.Errorf("Fetched log contains \"Internal Error\": %q", string(logs))
 | 
						|
	}
 | 
						|
	return string(logs), err
 | 
						|
}
 | 
						|
 | 
						|
// GetPodsInNamespace returns the pods in the given namespace.
 | 
						|
func GetPodsInNamespace(c clientset.Interface, ns string, ignoreLabels map[string]string) ([]*v1.Pod, error) {
 | 
						|
	pods, err := c.CoreV1().Pods(ns).List(metav1.ListOptions{})
 | 
						|
	if err != nil {
 | 
						|
		return []*v1.Pod{}, err
 | 
						|
	}
 | 
						|
	ignoreSelector := labels.SelectorFromSet(ignoreLabels)
 | 
						|
	filtered := []*v1.Pod{}
 | 
						|
	for _, p := range pods.Items {
 | 
						|
		if len(ignoreLabels) != 0 && ignoreSelector.Matches(labels.Set(p.Labels)) {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		filtered = append(filtered, &p)
 | 
						|
	}
 | 
						|
	return filtered, nil
 | 
						|
}
 |