mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-03 19:58:17 +00:00 
			
		
		
		
	Move functions from e2e framework util.go
The following functions are called at some specific places only, so this moves these functions to the places and makes them local. - WaitForPersistentVolumeClaimDeleted: Moved to e2e storage - PrintSummaries: Moved to e2e framework.go - GetHostExternalAddress: Moved to e2e node - WaitForMasters: Moved to e2e cloud gcp - WaitForApiserverUp: Moved to e2e network - WaitForKubeletUp: Moved to e2e storage vsphere
This commit is contained in:
		@@ -20,14 +20,18 @@ import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os/exec"
 | 
			
		||||
	"path"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/onsi/ginkgo"
 | 
			
		||||
	v1 "k8s.io/api/core/v1"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	clientset "k8s.io/client-go/kubernetes"
 | 
			
		||||
	"k8s.io/kubernetes/test/e2e/common"
 | 
			
		||||
	"k8s.io/kubernetes/test/e2e/framework"
 | 
			
		||||
	e2enode "k8s.io/kubernetes/test/e2e/framework/node"
 | 
			
		||||
	e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -111,6 +115,48 @@ func removeZoneFromZones(zones []string, zone string) []string {
 | 
			
		||||
	return zones
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// generateMasterRegexp returns a regex for matching master node name.
 | 
			
		||||
func generateMasterRegexp(prefix string) string {
 | 
			
		||||
	return prefix + "(-...)?"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// waitForMasters waits until the cluster has the desired number of ready masters in it.
 | 
			
		||||
func waitForMasters(masterPrefix string, c clientset.Interface, size int, timeout time.Duration) error {
 | 
			
		||||
	for start := time.Now(); time.Since(start) < timeout; time.Sleep(20 * time.Second) {
 | 
			
		||||
		nodes, err := c.CoreV1().Nodes().List(metav1.ListOptions{})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			framework.Logf("Failed to list nodes: %v", err)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Filter out nodes that are not master replicas
 | 
			
		||||
		e2enode.Filter(nodes, func(node v1.Node) bool {
 | 
			
		||||
			res, err := regexp.Match(generateMasterRegexp(masterPrefix), ([]byte)(node.Name))
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				framework.Logf("Failed to match regexp to node name: %v", err)
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
			return res
 | 
			
		||||
		})
 | 
			
		||||
 | 
			
		||||
		numNodes := len(nodes.Items)
 | 
			
		||||
 | 
			
		||||
		// Filter out not-ready nodes.
 | 
			
		||||
		e2enode.Filter(nodes, func(node v1.Node) bool {
 | 
			
		||||
			return e2enode.IsConditionSetAsExpected(&node, v1.NodeReady, true)
 | 
			
		||||
		})
 | 
			
		||||
 | 
			
		||||
		numReady := len(nodes.Items)
 | 
			
		||||
 | 
			
		||||
		if numNodes == size && numReady == size {
 | 
			
		||||
			framework.Logf("Cluster has reached the desired number of masters %d", size)
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		framework.Logf("Waiting for the number of masters %d, current %d, not ready master nodes %d", size, numNodes, numNodes-numReady)
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Errorf("timeout waiting %v for the number of masters to be %d", timeout, size)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var _ = SIGDescribe("HA-master [Feature:HAMaster]", func() {
 | 
			
		||||
	f := framework.NewDefaultFramework("ha-master")
 | 
			
		||||
	var c clientset.Interface
 | 
			
		||||
@@ -123,7 +169,7 @@ var _ = SIGDescribe("HA-master [Feature:HAMaster]", func() {
 | 
			
		||||
		framework.SkipUnlessProviderIs("gce")
 | 
			
		||||
		c = f.ClientSet
 | 
			
		||||
		ns = f.Namespace.Name
 | 
			
		||||
		framework.ExpectNoError(framework.WaitForMasters(framework.TestContext.CloudConfig.MasterName, c, 1, 10*time.Minute))
 | 
			
		||||
		framework.ExpectNoError(waitForMasters(framework.TestContext.CloudConfig.MasterName, c, 1, 10*time.Minute))
 | 
			
		||||
		additionalReplicaZones = make([]string, 0)
 | 
			
		||||
		existingRCs = make([]string, 0)
 | 
			
		||||
	})
 | 
			
		||||
@@ -139,7 +185,7 @@ var _ = SIGDescribe("HA-master [Feature:HAMaster]", func() {
 | 
			
		||||
		for _, zone := range additionalReplicaZones {
 | 
			
		||||
			removeMasterReplica(zone)
 | 
			
		||||
		}
 | 
			
		||||
		framework.ExpectNoError(framework.WaitForMasters(framework.TestContext.CloudConfig.MasterName, c, 1, 10*time.Minute))
 | 
			
		||||
		framework.ExpectNoError(waitForMasters(framework.TestContext.CloudConfig.MasterName, c, 1, 10*time.Minute))
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	type Action int
 | 
			
		||||
@@ -167,7 +213,7 @@ var _ = SIGDescribe("HA-master [Feature:HAMaster]", func() {
 | 
			
		||||
			framework.ExpectNoError(removeWorkerNodes(zone))
 | 
			
		||||
			additionalNodesZones = removeZoneFromZones(additionalNodesZones, zone)
 | 
			
		||||
		}
 | 
			
		||||
		framework.ExpectNoError(framework.WaitForMasters(framework.TestContext.CloudConfig.MasterName, c, len(additionalReplicaZones)+1, 10*time.Minute))
 | 
			
		||||
		framework.ExpectNoError(waitForMasters(framework.TestContext.CloudConfig.MasterName, c, len(additionalReplicaZones)+1, 10*time.Minute))
 | 
			
		||||
		framework.ExpectNoError(framework.AllNodesReady(c, 5*time.Minute))
 | 
			
		||||
 | 
			
		||||
		// Verify that API server works correctly with HA master.
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,9 @@ package framework
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"math/rand"
 | 
			
		||||
	"path"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
@@ -276,6 +278,43 @@ func (f *Framework) BeforeEach() {
 | 
			
		||||
	f.flakeReport = NewFlakeReport()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// printSummaries prints summaries of tests.
 | 
			
		||||
func printSummaries(summaries []TestDataSummary, testBaseName string) {
 | 
			
		||||
	now := time.Now()
 | 
			
		||||
	for i := range summaries {
 | 
			
		||||
		Logf("Printing summary: %v", summaries[i].SummaryKind())
 | 
			
		||||
		switch TestContext.OutputPrintType {
 | 
			
		||||
		case "hr":
 | 
			
		||||
			if TestContext.ReportDir == "" {
 | 
			
		||||
				Logf(summaries[i].PrintHumanReadable())
 | 
			
		||||
			} else {
 | 
			
		||||
				// TODO: learn to extract test name and append it to the kind instead of timestamp.
 | 
			
		||||
				filePath := path.Join(TestContext.ReportDir, summaries[i].SummaryKind()+"_"+testBaseName+"_"+now.Format(time.RFC3339)+".txt")
 | 
			
		||||
				if err := ioutil.WriteFile(filePath, []byte(summaries[i].PrintHumanReadable()), 0644); err != nil {
 | 
			
		||||
					Logf("Failed to write file %v with test performance data: %v", filePath, err)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		case "json":
 | 
			
		||||
			fallthrough
 | 
			
		||||
		default:
 | 
			
		||||
			if TestContext.OutputPrintType != "json" {
 | 
			
		||||
				Logf("Unknown output type: %v. Printing JSON", TestContext.OutputPrintType)
 | 
			
		||||
			}
 | 
			
		||||
			if TestContext.ReportDir == "" {
 | 
			
		||||
				Logf("%v JSON\n%v", summaries[i].SummaryKind(), summaries[i].PrintJSON())
 | 
			
		||||
				Logf("Finished")
 | 
			
		||||
			} else {
 | 
			
		||||
				// TODO: learn to extract test name and append it to the kind instead of timestamp.
 | 
			
		||||
				filePath := path.Join(TestContext.ReportDir, summaries[i].SummaryKind()+"_"+testBaseName+"_"+now.Format(time.RFC3339)+".json")
 | 
			
		||||
				Logf("Writing to %s", filePath)
 | 
			
		||||
				if err := ioutil.WriteFile(filePath, []byte(summaries[i].PrintJSON()), 0644); err != nil {
 | 
			
		||||
					Logf("Failed to write file %v with test performance data: %v", filePath, err)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AfterEach deletes the namespace, after reading its events.
 | 
			
		||||
func (f *Framework) AfterEach() {
 | 
			
		||||
	RemoveCleanupAction(f.cleanupHandle)
 | 
			
		||||
@@ -368,7 +407,7 @@ func (f *Framework) AfterEach() {
 | 
			
		||||
		f.flakeReport = nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	PrintSummaries(f.TestSummaries, f.BaseName)
 | 
			
		||||
	printSummaries(f.TestSummaries, f.BaseName)
 | 
			
		||||
 | 
			
		||||
	// Check whether all nodes are ready after the test.
 | 
			
		||||
	// This is explicitly done at the very end of the test, to avoid
 | 
			
		||||
 
 | 
			
		||||
@@ -31,7 +31,6 @@ import (
 | 
			
		||||
	"os/exec"
 | 
			
		||||
	"path"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
@@ -1640,21 +1639,6 @@ func RestartKubelet(host string) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WaitForKubeletUp waits for the kubelet on the given host to be up.
 | 
			
		||||
func WaitForKubeletUp(host string) error {
 | 
			
		||||
	cmd := "curl http://localhost:" + strconv.Itoa(ports.KubeletReadOnlyPort) + "/healthz"
 | 
			
		||||
	for start := time.Now(); time.Since(start) < time.Minute; time.Sleep(5 * time.Second) {
 | 
			
		||||
		result, err := e2essh.SSH(cmd, host, TestContext.Provider)
 | 
			
		||||
		if err != nil || result.Code != 0 {
 | 
			
		||||
			e2essh.LogResult(result)
 | 
			
		||||
		}
 | 
			
		||||
		if result.Stdout == "ok" {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Errorf("waiting for kubelet timed out")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RestartApiserver restarts the kube-apiserver.
 | 
			
		||||
func RestartApiserver(cs clientset.Interface) error {
 | 
			
		||||
	// TODO: Make it work for all providers.
 | 
			
		||||
@@ -1699,17 +1683,6 @@ func sshRestartMaster() error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WaitForApiserverUp waits for the kube-apiserver to be up.
 | 
			
		||||
func WaitForApiserverUp(c clientset.Interface) error {
 | 
			
		||||
	for start := time.Now(); time.Since(start) < time.Minute; time.Sleep(5 * time.Second) {
 | 
			
		||||
		body, err := c.CoreV1().RESTClient().Get().AbsPath("/healthz").Do().Raw()
 | 
			
		||||
		if err == nil && string(body) == "ok" {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Errorf("waiting for apiserver timed out")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// waitForApiserverRestarted waits until apiserver's restart count increased.
 | 
			
		||||
func waitForApiserverRestarted(c clientset.Interface, initialRestartCount int32) error {
 | 
			
		||||
	for start := time.Now(); time.Since(start) < time.Minute; time.Sleep(5 * time.Second) {
 | 
			
		||||
@@ -1780,101 +1753,6 @@ func WaitForControllerManagerUp() error {
 | 
			
		||||
	return fmt.Errorf("waiting for controller-manager timed out")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GenerateMasterRegexp returns a regex for matching master node name.
 | 
			
		||||
func GenerateMasterRegexp(prefix string) string {
 | 
			
		||||
	return prefix + "(-...)?"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WaitForMasters waits until the cluster has the desired number of ready masters in it.
 | 
			
		||||
func WaitForMasters(masterPrefix string, c clientset.Interface, size int, timeout time.Duration) error {
 | 
			
		||||
	for start := time.Now(); time.Since(start) < timeout; time.Sleep(20 * time.Second) {
 | 
			
		||||
		nodes, err := c.CoreV1().Nodes().List(metav1.ListOptions{})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			Logf("Failed to list nodes: %v", err)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Filter out nodes that are not master replicas
 | 
			
		||||
		e2enode.Filter(nodes, func(node v1.Node) bool {
 | 
			
		||||
			res, err := regexp.Match(GenerateMasterRegexp(masterPrefix), ([]byte)(node.Name))
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				Logf("Failed to match regexp to node name: %v", err)
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
			return res
 | 
			
		||||
		})
 | 
			
		||||
 | 
			
		||||
		numNodes := len(nodes.Items)
 | 
			
		||||
 | 
			
		||||
		// Filter out not-ready nodes.
 | 
			
		||||
		e2enode.Filter(nodes, func(node v1.Node) bool {
 | 
			
		||||
			return e2enode.IsConditionSetAsExpected(&node, v1.NodeReady, true)
 | 
			
		||||
		})
 | 
			
		||||
 | 
			
		||||
		numReady := len(nodes.Items)
 | 
			
		||||
 | 
			
		||||
		if numNodes == size && numReady == size {
 | 
			
		||||
			Logf("Cluster has reached the desired number of masters %d", size)
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		Logf("Waiting for the number of masters %d, current %d, not ready master nodes %d", size, numNodes, numNodes-numReady)
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Errorf("timeout waiting %v for the number of masters to be %d", timeout, size)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetHostExternalAddress gets the node for a pod and returns the first External
 | 
			
		||||
// address. Returns an error if the node the pod is on doesn't have an External
 | 
			
		||||
// address.
 | 
			
		||||
func GetHostExternalAddress(client clientset.Interface, p *v1.Pod) (externalAddress string, err error) {
 | 
			
		||||
	node, err := client.CoreV1().Nodes().Get(p.Spec.NodeName, metav1.GetOptions{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	for _, address := range node.Status.Addresses {
 | 
			
		||||
		if address.Type == v1.NodeExternalIP {
 | 
			
		||||
			if address.Address != "" {
 | 
			
		||||
				externalAddress = address.Address
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if externalAddress == "" {
 | 
			
		||||
		err = fmt.Errorf("No external address for pod %v on node %v",
 | 
			
		||||
			p.Name, p.Spec.NodeName)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetHostAddress gets the node for a pod and returns the first
 | 
			
		||||
// address. Returns an error if the node the pod is on doesn't have an
 | 
			
		||||
// address.
 | 
			
		||||
func GetHostAddress(client clientset.Interface, p *v1.Pod) (string, error) {
 | 
			
		||||
	node, err := client.CoreV1().Nodes().Get(p.Spec.NodeName, metav1.GetOptions{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	// Try externalAddress first
 | 
			
		||||
	for _, address := range node.Status.Addresses {
 | 
			
		||||
		if address.Type == v1.NodeExternalIP {
 | 
			
		||||
			if address.Address != "" {
 | 
			
		||||
				return address.Address, nil
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// If no externalAddress found, try internalAddress
 | 
			
		||||
	for _, address := range node.Status.Addresses {
 | 
			
		||||
		if address.Type == v1.NodeInternalIP {
 | 
			
		||||
			if address.Address != "" {
 | 
			
		||||
				return address.Address, nil
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If not found, return error
 | 
			
		||||
	return "", fmt.Errorf("No address for pod %v on node %v",
 | 
			
		||||
		p.Name, p.Spec.NodeName)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type extractRT struct {
 | 
			
		||||
	http.Header
 | 
			
		||||
}
 | 
			
		||||
@@ -2236,43 +2114,6 @@ func CreateEmptyFileOnPod(namespace string, podName string, filePath string) err
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PrintSummaries prints summaries of tests.
 | 
			
		||||
func PrintSummaries(summaries []TestDataSummary, testBaseName string) {
 | 
			
		||||
	now := time.Now()
 | 
			
		||||
	for i := range summaries {
 | 
			
		||||
		Logf("Printing summary: %v", summaries[i].SummaryKind())
 | 
			
		||||
		switch TestContext.OutputPrintType {
 | 
			
		||||
		case "hr":
 | 
			
		||||
			if TestContext.ReportDir == "" {
 | 
			
		||||
				Logf(summaries[i].PrintHumanReadable())
 | 
			
		||||
			} else {
 | 
			
		||||
				// TODO: learn to extract test name and append it to the kind instead of timestamp.
 | 
			
		||||
				filePath := path.Join(TestContext.ReportDir, summaries[i].SummaryKind()+"_"+testBaseName+"_"+now.Format(time.RFC3339)+".txt")
 | 
			
		||||
				if err := ioutil.WriteFile(filePath, []byte(summaries[i].PrintHumanReadable()), 0644); err != nil {
 | 
			
		||||
					Logf("Failed to write file %v with test performance data: %v", filePath, err)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		case "json":
 | 
			
		||||
			fallthrough
 | 
			
		||||
		default:
 | 
			
		||||
			if TestContext.OutputPrintType != "json" {
 | 
			
		||||
				Logf("Unknown output type: %v. Printing JSON", TestContext.OutputPrintType)
 | 
			
		||||
			}
 | 
			
		||||
			if TestContext.ReportDir == "" {
 | 
			
		||||
				Logf("%v JSON\n%v", summaries[i].SummaryKind(), summaries[i].PrintJSON())
 | 
			
		||||
				Logf("Finished")
 | 
			
		||||
			} else {
 | 
			
		||||
				// TODO: learn to extract test name and append it to the kind instead of timestamp.
 | 
			
		||||
				filePath := path.Join(TestContext.ReportDir, summaries[i].SummaryKind()+"_"+testBaseName+"_"+now.Format(time.RFC3339)+".json")
 | 
			
		||||
				Logf("Writing to %s", filePath)
 | 
			
		||||
				if err := ioutil.WriteFile(filePath, []byte(summaries[i].PrintJSON()), 0644); err != nil {
 | 
			
		||||
					Logf("Failed to write file %v with test performance data: %v", filePath, err)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DumpDebugInfo dumps debug info of tests.
 | 
			
		||||
func DumpDebugInfo(c clientset.Interface, ns string) {
 | 
			
		||||
	sl, _ := c.CoreV1().Pods(ns).List(metav1.ListOptions{LabelSelector: labels.Everything().String()})
 | 
			
		||||
@@ -2326,22 +2167,6 @@ func DsFromManifest(url string) (*appsv1.DaemonSet, error) {
 | 
			
		||||
	return &ds, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WaitForPersistentVolumeClaimDeleted waits for a PersistentVolumeClaim to be removed from the system until timeout occurs, whichever comes first.
 | 
			
		||||
func WaitForPersistentVolumeClaimDeleted(c clientset.Interface, ns string, pvcName string, Poll, timeout time.Duration) error {
 | 
			
		||||
	Logf("Waiting up to %v for PersistentVolumeClaim %s to be removed", timeout, pvcName)
 | 
			
		||||
	for start := time.Now(); time.Since(start) < timeout; time.Sleep(Poll) {
 | 
			
		||||
		_, err := c.CoreV1().PersistentVolumeClaims(ns).Get(pvcName, metav1.GetOptions{})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if apierrs.IsNotFound(err) {
 | 
			
		||||
				Logf("Claim %q in namespace %q doesn't exist in the system", pvcName, ns)
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
			Logf("Failed to get claim %q in namespace %q, retrying in %v. Error: %v", pvcName, ns, Poll, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Errorf("PersistentVolumeClaim %s is not removed from the system within %v", pvcName, timeout)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetClusterZones returns the values of zone label collected from all nodes.
 | 
			
		||||
func GetClusterZones(c clientset.Interface) (sets.String, error) {
 | 
			
		||||
	nodes, err := c.CoreV1().Nodes().List(metav1.ListOptions{})
 | 
			
		||||
 
 | 
			
		||||
@@ -120,6 +120,17 @@ func restartKubeProxy(host string) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// waitForApiserverUp waits for the kube-apiserver to be up.
 | 
			
		||||
func waitForApiserverUp(c clientset.Interface) error {
 | 
			
		||||
	for start := time.Now(); time.Since(start) < time.Minute; time.Sleep(5 * time.Second) {
 | 
			
		||||
		body, err := c.CoreV1().RESTClient().Get().AbsPath("/healthz").Do().Raw()
 | 
			
		||||
		if err == nil && string(body) == "ok" {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Errorf("waiting for apiserver timed out")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var _ = SIGDescribe("Services", func() {
 | 
			
		||||
	f := framework.NewDefaultFramework("services")
 | 
			
		||||
 | 
			
		||||
@@ -542,7 +553,7 @@ var _ = SIGDescribe("Services", func() {
 | 
			
		||||
			framework.Failf("error restarting apiserver: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
		ginkgo.By("Waiting for apiserver to come up by polling /healthz")
 | 
			
		||||
		if err := framework.WaitForApiserverUp(cs); err != nil {
 | 
			
		||||
		if err := waitForApiserverUp(cs); err != nil {
 | 
			
		||||
			framework.Failf("error while waiting for apiserver up: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
		framework.ExpectNoError(e2eservice.VerifyServeHostnameServiceUp(cs, ns, host, podNames1, svc1IP, servicePort))
 | 
			
		||||
 
 | 
			
		||||
@@ -177,6 +177,29 @@ func createPodUsingNfs(f *framework.Framework, c clientset.Interface, ns, nfsIP,
 | 
			
		||||
	return rtnPod
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// getHostExternalAddress gets the node for a pod and returns the first External
 | 
			
		||||
// address. Returns an error if the node the pod is on doesn't have an External
 | 
			
		||||
// address.
 | 
			
		||||
func getHostExternalAddress(client clientset.Interface, p *v1.Pod) (externalAddress string, err error) {
 | 
			
		||||
	node, err := client.CoreV1().Nodes().Get(p.Spec.NodeName, metav1.GetOptions{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	for _, address := range node.Status.Addresses {
 | 
			
		||||
		if address.Type == v1.NodeExternalIP {
 | 
			
		||||
			if address.Address != "" {
 | 
			
		||||
				externalAddress = address.Address
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if externalAddress == "" {
 | 
			
		||||
		err = fmt.Errorf("No external address for pod %v on node %v",
 | 
			
		||||
			p.Name, p.Spec.NodeName)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Checks for a lingering nfs mount and/or uid directory on the pod's host. The host IP is used
 | 
			
		||||
// so that this test runs in GCE, where it appears that SSH cannot resolve the hostname.
 | 
			
		||||
// If expectClean is true then we expect the node to be cleaned up and thus commands like
 | 
			
		||||
@@ -189,7 +212,7 @@ func checkPodCleanup(c clientset.Interface, pod *v1.Pod, expectClean bool) {
 | 
			
		||||
	podDir := filepath.Join("/var/lib/kubelet/pods", string(pod.UID))
 | 
			
		||||
	mountDir := filepath.Join(podDir, "volumes", "kubernetes.io~nfs")
 | 
			
		||||
	// use ip rather than hostname in GCE
 | 
			
		||||
	nodeIP, err := framework.GetHostExternalAddress(c, pod)
 | 
			
		||||
	nodeIP, err := getHostExternalAddress(c, pod)
 | 
			
		||||
	framework.ExpectNoError(err)
 | 
			
		||||
 | 
			
		||||
	condMsg := "deleted"
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,11 @@ import (
 | 
			
		||||
	"github.com/onsi/ginkgo"
 | 
			
		||||
	"github.com/onsi/gomega"
 | 
			
		||||
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"k8s.io/api/core/v1"
 | 
			
		||||
	apierrs "k8s.io/apimachinery/pkg/api/errors"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	clientset "k8s.io/client-go/kubernetes"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/util/slice"
 | 
			
		||||
@@ -32,6 +36,22 @@ import (
 | 
			
		||||
	"k8s.io/kubernetes/test/e2e/storage/utils"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// waitForPersistentVolumeClaimDeleted waits for a PersistentVolumeClaim to be removed from the system until timeout occurs, whichever comes first.
 | 
			
		||||
func waitForPersistentVolumeClaimDeleted(c clientset.Interface, ns string, pvcName string, Poll, timeout time.Duration) error {
 | 
			
		||||
	framework.Logf("Waiting up to %v for PersistentVolumeClaim %s to be removed", timeout, pvcName)
 | 
			
		||||
	for start := time.Now(); time.Since(start) < timeout; time.Sleep(Poll) {
 | 
			
		||||
		_, err := c.CoreV1().PersistentVolumeClaims(ns).Get(pvcName, metav1.GetOptions{})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if apierrs.IsNotFound(err) {
 | 
			
		||||
				framework.Logf("Claim %q in namespace %q doesn't exist in the system", pvcName, ns)
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
			framework.Logf("Failed to get claim %q in namespace %q, retrying in %v. Error: %v", pvcName, ns, Poll, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Errorf("PersistentVolumeClaim %s is not removed from the system within %v", pvcName, timeout)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var _ = utils.SIGDescribe("PVC Protection", func() {
 | 
			
		||||
	var (
 | 
			
		||||
		client                  clientset.Interface
 | 
			
		||||
@@ -92,7 +112,7 @@ var _ = utils.SIGDescribe("PVC Protection", func() {
 | 
			
		||||
		ginkgo.By("Deleting the PVC")
 | 
			
		||||
		err = client.CoreV1().PersistentVolumeClaims(pvc.Namespace).Delete(pvc.Name, metav1.NewDeleteOptions(0))
 | 
			
		||||
		framework.ExpectNoError(err, "Error deleting PVC")
 | 
			
		||||
		framework.WaitForPersistentVolumeClaimDeleted(client, pvc.Namespace, pvc.Name, framework.Poll, framework.ClaimDeletingTimeout)
 | 
			
		||||
		waitForPersistentVolumeClaimDeleted(client, pvc.Namespace, pvc.Name, framework.Poll, framework.ClaimDeletingTimeout)
 | 
			
		||||
		pvcCreatedAndNotDeleted = false
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
@@ -111,7 +131,7 @@ var _ = utils.SIGDescribe("PVC Protection", func() {
 | 
			
		||||
		framework.ExpectNoError(err, "Error terminating and deleting pod")
 | 
			
		||||
 | 
			
		||||
		ginkgo.By("Checking that the PVC is automatically removed from the system because it's no longer in active use by a pod")
 | 
			
		||||
		framework.WaitForPersistentVolumeClaimDeleted(client, pvc.Namespace, pvc.Name, framework.Poll, framework.ClaimDeletingTimeout)
 | 
			
		||||
		waitForPersistentVolumeClaimDeleted(client, pvc.Namespace, pvc.Name, framework.Poll, framework.ClaimDeletingTimeout)
 | 
			
		||||
		pvcCreatedAndNotDeleted = false
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
@@ -143,7 +163,7 @@ var _ = utils.SIGDescribe("PVC Protection", func() {
 | 
			
		||||
		framework.ExpectNoError(err, "Error terminating and deleting pod")
 | 
			
		||||
 | 
			
		||||
		ginkgo.By("Checking that the PVC is automatically removed from the system because it's no longer in active use by a pod")
 | 
			
		||||
		framework.WaitForPersistentVolumeClaimDeleted(client, pvc.Namespace, pvc.Name, framework.Poll, framework.ClaimDeletingTimeout)
 | 
			
		||||
		waitForPersistentVolumeClaimDeleted(client, pvc.Namespace, pvc.Name, framework.Poll, framework.ClaimDeletingTimeout)
 | 
			
		||||
		pvcCreatedAndNotDeleted = false
 | 
			
		||||
	})
 | 
			
		||||
})
 | 
			
		||||
 
 | 
			
		||||
@@ -112,6 +112,36 @@ func isSudoPresent(nodeIP string, provider string) bool {
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// getHostAddress gets the node for a pod and returns the first
 | 
			
		||||
// address. Returns an error if the node the pod is on doesn't have an
 | 
			
		||||
// address.
 | 
			
		||||
func getHostAddress(client clientset.Interface, p *v1.Pod) (string, error) {
 | 
			
		||||
	node, err := client.CoreV1().Nodes().Get(p.Spec.NodeName, metav1.GetOptions{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	// Try externalAddress first
 | 
			
		||||
	for _, address := range node.Status.Addresses {
 | 
			
		||||
		if address.Type == v1.NodeExternalIP {
 | 
			
		||||
			if address.Address != "" {
 | 
			
		||||
				return address.Address, nil
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// If no externalAddress found, try internalAddress
 | 
			
		||||
	for _, address := range node.Status.Addresses {
 | 
			
		||||
		if address.Type == v1.NodeInternalIP {
 | 
			
		||||
			if address.Address != "" {
 | 
			
		||||
				return address.Address, nil
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If not found, return error
 | 
			
		||||
	return "", fmt.Errorf("No address for pod %v on node %v",
 | 
			
		||||
		p.Name, p.Spec.NodeName)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// KubeletCommand performs `start`, `restart`, or `stop` on the kubelet running on the node of the target pod and waits
 | 
			
		||||
// for the desired statues..
 | 
			
		||||
// - First issues the command via `systemctl`
 | 
			
		||||
@@ -123,7 +153,7 @@ func KubeletCommand(kOp KubeletOpt, c clientset.Interface, pod *v1.Pod) {
 | 
			
		||||
	systemctlPresent := false
 | 
			
		||||
	kubeletPid := ""
 | 
			
		||||
 | 
			
		||||
	nodeIP, err := framework.GetHostAddress(c, pod)
 | 
			
		||||
	nodeIP, err := getHostAddress(c, pod)
 | 
			
		||||
	framework.ExpectNoError(err)
 | 
			
		||||
	nodeIP = nodeIP + ":22"
 | 
			
		||||
 | 
			
		||||
@@ -239,7 +269,7 @@ func TestKubeletRestartsAndRestoresMap(c clientset.Interface, f *framework.Frame
 | 
			
		||||
// forceDelete is true indicating whether the pod is forcefully deleted.
 | 
			
		||||
// checkSubpath is true indicating whether the subpath should be checked.
 | 
			
		||||
func TestVolumeUnmountsFromDeletedPodWithForceOption(c clientset.Interface, f *framework.Framework, clientPod *v1.Pod, forceDelete bool, checkSubpath bool) {
 | 
			
		||||
	nodeIP, err := framework.GetHostAddress(c, clientPod)
 | 
			
		||||
	nodeIP, err := getHostAddress(c, clientPod)
 | 
			
		||||
	framework.ExpectNoError(err)
 | 
			
		||||
	nodeIP = nodeIP + ":22"
 | 
			
		||||
 | 
			
		||||
@@ -315,7 +345,7 @@ func TestVolumeUnmountsFromForceDeletedPod(c clientset.Interface, f *framework.F
 | 
			
		||||
// TestVolumeUnmapsFromDeletedPodWithForceOption tests that a volume unmaps if the client pod was deleted while the kubelet was down.
 | 
			
		||||
// forceDelete is true indicating whether the pod is forcefully deleted.
 | 
			
		||||
func TestVolumeUnmapsFromDeletedPodWithForceOption(c clientset.Interface, f *framework.Framework, clientPod *v1.Pod, forceDelete bool) {
 | 
			
		||||
	nodeIP, err := framework.GetHostAddress(c, clientPod)
 | 
			
		||||
	nodeIP, err := getHostAddress(c, clientPod)
 | 
			
		||||
	framework.ExpectNoError(err, "Failed to get nodeIP.")
 | 
			
		||||
	nodeIP = nodeIP + ":22"
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -40,6 +40,7 @@ go_library(
 | 
			
		||||
    importpath = "k8s.io/kubernetes/test/e2e/storage/vsphere",
 | 
			
		||||
    deps = [
 | 
			
		||||
        "//pkg/controller/volume/events:go_default_library",
 | 
			
		||||
        "//pkg/master/ports:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/api/apps/v1:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/api/core/v1:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/api/storage/v1:go_default_library",
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,8 @@ package vsphere
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/onsi/ginkgo"
 | 
			
		||||
	"github.com/onsi/gomega"
 | 
			
		||||
@@ -26,12 +28,29 @@ import (
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/util/uuid"
 | 
			
		||||
	clientset "k8s.io/client-go/kubernetes"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/master/ports"
 | 
			
		||||
	"k8s.io/kubernetes/test/e2e/framework"
 | 
			
		||||
	e2enode "k8s.io/kubernetes/test/e2e/framework/node"
 | 
			
		||||
	e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
 | 
			
		||||
	e2essh "k8s.io/kubernetes/test/e2e/framework/ssh"
 | 
			
		||||
	"k8s.io/kubernetes/test/e2e/storage/utils"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// waitForKubeletUp waits for the kubelet on the given host to be up.
 | 
			
		||||
func waitForKubeletUp(host string) error {
 | 
			
		||||
	cmd := "curl http://localhost:" + strconv.Itoa(ports.KubeletReadOnlyPort) + "/healthz"
 | 
			
		||||
	for start := time.Now(); time.Since(start) < time.Minute; time.Sleep(5 * time.Second) {
 | 
			
		||||
		result, err := e2essh.SSH(cmd, host, framework.TestContext.Provider)
 | 
			
		||||
		if err != nil || result.Code != 0 {
 | 
			
		||||
			e2essh.LogResult(result)
 | 
			
		||||
		}
 | 
			
		||||
		if result.Stdout == "ok" {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Errorf("waiting for kubelet timed out")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
	Test to verify volume remains attached after kubelet restart on master node
 | 
			
		||||
	For the number of schedulable nodes,
 | 
			
		||||
@@ -117,7 +136,7 @@ var _ = utils.SIGDescribe("Volume Attach Verify [Feature:vsphere][Serial][Disrup
 | 
			
		||||
		framework.ExpectNoError(err, "Unable to restart kubelet on master node")
 | 
			
		||||
 | 
			
		||||
		ginkgo.By("Verifying the kubelet on master node is up")
 | 
			
		||||
		err = framework.WaitForKubeletUp(masterAddress)
 | 
			
		||||
		err = waitForKubeletUp(masterAddress)
 | 
			
		||||
		framework.ExpectNoError(err)
 | 
			
		||||
 | 
			
		||||
		for i, pod := range pods {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user