@@ -21,11 +21,14 @@ import (
21
21
"encoding/json"
22
22
"errors"
23
23
"net/http"
24
+ "slices"
24
25
26
+ "gomodules.xyz/jsonpatch/v2"
25
27
admissionv1 "k8s.io/api/admission/v1"
26
28
apierrors "k8s.io/apimachinery/pkg/api/errors"
27
29
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28
30
"k8s.io/apimachinery/pkg/runtime"
31
+ "k8s.io/apimachinery/pkg/util/sets"
29
32
)
30
33
31
34
// CustomDefaulter defines functions for setting defaults on resources.
@@ -71,32 +74,59 @@ func (h *defaulterForType) Handle(ctx context.Context, req Request) Response {
71
74
ctx = NewContextWithRequest (ctx , req )
72
75
73
76
// 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 {
76
79
return Errored (http .StatusBadRequest , err )
77
80
}
78
81
82
+ // Keep a copy of the object
83
+ originalObj := obj .DeepCopyObject ()
84
+
79
85
// 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 {
82
87
var apiStatus apierrors.APIStatus
83
88
if errors .As (err , & apiStatus ) {
84
89
return validationResponseFromStatus (false , apiStatus .Status ())
85
90
}
86
91
return Denied (err .Error ())
87
92
}
88
93
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 )
94
96
if err != nil {
95
97
return Errored (http .StatusInternalServerError , err )
96
98
}
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 )
98
117
if err != nil {
99
118
return Errored (http .StatusInternalServerError , err )
100
119
}
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
102
132
}
0 commit comments