diff --git a/pkg/kubelet/probe.go b/pkg/kubelet/probe.go index 413a14e18e..98937f3d0e 100644 --- a/pkg/kubelet/probe.go +++ b/pkg/kubelet/probe.go @@ -67,7 +67,11 @@ func extractPort(param util.IntOrString, container api.Container) (int, error) { var err error switch param.Kind { case util.IntstrInt: - return param.IntVal, nil + port := param.IntVal + if port > 0 && port < 65536 { + return port, nil + } + return port, fmt.Errorf("invalid port number: %v", port) case util.IntstrString: port = findPortByName(container, param.StrVal) if port == -1 { @@ -76,7 +80,10 @@ func extractPort(param util.IntOrString, container api.Container) (int, error) { return port, err } } - return port, nil + if port > 0 && port < 65536 { + return port, nil + } + return port, fmt.Errorf("invalid port number: %v", port) default: return port, fmt.Errorf("IntOrString had no kind: %+v", param) } diff --git a/pkg/kubelet/probe_test.go b/pkg/kubelet/probe_test.go new file mode 100644 index 0000000000..9855e30a0e --- /dev/null +++ b/pkg/kubelet/probe_test.go @@ -0,0 +1,126 @@ +/* +Copyright 2015 Google Inc. All rights reserved. + +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 kubelet + +import ( + "testing" + + "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + "github.com/GoogleCloudPlatform/kubernetes/pkg/util" +) + +func TestFindPortByName(t *testing.T) { + container := api.Container{ + Ports: []api.Port{ + { + Name: "foo", + HostPort: 8080, + }, + { + Name: "bar", + HostPort: 9000, + }, + }, + } + want := 8080 + got := findPortByName(container, "foo") + if got != want { + t.Errorf("Expected %v, got %v", want, got) + } +} + +func TestGetURLParts(t *testing.T) { + testCases := []struct { + probe *api.HTTPGetAction + ok bool + host string + port int + path string + }{ + {&api.HTTPGetAction{Host: "", Port: util.NewIntOrStringFromInt(-1), Path: ""}, false, "", -1, ""}, + {&api.HTTPGetAction{Host: "", Port: util.NewIntOrStringFromString(""), Path: ""}, false, "", -1, ""}, + {&api.HTTPGetAction{Host: "", Port: util.NewIntOrStringFromString("-1"), Path: ""}, false, "", -1, ""}, + {&api.HTTPGetAction{Host: "", Port: util.NewIntOrStringFromString("not-found"), Path: ""}, false, "", -1, ""}, + {&api.HTTPGetAction{Host: "", Port: util.NewIntOrStringFromString("found"), Path: ""}, true, "127.0.0.1", 93, ""}, + {&api.HTTPGetAction{Host: "", Port: util.NewIntOrStringFromInt(76), Path: ""}, true, "127.0.0.1", 76, ""}, + {&api.HTTPGetAction{Host: "", Port: util.NewIntOrStringFromString("118"), Path: ""}, true, "127.0.0.1", 118, ""}, + {&api.HTTPGetAction{Host: "hostname", Port: util.NewIntOrStringFromInt(76), Path: "path"}, true, "hostname", 76, "path"}, + } + + for _, test := range testCases { + state := api.PodStatus{PodIP: "127.0.0.1"} + container := api.Container{ + Ports: []api.Port{{Name: "found", HostPort: 93}}, + LivenessProbe: &api.LivenessProbe{ + HTTPGet: test.probe, + }, + } + p, err := extractPort(test.probe.Port, container) + if test.ok && err != nil { + t.Errorf("Unexpected error: %v", err) + } + host, port, path := extractGetParams(test.probe, state, p) + if !test.ok && err == nil { + t.Errorf("Expected error for %+v, got %s:%d/%s", test, host, port, path) + } + if test.ok { + if host != test.host || port != test.port || path != test.path { + t.Errorf("Expected %s:%d/%s, got %s:%d/%s", + test.host, test.port, test.path, host, port, path) + } + } + } +} + +func TestGetTCPAddrParts(t *testing.T) { + testCases := []struct { + probe *api.TCPSocketAction + ok bool + host string + port int + }{ + {&api.TCPSocketAction{Port: util.NewIntOrStringFromInt(-1)}, false, "", -1}, + {&api.TCPSocketAction{Port: util.NewIntOrStringFromString("")}, false, "", -1}, + {&api.TCPSocketAction{Port: util.NewIntOrStringFromString("-1")}, false, "", -1}, + {&api.TCPSocketAction{Port: util.NewIntOrStringFromString("not-found")}, false, "", -1}, + {&api.TCPSocketAction{Port: util.NewIntOrStringFromString("found")}, true, "1.2.3.4", 93}, + {&api.TCPSocketAction{Port: util.NewIntOrStringFromInt(76)}, true, "1.2.3.4", 76}, + {&api.TCPSocketAction{Port: util.NewIntOrStringFromString("118")}, true, "1.2.3.4", 118}, + } + + for _, test := range testCases { + host := "1.2.3.4" + container := api.Container{ + Ports: []api.Port{{Name: "found", HostPort: 93}}, + LivenessProbe: &api.LivenessProbe{ + TCPSocket: test.probe, + }, + } + port, err := extractPort(test.probe.Port, container) + if !test.ok && err == nil { + t.Errorf("Expected error for %+v, got %s:%d", test, host, port) + } + if test.ok && err != nil { + t.Errorf("Unexpected error: %v", err) + } + if test.ok { + if host != test.host || port != test.port { + t.Errorf("Expected %s:%d, got %s:%d", test.host, test.port, host, port) + } + } + } +} diff --git a/pkg/probe/exec/exec_test.go b/pkg/probe/exec/exec_test.go new file mode 100644 index 0000000000..78c1a76be0 --- /dev/null +++ b/pkg/probe/exec/exec_test.go @@ -0,0 +1,68 @@ +/* +Copyright 2015 Google Inc. All rights reserved. + +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 exec + +import ( + "fmt" + "testing" + + "github.com/GoogleCloudPlatform/kubernetes/pkg/probe" +) + +type FakeCmd struct { + out []byte + err error +} + +func (f *FakeCmd) CombinedOutput() ([]byte, error) { + return f.out, f.err +} + +func (f *FakeCmd) SetDir(dir string) {} + +type healthCheckTest struct { + expectedStatus probe.Status + expectError bool + output []byte + err error +} + +func TestExec(t *testing.T) { + fake := FakeCmd{} + tests := []healthCheckTest{ + // Ok + {probe.Healthy, false, []byte("OK"), nil}, + // Run returns error + {probe.Unknown, true, []byte("OK, NOT"), fmt.Errorf("test error")}, + // Unhealthy + {probe.Unhealthy, false, []byte("Fail"), nil}, + } + for _, test := range tests { + fake.out = test.output + fake.err = test.err + status, err := Probe(&fake) + if status != test.expectedStatus { + t.Errorf("expected %v, got %v", test.expectedStatus, status) + } + if err != nil && test.expectError == false { + t.Errorf("unexpected error: %v", err) + } + if err == nil && test.expectError == true { + t.Errorf("unexpected non-error") + } + } +} diff --git a/pkg/probe/http/http_test.go b/pkg/probe/http/http_test.go new file mode 100644 index 0000000000..146bf21b5c --- /dev/null +++ b/pkg/probe/http/http_test.go @@ -0,0 +1,84 @@ +/* +Copyright 2015 Google Inc. All rights reserved. + +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 http + +import ( + "net" + "net/http" + "net/http/httptest" + "net/url" + "strconv" + "testing" + + "github.com/GoogleCloudPlatform/kubernetes/pkg/probe" +) + +func TestFormatURL(t *testing.T) { + testCases := []struct { + host string + port int + path string + result string + }{ + {"localhost", 93, "", "http://localhost:93"}, + {"localhost", 93, "/path", "http://localhost:93/path"}, + } + for _, test := range testCases { + url := formatURL(test.host, test.port, test.path) + if url != test.result { + t.Errorf("Expected %s, got %s", test.result, url) + } + } +} + +func TestHTTPProbeChecker(t *testing.T) { + testCases := []struct { + status int + health probe.Status + }{ + // The probe will be filled in below. This is primarily testing that an HTTP GET happens. + {http.StatusOK, probe.Healthy}, + {-1, probe.Unhealthy}, + } + for _, test := range testCases { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(test.status) + })) + u, err := url.Parse(ts.URL) + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + host, port, err := net.SplitHostPort(u.Host) + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + p, err := strconv.Atoi(port) + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + health, err := Probe(host, p, "") + if test.health == probe.Unknown && err == nil { + t.Errorf("Expected error") + } + if test.health != probe.Unknown && err != nil { + t.Errorf("Unexpected error: %v", err) + } + if health != test.health { + t.Errorf("Expected %v, got %v", test.health, health) + } + } +} diff --git a/pkg/probe/tcp/tcp_test.go b/pkg/probe/tcp/tcp_test.go new file mode 100644 index 0000000000..2673ee3bcc --- /dev/null +++ b/pkg/probe/tcp/tcp_test.go @@ -0,0 +1,72 @@ +/* +Copyright 2015 Google Inc. All rights reserved. + +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 tcp + +import ( + "net" + "net/http" + "net/http/httptest" + "net/url" + "strconv" + "testing" + + "github.com/GoogleCloudPlatform/kubernetes/pkg/probe" +) + +func TestTcpHealthChecker(t *testing.T) { + tests := []struct { + expectedStatus probe.Status + usePort bool + expectError bool + }{ + // The probe will be filled in below. This is primarily testing that a connection is made. + {probe.Healthy, true, false}, + {probe.Unhealthy, false, false}, + } + + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + })) + defer server.Close() + u, err := url.Parse(server.URL) + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + host, port, err := net.SplitHostPort(u.Host) + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + for _, test := range tests { + p, err := strconv.Atoi(port) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + if !test.usePort { + p = -1 + } + status, err := Probe(host, p) + if status != test.expectedStatus { + t.Errorf("expected: %v, got: %v", test.expectedStatus, status) + } + if err != nil && !test.expectError { + t.Errorf("unexpected error: %v", err) + } + if err == nil && test.expectError { + t.Errorf("unexpected non-error.") + } + } +}