Merged by openshift-bot
OpenShift Bot authored on 2016/12/27 09:18:21... | ... |
@@ -3,11 +3,14 @@ package images |
3 | 3 |
import ( |
4 | 4 |
"fmt" |
5 | 5 |
"net/http" |
6 |
+ "strconv" |
|
7 |
+ "strings" |
|
6 | 8 |
"time" |
7 | 9 |
|
8 | 10 |
g "github.com/onsi/ginkgo" |
9 | 11 |
o "github.com/onsi/gomega" |
10 | 12 |
|
13 |
+ kapi "k8s.io/kubernetes/pkg/api" |
|
11 | 14 |
"k8s.io/kubernetes/pkg/util/wait" |
12 | 15 |
e2e "k8s.io/kubernetes/test/e2e/framework" |
13 | 16 |
|
... | ... |
@@ -32,6 +35,9 @@ var _ = g.Describe("[networking][router] openshift routers", func() { |
32 | 32 |
g.Describe("The HAProxy router", func() { |
33 | 33 |
g.It("should serve the correct routes when scoped to a single namespace and label set", func() { |
34 | 34 |
oc.SetOutputDir(exutil.TestContext.OutputDir) |
35 |
+ ns := oc.KubeFramework().Namespace.Name |
|
36 |
+ execPodName := exutil.CreateExecPodOrFail(oc.AdminKubeClient().Core(), ns, "execpod") |
|
37 |
+ defer func() { oc.AdminKubeClient().Core().Pods(ns).Delete(execPodName, kapi.NewDeleteOptions(1)) }() |
|
35 | 38 |
|
36 | 39 |
g.By(fmt.Sprintf("creating a scoped router from a config file %q", configPath)) |
37 | 40 |
|
... | ... |
@@ -54,28 +60,25 @@ var _ = g.Describe("[networking][router] openshift routers", func() { |
54 | 54 |
|
55 | 55 |
g.By("waiting for the healthz endpoint to respond") |
56 | 56 |
healthzURI := fmt.Sprintf("http://%s:1936/healthz", routerIP) |
57 |
- err = waitForRouterOKResponse(healthzURI, routerIP, 2*time.Minute) |
|
57 |
+ err = waitForRouterOKResponseExec(ns, execPodName, healthzURI, routerIP, 60*2) |
|
58 | 58 |
o.Expect(err).NotTo(o.HaveOccurred()) |
59 | 59 |
|
60 | 60 |
g.By("waiting for the valid route to respond") |
61 |
- err = waitForRouterOKResponse(routerURL, "first.example.com", 2*time.Minute) |
|
61 |
+ err = waitForRouterOKResponseExec(ns, execPodName, routerURL, "first.example.com", 60*2) |
|
62 | 62 |
o.Expect(err).NotTo(o.HaveOccurred()) |
63 | 63 |
|
64 | 64 |
for _, host := range []string{"second.example.com", "third.example.com"} { |
65 | 65 |
g.By(fmt.Sprintf("checking that %s does not match a route", host)) |
66 |
- req, err := requestViaReverseProxy("GET", routerURL, host) |
|
67 |
- o.Expect(err).NotTo(o.HaveOccurred()) |
|
68 |
- resp, err := http.DefaultClient.Do(req) |
|
66 |
+ err = expectRouteStatusCodeExec(ns, execPodName, routerURL, host, http.StatusServiceUnavailable) |
|
69 | 67 |
o.Expect(err).NotTo(o.HaveOccurred()) |
70 |
- resp.Body.Close() |
|
71 |
- if resp.StatusCode != http.StatusServiceUnavailable { |
|
72 |
- e2e.Failf("should have had a 503 status code for %s", host) |
|
73 |
- } |
|
74 | 68 |
} |
75 | 69 |
}) |
70 |
+ |
|
76 | 71 |
g.It("should override the route host with a custom value", func() { |
77 | 72 |
oc.SetOutputDir(exutil.TestContext.OutputDir) |
78 | 73 |
ns := oc.KubeFramework().Namespace.Name |
74 |
+ execPodName := exutil.CreateExecPodOrFail(oc.AdminKubeClient().Core(), ns, "execpod") |
|
75 |
+ defer func() { oc.AdminKubeClient().Core().Pods(ns).Delete(execPodName, kapi.NewDeleteOptions(1)) }() |
|
79 | 76 |
|
80 | 77 |
g.By(fmt.Sprintf("creating a scoped router from a config file %q", configPath)) |
81 | 78 |
|
... | ... |
@@ -100,68 +103,91 @@ var _ = g.Describe("[networking][router] openshift routers", func() { |
100 | 100 |
|
101 | 101 |
g.By("waiting for the healthz endpoint to respond") |
102 | 102 |
healthzURI := fmt.Sprintf("http://%s:1936/healthz", routerIP) |
103 |
- err = waitForRouterOKResponse(healthzURI, routerIP, 2*time.Minute) |
|
103 |
+ err = waitForRouterOKResponseExec(ns, execPodName, healthzURI, routerIP, 60*2) |
|
104 | 104 |
o.Expect(err).NotTo(o.HaveOccurred()) |
105 | 105 |
|
106 |
+ oc.KubeFramework().Client.Pods(ns) |
|
107 |
+ |
|
106 | 108 |
g.By("waiting for the valid route to respond") |
107 |
- err = waitForRouterOKResponse(routerURL, fmt.Sprintf(pattern, "route-1", ns), 2*time.Minute) |
|
109 |
+ err = waitForRouterOKResponseExec(ns, execPodName, routerURL, fmt.Sprintf(pattern, "route-1", ns), 60*2) |
|
108 | 110 |
o.Expect(err).NotTo(o.HaveOccurred()) |
109 | 111 |
|
110 | 112 |
g.By("checking that the stored domain name does not match a route") |
111 | 113 |
host := "first.example.com" |
112 |
- req, err := requestViaReverseProxy("GET", routerURL, host) |
|
113 |
- o.Expect(err).NotTo(o.HaveOccurred()) |
|
114 |
- resp, err := http.DefaultClient.Do(req) |
|
114 |
+ err = expectRouteStatusCodeExec(ns, execPodName, routerURL, host, http.StatusServiceUnavailable) |
|
115 | 115 |
o.Expect(err).NotTo(o.HaveOccurred()) |
116 |
- resp.Body.Close() |
|
117 |
- if resp.StatusCode != http.StatusServiceUnavailable { |
|
118 |
- e2e.Failf("should have had a 503 status code for %s", host) |
|
119 |
- } |
|
120 | 116 |
|
121 | 117 |
for _, host := range []string{"route-1", "route-2"} { |
122 | 118 |
host = fmt.Sprintf(pattern, host, ns) |
123 |
- g.By(fmt.Sprintf("checking that %s does not match a route", host)) |
|
124 |
- req, err := requestViaReverseProxy("GET", routerURL, host) |
|
125 |
- o.Expect(err).NotTo(o.HaveOccurred()) |
|
126 |
- resp, err := http.DefaultClient.Do(req) |
|
119 |
+ g.By(fmt.Sprintf("checking that %s matches a route", host)) |
|
120 |
+ err = expectRouteStatusCodeExec(ns, execPodName, routerURL, host, http.StatusOK) |
|
127 | 121 |
o.Expect(err).NotTo(o.HaveOccurred()) |
128 |
- resp.Body.Close() |
|
129 |
- if resp.StatusCode != http.StatusOK { |
|
130 |
- e2e.Failf("should have had a 200 status code for %s", host) |
|
131 |
- } |
|
132 | 122 |
} |
133 | 123 |
}) |
134 | 124 |
}) |
135 | 125 |
}) |
136 | 126 |
|
137 |
-func waitForRouterOKResponse(url, host string, timeout time.Duration) error { |
|
138 |
- return wait.Poll(time.Second, timeout, func() (bool, error) { |
|
139 |
- req, err := requestViaReverseProxy("GET", url, host) |
|
140 |
- if err != nil { |
|
141 |
- return false, err |
|
142 |
- } |
|
143 |
- resp, err := http.DefaultClient.Do(req) |
|
144 |
- if err != nil { |
|
145 |
- return false, nil |
|
146 |
- } |
|
147 |
- resp.Body.Close() |
|
148 |
- if resp.StatusCode == http.StatusServiceUnavailable { |
|
149 |
- // not ready yet |
|
150 |
- return false, nil |
|
151 |
- } |
|
152 |
- if resp.StatusCode != http.StatusOK { |
|
153 |
- e2e.Logf("unexpected response: %#v", resp.StatusCode) |
|
154 |
- return false, nil |
|
155 |
- } |
|
156 |
- return true, nil |
|
157 |
- }) |
|
127 |
+func waitForRouterOKResponseExec(ns, execPodName, url, host string, timeoutSeconds int) error { |
|
128 |
+ cmd := fmt.Sprintf(` |
|
129 |
+ set -e |
|
130 |
+ for i in $(seq 1 %d); do |
|
131 |
+ code=$( curl -s -o /dev/null -w '%%{http_code}\n' --header 'Host: %s' %q ) |
|
132 |
+ echo $code |
|
133 |
+ if [[ $code -eq 200 ]]; then |
|
134 |
+ exit 0 |
|
135 |
+ fi |
|
136 |
+ if [[ $code -ne 503 ]]; then |
|
137 |
+ exit 1 |
|
138 |
+ fi |
|
139 |
+ sleep 1 |
|
140 |
+ done |
|
141 |
+ `, timeoutSeconds, host, url) |
|
142 |
+ output, err := e2e.RunHostCmd(ns, execPodName, cmd) |
|
143 |
+ if err != nil { |
|
144 |
+ return fmt.Errorf("host command failed: %v\n%s", err, output) |
|
145 |
+ } |
|
146 |
+ lines := strings.Split(strings.TrimSpace(output), "\n") |
|
147 |
+ if lines[len(lines)-1] != "200" { |
|
148 |
+ return fmt.Errorf("last response from server was not 200:\n%s", output) |
|
149 |
+ } |
|
150 |
+ return nil |
|
151 |
+} |
|
152 |
+ |
|
153 |
+func expectRouteStatusCodeRepeatedExec(ns, execPodName, url, host string, statusCode int, times int) error { |
|
154 |
+ cmd := fmt.Sprintf(` |
|
155 |
+ set -e |
|
156 |
+ for i in $(seq 1 %d); do |
|
157 |
+ code=$( curl -s -o /dev/null -w '%%{http_code}\n' --header 'Host: %s' %q ) |
|
158 |
+ echo $code |
|
159 |
+ if [[ $code -ne %d ]]; then |
|
160 |
+ exit 1 |
|
161 |
+ fi |
|
162 |
+ done |
|
163 |
+ `, times, host, url, statusCode) |
|
164 |
+ output, err := e2e.RunHostCmd(ns, execPodName, cmd) |
|
165 |
+ if err != nil { |
|
166 |
+ return fmt.Errorf("host command failed: %v\n%s", err, output) |
|
167 |
+ } |
|
168 |
+ return nil |
|
169 |
+} |
|
170 |
+ |
|
171 |
+func expectRouteStatusCodeExec(ns, execPodName, url, host string, statusCode int) error { |
|
172 |
+ cmd := fmt.Sprintf("curl -s -o /dev/null -w '%%{http_code}' --header 'Host: %s' %q", host, url) |
|
173 |
+ output, err := e2e.RunHostCmd(ns, execPodName, cmd) |
|
174 |
+ if err != nil { |
|
175 |
+ return fmt.Errorf("host command failed: %v\n%s", err, output) |
|
176 |
+ } |
|
177 |
+ if output != strconv.Itoa(statusCode) { |
|
178 |
+ return fmt.Errorf("last response from server was not %d: %s", statusCode, output) |
|
179 |
+ } |
|
180 |
+ return nil |
|
158 | 181 |
} |
159 | 182 |
|
160 |
-func requestViaReverseProxy(method, url, host string) (*http.Request, error) { |
|
161 |
- req, err := http.NewRequest(method, url, nil) |
|
183 |
+func getAuthenticatedRouteURLViaPod(ns, execPodName, url, host, user, pass string) (string, error) { |
|
184 |
+ cmd := fmt.Sprintf("curl -s -u %s:%s --header 'Host: %s' %q", user, pass, host, url) |
|
185 |
+ output, err := e2e.RunHostCmd(ns, execPodName, cmd) |
|
162 | 186 |
if err != nil { |
163 |
- return nil, err |
|
187 |
+ return "", fmt.Errorf("host command failed: %v\n%s", err, output) |
|
164 | 188 |
} |
165 |
- req.Host = host |
|
166 |
- return req, nil |
|
189 |
+ return output, nil |
|
167 | 190 |
} |
... | ... |
@@ -3,7 +3,6 @@ package images |
3 | 3 |
import ( |
4 | 4 |
"encoding/csv" |
5 | 5 |
"fmt" |
6 |
- "io/ioutil" |
|
7 | 6 |
"net/http" |
8 | 7 |
"strconv" |
9 | 8 |
"strings" |
... | ... |
@@ -12,6 +11,7 @@ import ( |
12 | 12 |
g "github.com/onsi/ginkgo" |
13 | 13 |
o "github.com/onsi/gomega" |
14 | 14 |
|
15 |
+ kapi "k8s.io/kubernetes/pkg/api" |
|
15 | 16 |
"k8s.io/kubernetes/pkg/util/wait" |
16 | 17 |
e2e "k8s.io/kubernetes/test/e2e/framework" |
17 | 18 |
|
... | ... |
@@ -35,8 +35,10 @@ var _ = g.Describe("[networking][router] weighted openshift router", func() { |
35 | 35 |
|
36 | 36 |
g.Describe("The HAProxy router", func() { |
37 | 37 |
g.It("should appropriately serve a route that points to two services", func() { |
38 |
- |
|
39 | 38 |
oc.SetOutputDir(exutil.TestContext.OutputDir) |
39 |
+ ns := oc.KubeFramework().Namespace.Name |
|
40 |
+ execPodName := exutil.CreateExecPodOrFail(oc.AdminKubeClient().Core(), ns, "execpod") |
|
41 |
+ defer func() { oc.AdminKubeClient().Core().Pods(ns).Delete(execPodName, kapi.NewDeleteOptions(1)) }() |
|
40 | 42 |
|
41 | 43 |
g.By(fmt.Sprintf("creating a weighted router from a config file %q", configPath)) |
42 | 44 |
|
... | ... |
@@ -60,34 +62,30 @@ var _ = g.Describe("[networking][router] weighted openshift router", func() { |
60 | 60 |
|
61 | 61 |
g.By("waiting for the healthz endpoint to respond") |
62 | 62 |
healthzURI := fmt.Sprintf("http://%s:1936/healthz", routerIP) |
63 |
- err = waitForRouterOKResponse(healthzURI, routerIP, 2*time.Minute) |
|
63 |
+ err = waitForRouterOKResponseExec(ns, execPodName, healthzURI, routerIP, 60*2) |
|
64 | 64 |
o.Expect(err).NotTo(o.HaveOccurred()) |
65 | 65 |
|
66 | 66 |
host := "weighted.example.com" |
67 |
- g.By(fmt.Sprintf("checking that 10 requests go through successfully")) |
|
68 |
- for i := 1; i <= 100; i++ { |
|
69 |
- err = waitForRouterOKResponse(routerURL, "weighted.example.com", 1*time.Minute) |
|
70 |
- o.Expect(err).NotTo(o.HaveOccurred()) |
|
71 |
- } |
|
72 |
- g.By(fmt.Sprintf("checking that stats can be obtained successfully")) |
|
73 |
- statsURL := fmt.Sprintf("http://%s:1936/;csv", routerIP) |
|
74 |
- req, err := requestViaReverseProxy("GET", statsURL, host) |
|
75 |
- req.SetBasicAuth("admin", "password") |
|
76 |
- resp, err := http.DefaultClient.Do(req) |
|
67 |
+ times := 100 |
|
68 |
+ g.By(fmt.Sprintf("checking that %d requests go through successfully", times)) |
|
69 |
+ // wait for the request to stabilize |
|
70 |
+ err = waitForRouterOKResponseExec(ns, execPodName, routerURL, "weighted.example.com", 60*2) |
|
77 | 71 |
o.Expect(err).NotTo(o.HaveOccurred()) |
78 |
- if resp.StatusCode != http.StatusOK { |
|
79 |
- e2e.Failf("unexpected response: %#v", resp.StatusCode) |
|
80 |
- } |
|
81 |
- |
|
82 |
- g.By(fmt.Sprintf("checking that weights are respected by the router")) |
|
83 |
- stats, err := ioutil.ReadAll(resp.Body) |
|
84 |
- defer resp.Body.Close() |
|
85 |
- trafficValues, err := parseStats(string(stats), "weightedroute", 7) |
|
72 |
+ // all requests should now succeed |
|
73 |
+ err = expectRouteStatusCodeRepeatedExec(ns, execPodName, routerURL, "weighted.example.com", http.StatusOK, times) |
|
86 | 74 |
o.Expect(err).NotTo(o.HaveOccurred()) |
87 | 75 |
|
88 |
- if len(trafficValues) != 2 { |
|
89 |
- e2e.Failf("Expected 2 weighted backends for incoming traffic, found %d", len(trafficValues)) |
|
90 |
- } |
|
76 |
+ g.By(fmt.Sprintf("checking that there are two weighted backends in the router stats")) |
|
77 |
+ var trafficValues []string |
|
78 |
+ err = wait.PollImmediate(100*time.Millisecond, 2*time.Minute, func() (bool, error) { |
|
79 |
+ statsURL := fmt.Sprintf("http://%s:1936/;csv", routerIP) |
|
80 |
+ stats, err := getAuthenticatedRouteURLViaPod(ns, execPodName, statsURL, host, "admin", "password") |
|
81 |
+ o.Expect(err).NotTo(o.HaveOccurred()) |
|
82 |
+ trafficValues, err = parseStats(stats, "weightedroute", 7) |
|
83 |
+ o.Expect(err).NotTo(o.HaveOccurred()) |
|
84 |
+ return len(trafficValues) == 2, nil |
|
85 |
+ }) |
|
86 |
+ o.Expect(err).NotTo(o.HaveOccurred()) |
|
91 | 87 |
|
92 | 88 |
trafficEP1, err := strconv.Atoi(trafficValues[0]) |
93 | 89 |
o.Expect(err).NotTo(o.HaveOccurred()) |
... | ... |
@@ -101,12 +99,8 @@ var _ = g.Describe("[networking][router] weighted openshift router", func() { |
101 | 101 |
|
102 | 102 |
g.By(fmt.Sprintf("checking that zero weights are also respected by the router")) |
103 | 103 |
host = "zeroweight.example.com" |
104 |
- req, _ = requestViaReverseProxy("GET", routerURL, host) |
|
105 |
- resp, err = http.DefaultClient.Do(req) |
|
104 |
+ err = expectRouteStatusCodeExec(ns, execPodName, routerURL, host, http.StatusServiceUnavailable) |
|
106 | 105 |
o.Expect(err).NotTo(o.HaveOccurred()) |
107 |
- if resp.StatusCode != http.StatusServiceUnavailable { |
|
108 |
- e2e.Failf("Expected zero weighted route to return a 503, but got %v", resp.StatusCode) |
|
109 |
- } |
|
110 | 106 |
}) |
111 | 107 |
}) |
112 | 108 |
}) |
... | ... |
@@ -33,6 +33,7 @@ import ( |
33 | 33 |
"k8s.io/kubernetes/pkg/util/sets" |
34 | 34 |
"k8s.io/kubernetes/pkg/util/uuid" |
35 | 35 |
"k8s.io/kubernetes/pkg/util/wait" |
36 |
+ "k8s.io/kubernetes/test/e2e/framework" |
|
36 | 37 |
|
37 | 38 |
buildapi "github.com/openshift/origin/pkg/build/api" |
38 | 39 |
"github.com/openshift/origin/pkg/client" |
... | ... |
@@ -1216,3 +1217,43 @@ func GetPodForImage(dockerImageReference string) *kapi.Pod { |
1216 | 1216 |
Image: dockerImageReference, |
1217 | 1217 |
}) |
1218 | 1218 |
} |
1219 |
+ |
|
1220 |
+// CreateExecPodOrFail creates a simple busybox pod in a sleep loop used as a |
|
1221 |
+// vessel for kubectl exec commands. |
|
1222 |
+// Returns the name of the created pod. |
|
1223 |
+// TODO: expose upstream |
|
1224 |
+func CreateExecPodOrFail(client kcoreclient.CoreInterface, ns, name string) string { |
|
1225 |
+ framework.Logf("Creating new exec pod") |
|
1226 |
+ execPod := framework.NewHostExecPodSpec(ns, name) |
|
1227 |
+ created, err := client.Pods(ns).Create(execPod) |
|
1228 |
+ o.Expect(err).NotTo(o.HaveOccurred()) |
|
1229 |
+ err = wait.PollImmediate(framework.Poll, 5*time.Minute, func() (bool, error) { |
|
1230 |
+ retrievedPod, err := client.Pods(execPod.Namespace).Get(created.Name) |
|
1231 |
+ if err != nil { |
|
1232 |
+ return false, nil |
|
1233 |
+ } |
|
1234 |
+ return retrievedPod.Status.Phase == kapi.PodRunning, nil |
|
1235 |
+ }) |
|
1236 |
+ o.Expect(err).NotTo(o.HaveOccurred()) |
|
1237 |
+ return created.Name |
|
1238 |
+} |
|
1239 |
+ |
|
1240 |
+// CreateExecPodOnNode launches a exec pod in the given namespace and node |
|
1241 |
+// waits until it's Running, created pod name would be returned |
|
1242 |
+// TODO: expose upstream |
|
1243 |
+func CreateExecPodOnNode(client kcoreclient.CoreInterface, ns, nodeName, name string) string { |
|
1244 |
+ framework.Logf("Creating exec pod %q in namespace %q", name, ns) |
|
1245 |
+ execPod := framework.NewHostExecPodSpec(ns, name) |
|
1246 |
+ execPod.Spec.NodeName = nodeName |
|
1247 |
+ created, err := client.Pods(ns).Create(execPod) |
|
1248 |
+ o.Expect(err).NotTo(o.HaveOccurred()) |
|
1249 |
+ err = wait.PollImmediate(framework.Poll, 5*time.Minute, func() (bool, error) { |
|
1250 |
+ retrievedPod, err := client.Pods(execPod.Namespace).Get(created.Name) |
|
1251 |
+ if err != nil { |
|
1252 |
+ return false, nil |
|
1253 |
+ } |
|
1254 |
+ return retrievedPod.Status.Phase == kapi.PodRunning, nil |
|
1255 |
+ }) |
|
1256 |
+ o.Expect(err).NotTo(o.HaveOccurred()) |
|
1257 |
+ return created.Name |
|
1258 |
+} |
... | ... |
@@ -181,6 +181,10 @@ func createTestingNS(baseName string, c *kclient.Client, labels map[string]strin |
181 | 181 |
addRoleToE2EServiceAccounts(osClient, []kapi.Namespace{*ns}, bootstrappolicy.ViewRoleName) |
182 | 182 |
} |
183 | 183 |
|
184 |
+ if isPackage("/kubernetes/test/e2e/scheduler_predicates.go") { |
|
185 |
+ allowAllNodeScheduling(c, ns.Name) |
|
186 |
+ } |
|
187 |
+ |
|
184 | 188 |
return ns, err |
185 | 189 |
} |
186 | 190 |
|
... | ... |
@@ -200,6 +204,25 @@ func checkSuiteSkips() { |
200 | 200 |
|
201 | 201 |
var longRetry = wait.Backoff{Steps: 100} |
202 | 202 |
|
203 |
+// allowAllNodeScheduling sets the annotation on namespace that allows all nodes to be scheduled onto. |
|
204 |
+func allowAllNodeScheduling(c *kclient.Client, namespace string) { |
|
205 |
+ err := kclient.RetryOnConflict(longRetry, func() error { |
|
206 |
+ ns, err := c.Namespaces().Get(namespace) |
|
207 |
+ if err != nil { |
|
208 |
+ return err |
|
209 |
+ } |
|
210 |
+ if ns.Annotations == nil { |
|
211 |
+ ns.Annotations = make(map[string]string) |
|
212 |
+ } |
|
213 |
+ ns.Annotations["openshift.io/node-selector"] = "" |
|
214 |
+ _, err = c.Namespaces().Update(ns) |
|
215 |
+ return err |
|
216 |
+ }) |
|
217 |
+ if err != nil { |
|
218 |
+ FatalErr(err) |
|
219 |
+ } |
|
220 |
+} |
|
221 |
+ |
|
203 | 222 |
func addE2EServiceAccountsToSCC(c *kclient.Client, namespaces []kapi.Namespace, sccName string) { |
204 | 223 |
// Because updates can race, we need to set the backoff retries to be > than the number of possible |
205 | 224 |
// parallel jobs starting at once. Set very high to allow future high parallelism. |