diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/BuilderUtils.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/BuilderUtils.java new file mode 100644 index 0000000000..1c6f388f62 --- /dev/null +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/BuilderUtils.java @@ -0,0 +1,35 @@ + +package io.javaoperatorsdk.operator; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +public final class BuilderUtils { + + // prevent instantiation of util class + private BuilderUtils() {} + + public static final B newBuilder(Class builderType, T item) { + Class builderTargetType = builderTargetType(builderType); + try { + Constructor constructor = builderType.getDeclaredConstructor(builderTargetType); + return constructor.newInstance(item); + } catch (NoSuchMethodException | SecurityException | InstantiationException + | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + throw new OperatorException( + "Failied to instantiate builder: " + builderType.getCanonicalName() + " using: " + item, + e); + } + } + + public static final Class builderTargetType(Class builderType) { + try { + Method method = builderType.getDeclaredMethod("build"); + return (Class) method.getReturnType(); + } catch (NoSuchMethodException | SecurityException e) { + throw new OperatorException( + "Failied to determine target type for builder: " + builderType.getCanonicalName(), e); + } + } +} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/ReconcilerUtils.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/ReconcilerUtils.java index d23362b2b0..b27f331faa 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/ReconcilerUtils.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/ReconcilerUtils.java @@ -10,6 +10,7 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; +import io.fabric8.kubernetes.api.builder.Builder; import io.fabric8.kubernetes.api.model.HasMetadata; import io.fabric8.kubernetes.client.KubernetesClientException; import io.fabric8.kubernetes.client.utils.Serialization; @@ -114,6 +115,10 @@ public static Object setSpec(HasMetadata resource, Object spec) { public static T loadYaml(Class clazz, Class loader, String yaml) { try (InputStream is = loader.getResourceAsStream(yaml)) { + if (Builder.class.isAssignableFrom(clazz)) { + return BuilderUtils.newBuilder(clazz, + Serialization.unmarshal(is, BuilderUtils.builderTargetType(clazz))); + } return Serialization.unmarshal(is, clazz); } catch (IOException ex) { throw new IllegalStateException("Cannot find yaml on classpath: " + yaml); diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/ReconcilerUtilsTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/ReconcilerUtilsTest.java index 5cbe1a4441..f629f517d3 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/ReconcilerUtilsTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/ReconcilerUtilsTest.java @@ -2,12 +2,14 @@ import org.junit.jupiter.api.Test; +import io.fabric8.kubernetes.api.model.ContainerBuilder; import io.fabric8.kubernetes.api.model.HasMetadata; import io.fabric8.kubernetes.api.model.Namespaced; import io.fabric8.kubernetes.api.model.Pod; import io.fabric8.kubernetes.api.model.PodSpec; import io.fabric8.kubernetes.api.model.PodTemplateSpec; import io.fabric8.kubernetes.api.model.apps.Deployment; +import io.fabric8.kubernetes.api.model.apps.DeploymentBuilder; import io.fabric8.kubernetes.api.model.apps.DeploymentSpec; import io.fabric8.kubernetes.client.CustomResource; import io.fabric8.kubernetes.client.KubernetesClientException; @@ -88,6 +90,16 @@ void setsSpecWithReflection() { assertThat(deployment.getSpec().getReplicas()).isEqualTo(1); } + @Test + void loadYamlAsBuilder() { + DeploymentBuilder builder = + ReconcilerUtils.loadYaml(DeploymentBuilder.class, getClass(), "deployment.yaml"); + builder.accept(ContainerBuilder.class, c -> c.withImage("my-image")); + + Deployment deployment = builder.editMetadata().withName("my-deployment").and().build(); + assertThat(deployment.getMetadata().getName()).isEqualTo("my-deployment"); + } + private Deployment createTestDeployment() { Deployment deployment = new Deployment(); deployment.setSpec(new DeploymentSpec()); diff --git a/operator-framework-core/src/test/resources/io/javaoperatorsdk/operator/deployment.yaml b/operator-framework-core/src/test/resources/io/javaoperatorsdk/operator/deployment.yaml new file mode 100644 index 0000000000..ca51b1ec4d --- /dev/null +++ b/operator-framework-core/src/test/resources/io/javaoperatorsdk/operator/deployment.yaml @@ -0,0 +1,21 @@ +apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2 +kind: Deployment +metadata: + name: "" +spec: + progressDeadlineSeconds: 600 + revisionHistoryLimit: 10 + selector: + matchLabels: + app: "test" + replicas: 1 + template: + metadata: + labels: + app: "test" + spec: + containers: + - name: nginx + image: nginx:1.17.0 + ports: + - containerPort: 80