Merge pull request #15967 from timstclair/probe-params

Auto commit by PR queue bot
pull/6/head
k8s-merge-robot 2015-11-07 01:35:18 -08:00
commit f88550ad5b
29 changed files with 24536 additions and 23996 deletions

View File

@ -13524,7 +13524,7 @@
},
"v1.Probe": {
"id": "v1.Probe",
"description": "Probe describes a liveness probe to be examined to the container.",
"description": "Probe describes a health check to be performed against a container to determine whether it is alive or ready to recieve traffic.",
"properties": {
"exec": {
"$ref": "v1.ExecAction",
@ -13546,7 +13546,22 @@
"timeoutSeconds": {
"type": "integer",
"format": "int64",
"description": "Number of seconds after which liveness probes timeout. Defaults to 1 second. More info: http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#container-probes"
"description": "Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#container-probes"
},
"periodSeconds": {
"type": "integer",
"format": "int64",
"description": "How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1."
},
"successThreshold": {
"type": "integer",
"format": "int32",
"description": "Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness. Minimum value is 1."
},
"failureThreshold": {
"type": "integer",
"format": "int32",
"description": "Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1."
}
}
},

View File

@ -3768,7 +3768,7 @@
},
"v1.Probe": {
"id": "v1.Probe",
"description": "Probe describes a liveness probe to be examined to the container.",
"description": "Probe describes a health check to be performed against a container to determine whether it is alive or ready to recieve traffic.",
"properties": {
"exec": {
"$ref": "v1.ExecAction",
@ -3790,7 +3790,22 @@
"timeoutSeconds": {
"type": "integer",
"format": "int64",
"description": "Number of seconds after which liveness probes timeout. Defaults to 1 second. More info: http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#container-probes"
"description": "Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#container-probes"
},
"periodSeconds": {
"type": "integer",
"format": "int64",
"description": "How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1."
},
"successThreshold": {
"type": "integer",
"format": "int32",
"description": "Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness. Minimum value is 1."
},
"failureThreshold": {
"type": "integer",
"format": "int32",
"description": "Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1."
}
}
},

View File

@ -3257,7 +3257,7 @@ Populated by the system when a graceful deletion is requested. Read-only. More i
<div class="sect2">
<h3 id="_v1_probe">v1.Probe</h3>
<div class="paragraph">
<p>Probe describes a liveness probe to be examined to the container.</p>
<p>Probe describes a health check to be performed against a container to determine whether it is alive or ready to recieve traffic.</p>
</div>
<table class="tableblock frame-all grid-all" style="width:100%; ">
<colgroup>
@ -3307,11 +3307,32 @@ Populated by the system when a graceful deletion is requested. Read-only. More i
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">timeoutSeconds</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Number of seconds after which liveness probes timeout. Defaults to 1 second. More info: <a href="http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#container-probes">http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#container-probes</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: <a href="http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#container-probes">http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#container-probes</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">integer (int64)</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">periodSeconds</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">integer (int64)</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">successThreshold</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness. Minimum value is 1.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">integer (int32)</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">failureThreshold</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">integer (int32)</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>
@ -4240,7 +4261,7 @@ Populated by the system when a graceful deletion is requested. Read-only. More i
</div>
<div id="footer">
<div id="footer-text">
Last updated 2015-11-04 22:53:25 UTC
Last updated 2015-11-06 18:46:07 UTC
</div>
</div>
</body>

View File

@ -5095,7 +5095,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
</div>
<div id="footer">
<div id="footer-text">
Last updated 2015-11-04 22:53:25 UTC
Last updated 2015-11-06 18:46:07 UTC
</div>
</div>
</body>

View File

@ -3023,7 +3023,7 @@ The resulting set of endpoints can be viewed as:<br>
<div class="sect2">
<h3 id="_v1_probe">v1.Probe</h3>
<div class="paragraph">
<p>Probe describes a liveness probe to be examined to the container.</p>
<p>Probe describes a health check to be performed against a container to determine whether it is alive or ready to recieve traffic.</p>
</div>
<table class="tableblock frame-all grid-all" style="width:100%; ">
<colgroup>
@ -3073,11 +3073,32 @@ The resulting set of endpoints can be viewed as:<br>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">timeoutSeconds</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Number of seconds after which liveness probes timeout. Defaults to 1 second. More info: <a href="http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#container-probes">http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#container-probes</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: <a href="http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#container-probes">http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#container-probes</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">integer (int64)</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">periodSeconds</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">integer (int64)</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">successThreshold</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness. Minimum value is 1.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">integer (int32)</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">failureThreshold</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">integer (int32)</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>
@ -6866,7 +6887,7 @@ The resulting set of endpoints can be viewed as:<br>
</div>
<div id="footer">
<div id="footer-text">
Last updated 2015-11-04 22:53:19 UTC
Last updated 2015-11-06 18:46:00 UTC
</div>
</div>
</body>

View File

@ -23664,7 +23664,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
</div>
<div id="footer">
<div id="footer-text">
Last updated 2015-11-04 22:53:19 UTC
Last updated 2015-11-06 18:46:00 UTC
</div>
</div>
</body>

View File

@ -1694,6 +1694,9 @@ func deepCopy_api_Probe(in Probe, out *Probe, c *conversion.Cloner) error {
}
out.InitialDelaySeconds = in.InitialDelaySeconds
out.TimeoutSeconds = in.TimeoutSeconds
out.PeriodSeconds = in.PeriodSeconds
out.SuccessThreshold = in.SuccessThreshold
out.FailureThreshold = in.FailureThreshold
return nil
}

