mirror of https://github.com/k3s-io/k3s.git
Move Prober to its own subpackage.
This will allow it to be reused by the runtimes.pull/6/head
parent
33b8f487f6
commit
f38c607285
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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.
|
|
@ -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
|
||||
}
|
Loading…
Reference in New Issue