mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-20 17:16:18 +00:00
Merge pull request #20426 from thockin/service-e2e
Service e2e cleanup and factoring
This commit is contained in:
commit
351138372e
@ -162,7 +162,7 @@ var _ = Describe("Upgrade [Feature:Upgrade]", func() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
f := NewFramework("cluster-upgrade")
|
f := NewFramework("cluster-upgrade")
|
||||||
var w *ServerTest
|
var w *ServiceTestFixture
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
By("Setting up the service, RC, and pods")
|
By("Setting up the service, RC, and pods")
|
||||||
w = NewServerTest(f.Client, f.Namespace.Name, svcName)
|
w = NewServerTest(f.Client, f.Namespace.Name, svcName)
|
||||||
@ -610,3 +610,57 @@ func migRollingUpdatePoll(id string, nt time.Duration) error {
|
|||||||
Logf("MIG rolling update complete after %v", time.Since(start))
|
Logf("MIG rolling update complete after %v", time.Since(start))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testLoadBalancerReachable(ingress api.LoadBalancerIngress, port int) bool {
|
||||||
|
return testLoadBalancerReachableInTime(ingress, port, loadBalancerLagTimeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testLoadBalancerReachableInTime(ingress api.LoadBalancerIngress, port int, timeout time.Duration) bool {
|
||||||
|
ip := ingress.IP
|
||||||
|
if ip == "" {
|
||||||
|
ip = ingress.Hostname
|
||||||
|
}
|
||||||
|
|
||||||
|
return testReachableInTime(conditionFuncDecorator(ip, port, testReachableHTTP, "/", "test-webserver"), timeout)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func conditionFuncDecorator(ip string, port int, fn func(string, int, string, string) (bool, error), request string, expect string) wait.ConditionFunc {
|
||||||
|
return func() (bool, error) {
|
||||||
|
return fn(ip, port, request, expect)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testReachableInTime(testFunc wait.ConditionFunc, timeout time.Duration) bool {
|
||||||
|
By(fmt.Sprintf("Waiting up to %v", timeout))
|
||||||
|
err := wait.PollImmediate(poll, timeout, testFunc)
|
||||||
|
if err != nil {
|
||||||
|
Expect(err).NotTo(HaveOccurred(), "Error waiting")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func waitForLoadBalancerIngress(c *client.Client, serviceName, namespace string) (*api.Service, error) {
|
||||||
|
// TODO: once support ticket 21807001 is resolved, reduce this timeout
|
||||||
|
// back to something reasonable
|
||||||
|
const timeout = 20 * time.Minute
|
||||||
|
var service *api.Service
|
||||||
|
By(fmt.Sprintf("waiting up to %v for service %s in namespace %s to have a LoadBalancer ingress point", timeout, serviceName, namespace))
|
||||||
|
i := 1
|
||||||
|
for start := time.Now(); time.Since(start) < timeout; time.Sleep(3 * time.Second) {
|
||||||
|
service, err := c.Services(namespace).Get(serviceName)
|
||||||
|
if err != nil {
|
||||||
|
Logf("Get service failed, ignoring for 5s: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if len(service.Status.LoadBalancer.Ingress) > 0 {
|
||||||
|
return service, nil
|
||||||
|
}
|
||||||
|
if i%5 == 0 {
|
||||||
|
Logf("Waiting for service %s in namespace %s to have a LoadBalancer ingress point (%v)", serviceName, namespace, time.Since(start))
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return service, fmt.Errorf("service %s in namespace %s doesn't have a LoadBalancer ingress point after %.2f seconds", serviceName, namespace, timeout.Seconds())
|
||||||
|
}
|
||||||
|
@ -18,16 +18,17 @@ package e2e
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
api "k8s.io/kubernetes/pkg/api"
|
api "k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||||
"k8s.io/kubernetes/pkg/apimachinery/registered"
|
"k8s.io/kubernetes/pkg/apimachinery/registered"
|
||||||
client "k8s.io/kubernetes/pkg/client/unversioned"
|
client "k8s.io/kubernetes/pkg/client/unversioned"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
kubeletEtcHostsImageName = "gcr.io/google_containers/netexec:1.0"
|
kubeletEtcHostsImageName = "gcr.io/google_containers/netexec:1.4"
|
||||||
kubeletEtcHostsPodName = "test-pod"
|
kubeletEtcHostsPodName = "test-pod"
|
||||||
kubeletEtcHostsHostNetworkPodName = "test-host-network-pod"
|
kubeletEtcHostsHostNetworkPodName = "test-host-network-pod"
|
||||||
etcHostsPartialContent = "# Kubernetes-managed hosts file."
|
etcHostsPartialContent = "# Kubernetes-managed hosts file."
|
||||||
|
@ -46,7 +46,7 @@ const (
|
|||||||
nodeHttpPort = 32080
|
nodeHttpPort = 32080
|
||||||
nodeUdpPort = 32081
|
nodeUdpPort = 32081
|
||||||
loadBalancerHttpPort = 100
|
loadBalancerHttpPort = 100
|
||||||
netexecImageName = "gcr.io/google_containers/netexec:1.0"
|
netexecImageName = "gcr.io/google_containers/netexec:1.4"
|
||||||
testPodName = "test-container-pod"
|
testPodName = "test-container-pod"
|
||||||
hostTestPodName = "host-test-container-pod"
|
hostTestPodName = "host-test-container-pod"
|
||||||
nodePortServiceName = "node-port-service"
|
nodePortServiceName = "node-port-service"
|
||||||
|
@ -19,13 +19,14 @@ package e2e
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||||
"k8s.io/kubernetes/pkg/apimachinery/registered"
|
"k8s.io/kubernetes/pkg/apimachinery/registered"
|
||||||
client "k8s.io/kubernetes/pkg/client/unversioned"
|
client "k8s.io/kubernetes/pkg/client/unversioned"
|
||||||
"net/url"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -36,7 +37,7 @@ const (
|
|||||||
notPrivilegedHttpPort = 9090
|
notPrivilegedHttpPort = 9090
|
||||||
notPrivilegedUdpPort = 9091
|
notPrivilegedUdpPort = 9091
|
||||||
notPrivilegedContainerName = "not-privileged-container"
|
notPrivilegedContainerName = "not-privileged-container"
|
||||||
privilegedContainerImage = "gcr.io/google_containers/netexec:1.1"
|
privilegedContainerImage = "gcr.io/google_containers/netexec:1.4"
|
||||||
privilegedCommand = "ip link add dummy1 type dummy"
|
privilegedCommand = "ip link add dummy1 type dummy"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -112,7 +112,7 @@ func ServeImageOrFail(f *Framework, test string, image string) {
|
|||||||
By("Trying to dial each unique pod")
|
By("Trying to dial each unique pod")
|
||||||
retryTimeout := 2 * time.Minute
|
retryTimeout := 2 * time.Minute
|
||||||
retryInterval := 5 * time.Second
|
retryInterval := 5 * time.Second
|
||||||
err = wait.Poll(retryInterval, retryTimeout, podResponseChecker{f.Client, f.Namespace.Name, label, name, true, pods}.checkAllResponses)
|
err = wait.Poll(retryInterval, retryTimeout, podProxyResponseChecker{f.Client, f.Namespace.Name, label, name, true, pods}.checkAllResponses)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Failf("Did not get expected responses within the timeout period of %.2f seconds.", retryTimeout.Seconds())
|
Failf("Did not get expected responses within the timeout period of %.2f seconds.", retryTimeout.Seconds())
|
||||||
}
|
}
|
||||||
|
1204
test/e2e/service.go
1204
test/e2e/service.go
File diff suppressed because it is too large
Load Diff
@ -12,7 +12,7 @@ spec:
|
|||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: netexec
|
- name: netexec
|
||||||
image: gcr.io/google_containers/netexec:1.0
|
image: gcr.io/google_containers/netexec:1.4
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 8080
|
- containerPort: 8080
|
||||||
# This is to force these pods to land on different hosts.
|
# This is to force these pods to land on different hosts.
|
||||||
|
@ -744,13 +744,13 @@ func deleteNS(c *client.Client, namespace string, timeout time.Duration) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Waits default ammount of time (podStartTimeout) for the specified pod to become running.
|
// Waits default amount of time (podStartTimeout) for the specified pod to become running.
|
||||||
// Returns an error if timeout occurs first, or pod goes in to failed state.
|
// Returns an error if timeout occurs first, or pod goes in to failed state.
|
||||||
func waitForPodRunningInNamespace(c *client.Client, podName string, namespace string) error {
|
func waitForPodRunningInNamespace(c *client.Client, podName string, namespace string) error {
|
||||||
return waitTimeoutForPodRunningInNamespace(c, podName, namespace, podStartTimeout)
|
return waitTimeoutForPodRunningInNamespace(c, podName, namespace, podStartTimeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Waits an extended ammount of time (slowPodStartTimeout) for the specified pod to become running.
|
// Waits an extended amount of time (slowPodStartTimeout) for the specified pod to become running.
|
||||||
// Returns an error if timeout occurs first, or pod goes in to failed state.
|
// Returns an error if timeout occurs first, or pod goes in to failed state.
|
||||||
func waitForPodRunningInNamespaceSlow(c *client.Client, podName string, namespace string) error {
|
func waitForPodRunningInNamespaceSlow(c *client.Client, podName string, namespace string) error {
|
||||||
return waitTimeoutForPodRunningInNamespace(c, podName, namespace, slowPodStartTimeout)
|
return waitTimeoutForPodRunningInNamespace(c, podName, namespace, slowPodStartTimeout)
|
||||||
@ -946,8 +946,9 @@ func waitForEndpoint(c *client.Client, ns, name string) error {
|
|||||||
return fmt.Errorf("Failed to get entpoints for %s/%s", ns, name)
|
return fmt.Errorf("Failed to get entpoints for %s/%s", ns, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Context for checking pods responses by issuing GETs to them and verifying if the answer with pod name.
|
// Context for checking pods responses by issuing GETs to them (via the API
|
||||||
type podResponseChecker struct {
|
// proxy) and verifying that they answer with ther own pod name.
|
||||||
|
type podProxyResponseChecker struct {
|
||||||
c *client.Client
|
c *client.Client
|
||||||
ns string
|
ns string
|
||||||
label labels.Selector
|
label labels.Selector
|
||||||
@ -956,8 +957,9 @@ type podResponseChecker struct {
|
|||||||
pods *api.PodList
|
pods *api.PodList
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkAllResponses issues GETs to all pods in the context and verify they reply with pod name.
|
// checkAllResponses issues GETs to all pods in the context and verify they
|
||||||
func (r podResponseChecker) checkAllResponses() (done bool, err error) {
|
// reply with their own pod name.
|
||||||
|
func (r podProxyResponseChecker) checkAllResponses() (done bool, err error) {
|
||||||
successes := 0
|
successes := 0
|
||||||
options := api.ListOptions{LabelSelector: r.label}
|
options := api.ListOptions{LabelSelector: r.label}
|
||||||
currentPods, err := r.c.Pods(r.ns).List(options)
|
currentPods, err := r.c.Pods(r.ns).List(options)
|
||||||
@ -1042,7 +1044,7 @@ func serverVersionGTE(v semver.Version, c client.ServerVersionInterface) (bool,
|
|||||||
func podsResponding(c *client.Client, ns, name string, wantName bool, pods *api.PodList) error {
|
func podsResponding(c *client.Client, ns, name string, wantName bool, pods *api.PodList) error {
|
||||||
By("trying to dial each unique pod")
|
By("trying to dial each unique pod")
|
||||||
label := labels.SelectorFromSet(labels.Set(map[string]string{"name": name}))
|
label := labels.SelectorFromSet(labels.Set(map[string]string{"name": name}))
|
||||||
return wait.PollImmediate(poll, podRespondingTimeout, podResponseChecker{c, ns, label, name, wantName, pods}.checkAllResponses)
|
return wait.PollImmediate(poll, podRespondingTimeout, podProxyResponseChecker{c, ns, label, name, wantName, pods}.checkAllResponses)
|
||||||
}
|
}
|
||||||
|
|
||||||
func serviceResponding(c *client.Client, ns, name string) error {
|
func serviceResponding(c *client.Client, ns, name string) error {
|
||||||
@ -2338,7 +2340,7 @@ func getSigner(provider string) (ssh.Signer, error) {
|
|||||||
// in namespace ns are running and ready, using c and waiting at most timeout.
|
// in namespace ns are running and ready, using c and waiting at most timeout.
|
||||||
func checkPodsRunningReady(c *client.Client, ns string, podNames []string, timeout time.Duration) bool {
|
func checkPodsRunningReady(c *client.Client, ns string, podNames []string, timeout time.Duration) bool {
|
||||||
np, desc := len(podNames), "running and ready"
|
np, desc := len(podNames), "running and ready"
|
||||||
Logf("Waiting up to %v for the following %d pods to be %s: %s", timeout, np, desc, podNames)
|
Logf("Waiting up to %v for %d pods to be %s: %s", timeout, np, desc, podNames)
|
||||||
result := make(chan bool, len(podNames))
|
result := make(chan bool, len(podNames))
|
||||||
for ix := range podNames {
|
for ix := range podNames {
|
||||||
// Launch off pod readiness checkers.
|
// Launch off pod readiness checkers.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
.PHONY: all netexec image push clean
|
.PHONY: all netexec image push clean
|
||||||
|
|
||||||
TAG = 1.3.1
|
TAG = 1.4
|
||||||
PREFIX = gcr.io/google_containers
|
PREFIX = gcr.io/google_containers
|
||||||
|
|
||||||
|
|
||||||
|
@ -56,15 +56,45 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func startHTTPServer(httpPort int) {
|
func startHTTPServer(httpPort int) {
|
||||||
http.HandleFunc("/shutdown", shutdownHandler)
|
http.HandleFunc("/", rootHandler)
|
||||||
http.HandleFunc("/hostName", hostNameHandler)
|
http.HandleFunc("/echo", echoHandler)
|
||||||
|
http.HandleFunc("/exit", exitHandler)
|
||||||
|
http.HandleFunc("/hostname", hostnameHandler)
|
||||||
http.HandleFunc("/shell", shellHandler)
|
http.HandleFunc("/shell", shellHandler)
|
||||||
http.HandleFunc("/upload", uploadHandler)
|
http.HandleFunc("/upload", uploadHandler)
|
||||||
http.HandleFunc("/dial", dialHandler)
|
http.HandleFunc("/dial", dialHandler)
|
||||||
|
// older handlers
|
||||||
|
http.HandleFunc("/hostName", hostNameHandler)
|
||||||
|
http.HandleFunc("/shutdown", shutdownHandler)
|
||||||
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", httpPort), nil))
|
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", httpPort), nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func rootHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
log.Printf("GET /")
|
||||||
|
fmt.Fprintf(w, "NOW: %v", time.Now())
|
||||||
|
}
|
||||||
|
|
||||||
|
func echoHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
log.Printf("GET /echo?msg=%s", r.FormValue("msg"))
|
||||||
|
fmt.Fprintf(w, "%s", r.FormValue("msg"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func exitHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
log.Printf("GET /exit?code=%s", r.FormValue("code"))
|
||||||
|
code, err := strconv.Atoi(r.FormValue("code"))
|
||||||
|
if err == nil || r.FormValue("code") == "" {
|
||||||
|
os.Exit(code)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, "argument 'code' must be an integer [0-127] or empty, got %q", r.FormValue("code"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func hostnameHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
log.Printf("GET /hostname")
|
||||||
|
fmt.Fprintf(w, getHostName())
|
||||||
|
}
|
||||||
|
|
||||||
func shutdownHandler(w http.ResponseWriter, r *http.Request) {
|
func shutdownHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
log.Printf("GET /shutdown")
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,6 +110,7 @@ func dialHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
request := values.Query().Get("request") // hostName
|
request := values.Query().Get("request") // hostName
|
||||||
protocol := values.Query().Get("protocol")
|
protocol := values.Query().Get("protocol")
|
||||||
tryParam := values.Query().Get("tries")
|
tryParam := values.Query().Get("tries")
|
||||||
|
log.Printf("GET /dial?host=%s&protocol=%s&port=%s&request=%s&tries=%s", host, protocol, port, request, tryParam)
|
||||||
tries := 1
|
tries := 1
|
||||||
if len(tryParam) > 0 {
|
if len(tryParam) > 0 {
|
||||||
tries, err = strconv.Atoi(tryParam)
|
tries, err = strconv.Atoi(tryParam)
|
||||||
@ -192,9 +223,12 @@ func dialUDP(request string, remoteAddress *net.UDPAddr) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func shellHandler(w http.ResponseWriter, r *http.Request) {
|
func shellHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
log.Println(r.FormValue("shellCommand"))
|
cmd := r.FormValue("shellCommand")
|
||||||
log.Printf("%s %s %s\n", shellPath, "-c", r.FormValue("shellCommand"))
|
if cmd == "" {
|
||||||
cmdOut, err := exec.Command(shellPath, "-c", r.FormValue("shellCommand")).CombinedOutput()
|
cmd = r.FormValue("cmd")
|
||||||
|
}
|
||||||
|
log.Printf("GET /shell?cmd=%s", cmd)
|
||||||
|
cmdOut, err := exec.Command(shellPath, "-c", cmd).CombinedOutput()
|
||||||
output := map[string]string{}
|
output := map[string]string{}
|
||||||
if len(cmdOut) > 0 {
|
if len(cmdOut) > 0 {
|
||||||
output["output"] = string(cmdOut)
|
output["output"] = string(cmdOut)
|
||||||
@ -212,6 +246,7 @@ func shellHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func uploadHandler(w http.ResponseWriter, r *http.Request) {
|
func uploadHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
log.Printf("GET /upload")
|
||||||
result := map[string]string{}
|
result := map[string]string{}
|
||||||
file, _, err := r.FormFile("file")
|
file, _, err := r.FormFile("file")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -287,10 +322,19 @@ func startUDPServer(udpPort int) {
|
|||||||
n, clientAddress, err := serverConn.ReadFromUDP(buf)
|
n, clientAddress, err := serverConn.ReadFromUDP(buf)
|
||||||
assertNoError(err)
|
assertNoError(err)
|
||||||
receivedText := strings.TrimSpace(string(buf[0:n]))
|
receivedText := strings.TrimSpace(string(buf[0:n]))
|
||||||
if receivedText == "hostName" {
|
if receivedText == "hostName" || receivedText == "hostname" {
|
||||||
log.Println("Sending udp hostName response")
|
log.Println("Sending udp hostName response")
|
||||||
_, err = serverConn.WriteToUDP([]byte(getHostName()), clientAddress)
|
_, err = serverConn.WriteToUDP([]byte(getHostName()), clientAddress)
|
||||||
assertNoError(err)
|
assertNoError(err)
|
||||||
|
} else if strings.HasPrefix(receivedText, "echo ") {
|
||||||
|
parts := strings.SplitN(receivedText, " ", 2)
|
||||||
|
resp := ""
|
||||||
|
if len(parts) == 2 {
|
||||||
|
resp = parts[1]
|
||||||
|
}
|
||||||
|
log.Println("Echoing %q")
|
||||||
|
_, err = serverConn.WriteToUDP([]byte(resp), clientAddress)
|
||||||
|
assertNoError(err)
|
||||||
} else if len(receivedText) > 0 {
|
} else if len(receivedText) > 0 {
|
||||||
log.Println("Unknown udp command received. ", receivedText)
|
log.Println("Unknown udp command received. ", receivedText)
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,9 @@ metadata:
|
|||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: netexec
|
- name: netexec
|
||||||
image: gcr.io/google_containers/netexec:1.3.1
|
image: gcr.io/google_containers/netexec:1.4
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 8080
|
- containerPort: 8080
|
||||||
|
protocol: TCP
|
||||||
- containerPort: 8081
|
- containerPort: 8081
|
||||||
|
protocol: UDP
|
||||||
|
Loading…
Reference in New Issue
Block a user