From e4d26f845e1e11ec75ce3a395ff0fc691347f3cf Mon Sep 17 00:00:00 2001 From: Matthias Bertschy Date: Wed, 7 Aug 2019 10:21:48 +0200 Subject: [PATCH] startupProbe: API changes --- pkg/api/pod/util.go | 26 +++++++++++++++++++ pkg/apis/core/types.go | 10 +++++-- pkg/apis/core/validation/validation.go | 8 ++++++ pkg/features/kube_features.go | 9 ++++++- staging/src/k8s.io/api/core/v1/types.go | 25 +++++++++++++++--- .../pkg/describe/versioned/describe.go | 4 +++ 6 files changed, 76 insertions(+), 6 deletions(-) diff --git a/pkg/api/pod/util.go b/pkg/api/pod/util.go index f513ce171ca..d08b09c3abb 100644 --- a/pkg/api/pod/util.go +++ b/pkg/api/pod/util.go @@ -383,6 +383,14 @@ func dropDisabledFields( }) } + if !utilfeature.DefaultFeatureGate.Enabled(features.StartupProbe) && !startupProbeInUse(oldPodSpec) { + // drop startupProbe from all containers if the feature is disabled + VisitContainers(podSpec, func(c *api.Container) bool { + c.StartupProbe = nil + return true + }) + } + dropDisabledVolumeDevicesFields(podSpec, oldPodSpec) dropDisabledRunAsGroupField(podSpec, oldPodSpec) @@ -819,6 +827,24 @@ func subpathExprInUse(podSpec *api.PodSpec) bool { return inUse } +// startupProbeInUse returns true if the pod spec is non-nil and has a container that has a startupProbe defined +func startupProbeInUse(podSpec *api.PodSpec) bool { + if podSpec == nil { + return false + } + + var inUse bool + VisitContainers(podSpec, func(c *api.Container) bool { + if c.StartupProbe != nil { + inUse = true + return false + } + return true + }) + + return inUse +} + // csiInUse returns true if any pod's spec include inline CSI volumes. func csiInUse(podSpec *api.PodSpec) bool { if podSpec == nil { diff --git a/pkg/apis/core/types.go b/pkg/apis/core/types.go index 03779eb910a..562b632fdff 100644 --- a/pkg/apis/core/types.go +++ b/pkg/apis/core/types.go @@ -1924,7 +1924,7 @@ type Probe struct { // +optional PeriodSeconds int32 // Minimum consecutive successes for the probe to be considered successful after having failed. - // Must be 1 for liveness. + // Must be 1 for liveness and startup. // +optional SuccessThreshold int32 // Minimum consecutive failures for the probe to be considered failed after having succeeded. @@ -2042,6 +2042,8 @@ type Container struct { // +optional ReadinessProbe *Probe // +optional + StartupProbe *Probe + // +optional Lifecycle *Lifecycle // Required. // +optional @@ -2090,7 +2092,7 @@ type Lifecycle struct { // +optional PostStart *Handler // PreStop is called immediately before a container is terminated due to an - // API request or management event such as liveness probe failure, + // API request or management event such as liveness/startup probe failure, // preemption, resource contention, etc. The handler is not called if the // container crashes or exits. The reason for termination is passed to the // handler. The Pod's termination grace period countdown begins before the @@ -2174,6 +2176,7 @@ type ContainerStatus struct { ImageID string // +optional ContainerID string + Started *bool } // PodPhase is a label for the condition of a pod at the current time. @@ -2936,6 +2939,9 @@ type EphemeralContainerCommon struct { // Probes are not allowed for ephemeral containers. // +optional ReadinessProbe *Probe + // Probes are not allowed for ephemeral containers. + // +optional + StartupProbe *Probe // Lifecycle is not allowed for ephemeral containers. // +optional Lifecycle *Lifecycle diff --git a/pkg/apis/core/validation/validation.go b/pkg/apis/core/validation/validation.go index f609b0980b4..16bfec7b1a4 100644 --- a/pkg/apis/core/validation/validation.go +++ b/pkg/apis/core/validation/validation.go @@ -2696,6 +2696,9 @@ func validateInitContainers(containers, otherContainers []core.Container, device if ctr.ReadinessProbe != nil { allErrs = append(allErrs, field.Invalid(idxPath.Child("readinessProbe"), ctr.ReadinessProbe, "must not be set for init containers")) } + if ctr.StartupProbe != nil { + allErrs = append(allErrs, field.Invalid(idxPath.Child("startupProbe"), ctr.StartupProbe, "must not be set for init containers")) + } } return allErrs } @@ -2738,6 +2741,11 @@ func validateContainers(containers []core.Container, isInitContainers bool, volu if ctr.LivenessProbe != nil && ctr.LivenessProbe.SuccessThreshold != 1 { allErrs = append(allErrs, field.Invalid(idxPath.Child("livenessProbe", "successThreshold"), ctr.LivenessProbe.SuccessThreshold, "must be 1")) } + allErrs = append(allErrs, validateProbe(ctr.StartupProbe, idxPath.Child("startupProbe"))...) + // Startup-specific validation + if ctr.StartupProbe != nil && ctr.StartupProbe.SuccessThreshold != 1 { + allErrs = append(allErrs, field.Invalid(idxPath.Child("startupProbe", "successThreshold"), ctr.StartupProbe.SuccessThreshold, "must be 1")) + } switch ctr.TerminationMessagePolicy { case core.TerminationMessageReadFile, core.TerminationMessageFallbackToLogsOnError: diff --git a/pkg/features/kube_features.go b/pkg/features/kube_features.go index 6866483e39d..6a2575de67e 100644 --- a/pkg/features/kube_features.go +++ b/pkg/features/kube_features.go @@ -489,6 +489,12 @@ const ( // // Schedule pods evenly across available topology domains. EvenPodsSpread featuregate.Feature = "EvenPodsSpread" + + // owner: @matthyx + // alpha: v1.16 + // + // Enables the startupProbe in kubelet worker. + StartupProbe featuregate.Feature = "StartupProbe" ) func init() { @@ -569,6 +575,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS IPv6DualStack: {Default: false, PreRelease: featuregate.Alpha}, EndpointSlice: {Default: false, PreRelease: featuregate.Alpha}, EvenPodsSpread: {Default: false, PreRelease: featuregate.Alpha}, + StartupProbe: {Default: false, PreRelease: featuregate.Alpha}, // inherited features from generic apiserver, relisted here to get a conflict if it is changed // unintentionally on either side: @@ -592,6 +599,6 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS // features that enable backwards compatibility but are scheduled to be removed // ... - HPAScaleToZero: {Default: false, PreRelease: featuregate.Alpha}, + HPAScaleToZero: {Default: false, PreRelease: featuregate.Alpha}, LegacyNodeRoleBehavior: {Default: true, PreRelease: featuregate.Alpha}, } diff --git a/staging/src/k8s.io/api/core/v1/types.go b/staging/src/k8s.io/api/core/v1/types.go index a3c94dd247d..947278bcd9a 100644 --- a/staging/src/k8s.io/api/core/v1/types.go +++ b/staging/src/k8s.io/api/core/v1/types.go @@ -2025,7 +2025,7 @@ type Probe struct { // +optional PeriodSeconds int32 `json:"periodSeconds,omitempty" protobuf:"varint,4,opt,name=periodSeconds"` // 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. + // Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. // +optional SuccessThreshold int32 `json:"successThreshold,omitempty" protobuf:"varint,5,opt,name=successThreshold"` // Minimum consecutive failures for the probe to be considered failed after having succeeded. @@ -2196,6 +2196,16 @@ type Container struct { // More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes // +optional ReadinessProbe *Probe `json:"readinessProbe,omitempty" protobuf:"bytes,11,opt,name=readinessProbe"` + // StartupProbe indicates that the Pod has successfully initialized. + // If specified, no other probes are executed until this completes successfully. + // If this probe fails, the Pod will be restarted, just as if the livenessProbe failed. + // This can be used to provide different probe parameters at the beginning of a Pod's lifecycle, + // when it might take a long time to load data or warm a cache, than during steady-state operation. + // This cannot be updated. + // This is an alpha feature enabled by the StartupProbe feature flag. + // More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + // +optional + StartupProbe *Probe `json:"startupProbe,omitempty" protobuf:"bytes,22,opt,name=startupProbe"` // Actions that the management system should take in response to container lifecycle events. // Cannot be updated. // +optional @@ -2282,7 +2292,7 @@ type Lifecycle struct { // +optional PostStart *Handler `json:"postStart,omitempty" protobuf:"bytes,1,opt,name=postStart"` // PreStop is called immediately before a container is terminated due to an - // API request or management event such as liveness probe failure, + // API request or management event such as liveness/startup probe failure, // preemption, resource contention, etc. The handler is not called if the // container crashes or exits. The reason for termination is passed to the // handler. The Pod's termination grace period countdown begins before the @@ -2390,6 +2400,12 @@ type ContainerStatus struct { // Container's ID in the format 'docker://'. // +optional ContainerID string `json:"containerID,omitempty" protobuf:"bytes,8,opt,name=containerID"` + // Specifies whether the container has passed its startup probe. + // Initialized as false, becomes true after startupProbe is considered successful. + // Resets to false when the container is restarted, or if kubelet loses state temporarily. + // Is always true when no startupProbe is defined. + // +optional + Started *bool `json:"started,omitempty" protobuf:"varint,9,opt,name=started"` } // PodPhase is a label for the condition of a pod at the current time. @@ -2825,7 +2841,7 @@ type PodSpec struct { // init container fails, the pod is considered to have failed and is handled according // to its restartPolicy. The name for an init container or normal container must be // unique among all containers. - // Init containers may not have Lifecycle actions, Readiness probes, or Liveness probes. + // Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. // The resourceRequirements of an init container are taken into account during scheduling // by finding the highest request/limit for each resource type, and then using the max of // of that value or the sum of the normal containers. Limits are applied to init containers @@ -3293,6 +3309,9 @@ type EphemeralContainerCommon struct { // Probes are not allowed for ephemeral containers. // +optional ReadinessProbe *Probe `json:"readinessProbe,omitempty" protobuf:"bytes,11,opt,name=readinessProbe"` + // Probes are not allowed for ephemeral containers. + // +optional + StartupProbe *Probe `json:"startupProbe,omitempty" protobuf:"bytes,22,opt,name=startupProbe"` // Lifecycle is not allowed for ephemeral containers. // +optional Lifecycle *Lifecycle `json:"lifecycle,omitempty" protobuf:"bytes,12,opt,name=lifecycle"` diff --git a/staging/src/k8s.io/kubectl/pkg/describe/versioned/describe.go b/staging/src/k8s.io/kubectl/pkg/describe/versioned/describe.go index 7ce165e2347..2b39bfdde7b 100644 --- a/staging/src/k8s.io/kubectl/pkg/describe/versioned/describe.go +++ b/staging/src/k8s.io/kubectl/pkg/describe/versioned/describe.go @@ -1707,6 +1707,10 @@ func describeContainerProbe(container corev1.Container, w PrefixWriter) { probe := DescribeProbe(container.ReadinessProbe) w.Write(LEVEL_2, "Readiness:\t%s\n", probe) } + if container.StartupProbe != nil { + probe := DescribeProbe(container.StartupProbe) + w.Write(LEVEL_2, "Startup:\t%s\n", probe) + } } func describeContainerVolumes(container corev1.Container, w PrefixWriter) {