From f38c607285e2ad07a912635e077efe47ae82667a Mon Sep 17 00:00:00 2001 From: Victor Marmol Date: Tue, 28 Apr 2015 16:05:26 -0700 Subject: [PATCH] Move Prober to its own subpackage. This will allow it to be reused by the runtimes. --- pkg/kubelet/container/helpers.go | 6 - pkg/kubelet/kubelet.go | 5 +- pkg/kubelet/kubelet_test.go | 289 +++++++++++++- pkg/kubelet/probe_test.go | 425 --------------------- pkg/kubelet/{probe.go => prober/prober.go} | 28 +- pkg/kubelet/prober/prober_test.go | 141 +++++++ 6 files changed, 456 insertions(+), 438 deletions(-) delete mode 100644 pkg/kubelet/probe_test.go rename pkg/kubelet/{probe.go => prober/prober.go} (92%) create mode 100644 pkg/kubelet/prober/prober_test.go diff --git a/pkg/kubelet/container/helpers.go b/pkg/kubelet/container/helpers.go index 0e66f511c2e..55432b2e092 100644 --- a/pkg/kubelet/container/helpers.go +++ b/pkg/kubelet/container/helpers.go @@ -20,7 +20,6 @@ import ( "strings" "github.com/GoogleCloudPlatform/kubernetes/pkg/api" - "github.com/GoogleCloudPlatform/kubernetes/pkg/probe" ) // HandlerRunner runs a lifecycle handler for a container. @@ -28,11 +27,6 @@ type HandlerRunner interface { Run(containerID string, pod *api.Pod, container *api.Container, handler *api.Handler) error } -// Prober checks the healthiness of a container. -type Prober interface { - Probe(pod *api.Pod, status api.PodStatus, container api.Container, containerID string, createdAt int64) (probe.Result, error) -} - // RunContainerOptionsGenerator generates the options that necessary for // container runtime to run a container. // TODO(yifan): Remove netMode, ipcMode. diff --git a/pkg/kubelet/kubelet.go b/pkg/kubelet/kubelet.go index 12c893683a2..e57484a4fd0 100644 --- a/pkg/kubelet/kubelet.go +++ b/pkg/kubelet/kubelet.go @@ -43,6 +43,7 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/envvars" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/metrics" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/network" + "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/prober" kubeletTypes "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/types" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" "github.com/GoogleCloudPlatform/kubernetes/pkg/probe" @@ -255,7 +256,7 @@ func NewMainKubelet( klet.containerManager = containerManager klet.podManager = newBasicPodManager(klet.kubeClient) - klet.prober = newProber(klet.runner, klet.readinessManager, klet.containerRefManager, klet.recorder) + klet.prober = prober.New(klet.runner, klet.readinessManager, klet.containerRefManager, klet.recorder) klet.handlerRunner = newHandlerRunner(klet.httpClient, klet.runner, klet.containerManager) runtimeCache, err := kubecontainer.NewRuntimeCache(containerManager) @@ -341,7 +342,7 @@ type Kubelet struct { networkPlugin network.NetworkPlugin // Healthy check prober. - prober kubecontainer.Prober + prober prober.Prober // Container lifecycle handler runner. handlerRunner kubecontainer.HandlerRunner diff --git a/pkg/kubelet/kubelet_test.go b/pkg/kubelet/kubelet_test.go index 5fd1b21712f..8e0487871d8 100644 --- a/pkg/kubelet/kubelet_test.go +++ b/pkg/kubelet/kubelet_test.go @@ -45,8 +45,11 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/dockertools" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/metrics" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/network" + "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/prober" + "github.com/GoogleCloudPlatform/kubernetes/pkg/probe" "github.com/GoogleCloudPlatform/kubernetes/pkg/types" "github.com/GoogleCloudPlatform/kubernetes/pkg/util" + uexec "github.com/GoogleCloudPlatform/kubernetes/pkg/util/exec" "github.com/GoogleCloudPlatform/kubernetes/pkg/version" "github.com/GoogleCloudPlatform/kubernetes/pkg/volume" _ "github.com/GoogleCloudPlatform/kubernetes/pkg/volume/host_path" @@ -116,7 +119,7 @@ func newTestKubelet(t *testing.T) *TestKubelet { }, fakeRecorder) kubelet.containerManager.Puller = &dockertools.FakeDockerPuller{} - kubelet.prober = newProber(nil, kubelet.readinessManager, kubelet.containerRefManager, kubelet.recorder) + kubelet.prober = prober.New(nil, kubelet.readinessManager, kubelet.containerRefManager, kubelet.recorder) kubelet.handlerRunner = newHandlerRunner(&fakeHTTP{}, &fakeContainerCommandRunner{}, kubelet.containerManager) kubelet.volumeManager = newVolumeManager() return &TestKubelet{kubelet, fakeDocker, mockCadvisor, fakeKubeClient, waitGroup, fakeMirrorClient} @@ -4317,3 +4320,287 @@ func TestFilterOutTerminatedPods(t *testing.T) { t.Errorf("expected %#v, got %#v", expected, actual) } } + +type fakeExecProber struct { + result probe.Result + err error +} + +func (p fakeExecProber) Probe(_ uexec.Cmd) (probe.Result, error) { + return p.result, p.err +} + +func makeTestKubelet(result probe.Result, err error) *Kubelet { + kl := &Kubelet{ + readinessManager: kubecontainer.NewReadinessManager(), + containerRefManager: kubecontainer.NewRefManager(), + } + + // TODO(vmarmol): Fix + fakeExec := fakeExecProber{ + result: result, + err: err, + } + kl.prober = prober.NewTestProber(fakeExec, kl.readinessManager, kl.containerRefManager, &record.FakeRecorder{}) + return kl +} + +// TestProbeContainer tests the functionality of probeContainer. +// Test cases are: +// +// No probe. +// Only LivenessProbe. +// Only ReadinessProbe. +// Both probes. +// +// Also, for each probe, there will be several cases covering whether the initial +// delay has passed, whether the probe handler will return Success, Failure, +// Unknown or error. +// +func TestProbeContainer(t *testing.T) { + dc := &docker.APIContainers{ + ID: "foobar", + Created: time.Now().Unix(), + } + tests := []struct { + testContainer api.Container + expectError bool + expectedResult probe.Result + expectedReadiness bool + }{ + // No probes. + { + testContainer: api.Container{}, + expectedResult: probe.Success, + expectedReadiness: true, + }, + // Only LivenessProbe. + { + testContainer: api.Container{ + LivenessProbe: &api.Probe{InitialDelaySeconds: 100}, + }, + expectedResult: probe.Success, + expectedReadiness: true, + }, + { + testContainer: api.Container{ + LivenessProbe: &api.Probe{InitialDelaySeconds: -100}, + }, + expectedResult: probe.Unknown, + expectedReadiness: false, + }, + { + testContainer: api.Container{ + LivenessProbe: &api.Probe{ + InitialDelaySeconds: -100, + Handler: api.Handler{ + Exec: &api.ExecAction{}, + }, + }, + }, + expectedResult: probe.Failure, + expectedReadiness: false, + }, + { + testContainer: api.Container{ + LivenessProbe: &api.Probe{ + InitialDelaySeconds: -100, + Handler: api.Handler{ + Exec: &api.ExecAction{}, + }, + }, + }, + expectedResult: probe.Success, + expectedReadiness: true, + }, + { + testContainer: api.Container{ + LivenessProbe: &api.Probe{ + InitialDelaySeconds: -100, + Handler: api.Handler{ + Exec: &api.ExecAction{}, + }, + }, + }, + expectedResult: probe.Unknown, + expectedReadiness: false, + }, + { + testContainer: api.Container{ + LivenessProbe: &api.Probe{ + InitialDelaySeconds: -100, + Handler: api.Handler{ + Exec: &api.ExecAction{}, + }, + }, + }, + expectError: true, + expectedResult: probe.Unknown, + expectedReadiness: false, + }, + // Only ReadinessProbe. + { + testContainer: api.Container{ + ReadinessProbe: &api.Probe{InitialDelaySeconds: 100}, + }, + expectedResult: probe.Success, + expectedReadiness: false, + }, + { + testContainer: api.Container{ + ReadinessProbe: &api.Probe{InitialDelaySeconds: -100}, + }, + expectedResult: probe.Success, + expectedReadiness: false, + }, + { + testContainer: api.Container{ + ReadinessProbe: &api.Probe{ + InitialDelaySeconds: -100, + Handler: api.Handler{ + Exec: &api.ExecAction{}, + }, + }, + }, + expectedResult: probe.Success, + expectedReadiness: true, + }, + { + testContainer: api.Container{ + ReadinessProbe: &api.Probe{ + InitialDelaySeconds: -100, + Handler: api.Handler{ + Exec: &api.ExecAction{}, + }, + }, + }, + expectedResult: probe.Success, + expectedReadiness: true, + }, + { + testContainer: api.Container{ + ReadinessProbe: &api.Probe{ + InitialDelaySeconds: -100, + Handler: api.Handler{ + Exec: &api.ExecAction{}, + }, + }, + }, + expectedResult: probe.Success, + expectedReadiness: true, + }, + { + testContainer: api.Container{ + ReadinessProbe: &api.Probe{ + InitialDelaySeconds: -100, + Handler: api.Handler{ + Exec: &api.ExecAction{}, + }, + }, + }, + expectError: false, + expectedResult: probe.Success, + expectedReadiness: true, + }, + // Both LivenessProbe and ReadinessProbe. + { + testContainer: api.Container{ + LivenessProbe: &api.Probe{InitialDelaySeconds: 100}, + ReadinessProbe: &api.Probe{InitialDelaySeconds: 100}, + }, + expectedResult: probe.Success, + expectedReadiness: false, + }, + { + testContainer: api.Container{ + LivenessProbe: &api.Probe{InitialDelaySeconds: 100}, + ReadinessProbe: &api.Probe{InitialDelaySeconds: -100}, + }, + expectedResult: probe.Success, + expectedReadiness: false, + }, + { + testContainer: api.Container{ + LivenessProbe: &api.Probe{InitialDelaySeconds: -100}, + ReadinessProbe: &api.Probe{InitialDelaySeconds: 100}, + }, + expectedResult: probe.Unknown, + expectedReadiness: false, + }, + { + testContainer: api.Container{ + LivenessProbe: &api.Probe{InitialDelaySeconds: -100}, + ReadinessProbe: &api.Probe{InitialDelaySeconds: -100}, + }, + expectedResult: probe.Unknown, + expectedReadiness: false, + }, + { + testContainer: api.Container{ + LivenessProbe: &api.Probe{ + InitialDelaySeconds: -100, + Handler: api.Handler{ + Exec: &api.ExecAction{}, + }, + }, + ReadinessProbe: &api.Probe{InitialDelaySeconds: -100}, + }, + expectedResult: probe.Unknown, + expectedReadiness: false, + }, + { + testContainer: api.Container{ + LivenessProbe: &api.Probe{ + InitialDelaySeconds: -100, + Handler: api.Handler{ + Exec: &api.ExecAction{}, + }, + }, + ReadinessProbe: &api.Probe{InitialDelaySeconds: -100}, + }, + expectedResult: probe.Failure, + expectedReadiness: false, + }, + { + testContainer: api.Container{ + LivenessProbe: &api.Probe{ + InitialDelaySeconds: -100, + Handler: api.Handler{ + Exec: &api.ExecAction{}, + }, + }, + ReadinessProbe: &api.Probe{ + InitialDelaySeconds: -100, + Handler: api.Handler{ + Exec: &api.ExecAction{}, + }, + }, + }, + expectedResult: probe.Success, + expectedReadiness: true, + }, + } + + for _, test := range tests { + var kl *Kubelet + + if test.expectError { + kl = makeTestKubelet(test.expectedResult, errors.New("error")) + } else { + kl = makeTestKubelet(test.expectedResult, nil) + } + result, err := kl.prober.Probe(&api.Pod{}, api.PodStatus{}, test.testContainer, dc.ID, dc.Created) + if test.expectError && err == nil { + t.Error("Expected error but did no error was returned.") + } + if !test.expectError && err != nil { + t.Errorf("Expected error but got: %v", err) + } + if test.expectedResult != result { + t.Errorf("Expected result was %v but probeContainer() returned %v", test.expectedResult, result) + } + if test.expectedReadiness != kl.readinessManager.GetReadiness(dc.ID) { + t.Errorf("Expected readiness was %v but probeContainer() set %v", test.expectedReadiness, kl.readinessManager.GetReadiness(dc.ID)) + } + } +} diff --git a/pkg/kubelet/probe_test.go b/pkg/kubelet/probe_test.go deleted file mode 100644 index c0f3fe47763..00000000000 --- a/pkg/kubelet/probe_test.go +++ /dev/null @@ -1,425 +0,0 @@ -/* -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 ( - "errors" - "testing" - "time" - - "github.com/GoogleCloudPlatform/kubernetes/pkg/api" - "github.com/GoogleCloudPlatform/kubernetes/pkg/client/record" - kubecontainer "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/container" - "github.com/GoogleCloudPlatform/kubernetes/pkg/probe" - "github.com/GoogleCloudPlatform/kubernetes/pkg/util" - "github.com/GoogleCloudPlatform/kubernetes/pkg/util/exec" - - "github.com/fsouza/go-dockerclient" -) - -func TestFindPortByName(t *testing.T) { - container := api.Container{ - Ports: []api.ContainerPort{ - { - 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.ContainerPort{{Name: "found", HostPort: 93}}, - LivenessProbe: &api.Probe{ - Handler: api.Handler{ - 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.ContainerPort{{Name: "found", HostPort: 93}}, - LivenessProbe: &api.Probe{ - Handler: api.Handler{ - 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) - } - } - } -} - -type fakeExecProber struct { - result probe.Result - err error -} - -func (p fakeExecProber) Probe(_ exec.Cmd) (probe.Result, error) { - return p.result, p.err -} - -func makeTestKubelet(result probe.Result, err error) *Kubelet { - kl := &Kubelet{ - readinessManager: kubecontainer.NewReadinessManager(), - containerRefManager: kubecontainer.NewRefManager(), - } - - kl.prober = &prober{ - exec: fakeExecProber{ - result: result, - err: err, - }, - readinessManager: kl.readinessManager, - refManager: kl.containerRefManager, - recorder: &record.FakeRecorder{}, - } - return kl -} - -// TestProbeContainer tests the functionality of probeContainer. -// Test cases are: -// -// No probe. -// Only LivenessProbe. -// Only ReadinessProbe. -// Both probes. -// -// Also, for each probe, there will be several cases covering whether the initial -// delay has passed, whether the probe handler will return Success, Failure, -// Unknown or error. -// -func TestProbeContainer(t *testing.T) { - dc := &docker.APIContainers{ - ID: "foobar", - Created: time.Now().Unix(), - } - tests := []struct { - testContainer api.Container - expectError bool - expectedResult probe.Result - expectedReadiness bool - }{ - // No probes. - { - testContainer: api.Container{}, - expectedResult: probe.Success, - expectedReadiness: true, - }, - // Only LivenessProbe. - { - testContainer: api.Container{ - LivenessProbe: &api.Probe{InitialDelaySeconds: 100}, - }, - expectedResult: probe.Success, - expectedReadiness: true, - }, - { - testContainer: api.Container{ - LivenessProbe: &api.Probe{InitialDelaySeconds: -100}, - }, - expectedResult: probe.Unknown, - expectedReadiness: false, - }, - { - testContainer: api.Container{ - LivenessProbe: &api.Probe{ - InitialDelaySeconds: -100, - Handler: api.Handler{ - Exec: &api.ExecAction{}, - }, - }, - }, - expectedResult: probe.Failure, - expectedReadiness: false, - }, - { - testContainer: api.Container{ - LivenessProbe: &api.Probe{ - InitialDelaySeconds: -100, - Handler: api.Handler{ - Exec: &api.ExecAction{}, - }, - }, - }, - expectedResult: probe.Success, - expectedReadiness: true, - }, - { - testContainer: api.Container{ - LivenessProbe: &api.Probe{ - InitialDelaySeconds: -100, - Handler: api.Handler{ - Exec: &api.ExecAction{}, - }, - }, - }, - expectedResult: probe.Unknown, - expectedReadiness: false, - }, - { - testContainer: api.Container{ - LivenessProbe: &api.Probe{ - InitialDelaySeconds: -100, - Handler: api.Handler{ - Exec: &api.ExecAction{}, - }, - }, - }, - expectError: true, - expectedResult: probe.Unknown, - expectedReadiness: false, - }, - // Only ReadinessProbe. - { - testContainer: api.Container{ - ReadinessProbe: &api.Probe{InitialDelaySeconds: 100}, - }, - expectedResult: probe.Success, - expectedReadiness: false, - }, - { - testContainer: api.Container{ - ReadinessProbe: &api.Probe{InitialDelaySeconds: -100}, - }, - expectedResult: probe.Success, - expectedReadiness: false, - }, - { - testContainer: api.Container{ - ReadinessProbe: &api.Probe{ - InitialDelaySeconds: -100, - Handler: api.Handler{ - Exec: &api.ExecAction{}, - }, - }, - }, - expectedResult: probe.Success, - expectedReadiness: true, - }, - { - testContainer: api.Container{ - ReadinessProbe: &api.Probe{ - InitialDelaySeconds: -100, - Handler: api.Handler{ - Exec: &api.ExecAction{}, - }, - }, - }, - expectedResult: probe.Success, - expectedReadiness: true, - }, - { - testContainer: api.Container{ - ReadinessProbe: &api.Probe{ - InitialDelaySeconds: -100, - Handler: api.Handler{ - Exec: &api.ExecAction{}, - }, - }, - }, - expectedResult: probe.Success, - expectedReadiness: true, - }, - { - testContainer: api.Container{ - ReadinessProbe: &api.Probe{ - InitialDelaySeconds: -100, - Handler: api.Handler{ - Exec: &api.ExecAction{}, - }, - }, - }, - expectError: false, - expectedResult: probe.Success, - expectedReadiness: true, - }, - // Both LivenessProbe and ReadinessProbe. - { - testContainer: api.Container{ - LivenessProbe: &api.Probe{InitialDelaySeconds: 100}, - ReadinessProbe: &api.Probe{InitialDelaySeconds: 100}, - }, - expectedResult: probe.Success, - expectedReadiness: false, - }, - { - testContainer: api.Container{ - LivenessProbe: &api.Probe{InitialDelaySeconds: 100}, - ReadinessProbe: &api.Probe{InitialDelaySeconds: -100}, - }, - expectedResult: probe.Success, - expectedReadiness: false, - }, - { - testContainer: api.Container{ - LivenessProbe: &api.Probe{InitialDelaySeconds: -100}, - ReadinessProbe: &api.Probe{InitialDelaySeconds: 100}, - }, - expectedResult: probe.Unknown, - expectedReadiness: false, - }, - { - testContainer: api.Container{ - LivenessProbe: &api.Probe{InitialDelaySeconds: -100}, - ReadinessProbe: &api.Probe{InitialDelaySeconds: -100}, - }, - expectedResult: probe.Unknown, - expectedReadiness: false, - }, - { - testContainer: api.Container{ - LivenessProbe: &api.Probe{ - InitialDelaySeconds: -100, - Handler: api.Handler{ - Exec: &api.ExecAction{}, - }, - }, - ReadinessProbe: &api.Probe{InitialDelaySeconds: -100}, - }, - expectedResult: probe.Unknown, - expectedReadiness: false, - }, - { - testContainer: api.Container{ - LivenessProbe: &api.Probe{ - InitialDelaySeconds: -100, - Handler: api.Handler{ - Exec: &api.ExecAction{}, - }, - }, - ReadinessProbe: &api.Probe{InitialDelaySeconds: -100}, - }, - expectedResult: probe.Failure, - expectedReadiness: false, - }, - { - testContainer: api.Container{ - LivenessProbe: &api.Probe{ - InitialDelaySeconds: -100, - Handler: api.Handler{ - Exec: &api.ExecAction{}, - }, - }, - ReadinessProbe: &api.Probe{ - InitialDelaySeconds: -100, - Handler: api.Handler{ - Exec: &api.ExecAction{}, - }, - }, - }, - expectedResult: probe.Success, - expectedReadiness: true, - }, - } - - for _, test := range tests { - var kl *Kubelet - - if test.expectError { - kl = makeTestKubelet(test.expectedResult, errors.New("error")) - } else { - kl = makeTestKubelet(test.expectedResult, nil) - } - result, err := kl.prober.Probe(&api.Pod{}, api.PodStatus{}, test.testContainer, dc.ID, dc.Created) - if test.expectError && err == nil { - t.Error("Expected error but did no error was returned.") - } - if !test.expectError && err != nil { - t.Errorf("Expected error but got: %v", err) - } - if test.expectedResult != result { - t.Errorf("Expected result was %v but probeContainer() returned %v", test.expectedResult, result) - } - if test.expectedReadiness != kl.readinessManager.GetReadiness(dc.ID) { - t.Errorf("Expected readiness was %v but probeContainer() set %v", test.expectedReadiness, kl.readinessManager.GetReadiness(dc.ID)) - } - } -} diff --git a/pkg/kubelet/probe.go b/pkg/kubelet/prober/prober.go similarity index 92% rename from pkg/kubelet/probe.go rename to pkg/kubelet/prober/prober.go index 7c3bf235128..ddbd79fc87c 100644 --- a/pkg/kubelet/probe.go +++ b/pkg/kubelet/prober/prober.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package kubelet +package prober import ( "fmt" @@ -37,7 +37,12 @@ import ( const maxProbeRetries = 3 -// prober helps to check the liveness/readiness of a container. +// Prober checks the healthiness of a container. +type Prober interface { + Probe(pod *api.Pod, status api.PodStatus, container api.Container, containerID string, createdAt int64) (probe.Result, error) +} + +// Prober helps to check the liveness/readiness of a container. type prober struct { exec execprobe.ExecProber http httprobe.HTTPProber @@ -51,11 +56,11 @@ type prober struct { // NewProber creates a Prober, it takes a command runner and // several container info managers. -func newProber( +func New( runner dockertools.ContainerCommandRunner, readinessManager *kubecontainer.ReadinessManager, refManager *kubecontainer.RefManager, - recorder record.EventRecorder) kubecontainer.Prober { + recorder record.EventRecorder) Prober { return &prober{ exec: execprobe.New(), @@ -69,6 +74,21 @@ func newProber( } } +// New prober for use in tests. +func NewTestProber( + exec execprobe.ExecProber, + readinessManager *kubecontainer.ReadinessManager, + refManager *kubecontainer.RefManager, + recorder record.EventRecorder) Prober { + + return &prober{ + exec: exec, + readinessManager: readinessManager, + refManager: refManager, + recorder: recorder, + } +} + // Probe checks the liveness/readiness of the given container. // If the container's liveness probe is unsuccessful, set readiness to false. // If liveness is successful, do a readiness check and set readiness accordingly. diff --git a/pkg/kubelet/prober/prober_test.go b/pkg/kubelet/prober/prober_test.go new file mode 100644 index 00000000000..d518715941f --- /dev/null +++ b/pkg/kubelet/prober/prober_test.go @@ -0,0 +1,141 @@ +/* +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 prober + +import ( + "testing" + + "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + "github.com/GoogleCloudPlatform/kubernetes/pkg/probe" + "github.com/GoogleCloudPlatform/kubernetes/pkg/util" + "github.com/GoogleCloudPlatform/kubernetes/pkg/util/exec" +) + +func TestFindPortByName(t *testing.T) { + container := api.Container{ + Ports: []api.ContainerPort{ + { + 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.ContainerPort{{Name: "found", HostPort: 93}}, + LivenessProbe: &api.Probe{ + Handler: api.Handler{ + 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.ContainerPort{{Name: "found", HostPort: 93}}, + LivenessProbe: &api.Probe{ + Handler: api.Handler{ + 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) + } + } + } +} + +type FakeExecProber struct { + result probe.Result + err error +} + +func (p FakeExecProber) Probe(_ exec.Cmd) (probe.Result, error) { + return p.result, p.err +}