mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-25 12:43:23 +00:00
validation and feature gate
This commit is contained in:
parent
bfa9c1091e
commit
792f03b1d2
@ -2647,6 +2647,19 @@ const (
|
|||||||
MaxDNSSearchListChars = 256
|
MaxDNSSearchListChars = 256
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func validateReadinessGates(readinessGates []core.PodReadinessGate, fldPath *field.Path) field.ErrorList {
|
||||||
|
allErrs := field.ErrorList{}
|
||||||
|
if !utilfeature.DefaultFeatureGate.Enabled(features.PodReadinessGates) && len(readinessGates) > 0 {
|
||||||
|
return append(allErrs, field.Forbidden(fldPath, "PodReadinessGates is disabled by feature gate"))
|
||||||
|
}
|
||||||
|
for i, value := range readinessGates {
|
||||||
|
for _, msg := range validation.IsQualifiedName(string(value.ConditionType)) {
|
||||||
|
allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("conditionType"), string(value.ConditionType), msg))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return allErrs
|
||||||
|
}
|
||||||
|
|
||||||
func validatePodDNSConfig(dnsConfig *core.PodDNSConfig, dnsPolicy *core.DNSPolicy, fldPath *field.Path) field.ErrorList {
|
func validatePodDNSConfig(dnsConfig *core.PodDNSConfig, dnsPolicy *core.DNSPolicy, fldPath *field.Path) field.ErrorList {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
|
|
||||||
@ -2935,6 +2948,7 @@ func ValidatePodSpec(spec *core.PodSpec, fldPath *field.Path) field.ErrorList {
|
|||||||
allErrs = append(allErrs, validateImagePullSecrets(spec.ImagePullSecrets, fldPath.Child("imagePullSecrets"))...)
|
allErrs = append(allErrs, validateImagePullSecrets(spec.ImagePullSecrets, fldPath.Child("imagePullSecrets"))...)
|
||||||
allErrs = append(allErrs, validateAffinity(spec.Affinity, fldPath.Child("affinity"))...)
|
allErrs = append(allErrs, validateAffinity(spec.Affinity, fldPath.Child("affinity"))...)
|
||||||
allErrs = append(allErrs, validatePodDNSConfig(spec.DNSConfig, &spec.DNSPolicy, fldPath.Child("dnsConfig"))...)
|
allErrs = append(allErrs, validatePodDNSConfig(spec.DNSConfig, &spec.DNSPolicy, fldPath.Child("dnsConfig"))...)
|
||||||
|
allErrs = append(allErrs, validateReadinessGates(spec.ReadinessGates, fldPath.Child("readinessGates"))...)
|
||||||
if len(spec.ServiceAccountName) > 0 {
|
if len(spec.ServiceAccountName) > 0 {
|
||||||
for _, msg := range ValidateServiceAccountName(spec.ServiceAccountName, false) {
|
for _, msg := range ValidateServiceAccountName(spec.ServiceAccountName, false) {
|
||||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("serviceAccountName"), spec.ServiceAccountName, msg))
|
allErrs = append(allErrs, field.Invalid(fldPath.Child("serviceAccountName"), spec.ServiceAccountName, msg))
|
||||||
@ -3485,6 +3499,7 @@ func ValidatePodStatusUpdate(newPod, oldPod *core.Pod) field.ErrorList {
|
|||||||
fldPath := field.NewPath("metadata")
|
fldPath := field.NewPath("metadata")
|
||||||
allErrs := ValidateObjectMetaUpdate(&newPod.ObjectMeta, &oldPod.ObjectMeta, fldPath)
|
allErrs := ValidateObjectMetaUpdate(&newPod.ObjectMeta, &oldPod.ObjectMeta, fldPath)
|
||||||
allErrs = append(allErrs, ValidatePodSpecificAnnotationUpdates(newPod, oldPod, fldPath.Child("annotations"))...)
|
allErrs = append(allErrs, ValidatePodSpecificAnnotationUpdates(newPod, oldPod, fldPath.Child("annotations"))...)
|
||||||
|
allErrs = append(allErrs, validatePodConditions(newPod.Status.Conditions, fldPath.Child("conditions"))...)
|
||||||
|
|
||||||
fldPath = field.NewPath("status")
|
fldPath = field.NewPath("status")
|
||||||
if newPod.Spec.NodeName != oldPod.Spec.NodeName {
|
if newPod.Spec.NodeName != oldPod.Spec.NodeName {
|
||||||
@ -3508,6 +3523,21 @@ func ValidatePodStatusUpdate(newPod, oldPod *core.Pod) field.ErrorList {
|
|||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// validatePodConditions tests if the custom pod conditions are valid.
|
||||||
|
func validatePodConditions(conditions []core.PodCondition, fldPath *field.Path) field.ErrorList {
|
||||||
|
allErrs := field.ErrorList{}
|
||||||
|
systemConditions := sets.NewString(string(core.PodScheduled), string(core.PodReady), string(core.PodInitialized))
|
||||||
|
for i, condition := range conditions {
|
||||||
|
if systemConditions.Has(string(condition.Type)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, msg := range validation.IsQualifiedName(string(condition.Type)) {
|
||||||
|
allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("Type"), string(condition.Type), msg))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return allErrs
|
||||||
|
}
|
||||||
|
|
||||||
// ValidatePodBinding tests if required fields in the pod binding are legal.
|
// ValidatePodBinding tests if required fields in the pod binding are legal.
|
||||||
func ValidatePodBinding(binding *core.Binding) field.ErrorList {
|
func ValidatePodBinding(binding *core.Binding) field.ErrorList {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
|
@ -5865,6 +5865,149 @@ func TestValidatePodDNSConfig(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestValidatePodReadinessGates(t *testing.T) {
|
||||||
|
podReadinessGatesEnabled := utilfeature.DefaultFeatureGate.Enabled(features.PodReadinessGates)
|
||||||
|
defer func() {
|
||||||
|
// Restoring the old value.
|
||||||
|
if err := utilfeature.DefaultFeatureGate.Set(fmt.Sprintf("%s=%v", features.PodReadinessGates, podReadinessGatesEnabled)); err != nil {
|
||||||
|
t.Errorf("Failed to restore PodReadinessGates feature gate: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
if err := utilfeature.DefaultFeatureGate.Set(fmt.Sprintf("%s=true", features.PodReadinessGates)); err != nil {
|
||||||
|
t.Errorf("Failed to enable PodReadinessGates feature gate: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
successCases := []struct {
|
||||||
|
desc string
|
||||||
|
readinessGates []core.PodReadinessGate
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"no gate",
|
||||||
|
[]core.PodReadinessGate{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"one readiness gate",
|
||||||
|
[]core.PodReadinessGate{
|
||||||
|
{
|
||||||
|
ConditionType: core.PodConditionType("example.com/condition"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"two readiness gates",
|
||||||
|
[]core.PodReadinessGate{
|
||||||
|
{
|
||||||
|
ConditionType: core.PodConditionType("example.com/condition1"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ConditionType: core.PodConditionType("example.com/condition2"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range successCases {
|
||||||
|
if errs := validateReadinessGates(tc.readinessGates, field.NewPath("field")); len(errs) != 0 {
|
||||||
|
t.Errorf("expect tc %q to success: %v", tc.desc, errs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
errorCases := []struct {
|
||||||
|
desc string
|
||||||
|
readinessGates []core.PodReadinessGate
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"invalid condition type",
|
||||||
|
[]core.PodReadinessGate{
|
||||||
|
{
|
||||||
|
ConditionType: core.PodConditionType("invalid/condition/type"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range errorCases {
|
||||||
|
if errs := validateReadinessGates(tc.readinessGates, field.NewPath("field")); len(errs) == 0 {
|
||||||
|
t.Errorf("expected tc %q to fail", tc.desc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidatePodConditions(t *testing.T) {
|
||||||
|
successCases := []struct {
|
||||||
|
desc string
|
||||||
|
podConditions []core.PodCondition
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"no condition",
|
||||||
|
[]core.PodCondition{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"one system condition",
|
||||||
|
[]core.PodCondition{
|
||||||
|
{
|
||||||
|
Type: core.PodReady,
|
||||||
|
Status: core.ConditionTrue,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"one system condition and one custom condition",
|
||||||
|
[]core.PodCondition{
|
||||||
|
{
|
||||||
|
Type: core.PodReady,
|
||||||
|
Status: core.ConditionTrue,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: core.PodConditionType("example.com/condition"),
|
||||||
|
Status: core.ConditionFalse,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"two custom condition",
|
||||||
|
[]core.PodCondition{
|
||||||
|
{
|
||||||
|
Type: core.PodConditionType("foobar"),
|
||||||
|
Status: core.ConditionTrue,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: core.PodConditionType("example.com/condition"),
|
||||||
|
Status: core.ConditionFalse,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range successCases {
|
||||||
|
if errs := validatePodConditions(tc.podConditions, field.NewPath("field")); len(errs) != 0 {
|
||||||
|
t.Errorf("expected tc %q to success, but got: %v", tc.desc, errs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
errorCases := []struct {
|
||||||
|
desc string
|
||||||
|
podConditions []core.PodCondition
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"one system condition and a invalid custom condition",
|
||||||
|
[]core.PodCondition{
|
||||||
|
{
|
||||||
|
Type: core.PodReady,
|
||||||
|
Status: core.ConditionStatus("True"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: core.PodConditionType("invalid/custom/condition"),
|
||||||
|
Status: core.ConditionStatus("True"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range errorCases {
|
||||||
|
if errs := validatePodConditions(tc.podConditions, field.NewPath("field")); len(errs) == 0 {
|
||||||
|
t.Errorf("expected tc %q to fail", tc.desc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestValidatePodSpec(t *testing.T) {
|
func TestValidatePodSpec(t *testing.T) {
|
||||||
activeDeadlineSeconds := int64(30)
|
activeDeadlineSeconds := int64(30)
|
||||||
activeDeadlineSecondsMax := int64(math.MaxInt32)
|
activeDeadlineSecondsMax := int64(math.MaxInt32)
|
||||||
|
@ -292,6 +292,12 @@ const (
|
|||||||
// while making decisions.
|
// while making decisions.
|
||||||
BalanceAttachedNodeVolumes utilfeature.Feature = "BalanceAttachedNodeVolumes"
|
BalanceAttachedNodeVolumes utilfeature.Feature = "BalanceAttachedNodeVolumes"
|
||||||
|
|
||||||
|
// owner @freehan
|
||||||
|
// beta: v1.11
|
||||||
|
//
|
||||||
|
// Support Pod Ready++
|
||||||
|
PodReadinessGates utilfeature.Feature = "PodReadinessGates"
|
||||||
|
|
||||||
// owner: @lichuqiang
|
// owner: @lichuqiang
|
||||||
// alpha: v1.11
|
// alpha: v1.11
|
||||||
//
|
//
|
||||||
@ -362,6 +368,7 @@ var defaultKubernetesFeatureGates = map[utilfeature.Feature]utilfeature.FeatureS
|
|||||||
VolumeSubpath: {Default: true, PreRelease: utilfeature.GA},
|
VolumeSubpath: {Default: true, PreRelease: utilfeature.GA},
|
||||||
BalanceAttachedNodeVolumes: {Default: false, PreRelease: utilfeature.Alpha},
|
BalanceAttachedNodeVolumes: {Default: false, PreRelease: utilfeature.Alpha},
|
||||||
DynamicProvisioningScheduling: {Default: false, PreRelease: utilfeature.Alpha},
|
DynamicProvisioningScheduling: {Default: false, PreRelease: utilfeature.Alpha},
|
||||||
|
PodReadinessGates: {Default: false, PreRelease: utilfeature.Beta},
|
||||||
VolumeSubpathEnvExpansion: {Default: false, PreRelease: utilfeature.Alpha},
|
VolumeSubpathEnvExpansion: {Default: false, PreRelease: utilfeature.Alpha},
|
||||||
KubeletPluginsWatcher: {Default: false, PreRelease: utilfeature.Alpha},
|
KubeletPluginsWatcher: {Default: false, PreRelease: utilfeature.Alpha},
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user