View File

@ -283,6 +283,18 @@ func FuzzerFor(t *testing.T, version string, src rand.Source) *fuzz.Fuzzer {
c.FuzzNoCustom(ct) // fuzz self without calling this function again
ct.TerminationMessagePath = "/" + ct.TerminationMessagePath // Must be non-empty
},
func(p *api.Probe, c fuzz.Continue) {
c.FuzzNoCustom(p)
// These fields have default values.
intFieldsWithDefaults := [...]string{"TimeoutSeconds", "PeriodSeconds", "SuccessThreshold", "FailureThreshold"}
v := reflect.ValueOf(p).Elem()
for _, field := range intFieldsWithDefaults {
f := v.FieldByName(field)
if f.Int() == 0 {
f.SetInt(1)
}
}
},
func(ev *api.EnvVar, c fuzz.Continue) {
ev.Name = c.RandString()
if c.RandBool() {

File diff suppressed because it is too large Load Diff

View File

@ -713,7 +713,8 @@ type ExecAction struct {
Command []string `json:"command,omitempty"`
}
// Probe describes a liveness probe to be examined to the container.
// Probe describes a health check to be performed against a container to determine whether it is
// alive or ready to recieve traffic.
type Probe struct {
// The action taken to determine the health of a container
Handler `json:",inline"`
@ -721,6 +722,13 @@ type Probe struct {
InitialDelaySeconds int64 `json:"initialDelaySeconds,omitempty"`
// Length of time before health checking times out. In seconds.
TimeoutSeconds int64 `json:"timeoutSeconds,omitempty"`
// How often (in seconds) to perform the probe.
PeriodSeconds int64 `json:"periodSeconds,omitempty"`
// Minimum consecutive successes for the probe to be considered successful after having failed.
// Must be 1 for liveness.
SuccessThreshold int `json:"successThreshold,omitempty"`
// Minimum consecutive failures for the probe to be considered failed after having succeeded.
FailureThreshold int `json:"failureThreshold,omitempty"`
}
// PullPolicy describes a policy for if/when to pull a container image

View File

@ -2243,6 +2243,9 @@ func autoconvert_api_Probe_To_v1_Probe(in *api.Probe, out *Probe, s conversion.S
}
out.InitialDelaySeconds = in.InitialDelaySeconds
out.TimeoutSeconds = in.TimeoutSeconds
out.PeriodSeconds = in.PeriodSeconds
out.SuccessThreshold = in.SuccessThreshold
out.FailureThreshold = in.FailureThreshold
return nil
}
@ -5268,6 +5271,9 @@ func autoconvert_v1_Probe_To_api_Probe(in *Probe, out *api.Probe, s conversion.S
}
out.InitialDelaySeconds = in.InitialDelaySeconds
out.TimeoutSeconds = in.TimeoutSeconds
out.PeriodSeconds = in.PeriodSeconds
out.SuccessThreshold = in.SuccessThreshold
out.FailureThreshold = in.FailureThreshold
return nil
}

View File

@ -1713,6 +1713,9 @@ func deepCopy_v1_Probe(in Probe, out *Probe, c *conversion.Cloner) error {
}
out.InitialDelaySeconds = in.InitialDelaySeconds
out.TimeoutSeconds = in.TimeoutSeconds
out.PeriodSeconds = in.PeriodSeconds
out.SuccessThreshold = in.SuccessThreshold
out.FailureThreshold = in.FailureThreshold
return nil
}

