Merge pull request #113375 from atiratree/PodHealthyPolicy-api
api: add unhealthyPodEvictionPolicy for PDBs
This commit is contained in:
@@ -42,8 +42,56 @@ type PodDisruptionBudgetSpec struct {
|
||||
// by specifying 0. This is a mutually exclusive setting with "minAvailable".
|
||||
// +optional
|
||||
MaxUnavailable *intstr.IntOrString
|
||||
|
||||
// UnhealthyPodEvictionPolicy defines the criteria for when unhealthy pods
|
||||
// should be considered for eviction. Current implementation considers healthy pods,
|
||||
// as pods that have status.conditions item with type="Ready",status="True".
|
||||
//
|
||||
// Valid policies are IfHealthyBudget and AlwaysAllow.
|
||||
// If no policy is specified, the default behavior will be used,
|
||||
// which corresponds to the IfHealthyBudget policy.
|
||||
//
|
||||
// IfHealthyBudget policy means that running pods (status.phase="Running"),
|
||||
// but not yet healthy can be evicted only if the guarded application is not
|
||||
// disrupted (status.currentHealthy is at least equal to status.desiredHealthy).
|
||||
// Healthy pods will be subject to the PDB for eviction.
|
||||
//
|
||||
// AlwaysAllow policy means that all running pods (status.phase="Running"),
|
||||
// but not yet healthy are considered disrupted and can be evicted regardless
|
||||
// of whether the criteria in a PDB is met. This means perspective running
|
||||
// pods of a disrupted application might not get a chance to become healthy.
|
||||
// Healthy pods will be subject to the PDB for eviction.
|
||||
//
|
||||
// Additional policies may be added in the future.
|
||||
// Clients making eviction decisions should disallow eviction of unhealthy pods
|
||||
// if they encounter an unrecognized policy in this field.
|
||||
//
|
||||
// This field is alpha-level. The eviction API uses this field when
|
||||
// the feature gate PDBUnhealthyPodEvictionPolicy is enabled (disabled by default).
|
||||
// +optional
|
||||
UnhealthyPodEvictionPolicy *UnhealthyPodEvictionPolicyType
|
||||
}
|
||||
|
||||
// UnhealthyPodEvictionPolicyType defines the criteria for when unhealthy pods
|
||||
// should be considered for eviction.
|
||||
// +enum
|
||||
type UnhealthyPodEvictionPolicyType string
|
||||
|
||||
const (
|
||||
// IfHealthyBudget policy means that running pods (status.phase="Running"),
|
||||
// but not yet healthy can be evicted only if the guarded application is not
|
||||
// disrupted (status.currentHealthy is at least equal to status.desiredHealthy).
|
||||
// Healthy pods will be subject to the PDB for eviction.
|
||||
IfHealthyBudget UnhealthyPodEvictionPolicyType = "IfHealthyBudget"
|
||||
|
||||
// AlwaysAllow policy means that all running pods (status.phase="Running"),
|
||||
// but not yet healthy are considered disrupted and can be evicted regardless
|
||||
// of whether the criteria in a PDB is met. This means perspective running
|
||||
// pods of a disrupted application might not get a chance to become healthy.
|
||||
// Healthy pods will be subject to the PDB for eviction.
|
||||
AlwaysAllow UnhealthyPodEvictionPolicyType = "AlwaysAllow"
|
||||
)
|
||||
|
||||
// PodDisruptionBudgetStatus represents information about the status of a
|
||||
// PodDisruptionBudget. Status may trail the actual state of a system.
|
||||
type PodDisruptionBudgetStatus struct {
|
||||
|
2
pkg/apis/policy/v1/zz_generated.conversion.go
generated
2
pkg/apis/policy/v1/zz_generated.conversion.go
generated
@@ -187,6 +187,7 @@ func autoConvert_v1_PodDisruptionBudgetSpec_To_policy_PodDisruptionBudgetSpec(in
|
||||
out.MinAvailable = (*intstr.IntOrString)(unsafe.Pointer(in.MinAvailable))
|
||||
out.Selector = (*metav1.LabelSelector)(unsafe.Pointer(in.Selector))
|
||||
out.MaxUnavailable = (*intstr.IntOrString)(unsafe.Pointer(in.MaxUnavailable))
|
||||
out.UnhealthyPodEvictionPolicy = (*policy.UnhealthyPodEvictionPolicyType)(unsafe.Pointer(in.UnhealthyPodEvictionPolicy))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -199,6 +200,7 @@ func autoConvert_policy_PodDisruptionBudgetSpec_To_v1_PodDisruptionBudgetSpec(in
|
||||
out.MinAvailable = (*intstr.IntOrString)(unsafe.Pointer(in.MinAvailable))
|
||||
out.Selector = (*metav1.LabelSelector)(unsafe.Pointer(in.Selector))
|
||||
out.MaxUnavailable = (*intstr.IntOrString)(unsafe.Pointer(in.MaxUnavailable))
|
||||
out.UnhealthyPodEvictionPolicy = (*v1.UnhealthyPodEvictionPolicyType)(unsafe.Pointer(in.UnhealthyPodEvictionPolicy))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@@ -452,6 +452,7 @@ func autoConvert_v1beta1_PodDisruptionBudgetSpec_To_policy_PodDisruptionBudgetSp
|
||||
out.MinAvailable = (*intstr.IntOrString)(unsafe.Pointer(in.MinAvailable))
|
||||
out.Selector = (*v1.LabelSelector)(unsafe.Pointer(in.Selector))
|
||||
out.MaxUnavailable = (*intstr.IntOrString)(unsafe.Pointer(in.MaxUnavailable))
|
||||
out.UnhealthyPodEvictionPolicy = (*policy.UnhealthyPodEvictionPolicyType)(unsafe.Pointer(in.UnhealthyPodEvictionPolicy))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -464,6 +465,7 @@ func autoConvert_policy_PodDisruptionBudgetSpec_To_v1beta1_PodDisruptionBudgetSp
|
||||
out.MinAvailable = (*intstr.IntOrString)(unsafe.Pointer(in.MinAvailable))
|
||||
out.Selector = (*v1.LabelSelector)(unsafe.Pointer(in.Selector))
|
||||
out.MaxUnavailable = (*intstr.IntOrString)(unsafe.Pointer(in.MaxUnavailable))
|
||||
out.UnhealthyPodEvictionPolicy = (*v1beta1.UnhealthyPodEvictionPolicyType)(unsafe.Pointer(in.UnhealthyPodEvictionPolicy))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@@ -44,6 +44,11 @@ const (
|
||||
seccompAllowedProfilesAnnotationKey = "seccomp.security.alpha.kubernetes.io/allowedProfileNames"
|
||||
)
|
||||
|
||||
var supportedUnhealthyPodEvictionPolicies = sets.NewString(
|
||||
string(policy.IfHealthyBudget),
|
||||
string(policy.AlwaysAllow),
|
||||
)
|
||||
|
||||
type PodDisruptionBudgetValidationOptions struct {
|
||||
AllowInvalidLabelValueInSelector bool
|
||||
}
|
||||
@@ -78,6 +83,10 @@ func ValidatePodDisruptionBudgetSpec(spec policy.PodDisruptionBudgetSpec, opts P
|
||||
|
||||
allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(spec.Selector, labelSelectorValidationOptions, fldPath.Child("selector"))...)
|
||||
|
||||
if spec.UnhealthyPodEvictionPolicy != nil && !supportedUnhealthyPodEvictionPolicies.Has(string(*spec.UnhealthyPodEvictionPolicy)) {
|
||||
allErrs = append(allErrs, field.NotSupported(fldPath.Child("unhealthyPodEvictionPolicy"), *spec.UnhealthyPodEvictionPolicy, supportedUnhealthyPodEvictionPolicies.List()))
|
||||
}
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
|
@@ -98,6 +98,63 @@ func TestValidateMinAvailablePodAndMaxUnavailableDisruptionBudgetSpec(t *testing
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateUnhealthyPodEvictionPolicyDisruptionBudgetSpec(t *testing.T) {
|
||||
c1 := intstr.FromString("10%")
|
||||
alwaysAllowPolicy := policy.AlwaysAllow
|
||||
invalidPolicy := policy.UnhealthyPodEvictionPolicyType("Invalid")
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
pdbSpec policy.PodDisruptionBudgetSpec
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
name: "valid nil UnhealthyPodEvictionPolicy",
|
||||
pdbSpec: policy.PodDisruptionBudgetSpec{
|
||||
MinAvailable: &c1,
|
||||
UnhealthyPodEvictionPolicy: nil,
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "valid UnhealthyPodEvictionPolicy",
|
||||
pdbSpec: policy.PodDisruptionBudgetSpec{
|
||||
MinAvailable: &c1,
|
||||
UnhealthyPodEvictionPolicy: &alwaysAllowPolicy,
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "empty UnhealthyPodEvictionPolicy",
|
||||
pdbSpec: policy.PodDisruptionBudgetSpec{
|
||||
MinAvailable: &c1,
|
||||
UnhealthyPodEvictionPolicy: new(policy.UnhealthyPodEvictionPolicyType),
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid UnhealthyPodEvictionPolicy",
|
||||
pdbSpec: policy.PodDisruptionBudgetSpec{
|
||||
MinAvailable: &c1,
|
||||
UnhealthyPodEvictionPolicy: &invalidPolicy,
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
errs := ValidatePodDisruptionBudgetSpec(tc.pdbSpec, PodDisruptionBudgetValidationOptions{true}, field.NewPath("foo"))
|
||||
if len(errs) == 0 && tc.expectErr {
|
||||
t.Errorf("unexpected success for %v", tc.pdbSpec)
|
||||
}
|
||||
if len(errs) != 0 && !tc.expectErr {
|
||||
t.Errorf("unexpected failure for %v", tc.pdbSpec)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidatePodDisruptionBudgetStatus(t *testing.T) {
|
||||
const expectNoErrors = false
|
||||
const expectErrors = true
|
||||
|
5
pkg/apis/policy/zz_generated.deepcopy.go
generated
5
pkg/apis/policy/zz_generated.deepcopy.go
generated
@@ -239,6 +239,11 @@ func (in *PodDisruptionBudgetSpec) DeepCopyInto(out *PodDisruptionBudgetSpec) {
|
||||
*out = new(intstr.IntOrString)
|
||||
**out = **in
|
||||
}
|
||||
if in.UnhealthyPodEvictionPolicy != nil {
|
||||
in, out := &in.UnhealthyPodEvictionPolicy, &out.UnhealthyPodEvictionPolicy
|
||||
*out = new(UnhealthyPodEvictionPolicyType)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user