From b7e948fe50e6c3ee9f0b12466fef1fa4c5daf9d7 Mon Sep 17 00:00:00 2001 From: Stephane Epardaud Date: Thu, 4 Feb 2021 14:21:28 +0100 Subject: [PATCH] Fix for #14744: support KibernetesServer test resource for CRUD operations --- .../AbstractKubernetesTestResource.java | 82 +++++++++++++++++ .../KubernetesMockServerTestResource.java | 91 ++++++++----------- .../client/KubernetesServerTestResource.java | 42 +++++++++ .../test/kubernetes/client/Server.java | 18 ++++ 4 files changed, 178 insertions(+), 55 deletions(-) create mode 100644 test-framework/kubernetes-client/src/main/java/io/quarkus/test/kubernetes/client/AbstractKubernetesTestResource.java create mode 100644 test-framework/kubernetes-client/src/main/java/io/quarkus/test/kubernetes/client/KubernetesServerTestResource.java create mode 100644 test-framework/kubernetes-client/src/main/java/io/quarkus/test/kubernetes/client/Server.java diff --git a/test-framework/kubernetes-client/src/main/java/io/quarkus/test/kubernetes/client/AbstractKubernetesTestResource.java b/test-framework/kubernetes-client/src/main/java/io/quarkus/test/kubernetes/client/AbstractKubernetesTestResource.java new file mode 100644 index 0000000000000..b4dedcdd08828 --- /dev/null +++ b/test-framework/kubernetes-client/src/main/java/io/quarkus/test/kubernetes/client/AbstractKubernetesTestResource.java @@ -0,0 +1,82 @@ +package io.quarkus.test.kubernetes.client; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.Map; + +import io.fabric8.kubernetes.client.Config; +import io.fabric8.kubernetes.client.GenericKubernetesClient; +import io.fabric8.kubernetes.client.server.mock.KubernetesMockServer; +import io.quarkus.test.common.QuarkusTestResourceLifecycleManager; + +public abstract class AbstractKubernetesTestResource implements QuarkusTestResourceLifecycleManager { + protected T server; + + @Override + public Map start() { + final Map systemProps = new HashMap<>(); + systemProps.put(Config.KUBERNETES_TRUST_CERT_SYSTEM_PROPERTY, "true"); + systemProps.put(Config.KUBERNETES_AUTH_TRYKUBECONFIG_SYSTEM_PROPERTY, "false"); + systemProps.put(Config.KUBERNETES_AUTH_TRYSERVICEACCOUNT_SYSTEM_PROPERTY, "false"); + systemProps.put(Config.KUBERNETES_NAMESPACE_SYSTEM_PROPERTY, "test"); + systemProps.put(Config.KUBERNETES_HTTP2_DISABLE, "true"); + + server = createServer(); + initServer(); + + try (GenericKubernetesClient client = getClient()) { + systemProps.put(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY, client.getConfiguration().getMasterUrl()); + } + + configureServer(); + + return systemProps; + } + + protected abstract GenericKubernetesClient getClient(); + + /** + * Can be used by subclasses in order to + * setup the mock server before the Quarkus application starts + */ + protected void configureServer() {} + + protected void initServer() {} + + protected abstract T createServer(); + + protected boolean useHttps() { + return Boolean.getBoolean("quarkus.kubernetes-client.test.https"); + } + + @Override + public void inject(Object testInstance) { + Class c = testInstance.getClass(); + Class annotation = getInjectionAnnotation(); + Class injectedClass = getInjectedClass(); + while (c != Object.class) { + for (Field f : c.getDeclaredFields()) { + if (f.getAnnotation(annotation) != null) { + if (!injectedClass.isAssignableFrom(f.getType())) { + throw new RuntimeException(annotation+" can only be used on fields of type "+injectedClass); + } + + f.setAccessible(true); + try { + f.set(testInstance, server); + return; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + c = c.getSuperclass(); + } + } + + protected abstract Class getInjectedClass(); + + protected abstract Class getInjectionAnnotation(); + +} diff --git a/test-framework/kubernetes-client/src/main/java/io/quarkus/test/kubernetes/client/KubernetesMockServerTestResource.java b/test-framework/kubernetes-client/src/main/java/io/quarkus/test/kubernetes/client/KubernetesMockServerTestResource.java index 445979ba56800..8bfcad5d39d16 100644 --- a/test-framework/kubernetes-client/src/main/java/io/quarkus/test/kubernetes/client/KubernetesMockServerTestResource.java +++ b/test-framework/kubernetes-client/src/main/java/io/quarkus/test/kubernetes/client/KubernetesMockServerTestResource.java @@ -1,81 +1,62 @@ package io.quarkus.test.kubernetes.client; -import java.lang.reflect.Field; -import java.util.HashMap; -import java.util.Map; +import java.lang.annotation.Annotation; -import io.fabric8.kubernetes.client.Config; -import io.fabric8.kubernetes.client.NamespacedKubernetesClient; +import io.fabric8.kubernetes.client.GenericKubernetesClient; import io.fabric8.kubernetes.client.server.mock.KubernetesMockServer; -import io.quarkus.test.common.QuarkusTestResourceLifecycleManager; -public class KubernetesMockServerTestResource implements QuarkusTestResourceLifecycleManager { - - private KubernetesMockServer mockServer; +public class KubernetesMockServerTestResource extends AbstractKubernetesTestResource { @Override - public Map start() { - final Map systemProps = new HashMap<>(); - systemProps.put(Config.KUBERNETES_TRUST_CERT_SYSTEM_PROPERTY, "true"); - systemProps.put(Config.KUBERNETES_AUTH_TRYKUBECONFIG_SYSTEM_PROPERTY, "false"); - systemProps.put(Config.KUBERNETES_AUTH_TRYSERVICEACCOUNT_SYSTEM_PROPERTY, "false"); - systemProps.put(Config.KUBERNETES_NAMESPACE_SYSTEM_PROPERTY, "test"); - systemProps.put(Config.KUBERNETES_HTTP2_DISABLE, "true"); - - mockServer = createMockServer(); - mockServer.init(); - try (NamespacedKubernetesClient client = mockServer.createClient()) { - systemProps.put(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY, client.getConfiguration().getMasterUrl()); - } - - configureMockServer(mockServer); - - return systemProps; + protected GenericKubernetesClient getClient() { + return server.createClient(); } - + + @Override + protected void initServer() { + server.init(); + } + + @Override + protected KubernetesMockServer createServer() { + return createMockServer(); + } + + /** + * @deprecated use {@link #createServer()} + */ + @Deprecated protected KubernetesMockServer createMockServer() { return new KubernetesMockServer(useHttps()); } + @Override + protected void configureServer() { + configureMockServer(server); + } + /** - * Can be used by subclasses of {@code KubernetesMockServerTestResource} in order to - * setup the mock server before the Quarkus application starts + * @deprecated use {@link #configureServer()} */ + @Deprecated public void configureMockServer(KubernetesMockServer mockServer) { - } @Override public void stop() { - if (mockServer != null) { - mockServer.destroy(); + if (server != null) { + server.destroy(); + server = null; } } @Override - public void inject(Object testInstance) { - Class c = testInstance.getClass(); - while (c != Object.class) { - for (Field f : c.getDeclaredFields()) { - if (f.getAnnotation(MockServer.class) != null) { - if (!KubernetesMockServer.class.isAssignableFrom(f.getType())) { - throw new RuntimeException("@MockServer can only be used on fields of type KubernetesMockServer"); - } - - f.setAccessible(true); - try { - f.set(testInstance, mockServer); - return; - } catch (Exception e) { - throw new RuntimeException(e); - } - } - } - c = c.getSuperclass(); - } + protected Class getInjectedClass() { + return KubernetesMockServer.class; } - - protected boolean useHttps() { - return Boolean.getBoolean("quarkus.kubernetes-client.test.https"); + + @Override + protected Class getInjectionAnnotation() { + return MockServer.class; } } diff --git a/test-framework/kubernetes-client/src/main/java/io/quarkus/test/kubernetes/client/KubernetesServerTestResource.java b/test-framework/kubernetes-client/src/main/java/io/quarkus/test/kubernetes/client/KubernetesServerTestResource.java new file mode 100644 index 0000000000000..f4855d7d11be7 --- /dev/null +++ b/test-framework/kubernetes-client/src/main/java/io/quarkus/test/kubernetes/client/KubernetesServerTestResource.java @@ -0,0 +1,42 @@ +package io.quarkus.test.kubernetes.client; + +import java.lang.annotation.Annotation; + +import io.fabric8.kubernetes.client.GenericKubernetesClient; +import io.fabric8.kubernetes.client.server.mock.KubernetesServer; + +public class KubernetesServerTestResource extends AbstractKubernetesTestResource { + + @Override + protected GenericKubernetesClient getClient() { + return server.getClient(); + } + + @Override + protected void initServer() { + server.before(); + } + + @Override + protected KubernetesServer createServer() { + return new KubernetesServer(useHttps(), true); + } + + @Override + public void stop() { + if (server != null) { + server.after(); + server = null; + } + } + + @Override + protected Class getInjectedClass() { + return KubernetesServer.class; + } + + @Override + protected Class getInjectionAnnotation() { + return Server.class; + } +} diff --git a/test-framework/kubernetes-client/src/main/java/io/quarkus/test/kubernetes/client/Server.java b/test-framework/kubernetes-client/src/main/java/io/quarkus/test/kubernetes/client/Server.java new file mode 100644 index 0000000000000..2cdb47708d0fd --- /dev/null +++ b/test-framework/kubernetes-client/src/main/java/io/quarkus/test/kubernetes/client/Server.java @@ -0,0 +1,18 @@ +package io.quarkus.test.kubernetes.client; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import io.fabric8.kubernetes.client.server.mock.KubernetesServer; + +/** + * Used to specify that the field should be injected with the mock Kubernetes API server + * Can only be used on type {@link KubernetesServer} + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface Server { + +}