View File

@ -128,6 +128,15 @@ func addDefaultingFuncs() {
if obj.TimeoutSeconds == 0 {
obj.TimeoutSeconds = 1
}
if obj.PeriodSeconds == 0 {
obj.PeriodSeconds = 10
}
if obj.SuccessThreshold == 0 {
obj.SuccessThreshold = 1
}
if obj.FailureThreshold == 0 {
obj.FailureThreshold = 3
}
},
func(obj *Secret) {
if obj.Type == "" {

View File

@ -545,3 +545,26 @@ func TestSetDefaultLimitRangeItem(t *testing.T) {
t.Errorf("Expected request memory: %s, got: %s", "100Mi", requestMinValue.String())
}
}
func TestSetDefaultProbe(t *testing.T) {
originalProbe := versioned.Probe{}
expectedProbe := versioned.Probe{
InitialDelaySeconds: 0,
TimeoutSeconds: 1,
PeriodSeconds: 10,
SuccessThreshold: 1,
FailureThreshold: 3,
}
pod := &versioned.Pod{
Spec: versioned.PodSpec{
Containers: []versioned.Container{{LivenessProbe: &originalProbe}},
},
}
output := roundTrip(t, runtime.Object(pod)).(*versioned.Pod)
actualProbe := *output.Spec.Containers[0].LivenessProbe
if actualProbe != expectedProbe {
t.Errorf("Expected probe: %+v\ngot: %+v\n", expectedProbe, actualProbe)
}
}

File diff suppressed because it is too large Load Diff

View File

@ -846,17 +846,27 @@ type ExecAction struct {
Command []string `json:"command,omitempty"`
}
// Probe describes a liveness probe to be examined to the container.
// Probe describes a health check to be performed against a container to determine whether it is
// alive or ready to recieve traffic.
type Probe struct {
// The action taken to determine the health of a container
Handler `json:",inline"`
// Number of seconds after the container has started before liveness probes are initiated.
// More info: http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#container-probes
InitialDelaySeconds int64 `json:"initialDelaySeconds,omitempty"`
// Number of seconds after which liveness probes timeout.
// Defaults to 1 second.
// Number of seconds after which the probe times out.
// Defaults to 1 second. Minimum value is 1.
// More info: http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#container-probes
TimeoutSeconds int64 `json:"timeoutSeconds,omitempty"`
// How often (in seconds) to perform the probe.
// Default to 10 seconds. Minimum value is 1.
PeriodSeconds int64 `json:"periodSeconds,omitempty"`
// Minimum consecutive successes for the probe to be considered successful after having failed.
// Defaults to 1. Must be 1 for liveness. Minimum value is 1.
SuccessThreshold int `json:"successThreshold,omitempty"`
// Minimum consecutive failures for the probe to be considered failed after having succeeded.
// Defaults to 3. Minimum value is 1.
FailureThreshold int `json:"failureThreshold,omitempty"`
}
// PullPolicy describes a policy for if/when to pull a container image

View File

@ -1068,9 +1068,12 @@ func (PodTemplateSpec) SwaggerDoc() map[string]string {
}
var map_Probe = map[string]string{
"": "Probe describes a liveness probe to be examined to the container.",
"": "Probe describes a health check to be performed against a container to determine whether it is alive or ready to recieve traffic.",
"initialDelaySeconds": "Number of seconds after the container has started before liveness probes are initiated. More info: http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#container-probes",
"timeoutSeconds": "Number of seconds after which liveness probes timeout. Defaults to 1 second. More info: http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#container-probes",
"timeoutSeconds": "Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: http://releases.k8s.io/HEAD/docs/user-guide/pod-states.md#container-probes",
"periodSeconds": "How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.",
"successThreshold": "Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness. Minimum value is 1.",
"failureThreshold": "Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.",
}
func (Probe) SwaggerDoc() map[string]string {

View File

@ -881,12 +881,11 @@ func validateProbe(probe *api.Probe) errs.ValidationErrorList {
return allErrs
}
allErrs = append(allErrs, validateHandler(&probe.Handler)...)
if probe.InitialDelaySeconds < 0 {
allErrs = append(allErrs, errs.NewFieldInvalid("initialDelay", probe.InitialDelaySeconds, "may not be less than zero"))
}
if probe.TimeoutSeconds < 0 {
allErrs = append(allErrs, errs.NewFieldInvalid("timeout", probe.TimeoutSeconds, "may not be less than zero"))
}
allErrs = append(allErrs, ValidatePositiveField(probe.InitialDelaySeconds, "initialDelaySeconds")...)
allErrs = append(allErrs, ValidatePositiveField(probe.TimeoutSeconds, "timeoutSeconds")...)
allErrs = append(allErrs, ValidatePositiveField(int64(probe.PeriodSeconds), "periodSeconds")...)
allErrs = append(allErrs, ValidatePositiveField(int64(probe.SuccessThreshold), "successThreshold")...)
allErrs = append(allErrs, ValidatePositiveField(int64(probe.FailureThreshold), "failureThreshold")...)
return allErrs
}
@ -1030,6 +1029,11 @@ func validateContainers(containers []api.Container, volumes sets.String) errs.Va
cErrs = append(cErrs, validateLifecycle(ctr.Lifecycle).Prefix("lifecycle")...)
}
cErrs = append(cErrs, validateProbe(ctr.LivenessProbe).Prefix("livenessProbe")...)
// Liveness-specific validation
if ctr.LivenessProbe != nil && ctr.LivenessProbe.SuccessThreshold != 1 {
allErrs = append(allErrs, errs.NewFieldForbidden("livenessProbe.successThreshold", "must be 1"))
}
cErrs = append(cErrs, validateProbe(ctr.ReadinessProbe).Prefix("readinessProbe")...)
cErrs = append(cErrs, validatePorts(ctr.Ports).Prefix("ports")...)
cErrs = append(cErrs, validateEnv(ctr.Env).Prefix("env")...)

