@@ -21,11 +21,14 @@ import (
2121"encoding/json"
2222"errors"
2323"net/http"
24+ "slices"
2425
26+ "gomodules.xyz/jsonpatch/v2"
2527admissionv1 "k8s.io/api/admission/v1"
2628apierrors "k8s.io/apimachinery/pkg/api/errors"
2729metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2830"k8s.io/apimachinery/pkg/runtime"
31+ "k8s.io/apimachinery/pkg/util/sets"
2932)
3033
3134// CustomDefaulter defines functions for setting defaults on resources.
@@ -71,32 +74,59 @@ func (h *defaulterForType) Handle(ctx context.Context, req Request) Response {
7174ctx = NewContextWithRequest (ctx , req )
7275
7376// Get the object in the request
74- original := h .object .DeepCopyObject ()
75- if err := h .decoder .Decode (req , original ); err != nil {
77+ obj := h .object .DeepCopyObject ()
78+ if err := h .decoder .Decode (req , obj ); err != nil {
7679return Errored (http .StatusBadRequest , err )
7780}
7881
82+ // Keep a copy of the object
83+ originalObj := obj .DeepCopyObject ()
84+
7985// Default the object
80- updated := original .DeepCopyObject ()
81- if err := h .defaulter .Default (ctx , updated ); err != nil {
86+ if err := h .defaulter .Default (ctx , obj ); err != nil {
8287var apiStatus apierrors.APIStatus
8388if errors .As (err , & apiStatus ) {
8489return validationResponseFromStatus (false , apiStatus .Status ())
8590}
8691return Denied (err .Error ())
8792}
8893
89- // Create the patch.
90- // We need to decode and marshall the original because the type registered in the
91- // decoder might not match the latest version of the API.
92- // Creating a diff from the raw object might cause new fields to be dropped.
93- marshalledOriginal , err := json .Marshal (original )
94+ // Create the patch
95+ marshalled , err := json .Marshal (obj )
9496if err != nil {
9597return Errored (http .StatusInternalServerError , err )
9698}
97- marshalledUpdated , err := json .Marshal (updated )
99+ handlerResponse := PatchResponseFromRaw (req .Object .Raw , marshalled )
100+
101+ return h .dropSchemeRemovals (handlerResponse , originalObj , req .Object .Raw )
102+ }
103+
104+ func (h * defaulterForType ) dropSchemeRemovals (r Response , original runtime.Object , raw []byte ) Response {
105+ const opRemove = "remove"
106+ if ! r .Allowed || r .PatchType == nil {
107+ return r
108+ }
109+
110+ // If we don't have removals in the patch.
111+ if ! slices .ContainsFunc (r .Patches , func (o jsonpatch.JsonPatchOperation ) bool { return o .Operation == opRemove }) {
112+ return r
113+ }
114+
115+ // Get the raw to original patch
116+ marshalledOriginal , err := json .Marshal (original )
98117if err != nil {
99118return Errored (http .StatusInternalServerError , err )
100119}
101- return PatchResponseFromRaw (marshalledOriginal , marshalledUpdated )
120+
121+ patchOriginal := PatchResponseFromRaw (raw , marshalledOriginal ).Patches
122+ removedByScheme := sets .New (slices .DeleteFunc (patchOriginal , func (p jsonpatch.JsonPatchOperation ) bool { return p .Operation != opRemove })... )
123+
124+ r .Patches = slices .DeleteFunc (r .Patches , func (p jsonpatch.JsonPatchOperation ) bool {
125+ return removedByScheme .Has (p )
126+ })
127+
128+ if len (r .Patches ) == 0 {
129+ r .PatchType = nil
130+ }
131+ return r
102132}
0 commit comments