mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-04 09:49:50 +00:00
Rewrite services shell test in Go.
This commit is contained in:
parent
1a49ba1bdb
commit
fdd7f1e4b2
@ -16,6 +16,11 @@
|
|||||||
|
|
||||||
# Verifies that services and virtual IPs work.
|
# Verifies that services and virtual IPs work.
|
||||||
|
|
||||||
|
|
||||||
|
# TODO(wojtek-t): Remove this test once the following go tests are stable:
|
||||||
|
# - "should work after restarting kube-proxy"
|
||||||
|
# - "should work after restarting apiserver"
|
||||||
|
|
||||||
set -o errexit
|
set -o errexit
|
||||||
set -o nounset
|
set -o nounset
|
||||||
set -o pipefail
|
set -o pipefail
|
||||||
|
@ -94,6 +94,7 @@ GCE_PARALLEL_SKIP_TESTS=(
|
|||||||
"Nodes\sNetwork"
|
"Nodes\sNetwork"
|
||||||
"Nodes\sResize"
|
"Nodes\sResize"
|
||||||
"MaxPods"
|
"MaxPods"
|
||||||
|
"Services.*restarting"
|
||||||
"Shell.*services"
|
"Shell.*services"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -230,6 +230,131 @@ var _ = Describe("Services", func() {
|
|||||||
validateEndpointsOrFail(c, ns, serviceName, map[string][]int{})
|
validateEndpointsOrFail(c, ns, serviceName, map[string][]int{})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It("should be able to up and down services", func() {
|
||||||
|
ns := namespaces[0]
|
||||||
|
numPods, servicePort := 3, 80
|
||||||
|
|
||||||
|
podNames1, svc1IP, err := startServeHostnameService(c, ns, "service1", servicePort, numPods)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
podNames2, svc2IP, err := startServeHostnameService(c, ns, "service2", servicePort, numPods)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
hosts, err := NodeSSHHosts(c)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
if len(hosts) == 0 {
|
||||||
|
Failf("No ssh-able nodes")
|
||||||
|
}
|
||||||
|
host := hosts[0]
|
||||||
|
|
||||||
|
expectNoError(verifyServeHostnameServiceUp(c, host, podNames1, svc1IP, servicePort))
|
||||||
|
expectNoError(verifyServeHostnameServiceUp(c, host, podNames2, svc2IP, servicePort))
|
||||||
|
|
||||||
|
// Stop service 1 and make sure it is gone.
|
||||||
|
expectNoError(stopServeHostnameService(c, ns, "service1"))
|
||||||
|
|
||||||
|
expectNoError(verifyServeHostnameServiceDown(c, host, svc1IP, servicePort))
|
||||||
|
expectNoError(verifyServeHostnameServiceUp(c, host, podNames2, svc2IP, servicePort))
|
||||||
|
|
||||||
|
// Start another service and verify both are up.
|
||||||
|
podNames3, svc3IP, err := startServeHostnameService(c, ns, "service3", servicePort, numPods)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
if svc2IP == svc3IP {
|
||||||
|
Failf("VIPs conflict: %v", svc2IP)
|
||||||
|
}
|
||||||
|
|
||||||
|
expectNoError(verifyServeHostnameServiceUp(c, host, podNames2, svc2IP, servicePort))
|
||||||
|
expectNoError(verifyServeHostnameServiceUp(c, host, podNames3, svc3IP, servicePort))
|
||||||
|
|
||||||
|
expectNoError(stopServeHostnameService(c, ns, "service2"))
|
||||||
|
expectNoError(stopServeHostnameService(c, ns, "service3"))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("should work after restarting kube-proxy", func() {
|
||||||
|
SkipUnlessProviderIs("gce", "gke")
|
||||||
|
|
||||||
|
ns := namespaces[0]
|
||||||
|
numPods, servicePort := 3, 80
|
||||||
|
|
||||||
|
defer func() { expectNoError(stopServeHostnameService(c, ns, "service1")) }()
|
||||||
|
podNames1, svc1IP, err := startServeHostnameService(c, ns, "service1", servicePort, numPods)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
defer func() { expectNoError(stopServeHostnameService(c, ns, "service2")) }()
|
||||||
|
podNames2, svc2IP, err := startServeHostnameService(c, ns, "service2", servicePort, numPods)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
if svc1IP == svc2IP {
|
||||||
|
Failf("VIPs conflict: %v", svc1IP)
|
||||||
|
}
|
||||||
|
|
||||||
|
hosts, err := NodeSSHHosts(c)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
if len(hosts) == 0 {
|
||||||
|
Failf("No ssh-able nodes")
|
||||||
|
}
|
||||||
|
host := hosts[0]
|
||||||
|
|
||||||
|
expectNoError(verifyServeHostnameServiceUp(c, host, podNames1, svc1IP, servicePort))
|
||||||
|
expectNoError(verifyServeHostnameServiceUp(c, host, podNames2, svc2IP, servicePort))
|
||||||
|
|
||||||
|
// Restart kube-proxy and verify that services are still reachable (after some time).
|
||||||
|
if err := restartKubeProxy(host); err != nil {
|
||||||
|
Failf("error restarting kube-proxy: %v", err)
|
||||||
|
}
|
||||||
|
expectNoError(verifyServeHostnameServiceUp(c, host, podNames1, svc1IP, servicePort))
|
||||||
|
expectNoError(verifyServeHostnameServiceUp(c, host, podNames2, svc2IP, servicePort))
|
||||||
|
|
||||||
|
// Remove iptable rules and make sure they come back.
|
||||||
|
By("Remove iptable rules and make sure they come back")
|
||||||
|
_, _, code, err := SSH(`
|
||||||
|
sudo iptables -t nat -F KUBE-PORTALS-HOST || true;
|
||||||
|
sudo iptables -t nat -F KUBE-PORTALS-CONTAINER || true`, host, testContext.Provider)
|
||||||
|
if err != nil || code != 0 {
|
||||||
|
Failf("couldn't remove iptable rules: %v (code %v)", err, code)
|
||||||
|
}
|
||||||
|
expectNoError(verifyServeHostnameServiceUp(c, host, podNames1, svc1IP, servicePort))
|
||||||
|
expectNoError(verifyServeHostnameServiceUp(c, host, podNames2, svc2IP, servicePort))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("should work after restarting apiserver", func() {
|
||||||
|
// TODO: restartApiserver doesn't work in GKE - fix it and reenable this test.
|
||||||
|
SkipUnlessProviderIs("gce")
|
||||||
|
|
||||||
|
ns := namespaces[0]
|
||||||
|
numPods, servicePort := 3, 80
|
||||||
|
|
||||||
|
defer func() { expectNoError(stopServeHostnameService(c, ns, "service1")) }()
|
||||||
|
podNames1, svc1IP, err := startServeHostnameService(c, ns, "service1", servicePort, numPods)
|
||||||
|
|
||||||
|
hosts, err := NodeSSHHosts(c)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
if len(hosts) == 0 {
|
||||||
|
Failf("No ssh-able nodes")
|
||||||
|
}
|
||||||
|
host := hosts[0]
|
||||||
|
|
||||||
|
expectNoError(verifyServeHostnameServiceUp(c, host, podNames1, svc1IP, servicePort))
|
||||||
|
|
||||||
|
// Restart apiserver
|
||||||
|
if err := restartApiserver(); err != nil {
|
||||||
|
Failf("error restarting apiserver: %v", err)
|
||||||
|
}
|
||||||
|
if err := waitForApiserverUp(c); err != nil {
|
||||||
|
Failf("error while waiting for apiserver up: %v", err)
|
||||||
|
}
|
||||||
|
expectNoError(verifyServeHostnameServiceUp(c, host, podNames1, svc1IP, servicePort))
|
||||||
|
|
||||||
|
// Create a new service and check if it's not reusing IP.
|
||||||
|
defer func() { expectNoError(stopServeHostnameService(c, ns, "service2")) }()
|
||||||
|
podNames2, svc2IP, err := startServeHostnameService(c, ns, "service2", servicePort, numPods)
|
||||||
|
|
||||||
|
if svc1IP == svc2IP {
|
||||||
|
Failf("VIPs conflict: %v", svc1IP)
|
||||||
|
}
|
||||||
|
expectNoError(verifyServeHostnameServiceUp(c, host, podNames1, svc1IP, servicePort))
|
||||||
|
expectNoError(verifyServeHostnameServiceUp(c, host, podNames2, svc2IP, servicePort))
|
||||||
|
})
|
||||||
|
|
||||||
It("should be able to create a functioning external load balancer", func() {
|
It("should be able to create a functioning external load balancer", func() {
|
||||||
// requires ExternalLoadBalancer
|
// requires ExternalLoadBalancer
|
||||||
SkipUnlessProviderIs("gce", "gke", "aws")
|
SkipUnlessProviderIs("gce", "gke", "aws")
|
||||||
@ -1112,6 +1237,131 @@ func testNotReachable(ip string, port int) {
|
|||||||
Expect(err).NotTo(HaveOccurred(), "Error waiting for %s", desc)
|
Expect(err).NotTo(HaveOccurred(), "Error waiting for %s", desc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Creates a replication controller that serves its hostname and a service on top of it.
|
||||||
|
func startServeHostnameService(c *client.Client, ns, name string, port, replicas int) ([]string, string, error) {
|
||||||
|
podNames := make([]string, replicas)
|
||||||
|
|
||||||
|
_, err := c.Services(ns).Create(&api.Service{
|
||||||
|
ObjectMeta: api.ObjectMeta{
|
||||||
|
Name: name,
|
||||||
|
},
|
||||||
|
Spec: api.ServiceSpec{
|
||||||
|
Ports: []api.ServicePort{{
|
||||||
|
Port: port,
|
||||||
|
TargetPort: util.NewIntOrStringFromInt(9376),
|
||||||
|
Protocol: "TCP",
|
||||||
|
}},
|
||||||
|
Selector: map[string]string{
|
||||||
|
"name": name,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return podNames, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
var createdPods []*api.Pod
|
||||||
|
maxContainerFailures := 0
|
||||||
|
config := RCConfig{
|
||||||
|
Client: c,
|
||||||
|
Image: "gcr.io/google_containers/serve_hostname:1.1",
|
||||||
|
Name: name,
|
||||||
|
Namespace: ns,
|
||||||
|
PollInterval: 3 * time.Second,
|
||||||
|
Timeout: 30 * time.Second,
|
||||||
|
Replicas: replicas,
|
||||||
|
CreatedPods: &createdPods,
|
||||||
|
MaxContainerFailures: &maxContainerFailures,
|
||||||
|
}
|
||||||
|
err = RunRC(config)
|
||||||
|
if err != nil {
|
||||||
|
return podNames, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(createdPods) != replicas {
|
||||||
|
return podNames, "", fmt.Errorf("Incorrect number of running pods: %v", len(createdPods))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range createdPods {
|
||||||
|
podNames[i] = createdPods[i].ObjectMeta.Name
|
||||||
|
}
|
||||||
|
sort.StringSlice(podNames).Sort()
|
||||||
|
|
||||||
|
service, err := c.Services(ns).Get(name)
|
||||||
|
if err != nil {
|
||||||
|
return podNames, "", err
|
||||||
|
}
|
||||||
|
if service.Spec.ClusterIP == "" {
|
||||||
|
return podNames, "", fmt.Errorf("Service IP is blank for %v", name)
|
||||||
|
}
|
||||||
|
serviceIP := service.Spec.ClusterIP
|
||||||
|
return podNames, serviceIP, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func stopServeHostnameService(c *client.Client, ns, name string) error {
|
||||||
|
if err := DeleteRC(c, ns, name); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := c.Services(ns).Delete(name); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func verifyServeHostnameServiceUp(c *client.Client, host string, expectedPods []string, serviceIP string, servicePort int) error {
|
||||||
|
command := fmt.Sprintf(
|
||||||
|
"for i in $(seq 1 %d); do wget -q -T 1 -O - http://%s:%d || true; echo; done",
|
||||||
|
3*len(expectedPods), serviceIP, servicePort)
|
||||||
|
|
||||||
|
commands := []string{
|
||||||
|
// verify service from node
|
||||||
|
fmt.Sprintf(`set -e; %s | sort -n | uniq`, command),
|
||||||
|
// verify service from container
|
||||||
|
fmt.Sprintf(`set -e;
|
||||||
|
sudo docker pull gcr.io/google_containers/busybox > /dev/null;
|
||||||
|
sudo docker run gcr.io/google_containers/busybox sh -c '%v' | sort -n | uniq`,
|
||||||
|
command),
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, cmd := range commands {
|
||||||
|
passed := false
|
||||||
|
for start := time.Now(); time.Since(start) < time.Minute; time.Sleep(5) {
|
||||||
|
stdout, _, code, err := SSH(cmd, host, testContext.Provider)
|
||||||
|
if err != nil || code != 0 {
|
||||||
|
Logf("error while SSH-ing to node: %v (code %v)", err, code)
|
||||||
|
}
|
||||||
|
pods := strings.Split(strings.TrimSpace(stdout), "\n")
|
||||||
|
sort.StringSlice(pods).Sort()
|
||||||
|
if api.Semantic.DeepEqual(pods, expectedPods) {
|
||||||
|
passed = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
Logf("Expected pods: %v, got: %v", expectedPods, pods)
|
||||||
|
}
|
||||||
|
if !passed {
|
||||||
|
return fmt.Errorf("service verification failed for:\n %s", cmd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func verifyServeHostnameServiceDown(c *client.Client, host string, serviceIP string, servicePort int) error {
|
||||||
|
command := fmt.Sprintf(
|
||||||
|
"curl -s --connect-timeout 2 http://%s:%d && exit 99", serviceIP, servicePort)
|
||||||
|
|
||||||
|
for start := time.Now(); time.Since(start) < time.Minute; time.Sleep(5 * time.Second) {
|
||||||
|
_, _, code, err := SSH(command, host, testContext.Provider)
|
||||||
|
if err != nil {
|
||||||
|
Logf("error while SSH-ing to node: %v", err)
|
||||||
|
}
|
||||||
|
if code != 99 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
Logf("service still alive - still waiting")
|
||||||
|
}
|
||||||
|
return fmt.Errorf("waiting for service to be down timed out")
|
||||||
|
}
|
||||||
|
|
||||||
// Does an HTTP GET, but does not reuse TCP connections
|
// Does an HTTP GET, but does not reuse TCP connections
|
||||||
// This masks problems where the iptables rule has changed, but we don't see it
|
// This masks problems where the iptables rule has changed, but we don't see it
|
||||||
// This is intended for relatively quick requests (status checks), so we set a short (5 seconds) timeout
|
// This is intended for relatively quick requests (status checks), so we set a short (5 seconds) timeout
|
||||||
|
@ -1740,3 +1740,43 @@ func parseKVLines(output, key string) string {
|
|||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func restartKubeProxy(host string) error {
|
||||||
|
// TODO: Make it work for all providers.
|
||||||
|
if !providerIs("gce", "gke", "aws") {
|
||||||
|
return fmt.Errorf("unsupported provider: %s", testContext.Provider)
|
||||||
|
}
|
||||||
|
_, _, code, err := SSH("sudo /etc/init.d/kube-proxy restart", host, testContext.Provider)
|
||||||
|
if err != nil || code != 0 {
|
||||||
|
return fmt.Errorf("couldn't restart kube-proxy: %v (code %v)", err, code)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func restartApiserver() error {
|
||||||
|
// TODO: Make it work for all providers.
|
||||||
|
if !providerIs("gce", "gke", "aws") {
|
||||||
|
return fmt.Errorf("unsupported provider: %s", testContext.Provider)
|
||||||
|
}
|
||||||
|
var command string
|
||||||
|
if providerIs("gce", "gke") {
|
||||||
|
command = "sudo docker ps | grep /kube-apiserver | cut -d ' ' -f 1 | xargs sudo docker kill"
|
||||||
|
} else {
|
||||||
|
command = "sudo /etc/init.d/kube-apiserver restart"
|
||||||
|
}
|
||||||
|
_, _, code, err := SSH(command, getMasterHost()+":22", testContext.Provider)
|
||||||
|
if err != nil || code != 0 {
|
||||||
|
return fmt.Errorf("couldn't restart apiserver: %v (code %v)", err, code)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func waitForApiserverUp(c *client.Client) error {
|
||||||
|
for start := time.Now(); time.Since(start) < time.Minute; time.Sleep(5 * time.Second) {
|
||||||
|
body, err := c.Get().AbsPath("/healthz").Do().Raw()
|
||||||
|
if err == nil && string(body) == "ok" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Errorf("waiting for apiserver timed out")
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user