From fc4ab63c483778e2166bbddfeded2f822dccf933 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 17 Nov 2025 22:47:01 +0000 Subject: [PATCH 1/3] Initial plan From ea892a22072d74e66de0c2ca5fbf0b6754e1255a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 17 Nov 2025 22:58:18 +0000 Subject: [PATCH 2/3] Initial investigation and test case for Watch.Response deserialization issue Co-authored-by: brendandburns <5751682+brendandburns@users.noreply.github.com> --- .../client/util/WatchDynamicObjectTest.java | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 util/src/test/java/io/kubernetes/client/util/WatchDynamicObjectTest.java diff --git a/util/src/test/java/io/kubernetes/client/util/WatchDynamicObjectTest.java b/util/src/test/java/io/kubernetes/client/util/WatchDynamicObjectTest.java new file mode 100644 index 0000000000..d4dee5a7a0 --- /dev/null +++ b/util/src/test/java/io/kubernetes/client/util/WatchDynamicObjectTest.java @@ -0,0 +1,81 @@ +/* +Copyright 2025 The Kubernetes Authors. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package io.kubernetes.client.util; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.google.gson.reflect.TypeToken; +import io.kubernetes.client.openapi.JSON; +import io.kubernetes.client.util.generic.dynamic.DynamicKubernetesObject; +import java.io.IOException; +import org.junit.jupiter.api.Test; + +/** Tests for Watch with DynamicKubernetesObject */ +class WatchDynamicObjectTest { + + @Test + void watchAddedEventShouldPreserveRawData() throws IOException { + JSON json = new JSON(); + Watch watch = + new Watch<>( + json, null, new TypeToken>() {}.getType(), null); + + String addedEvent = "{\"type\":\"ADDED\"," + + "\"object\":{" + + "\"apiVersion\":\"v1\"," + + "\"kind\":\"Pod\"," + + "\"metadata\":{\"name\":\"test-pod\",\"namespace\":\"default\"}," + + "\"spec\":{\"containers\":[{\"name\":\"nginx\",\"image\":\"nginx:latest\"}]}" + + "}}"; + + Watch.Response response = watch.parseLine(addedEvent); + assertThat(response.type).isEqualTo("ADDED"); + assertThat(response.object).isNotNull(); + assertThat(response.object.getMetadata()).isNotNull(); + assertThat(response.object.getMetadata().getName()).isEqualTo("test-pod"); + + // Check that raw data is preserved + assertThat(response.object.getRaw()).isNotNull(); + assertThat(response.object.getRaw().size()).isGreaterThan(2); // Should have more than just metadata + assertThat(response.object.getRaw().has("spec")).isTrue(); + assertThat(response.object.getRaw().get("spec")).isNotNull(); + } + + @Test + void watchModifiedEventShouldPreserveRawData() throws IOException { + JSON json = new JSON(); + Watch watch = + new Watch<>( + json, null, new TypeToken>() {}.getType(), null); + + String modifiedEvent = "{\"type\":\"MODIFIED\"," + + "\"object\":{" + + "\"apiVersion\":\"v1\"," + + "\"kind\":\"Pod\"," + + "\"metadata\":{\"name\":\"test-pod\",\"namespace\":\"default\"}," + + "\"spec\":{\"containers\":[{\"name\":\"nginx\",\"image\":\"nginx:1.19\"}]}" + + "}}"; + + Watch.Response response = watch.parseLine(modifiedEvent); + assertThat(response.type).isEqualTo("MODIFIED"); + assertThat(response.object).isNotNull(); + assertThat(response.object.getMetadata()).isNotNull(); + assertThat(response.object.getMetadata().getName()).isEqualTo("test-pod"); + + // Check that raw data is preserved + assertThat(response.object.getRaw()).isNotNull(); + assertThat(response.object.getRaw().size()).isGreaterThan(2); // Should have more than just metadata + assertThat(response.object.getRaw().has("spec")).isTrue(); + assertThat(response.object.getRaw().get("spec")).isNotNull(); + } +} From 9ef877b3958bff1b1e17a0fd21fc7c7c1f15533b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 17 Nov 2025 23:02:37 +0000 Subject: [PATCH 3/3] Fix Watch.Response deserialization to use JSON instance's GSON Co-authored-by: brendandburns <5751682+brendandburns@users.noreply.github.com> --- util/src/main/java/io/kubernetes/client/util/Watch.java | 4 ++-- .../io/kubernetes/client/util/WatchDynamicObjectTest.java | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/util/src/main/java/io/kubernetes/client/util/Watch.java b/util/src/main/java/io/kubernetes/client/util/Watch.java index 6771eb04cf..2573b3fad7 100644 --- a/util/src/main/java/io/kubernetes/client/util/Watch.java +++ b/util/src/main/java/io/kubernetes/client/util/Watch.java @@ -169,10 +169,10 @@ protected boolean isStatus(String line) throws IOException { protected Response parseLine(String line) throws IOException { if (!isStatus(line)) { - return json.deserialize(line, watchType); + return json.getGson().fromJson(line, watchType); } Type statusType = new TypeToken>() {}.getType(); - Response status = json.deserialize(line, statusType); + Response status = json.getGson().fromJson(line, statusType); return new Response(status.type, status.object); } diff --git a/util/src/test/java/io/kubernetes/client/util/WatchDynamicObjectTest.java b/util/src/test/java/io/kubernetes/client/util/WatchDynamicObjectTest.java index d4dee5a7a0..fb03a4243b 100644 --- a/util/src/test/java/io/kubernetes/client/util/WatchDynamicObjectTest.java +++ b/util/src/test/java/io/kubernetes/client/util/WatchDynamicObjectTest.java @@ -26,6 +26,10 @@ class WatchDynamicObjectTest { @Test void watchAddedEventShouldPreserveRawData() throws IOException { JSON json = new JSON(); + // Register the DynamicKubernetesTypeAdaptorFactory to properly handle DynamicKubernetesObject + json.setGson(json.getGson().newBuilder() + .registerTypeAdapterFactory(new io.kubernetes.client.util.generic.dynamic.DynamicKubernetesTypeAdaptorFactory()) + .create()); Watch watch = new Watch<>( json, null, new TypeToken>() {}.getType(), null); @@ -54,6 +58,10 @@ void watchAddedEventShouldPreserveRawData() throws IOException { @Test void watchModifiedEventShouldPreserveRawData() throws IOException { JSON json = new JSON(); + // Register the DynamicKubernetesTypeAdaptorFactory to properly handle DynamicKubernetesObject + json.setGson(json.getGson().newBuilder() + .registerTypeAdapterFactory(new io.kubernetes.client.util.generic.dynamic.DynamicKubernetesTypeAdaptorFactory()) + .create()); Watch watch = new Watch<>( json, null, new TypeToken>() {}.getType(), null);