View File

@ -18,6 +18,7 @@ package validation
import (
"math/rand"
"reflect"
"strings"
"testing"
"time"
@ -807,22 +808,26 @@ func TestValidateVolumeMounts(t *testing.T) {
func TestValidateProbe(t *testing.T) {
handler := api.Handler{Exec: &api.ExecAction{Command: []string{"echo"}}}
successCases := []*api.Probe{
nil,
{TimeoutSeconds: 10, InitialDelaySeconds: 0, Handler: handler},
{TimeoutSeconds: 0, InitialDelaySeconds: 10, Handler: handler},
// These fields must be positive.
positiveFields := [...]string{"InitialDelaySeconds", "TimeoutSeconds", "PeriodSeconds", "SuccessThreshold", "FailureThreshold"}
successCases := []*api.Probe{nil}
for _, field := range positiveFields {
probe := &api.Probe{Handler: handler}
reflect.ValueOf(probe).Elem().FieldByName(field).SetInt(10)
successCases = append(successCases, probe)
}
for _, p := range successCases {
if errs := validateProbe(p); len(errs) != 0 {
t.Errorf("expected success: %v", errs)
}
}
errorCases := []*api.Probe{
{TimeoutSeconds: 10, InitialDelaySeconds: 10},
{TimeoutSeconds: 10, InitialDelaySeconds: -10, Handler: handler},
{TimeoutSeconds: -10, InitialDelaySeconds: 10, Handler: handler},
{TimeoutSeconds: -10, InitialDelaySeconds: -10, Handler: handler},
errorCases := []*api.Probe{{TimeoutSeconds: 10, InitialDelaySeconds: 10}}
for _, field := range positiveFields {
probe := &api.Probe{Handler: handler}
reflect.ValueOf(probe).Elem().FieldByName(field).SetInt(-10)
errorCases = append(errorCases, probe)
}
for _, p := range errorCases {
if errs := validateProbe(p); len(errs) == 0 {

View File

@ -583,6 +583,9 @@ func deepCopy_api_Probe(in api.Probe, out *api.Probe, c *conversion.Cloner) erro
}
out.InitialDelaySeconds = in.InitialDelaySeconds
out.TimeoutSeconds = in.TimeoutSeconds
out.PeriodSeconds = in.PeriodSeconds
out.SuccessThreshold = in.SuccessThreshold
out.FailureThreshold = in.FailureThreshold
return nil
}

View File

@ -755,6 +755,9 @@ func autoconvert_api_Probe_To_v1_Probe(in *api.Probe, out *v1.Probe, s conversio
}
out.InitialDelaySeconds = in.InitialDelaySeconds
out.TimeoutSeconds = in.TimeoutSeconds
out.PeriodSeconds = in.PeriodSeconds
out.SuccessThreshold = in.SuccessThreshold
out.FailureThreshold = in.FailureThreshold
return nil
}
@ -1815,6 +1818,9 @@ func autoconvert_v1_Probe_To_api_Probe(in *v1.Probe, out *api.Probe, s conversio
}
out.InitialDelaySeconds = in.InitialDelaySeconds
out.TimeoutSeconds = in.TimeoutSeconds
out.PeriodSeconds = in.PeriodSeconds
out.SuccessThreshold = in.SuccessThreshold
out.FailureThreshold = in.FailureThreshold
return nil
}

View File

@ -620,6 +620,9 @@ func deepCopy_v1_Probe(in v1.Probe, out *v1.Probe, c *conversion.Cloner) error {
}
out.InitialDelaySeconds = in.InitialDelaySeconds
out.TimeoutSeconds = in.TimeoutSeconds
out.PeriodSeconds = in.PeriodSeconds
out.SuccessThreshold = in.SuccessThreshold
out.FailureThreshold = in.FailureThreshold
return nil
}

View File

@ -418,7 +418,6 @@ func NewMainKubelet(
klet.statusManager = status.NewManager(kubeClient, klet.podManager)
klet.probeManager = prober.NewManager(
klet.resyncInterval,
klet.statusManager,
readinessManager,
klet.livenessManager,

View File

@ -18,7 +18,6 @@ package prober
import (
"sync"
"time"
"github.com/golang/glog"
"k8s.io/kubernetes/pkg/api"
@ -71,13 +70,9 @@ type manager struct {
// prober executes the probe actions.
prober *prober
// Default period for workers to execute a probe.
defaultProbePeriod time.Duration
}
func NewManager(
defaultProbePeriod time.Duration,
statusManager status.Manager,
readinessManager results.Manager,
livenessManager results.Manager,
@ -86,12 +81,11 @@ func NewManager(
recorder record.EventRecorder) Manager {
prober := newProber(runner, refManager, recorder)
return &manager{
defaultProbePeriod: defaultProbePeriod,
statusManager: statusManager,
prober: prober,
readinessManager: readinessManager,
livenessManager: livenessManager,
workers: make(map[probeKey]*worker),
statusManager: statusManager,
prober: prober,
readinessManager: readinessManager,
livenessManager: livenessManager,
workers: make(map[probeKey]*worker),
}
}

View File

@ -34,6 +34,16 @@ import (
"k8s.io/kubernetes/pkg/util/wait"
)
var defaultProbe *api.Probe = &api.Probe{
Handler: api.Handler{
Exec: &api.ExecAction{},
},
TimeoutSeconds: 1,
PeriodSeconds: 1,
SuccessThreshold: 1,
FailureThreshold: 3,
}
func TestAddRemovePods(t *testing.T) {
noProbePod := api.Pod{
ObjectMeta: api.ObjectMeta{
@ -57,12 +67,12 @@ func TestAddRemovePods(t *testing.T) {
Name: "no_probe1",
}, {
Name: "readiness",
ReadinessProbe: &api.Probe{},
ReadinessProbe: defaultProbe,
}, {
Name: "no_probe2",
}, {
Name: "liveness",
LivenessProbe: &api.Probe{},
LivenessProbe: defaultProbe,
}},
},
}
@ -119,10 +129,10 @@ func TestCleanupPods(t *testing.T) {
Spec: api.PodSpec{
Containers: []api.Container{{
Name: "prober1",
ReadinessProbe: &api.Probe{},
ReadinessProbe: defaultProbe,
}, {
Name: "prober2",
LivenessProbe: &api.Probe{},
LivenessProbe: defaultProbe,
}},
},
}
@ -133,10 +143,10 @@ func TestCleanupPods(t *testing.T) {
Spec: api.PodSpec{
Containers: []api.Container{{
Name: "prober1",
ReadinessProbe: &api.Probe{},
ReadinessProbe: defaultProbe,
}, {
Name: "prober2",
LivenessProbe: &api.Probe{},
LivenessProbe: defaultProbe,
}},
},
}
@ -266,9 +276,7 @@ outer:
}
func newTestManager() *manager {
const probePeriod = 1
m := NewManager(
probePeriod,
status.NewManager(&testclient.Fake{}, kubepod.NewBasicPodManager(nil)),
results.NewManager(),
results.NewManager(),

View File

@ -118,8 +118,8 @@ func (pb *prober) runProbeWithRetries(p *api.Probe, pod *api.Pod, status api.Pod
var output string
for i := 0; i < retries; i++ {
result, output, err = pb.runProbe(p, pod, status, container, containerID)
if result == probe.Success {
return probe.Success, output, nil
if err == nil {
return result, output, nil
}
}
return result, output, err

View File

@ -56,6 +56,10 @@ type worker struct {
// The last known container ID for this worker.
containerID kubecontainer.ContainerID
// The last probe result for this worker.
lastResult results.Result
// How many times in a row the probe has returned the same result.
resultRun int
}
// Creates and starts a new probe worker.
@ -89,7 +93,7 @@ func newWorker(
// run periodically probes the container.
func (w *worker) run() {
probeTicker := time.NewTicker(w.probeManager.defaultProbePeriod)
probeTicker := time.NewTicker(time.Duration(w.spec.PeriodSeconds) * time.Second)
defer func() {
// Clean up.
@ -145,6 +149,7 @@ func (w *worker) doProbe() (keepGoing bool) {
w.resultsManager.Remove(w.containerID)
}
w.containerID = kubecontainer.ParseContainerID(c.ContainerID)
w.resultsManager.Set(w.containerID, w.initialValue, w.pod)
}
if c.State.Running == nil {
@ -159,14 +164,29 @@ func (w *worker) doProbe() (keepGoing bool) {
}
if int64(time.Since(c.State.Running.StartedAt.Time).Seconds()) < w.spec.InitialDelaySeconds {
w.resultsManager.Set(w.containerID, w.initialValue, w.pod)
return true
}
result, err := w.probeManager.prober.probe(w.probeType, w.pod, status, w.container, w.containerID)
if err == nil {
w.resultsManager.Set(w.containerID, result, w.pod)
if err != nil {
// Prober error, throw away the result.
return true
}
if w.lastResult == result {
w.resultRun++
} else {
w.lastResult = result
w.resultRun = 1
}
if (result == results.Failure && w.resultRun < w.spec.FailureThreshold) ||
(result == results.Success && w.resultRun < w.spec.SuccessThreshold) {
// Success or failure is below threshold - leave the probe state unchanged.
return true
}
w.resultsManager.Set(w.containerID, result, w.pod)
return true
}

View File

@ -18,6 +18,7 @@ package prober
import (
"fmt"
"reflect"
"testing"
"time"
@ -135,18 +136,8 @@ func TestInitialDelay(t *testing.T) {
})
m.statusManager.SetPodStatus(w.pod, getRunningStatus())
if !w.doProbe() {
t.Errorf("[%s] Expected to continue, but did not", probeType)
}
expectedResult := results.Result(probeType == liveness)
result, ok := resultsManager(m, probeType).Get(containerID)
if !ok {
t.Errorf("[%s] Expected result to be set during initial delay, but was not set", probeType)
} else if result != expectedResult {
t.Errorf("[%s] Expected result to be %v during initial delay, but was %v",
probeType, expectedResult, result)
}
expectContinue(t, w, w.doProbe(), "during initial delay")
expectResult(t, w, results.Result(probeType == liveness), "during initial delay")
// 100 seconds later...
laterStatus := getRunningStatus()
@ -155,16 +146,76 @@ func TestInitialDelay(t *testing.T) {
m.statusManager.SetPodStatus(w.pod, laterStatus)
// Second call should succeed (already waited).
if !w.doProbe() {
t.Errorf("[%s] Expected to continue, but did not", probeType)
expectContinue(t, w, w.doProbe(), "after initial delay")
expectResult(t, w, results.Success, "after initial delay")
}
}
func TestFailureThreshold(t *testing.T) {
m := newTestManager()
w := newTestWorker(m, readiness, api.Probe{})
m.statusManager.SetPodStatus(w.pod, getRunningStatus())
for i := 0; i < 2; i++ {
// First probe should succeed.
m.prober.exec = fakeExecProber{probe.Success, nil}
for j := 0; j < 3; j++ {
msg := fmt.Sprintf("%d success (%d)", j+1, i)
expectContinue(t, w, w.doProbe(), msg)
expectResult(t, w, results.Success, msg)
}
result, ok = resultsManager(m, probeType).Get(containerID)
if !ok {
t.Errorf("[%s] Expected result to be true, but was not set", probeType)
} else if !result {
t.Errorf("[%s] Expected result to be true, but was false", probeType)
// Prober starts failing :(
m.prober.exec = fakeExecProber{probe.Failure, nil}
// Next 2 probes should still be "success".
for j := 0; j < 2; j++ {
msg := fmt.Sprintf("%d failure (%d)", j+1, i)
expectContinue(t, w, w.doProbe(), msg)
expectResult(t, w, results.Success, msg)
}
// Third & following fail.
for j := 0; j < 3; j++ {
msg := fmt.Sprintf("%d failure (%d)", j+3, i)
expectContinue(t, w, w.doProbe(), msg)
expectResult(t, w, results.Failure, msg)
}
}
}
func TestSuccessThreshold(t *testing.T) {
m := newTestManager()
w := newTestWorker(m, readiness, api.Probe{SuccessThreshold: 3, FailureThreshold: 1})
m.statusManager.SetPodStatus(w.pod, getRunningStatus())
// Start out failure.
w.resultsManager.Set(containerID, results.Failure, nil)
for i := 0; i < 2; i++ {
// Probe defaults to Failure.
for j := 0; j < 2; j++ {
msg := fmt.Sprintf("%d success (%d)", j+1, i)
expectContinue(t, w, w.doProbe(), msg)
expectResult(t, w, results.Failure, msg)
}
// Continuing success!
for j := 0; j < 3; j++ {
msg := fmt.Sprintf("%d success (%d)", j+3, i)
expectContinue(t, w, w.doProbe(), msg)
expectResult(t, w, results.Success, msg)
}
// Prober flakes :(
m.prober.exec = fakeExecProber{probe.Failure, nil}
msg := fmt.Sprintf("1 failure (%d)", i)
expectContinue(t, w, w.doProbe(), msg)
expectResult(t, w, results.Failure, msg)
// Back to success.
m.prober.exec = fakeExecProber{probe.Success, nil}
}
}
@ -205,22 +256,22 @@ func TestCleanUp(t *testing.T) {
func TestHandleCrash(t *testing.T) {
m := newTestManager()
w := newTestWorker(m, readiness, api.Probe{})
m.statusManager.SetPodStatus(w.pod, getRunningStatus())
expectContinue(t, w, w.doProbe(), "Initial successful probe.")
expectResult(t, w, results.Success, "Initial successful probe.")
// Prober starts crashing.
m.prober = &prober{
refManager: kubecontainer.NewRefManager(),
recorder: &record.FakeRecorder{},
exec: crashingExecProber{},
}
w := newTestWorker(m, readiness, api.Probe{})
m.statusManager.SetPodStatus(w.pod, getRunningStatus())
// doProbe should recover from the crash, and keep going.
if !w.doProbe() {
t.Error("Expected to keep going, but terminated.")
}
if _, ok := m.readinessManager.Get(containerID); ok {
t.Error("Expected readiness to be unchanged from crash.")
}
expectContinue(t, w, w.doProbe(), "Crashing probe.")
expectResult(t, w, results.Success, "Crashing probe unchanged.")
}
func newTestWorker(m *manager, probeType probeType, probeSpec api.Probe) *worker {
@ -228,6 +279,19 @@ func newTestWorker(m *manager, probeType probeType, probeSpec api.Probe) *worker
probeSpec.Handler = api.Handler{
Exec: &api.ExecAction{},
}
// Apply default values.
defaults := map[string]int64{
"TimeoutSeconds": 1,
"PeriodSeconds": 10,
"SuccessThreshold": 1,
"FailureThreshold": 3,
}
for field, value := range defaults {
f := reflect.ValueOf(&probeSpec).Elem().FieldByName(field)
if f.Int() == 0 {
f.SetInt(value)
}
}
pod := getTestPod(probeType, probeSpec)
return newWorker(m, probeType, &pod, pod.Spec.Containers[0])
@ -266,6 +330,22 @@ func getTestPod(probeType probeType, probeSpec api.Probe) api.Pod {
return pod
}
func expectResult(t *testing.T, w *worker, expectedResult results.Result, msg string) {
result, ok := resultsManager(w.probeManager, w.probeType).Get(containerID)
if !ok {
t.Errorf("[%s - %s] Expected result to be set, but was not set", w.probeType, msg)
} else if result != expectedResult {
t.Errorf("[%s - %s] Expected result to be %v, but was %v",
w.probeType, msg, expectedResult, result)
}
}
func expectContinue(t *testing.T, w *worker, c bool, msg string) {
if !c {
t.Errorf("[%s - %s] Expected to continue, but did not", w.probeType, msg)
}
}
func resultsManager(m *manager, probeType probeType) results.Manager {
switch probeType {
case readiness:

View File

@ -560,6 +560,7 @@ var _ = Describe("Pods", func() {
},
},
InitialDelaySeconds: 15,
FailureThreshold: 1,
},
},
},
@ -586,6 +587,7 @@ var _ = Describe("Pods", func() {
},
},
InitialDelaySeconds: 15,
FailureThreshold: 1,
},
},
},
@ -613,6 +615,7 @@ var _ = Describe("Pods", func() {
},
},
InitialDelaySeconds: 15,
FailureThreshold: 1,
},
},
},
@ -640,6 +643,7 @@ var _ = Describe("Pods", func() {
},
},
InitialDelaySeconds: 5,
FailureThreshold: 1,
},
},
},
@ -673,6 +677,7 @@ var _ = Describe("Pods", func() {
},
},
InitialDelaySeconds: 15,
FailureThreshold: 1,
},
},
},