Merge pull request #132467 from sdowell/ssa-terminating

fix: prevent SSA from creating CR while CRD terminating
This commit is contained in:
Kubernetes Prow Robot
2025-06-24 12:08:27 -07:00
committed by GitHub
2 changed files with 77 additions and 6 deletions

View File

@@ -17,6 +17,8 @@ limitations under the License.
package apiserver
import (
"context"
"errors"
"fmt"
"net/http"
"sort"
@@ -384,17 +386,20 @@ func (r *crdHandler) serveResource(w http.ResponseWriter, req *http.Request, req
if justCreated {
time.Sleep(2 * time.Second)
}
a := r.admission
if terminating {
err := apierrors.NewMethodNotSupported(schema.GroupResource{Group: requestInfo.APIGroup, Resource: requestInfo.Resource}, requestInfo.Verb)
err.ErrStatus.Message = fmt.Sprintf("%v not allowed while custom resource definition is terminating", requestInfo.Verb)
responsewriters.ErrorNegotiated(err, Codecs, schema.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion}, w, req)
return nil
a = &forbidCreateAdmission{delegate: a}
}
return handlers.CreateResource(storage, requestScope, r.admission)
return handlers.CreateResource(storage, requestScope, a)
case "update":
return handlers.UpdateResource(storage, requestScope, r.admission)
case "patch":
return handlers.PatchResource(storage, requestScope, r.admission, supportedTypes)
a := r.admission
if terminating {
a = &forbidCreateAdmission{delegate: a}
}
return handlers.PatchResource(storage, requestScope, a, supportedTypes)
case "delete":
allowsOptions := true
return handlers.DeleteResource(storage, allowsOptions, requestScope, r.admission)
@@ -1452,3 +1457,36 @@ func buildOpenAPIModelsForApply(staticOpenAPISpec map[string]*spec.Schema, crd *
}
return mergedOpenAPI.Components.Schemas, nil
}
// forbidCreateAdmission is an admission.Interface wrapper that prevents a
// CustomResource from being created while its CRD is terminating.
type forbidCreateAdmission struct {
delegate admission.Interface
}
func (f *forbidCreateAdmission) Handles(operation admission.Operation) bool {
if operation == admission.Create {
return true
}
return f.delegate.Handles(operation)
}
func (f *forbidCreateAdmission) Admit(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces) error {
if a.GetOperation() == admission.Create {
return apierrors.NewForbidden(a.GetResource().GroupResource(), a.GetName(), errors.New("create not allowed while custom resource definition is terminating"))
}
if delegate, ok := f.delegate.(admission.MutationInterface); ok {
return delegate.Admit(ctx, a, o)
}
return nil
}
func (f *forbidCreateAdmission) Validate(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces) error {
if a.GetOperation() == admission.Create {
return apierrors.NewForbidden(a.GetResource().GroupResource(), a.GetName(), errors.New("create not allowed while custom resource definition is terminating"))
}
if delegate, ok := f.delegate.(admission.ValidationInterface); ok {
return delegate.Validate(ctx, a, o)
}
return nil
}

View File

@@ -163,3 +163,36 @@ func TestFinalizationAndDeletion(t *testing.T) {
t.Fatalf("unable to delete crd: %v", err)
}
}
func TestApplyCRDuringCRDFinalization(t *testing.T) {
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
require.NoError(t, err)
defer tearDown()
// Create a CRD with a finalizer which will stall deletion
noxuDefinition := fixtures.NewNoxuV1CustomResourceDefinition(apiextensionsv1.ClusterScoped)
noxuDefinition.SetFinalizers([]string{"noxu.example.com/finalizer"})
noxuDefinition, err = fixtures.CreateNewV1CustomResourceDefinition(noxuDefinition, apiExtensionClient, dynamicClient)
require.NoError(t, err)
// Delete the CRD. Since it has a finalizer it will be stuck in terminating state
err = apiExtensionClient.ApiextensionsV1().CustomResourceDefinitions().Delete(t.Context(), noxuDefinition.Name, metav1.DeleteOptions{})
require.NoError(t, err)
// Try to create a CR using SSA. This should fail due to the CRD validation
ns := "not-the-default"
name := "foo123"
noxuResourceClient := newNamespacedCustomResourceClient(ns, dynamicClient, noxuDefinition)
err = wait.PollUntilContextTimeout(t.Context(), 100*time.Millisecond, wait.ForeverTestTimeout, true, func(ctx context.Context) (bool, error) {
instance := fixtures.NewNoxuInstance(ns, name)
_, err := noxuResourceClient.Apply(ctx, name, instance, metav1.ApplyOptions{DryRun: []string{"All"}, FieldManager: "manager"})
if err == nil {
t.Log("apply was not blocked, retrying...")
return false, nil
}
return true, err
})
wantErr := `create not allowed while custom resource definition is terminating`
require.ErrorContains(t, err, wantErr)
}