From 48ff68a27de76045e7cdd58d85f5e5b1bd284fc1 Mon Sep 17 00:00:00 2001 From: Marc Nuri Date: Fri, 11 Nov 2022 10:51:53 +0100 Subject: [PATCH] feat: provided additional KubernetesClient.resource(InputStream) method Signed-off-by: Marc Nuri --- .../kubernetes/client/KubernetesClient.java | 21 ++++-- .../NamespacedKubernetesClientAdapter.java | 5 ++ .../client/impl/KubernetesClientImpl.java | 18 ++++- .../client/impl/KubernetesClientImplTest.java | 67 +++++++++++++++++++ 4 files changed, 104 insertions(+), 7 deletions(-) diff --git a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/KubernetesClient.java b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/KubernetesClient.java index 76e10ef3810..6d2f84ab984 100644 --- a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/KubernetesClient.java +++ b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/KubernetesClient.java @@ -286,10 +286,10 @@ MixedOperation> componentstatuses(); /** - * Load a Kubernetes resource object from file InputStream + * Load Kubernetes resource object(s) from the provided InputStream. * - * @param is File input stream object containing json/yaml content - * @return deserialized object + * @param is the input stream containing JSON/YAML content + * @return an operation instance to work on the list of Kubernetes Resource objects */ ParameterNamespaceListVisitFromServerGetDeleteRecreateWaitApplicable load(InputStream is); @@ -297,7 +297,7 @@ MixedOperation resourceList(String s); @@ -340,11 +340,20 @@ NamespaceListVisitFromServerGetDeleteRecreateWaitApplicable resourc * KubernetesResource operations. You can pass any Kubernetes resource as string object and do * all operations * - * @param s Kubernetes resource object as string + * @param s a Kubernetes resource object as string * @return operations object for Kubernetes resource */ NamespaceableResource resource(String s); + /** + * KubernetesResource operations. You can pass any Kubernetes resource as an InputStream object and perform + * all operations + * + * @param is the InputStream containing a serialized Kubernetes resource. + * @return operations object for Kubernetes resource. + */ + NamespaceableResource resource(InputStream is); + /** * Operations for Binding resource in APIgroup core/v1 * @@ -514,7 +523,7 @@ NamespaceListVisitFromServerGetDeleteRecreateWaitApplicable resourc /** * Visit all resources with the given {@link ApiVisitor}. - * + * * @param visitor */ void visitResources(ApiVisitor visitor); diff --git a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/NamespacedKubernetesClientAdapter.java b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/NamespacedKubernetesClientAdapter.java index 25bb532bee0..7bd8982778a 100644 --- a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/NamespacedKubernetesClientAdapter.java +++ b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/NamespacedKubernetesClientAdapter.java @@ -283,6 +283,11 @@ public NamespaceableResource resource(String s) { return getClient().resource(s); } + @Override + public NamespaceableResource resource(InputStream is) { + return getClient().resource(is); + } + @Override public MixedOperation, Resource> bindings() { return getClient().bindings(); diff --git a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/impl/KubernetesClientImpl.java b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/impl/KubernetesClientImpl.java index 35265ac6312..b87dd87deaa 100644 --- a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/impl/KubernetesClientImpl.java +++ b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/impl/KubernetesClientImpl.java @@ -357,12 +357,28 @@ public NamespaceableResource resource(T item) { return new NamespaceableResourceAdapter<>(item, op); } + private NamespaceableResource resource(Object resource) { + if (resource instanceof HasMetadata) { + return resource((HasMetadata) resource); + } + throw new KubernetesClientException("Unable to create a valid resource from the provided object (" + + resource.getClass().getName() + ")"); + } + /** * {@inheritDoc} */ @Override public NamespaceableResource resource(String s) { - return resource((HasMetadata) Serialization.unmarshal(s)); + return resource(Serialization.unmarshal(s)); + } + + /** + * {@inheritDoc} + */ + @Override + public NamespaceableResource resource(InputStream is) { + return resource(Serialization.unmarshal(is)); } /** diff --git a/kubernetes-client/src/test/java/io/fabric8/kubernetes/client/impl/KubernetesClientImplTest.java b/kubernetes-client/src/test/java/io/fabric8/kubernetes/client/impl/KubernetesClientImplTest.java index ca4b4203658..7eae89b001f 100644 --- a/kubernetes-client/src/test/java/io/fabric8/kubernetes/client/impl/KubernetesClientImplTest.java +++ b/kubernetes-client/src/test/java/io/fabric8/kubernetes/client/impl/KubernetesClientImplTest.java @@ -16,11 +16,14 @@ package io.fabric8.kubernetes.client.impl; import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.kubernetes.api.model.Pod; import io.fabric8.kubernetes.api.model.Service; import io.fabric8.kubernetes.api.model.apps.Deployment; import io.fabric8.kubernetes.client.Config; import io.fabric8.kubernetes.client.ConfigBuilder; import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.KubernetesClientException; +import io.fabric8.kubernetes.client.dsl.ParameterNamespaceListVisitFromServerGetDeleteRecreateWaitApplicable; import io.fabric8.kubernetes.client.http.BasicBuilder; import io.fabric8.kubernetes.client.http.HttpHeaders; import io.fabric8.kubernetes.client.utils.HttpClientUtils; @@ -35,6 +38,7 @@ import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; +import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.nio.file.Files; @@ -44,6 +48,8 @@ import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; @@ -153,4 +159,65 @@ void shouldPropagateImpersonateSettings() { assertArrayEquals(new String[] { "b" }, currentConfig.getImpersonateGroups()); assertEquals(Collections.singletonList("d"), currentConfig.getImpersonateExtras().get("c")); } + + @Test + @DisplayName("resource(String).get with HasMetadata should deserialize") + void resourceFromStringWithHasMetadata() { + assertThat(new KubernetesClientImpl().resource("apiVersion: v1\nkind: Pod").get()) + .isInstanceOf(Pod.class); + } + + @Test + @DisplayName("resource(String) with no HasMetadata should throwException") + void resourceFromStringWithInvalid() { + final KubernetesClient kc = new KubernetesClientImpl(); + assertThatExceptionOfType(KubernetesClientException.class) + .isThrownBy(() -> kc.resource("NotAPod")) + .withMessageStartingWith("Unable to create a valid resource from the provided object"); + } + + @Test + @DisplayName("resource(InputStream).get with HasMetadata should deserialize") + void resourceFromInputStreamWithHasMetadata() throws IOException { + final String podYaml = "apiVersion: v1\nkind: Pod"; + try (InputStream is = new ByteArrayInputStream(podYaml.getBytes(StandardCharsets.UTF_8))) { + assertThat(new KubernetesClientImpl().resource(is).get()) + .isInstanceOf(Pod.class); + } + } + + @Test + @DisplayName("resource(InputStream) with no HasMetadata should throwException") + void resourceFromInputStreamWithInvalid() throws IOException { + final KubernetesClient kc = new KubernetesClientImpl(); + final String podYaml = "NotAPod"; + try (InputStream is = new ByteArrayInputStream(podYaml.getBytes(StandardCharsets.UTF_8))) { + assertThatExceptionOfType(KubernetesClientException.class) + .isThrownBy(() -> kc.resource(is)) + .withMessageStartingWith("Unable to create a valid resource from the provided object"); + } + } + + @Test + @DisplayName("load(InputStream).get with HasMetadata should deserialize") + void loadFromInputStreamWithHasMetadata() throws IOException { + final String podYaml = "apiVersion: v1\nkind: Pod"; + try (InputStream is = new ByteArrayInputStream(podYaml.getBytes(StandardCharsets.UTF_8))) { + assertThat(new KubernetesClientImpl().load(is).get()) + .containsExactly(new Pod()); + } + } + + @Test + @DisplayName("load(InputStream).get with no HasMetadata should throwException") + void loadFromInputStreamWithInvalid() throws IOException { + final String podYaml = "NotAPod"; + try (InputStream is = new ByteArrayInputStream(podYaml.getBytes(StandardCharsets.UTF_8))) { + final ParameterNamespaceListVisitFromServerGetDeleteRecreateWaitApplicable load = + new KubernetesClientImpl().load(is); + assertThatIllegalArgumentException() + .isThrownBy(load::get) + .withMessageStartingWith("Could not convert item to a list of HasMetadata"); + } + } }