diff --git a/kubernetes/src/main/java/io/kubernetes/client/openapi/JSON.java b/kubernetes/src/main/java/io/kubernetes/client/openapi/JSON.java index 8d0f4c47dd..cff284332a 100644 --- a/kubernetes/src/main/java/io/kubernetes/client/openapi/JSON.java +++ b/kubernetes/src/main/java/io/kubernetes/client/openapi/JSON.java @@ -23,6 +23,8 @@ import io.gsonfire.GsonFireBuilder; import io.gsonfire.TypeSelector; +import io.kubernetes.client.gson.V1StatusPreProcessor; +import io.kubernetes.client.openapi.models.V1Status; import okio.ByteString; import java.io.IOException; @@ -34,7 +36,9 @@ import java.time.OffsetDateTime; import java.time.ZoneId; import java.time.ZoneOffset; -import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.format.DateTimeParseException; +import java.time.temporal.ChronoField; import java.util.Date; import java.util.Locale; import java.util.Map; @@ -50,9 +54,19 @@ public class JSON { private static Gson gson; private static boolean isLenientOnJson = false; + private static final DateTimeFormatter RFC3339MICRO_FORMATTER = + new DateTimeFormatterBuilder() + .parseDefaulting(ChronoField.OFFSET_SECONDS, 0) + .append(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss")) + .optionalStart() + .appendFraction(ChronoField.NANO_OF_SECOND, 6, 6, true) + .optionalEnd() + .appendLiteral("Z") + .toFormatter(); + private static DateTypeAdapter dateTypeAdapter = new DateTypeAdapter(); private static SqlDateTypeAdapter sqlDateTypeAdapter = new SqlDateTypeAdapter(); - private static OffsetDateTimeTypeAdapter offsetDateTimeTypeAdapter = new OffsetDateTimeTypeAdapter(); + private static OffsetDateTimeTypeAdapter offsetDateTimeTypeAdapter = new OffsetDateTimeTypeAdapter(RFC3339MICRO_FORMATTER); private static LocalDateTypeAdapter localDateTypeAdapter = new LocalDateTypeAdapter(); private static ByteArrayAdapter byteArrayAdapter = new ByteArrayAdapter(); @@ -65,7 +79,10 @@ public class JSON { public static GsonBuilder createGson() { GsonFireBuilder fireBuilder = new GsonFireBuilder() ; - GsonBuilder builder = fireBuilder.createGsonBuilder(); + GsonBuilder builder = + fireBuilder + .registerPreProcessor(V1Status.class, new V1StatusPreProcessor()) + .createGsonBuilder(); return builder; } @@ -784,11 +801,15 @@ public static class ByteArrayAdapter extends TypeAdapter { @Override public void write(JsonWriter out, byte[] value) throws IOException { + boolean oldHtmlSafe = out.isHtmlSafe(); + out.setHtmlSafe(false); + if (value == null) { out.nullValue(); } else { out.value(ByteString.of(value).base64()); } + out.setHtmlSafe(oldHtmlSafe); } @Override @@ -844,6 +865,12 @@ public OffsetDateTime read(JsonReader in) throws IOException { if (date.endsWith("+0000")) { date = date.substring(0, date.length()-5) + "Z"; } + try { + return OffsetDateTime.parse(date, formatter); + } catch (DateTimeParseException e) { + // backward-compatibility for ISO8601 timestamp format + return OffsetDateTime.parse(date, DateTimeFormatter.ISO_OFFSET_DATE_TIME); + } return OffsetDateTime.parse(date, formatter); } } diff --git a/kubernetes/src/main/java/io/kubernetes/client/openapi/models/V1ListMeta.java b/kubernetes/src/main/java/io/kubernetes/client/openapi/models/V1ListMeta.java index f161284a27..d3d563bbbf 100644 --- a/kubernetes/src/main/java/io/kubernetes/client/openapi/models/V1ListMeta.java +++ b/kubernetes/src/main/java/io/kubernetes/client/openapi/models/V1ListMeta.java @@ -258,7 +258,8 @@ public void write(JsonWriter out, V1ListMeta value) throws IOException { @Override public V1ListMeta read(JsonReader in) throws IOException { JsonElement jsonElement = elementAdapter.read(in); - validateJsonElement(jsonElement); + // Disable validation so delete API can tolerate non-status return object (graceful deletion) + // validateJsonObject(jsonObj); return thisAdapter.fromJsonTree(jsonElement); } diff --git a/kubernetes/src/main/java/io/kubernetes/client/openapi/models/V1Secret.java b/kubernetes/src/main/java/io/kubernetes/client/openapi/models/V1Secret.java index 5684f47768..d0fa36751c 100644 --- a/kubernetes/src/main/java/io/kubernetes/client/openapi/models/V1Secret.java +++ b/kubernetes/src/main/java/io/kubernetes/client/openapi/models/V1Secret.java @@ -18,6 +18,7 @@ import com.google.gson.annotations.SerializedName; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; +import io.kubernetes.client.custom.MapUtils; import io.kubernetes.client.openapi.models.V1ObjectMeta; import java.io.IOException; import java.util.Arrays; @@ -244,7 +245,7 @@ public boolean equals(Object o) { } V1Secret v1Secret = (V1Secret) o; return Objects.equals(this.apiVersion, v1Secret.apiVersion) && - Objects.equals(this.data, v1Secret.data) && + MapUtils.equals(this.data, v1Secret.data) && Objects.equals(this.immutable, v1Secret.immutable) && Objects.equals(this.kind, v1Secret.kind) && Objects.equals(this.metadata, v1Secret.metadata) && diff --git a/kubernetes/src/main/java/io/kubernetes/client/openapi/models/V1Status.java b/kubernetes/src/main/java/io/kubernetes/client/openapi/models/V1Status.java index b529e34551..c2268419a7 100644 --- a/kubernetes/src/main/java/io/kubernetes/client/openapi/models/V1Status.java +++ b/kubernetes/src/main/java/io/kubernetes/client/openapi/models/V1Status.java @@ -378,7 +378,8 @@ public void write(JsonWriter out, V1Status value) throws IOException { @Override public V1Status read(JsonReader in) throws IOException { JsonElement jsonElement = elementAdapter.read(in); - validateJsonElement(jsonElement); + // Disable validation so delete API can tolerate non-status return object (graceful deletion) + // validateJsonObject(jsonObj); return thisAdapter.fromJsonTree(jsonElement); }