@@ -145,6 +145,106 @@ func TestApplyAlsoCreates(t *testing.T) {
145
145
}
146
146
}
147
147
148
+ // TestNoOpUpdateSameResourceVersion makes sure that PUT requests which change nothing
149
+ // will not change the resource version (no write to etcd is done)
150
+ func TestNoOpUpdateSameResourceVersion (t * testing.T ) {
151
+ defer featuregatetesting .SetFeatureGateDuringTest (t , utilfeature .DefaultFeatureGate , genericfeatures .ServerSideApply , true )()
152
+
153
+ _ , client , closeFn := setup (t )
154
+ defer closeFn ()
155
+
156
+ podName := "no-op"
157
+ podResource := "pods"
158
+ podBytes := []byte (`{
159
+ "apiVersion": "v1",
160
+ "kind": "Pod",
161
+ "metadata": {
162
+ "name": "` + podName + `",
163
+ "labels": {
164
+ "a": "one",
165
+ "c": "two",
166
+ "b": "three"
167
+ }
168
+ },
169
+ "spec": {
170
+ "containers": [{
171
+ "name": "test-container-a",
172
+ "image": "test-image-one"
173
+ },{
174
+ "name": "test-container-c",
175
+ "image": "test-image-two"
176
+ },{
177
+ "name": "test-container-b",
178
+ "image": "test-image-three"
179
+ }]
180
+ }
181
+ }` )
182
+
183
+ _ , err := client .CoreV1 ().RESTClient ().Post ().
184
+ Namespace ("default" ).
185
+ Resource (podResource ).
186
+ Body (podBytes ).
187
+ Do ().
188
+ Get ()
189
+ if err != nil {
190
+ t .Fatalf ("Failed to create object: %v" , err )
191
+ }
192
+
193
+ // Sleep for one second to make sure that the times of each update operation is different.
194
+ time .Sleep (1 * time .Second )
195
+
196
+ createdObject , err := client .CoreV1 ().RESTClient ().Get ().Namespace ("default" ).Resource (podResource ).Name (podName ).Do ().Get ()
197
+ if err != nil {
198
+ t .Fatalf ("Failed to retrieve created object: %v" , err )
199
+ }
200
+
201
+ createdAccessor , err := meta .Accessor (createdObject )
202
+ if err != nil {
203
+ t .Fatalf ("Failed to get meta accessor for created object: %v" , err )
204
+ }
205
+
206
+ createdBytes , err := json .MarshalIndent (createdObject , "\t " , "\t " )
207
+ if err != nil {
208
+ t .Fatalf ("Failed to marshal created object: %v" , err )
209
+ }
210
+
211
+ // Test that we can put the same object and don't change the RV
212
+ _ , err = client .CoreV1 ().RESTClient ().Put ().
213
+ Namespace ("default" ).
214
+ Resource (podResource ).
215
+ Name (podName ).
216
+ Body (createdBytes ).
217
+ Do ().
218
+ Get ()
219
+ if err != nil {
220
+ t .Fatalf ("Failed to apply no-op update: %v" , err )
221
+ }
222
+
223
+ updatedObject , err := client .CoreV1 ().RESTClient ().Get ().Namespace ("default" ).Resource (podResource ).Name (podName ).Do ().Get ()
224
+ if err != nil {
225
+ t .Fatalf ("Failed to retrieve updated object: %v" , err )
226
+ }
227
+
228
+ updatedAccessor , err := meta .Accessor (updatedObject )
229
+ if err != nil {
230
+ t .Fatalf ("Failed to get meta accessor for updated object: %v" , err )
231
+ }
232
+
233
+ updatedBytes , err := json .MarshalIndent (updatedObject , "\t " , "\t " )
234
+ if err != nil {
235
+ t .Fatalf ("Failed to marshal updated object: %v" , err )
236
+ }
237
+
238
+ if createdAccessor .GetResourceVersion () != updatedAccessor .GetResourceVersion () {
239
+ t .Fatalf ("Expected same resource version to be %v but got: %v\n old object:\n %v\n new object:\n %v" ,
240
+ createdAccessor .GetResourceVersion (),
241
+ updatedAccessor .GetResourceVersion (),
242
+ string (createdBytes ),
243
+ string (updatedBytes ),
244
+ )
245
+ }
246
+ }
247
+
148
248
// TestCreateOnApplyFailsWithUID makes sure that PATCH requests with the apply content type
149
249
// will not create the object if it doesn't already exist and it specifies a UID
150
250
func TestCreateOnApplyFailsWithUID (t * testing.T ) {
0 commit comments