mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-03 17:30:00 +00:00
api: add a new field to meta/v1 DeleteOptions
- add a new boolean field IgnoreStoreReadErrorWithClusterBreakingPotential to meta/v1 DeleteOptions - add validation for the new delete option add validation for the new field in the delete options ignoreStoreReadErrorWithClusterBreakingPotential - prevent the pod eviction handler from issuing an unsafe pod delete prevent the pod eviction handler from enabling the 'ignoreStoreReadErrorWithClusterBreakingPotential' delete option
This commit is contained in:
parent
847be85000
commit
b6773f1589
@ -30,6 +30,7 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
"k8s.io/apiserver/pkg/registry/rest"
|
"k8s.io/apiserver/pkg/registry/rest"
|
||||||
"k8s.io/apiserver/pkg/util/dryrun"
|
"k8s.io/apiserver/pkg/util/dryrun"
|
||||||
@ -39,6 +40,8 @@ import (
|
|||||||
podutil "k8s.io/kubernetes/pkg/api/pod"
|
podutil "k8s.io/kubernetes/pkg/api/pod"
|
||||||
api "k8s.io/kubernetes/pkg/apis/core"
|
api "k8s.io/kubernetes/pkg/apis/core"
|
||||||
"k8s.io/kubernetes/pkg/apis/policy"
|
"k8s.io/kubernetes/pkg/apis/policy"
|
||||||
|
|
||||||
|
"k8s.io/utils/ptr"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -133,6 +136,13 @@ func (r *EvictionREST) Create(ctx context.Context, name string, obj runtime.Obje
|
|||||||
return nil, errors.NewBadRequest("name in URL does not match name in Eviction object")
|
return nil, errors.NewBadRequest("name in URL does not match name in Eviction object")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if eviction.DeleteOptions != nil && ptr.Deref(eviction.DeleteOptions.IgnoreStoreReadErrorWithClusterBreakingPotential, false) {
|
||||||
|
errs := field.ErrorList{
|
||||||
|
field.Invalid(field.NewPath("deleteOptions").Child("ignoreStoreReadErrorWithClusterBreakingPotential"), true, "can not be set for pod eviction, try after removing the option"),
|
||||||
|
}
|
||||||
|
return nil, errors.NewInvalid(v1Eviction.GroupKind(), name, errs)
|
||||||
|
}
|
||||||
|
|
||||||
originalDeleteOptions, err := propagateDryRun(eviction, options)
|
originalDeleteOptions, err := propagateDryRun(eviction, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -24,6 +24,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
|
||||||
policyv1 "k8s.io/api/policy/v1"
|
policyv1 "k8s.io/api/policy/v1"
|
||||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||||
@ -31,6 +33,7 @@ import (
|
|||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||||
"k8s.io/apimachinery/pkg/watch"
|
"k8s.io/apimachinery/pkg/watch"
|
||||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||||
"k8s.io/apiserver/pkg/registry/rest"
|
"k8s.io/apiserver/pkg/registry/rest"
|
||||||
@ -38,6 +41,8 @@ import (
|
|||||||
podapi "k8s.io/kubernetes/pkg/api/pod"
|
podapi "k8s.io/kubernetes/pkg/api/pod"
|
||||||
api "k8s.io/kubernetes/pkg/apis/core"
|
api "k8s.io/kubernetes/pkg/apis/core"
|
||||||
"k8s.io/kubernetes/pkg/apis/policy"
|
"k8s.io/kubernetes/pkg/apis/policy"
|
||||||
|
|
||||||
|
"k8s.io/utils/ptr"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestEviction(t *testing.T) {
|
func TestEviction(t *testing.T) {
|
||||||
@ -581,30 +586,31 @@ func TestEvictionIgnorePDB(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEvictionDryRun(t *testing.T) {
|
func TestEvictionWithDeleteOptions(t *testing.T) {
|
||||||
testcases := []struct {
|
testcases := []struct {
|
||||||
name string
|
name string
|
||||||
evictionOptions *metav1.DeleteOptions
|
evictionOptions *metav1.DeleteOptions
|
||||||
requestOptions *metav1.CreateOptions
|
requestOptions *metav1.CreateOptions
|
||||||
pdbs []runtime.Object
|
pdbs []runtime.Object
|
||||||
|
err error
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "just request-options",
|
name: "dry run - just request-options",
|
||||||
requestOptions: &metav1.CreateOptions{DryRun: []string{"All"}},
|
requestOptions: &metav1.CreateOptions{DryRun: []string{"All"}},
|
||||||
evictionOptions: &metav1.DeleteOptions{},
|
evictionOptions: &metav1.DeleteOptions{},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "just eviction-options",
|
name: "dry run - just eviction-options",
|
||||||
requestOptions: &metav1.CreateOptions{},
|
requestOptions: &metav1.CreateOptions{},
|
||||||
evictionOptions: &metav1.DeleteOptions{DryRun: []string{"All"}},
|
evictionOptions: &metav1.DeleteOptions{DryRun: []string{"All"}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "both options",
|
name: "dry run - both options",
|
||||||
evictionOptions: &metav1.DeleteOptions{DryRun: []string{"All"}},
|
evictionOptions: &metav1.DeleteOptions{DryRun: []string{"All"}},
|
||||||
requestOptions: &metav1.CreateOptions{DryRun: []string{"All"}},
|
requestOptions: &metav1.CreateOptions{DryRun: []string{"All"}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "with pdbs",
|
name: "dry run - with pdbs",
|
||||||
evictionOptions: &metav1.DeleteOptions{DryRun: []string{"All"}},
|
evictionOptions: &metav1.DeleteOptions{DryRun: []string{"All"}},
|
||||||
requestOptions: &metav1.CreateOptions{DryRun: []string{"All"}},
|
requestOptions: &metav1.CreateOptions{DryRun: []string{"All"}},
|
||||||
pdbs: []runtime.Object{&policyv1.PodDisruptionBudget{
|
pdbs: []runtime.Object{&policyv1.PodDisruptionBudget{
|
||||||
@ -613,6 +619,17 @@ func TestEvictionDryRun(t *testing.T) {
|
|||||||
Status: policyv1.PodDisruptionBudgetStatus{DisruptionsAllowed: 1},
|
Status: policyv1.PodDisruptionBudgetStatus{DisruptionsAllowed: 1},
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "ignoreStoreReadErrorWithClusterBreakingPotential is set, invalid error expected",
|
||||||
|
requestOptions: &metav1.CreateOptions{},
|
||||||
|
evictionOptions: &metav1.DeleteOptions{
|
||||||
|
IgnoreStoreReadErrorWithClusterBreakingPotential: ptr.To[bool](true),
|
||||||
|
},
|
||||||
|
err: apierrors.NewInvalid(v1Eviction.GroupKind(), "foo", field.ErrorList{
|
||||||
|
field.Invalid(field.NewPath("deleteOptions").Child("ignoreStoreReadErrorWithClusterBreakingPotential"),
|
||||||
|
true, "can not be set for pod eviction, try after removing the option"),
|
||||||
|
}),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testcases {
|
for _, tc := range testcases {
|
||||||
@ -633,8 +650,8 @@ func TestEvictionDryRun(t *testing.T) {
|
|||||||
evictionRest := newEvictionStorage(storage.Store, client.PolicyV1())
|
evictionRest := newEvictionStorage(storage.Store, client.PolicyV1())
|
||||||
eviction := &policy.Eviction{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"}, DeleteOptions: tc.evictionOptions}
|
eviction := &policy.Eviction{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"}, DeleteOptions: tc.evictionOptions}
|
||||||
_, err := evictionRest.Create(testContext, pod.Name, eviction, nil, tc.requestOptions)
|
_, err := evictionRest.Create(testContext, pod.Name, eviction, nil, tc.requestOptions)
|
||||||
if err != nil {
|
if !cmp.Equal(tc.err, err) {
|
||||||
t.Fatalf("Failed to run eviction: %v", err)
|
t.Errorf("expected error to match, diff: %s", cmp.Diff(tc.err, err))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -560,6 +560,21 @@ type DeleteOptions struct {
|
|||||||
// +optional
|
// +optional
|
||||||
// +listType=atomic
|
// +listType=atomic
|
||||||
DryRun []string `json:"dryRun,omitempty" protobuf:"bytes,5,rep,name=dryRun"`
|
DryRun []string `json:"dryRun,omitempty" protobuf:"bytes,5,rep,name=dryRun"`
|
||||||
|
|
||||||
|
// if set to true, it will trigger an unsafe deletion of the resource in
|
||||||
|
// case the normal deletion flow fails with a corrupt object error.
|
||||||
|
// A resource is considered corrupt if it can not be retrieved from
|
||||||
|
// the underlying storage successfully because of a) its data can
|
||||||
|
// not be transformed e.g. decryption failure, or b) it fails
|
||||||
|
// to decode into an object.
|
||||||
|
// NOTE: unsafe deletion ignores finalizer constraints, skips
|
||||||
|
// precondition checks, and removes the object from the storage.
|
||||||
|
// WARNING: This may potentially break the cluster if the workload
|
||||||
|
// associated with the resource being unsafe-deleted relies on normal
|
||||||
|
// deletion flow. Use only if you REALLY know what you are doing.
|
||||||
|
// The default value is false, and the user must opt in to enable it
|
||||||
|
// +optional
|
||||||
|
IgnoreStoreReadErrorWithClusterBreakingPotential *bool `json:"ignoreStoreReadErrorWithClusterBreakingPotential,omitempty" protobuf:"varint,6,opt,name=ignoreStoreReadErrorWithClusterBreakingPotential"`
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -26,6 +26,8 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
"k8s.io/apimachinery/pkg/util/validation"
|
"k8s.io/apimachinery/pkg/util/validation"
|
||||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||||
|
|
||||||
|
"k8s.io/utils/ptr"
|
||||||
)
|
)
|
||||||
|
|
||||||
// LabelSelectorValidationOptions is a struct that can be passed to ValidateLabelSelector to record the validate options
|
// LabelSelectorValidationOptions is a struct that can be passed to ValidateLabelSelector to record the validate options
|
||||||
@ -165,6 +167,7 @@ func ValidateDeleteOptions(options *metav1.DeleteOptions) field.ErrorList {
|
|||||||
allErrs = append(allErrs, field.NotSupported(field.NewPath("propagationPolicy"), options.PropagationPolicy, []string{string(metav1.DeletePropagationForeground), string(metav1.DeletePropagationBackground), string(metav1.DeletePropagationOrphan), "nil"}))
|
allErrs = append(allErrs, field.NotSupported(field.NewPath("propagationPolicy"), options.PropagationPolicy, []string{string(metav1.DeletePropagationForeground), string(metav1.DeletePropagationBackground), string(metav1.DeletePropagationOrphan), "nil"}))
|
||||||
}
|
}
|
||||||
allErrs = append(allErrs, ValidateDryRun(field.NewPath("dryRun"), options.DryRun)...)
|
allErrs = append(allErrs, ValidateDryRun(field.NewPath("dryRun"), options.DryRun)...)
|
||||||
|
allErrs = append(allErrs, ValidateIgnoreStoreReadError(field.NewPath("ignoreStoreReadErrorWithClusterBreakingPotential"), options)...)
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -358,3 +361,31 @@ func isValidConditionReason(value string) []string {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ValidateIgnoreStoreReadError validates that delete options are valid when
|
||||||
|
// ignoreStoreReadErrorWithClusterBreakingPotential is enabled
|
||||||
|
func ValidateIgnoreStoreReadError(fldPath *field.Path, options *metav1.DeleteOptions) field.ErrorList {
|
||||||
|
allErrs := field.ErrorList{}
|
||||||
|
if enabled := ptr.Deref[bool](options.IgnoreStoreReadErrorWithClusterBreakingPotential, false); !enabled {
|
||||||
|
return allErrs
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(options.DryRun) > 0 {
|
||||||
|
allErrs = append(allErrs, field.Invalid(fldPath, true, "cannot be set together with .dryRun"))
|
||||||
|
}
|
||||||
|
if options.PropagationPolicy != nil {
|
||||||
|
allErrs = append(allErrs, field.Invalid(fldPath, true, "cannot be set together with .propagationPolicy"))
|
||||||
|
}
|
||||||
|
//nolint:staticcheck // Keep validation for deprecated OrphanDependents option until it's being removed
|
||||||
|
if options.OrphanDependents != nil {
|
||||||
|
allErrs = append(allErrs, field.Invalid(fldPath, true, "cannot be set together with .orphanDependents"))
|
||||||
|
}
|
||||||
|
if options.GracePeriodSeconds != nil {
|
||||||
|
allErrs = append(allErrs, field.Invalid(fldPath, true, "cannot be set together with .gracePeriodSeconds"))
|
||||||
|
}
|
||||||
|
if options.Preconditions != nil {
|
||||||
|
allErrs = append(allErrs, field.Invalid(fldPath, true, "cannot be set together with .preconditions"))
|
||||||
|
}
|
||||||
|
|
||||||
|
return allErrs
|
||||||
|
}
|
||||||
|
@ -21,9 +21,13 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||||
|
|
||||||
|
"k8s.io/utils/ptr"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestValidateLabels(t *testing.T) {
|
func TestValidateLabels(t *testing.T) {
|
||||||
@ -132,6 +136,96 @@ func boolPtr(b bool) *bool {
|
|||||||
return &b
|
return &b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestValidateDeleteOptionsWithIgnoreStoreReadError(t *testing.T) {
|
||||||
|
fieldPath := field.NewPath("ignoreStoreReadErrorWithClusterBreakingPotential")
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
opts metav1.DeleteOptions
|
||||||
|
expectedErrors field.ErrorList
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "option is nil",
|
||||||
|
opts: metav1.DeleteOptions{
|
||||||
|
IgnoreStoreReadErrorWithClusterBreakingPotential: nil,
|
||||||
|
DryRun: []string{"All"},
|
||||||
|
},
|
||||||
|
expectedErrors: field.ErrorList{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "option is false, PropagationPolicy is set",
|
||||||
|
opts: metav1.DeleteOptions{
|
||||||
|
IgnoreStoreReadErrorWithClusterBreakingPotential: ptr.To[bool](false),
|
||||||
|
DryRun: []string{"All"},
|
||||||
|
PropagationPolicy: ptr.To[metav1.DeletionPropagation](metav1.DeletePropagationBackground),
|
||||||
|
GracePeriodSeconds: ptr.To[int64](0),
|
||||||
|
Preconditions: &metav1.Preconditions{},
|
||||||
|
},
|
||||||
|
expectedErrors: field.ErrorList{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "option is false, OrphanDependents is set",
|
||||||
|
opts: metav1.DeleteOptions{
|
||||||
|
IgnoreStoreReadErrorWithClusterBreakingPotential: ptr.To[bool](false),
|
||||||
|
DryRun: []string{"All"},
|
||||||
|
//nolint:staticcheck // until it's being removed
|
||||||
|
OrphanDependents: ptr.To[bool](true),
|
||||||
|
GracePeriodSeconds: ptr.To[int64](0),
|
||||||
|
Preconditions: &metav1.Preconditions{},
|
||||||
|
},
|
||||||
|
expectedErrors: field.ErrorList{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "option is true, PropagationPolicy is set",
|
||||||
|
opts: metav1.DeleteOptions{
|
||||||
|
IgnoreStoreReadErrorWithClusterBreakingPotential: ptr.To[bool](true),
|
||||||
|
DryRun: []string{"All"},
|
||||||
|
PropagationPolicy: ptr.To[metav1.DeletionPropagation](metav1.DeletePropagationBackground),
|
||||||
|
GracePeriodSeconds: ptr.To[int64](0),
|
||||||
|
Preconditions: &metav1.Preconditions{},
|
||||||
|
},
|
||||||
|
expectedErrors: field.ErrorList{
|
||||||
|
field.Invalid(fieldPath, true, "cannot be set together with .dryRun"),
|
||||||
|
field.Invalid(fieldPath, true, "cannot be set together with .propagationPolicy"),
|
||||||
|
field.Invalid(fieldPath, true, "cannot be set together with .gracePeriodSeconds"),
|
||||||
|
field.Invalid(fieldPath, true, "cannot be set together with .preconditions"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "option is true, OrphanDependents is set",
|
||||||
|
opts: metav1.DeleteOptions{
|
||||||
|
IgnoreStoreReadErrorWithClusterBreakingPotential: ptr.To[bool](true),
|
||||||
|
DryRun: []string{"All"},
|
||||||
|
//nolint:staticcheck // until it's being removed
|
||||||
|
OrphanDependents: ptr.To[bool](true),
|
||||||
|
GracePeriodSeconds: ptr.To[int64](0),
|
||||||
|
Preconditions: &metav1.Preconditions{},
|
||||||
|
},
|
||||||
|
expectedErrors: field.ErrorList{
|
||||||
|
field.Invalid(fieldPath, true, "cannot be set together with .dryRun"),
|
||||||
|
field.Invalid(fieldPath, true, "cannot be set together with .orphanDependents"),
|
||||||
|
field.Invalid(fieldPath, true, "cannot be set together with .gracePeriodSeconds"),
|
||||||
|
field.Invalid(fieldPath, true, "cannot be set together with .preconditions"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "option is true, no other option is set",
|
||||||
|
opts: metav1.DeleteOptions{
|
||||||
|
IgnoreStoreReadErrorWithClusterBreakingPotential: ptr.To[bool](false),
|
||||||
|
},
|
||||||
|
expectedErrors: field.ErrorList{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
errGot := ValidateDeleteOptions(&test.opts)
|
||||||
|
if !cmp.Equal(test.expectedErrors, errGot) {
|
||||||
|
t.Errorf("expected error(s) to match, diff: %s", cmp.Diff(test.expectedErrors, errGot))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestValidPatchOptions(t *testing.T) {
|
func TestValidPatchOptions(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
opts metav1.PatchOptions
|
opts metav1.PatchOptions
|
||||||
|
@ -32,6 +32,7 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/validation"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/validation"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||||
"k8s.io/apiserver/pkg/admission"
|
"k8s.io/apiserver/pkg/admission"
|
||||||
"k8s.io/apiserver/pkg/audit"
|
"k8s.io/apiserver/pkg/audit"
|
||||||
"k8s.io/apiserver/pkg/endpoints/handlers/finisher"
|
"k8s.io/apiserver/pkg/endpoints/handlers/finisher"
|
||||||
@ -44,6 +45,7 @@ import (
|
|||||||
"k8s.io/apiserver/pkg/util/dryrun"
|
"k8s.io/apiserver/pkg/util/dryrun"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
"k8s.io/component-base/tracing"
|
"k8s.io/component-base/tracing"
|
||||||
|
"k8s.io/utils/ptr"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DeleteResource returns a function that will handle a resource deletion
|
// DeleteResource returns a function that will handle a resource deletion
|
||||||
@ -265,6 +267,16 @@ func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope *RequestSc
|
|||||||
scope.err(err, w, req)
|
scope.err(err, w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if options != nil && ptr.Deref(options.IgnoreStoreReadErrorWithClusterBreakingPotential, true) {
|
||||||
|
fieldErrList := field.ErrorList{
|
||||||
|
field.Invalid(field.NewPath("ignoreStoreReadErrorWithClusterBreakingPotential"), true, "is not allowed with DELETECOLLECTION, try again after removing the option"),
|
||||||
|
}
|
||||||
|
err := errors.NewInvalid(schema.GroupKind{Group: metav1.GroupName, Kind: "DeleteOptions"}, "", fieldErrList)
|
||||||
|
scope.err(err, w, req)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
options.TypeMeta.SetGroupVersionKind(metav1.SchemeGroupVersion.WithKind("DeleteOptions"))
|
options.TypeMeta.SetGroupVersionKind(metav1.SchemeGroupVersion.WithKind("DeleteOptions"))
|
||||||
|
|
||||||
admit = admission.WithAudit(admit)
|
admit = admission.WithAudit(admit)
|
||||||
|
Loading…
Reference in New Issue
Block a user