From 448f77dd793f0ba5d9840dd7055b0043113bbec1 Mon Sep 17 00:00:00 2001 From: Rohan Kumar Date: Fri, 7 Jun 2024 15:16:16 +0530 Subject: [PATCH] feat : Helm Install functionality exposed via Maven Mojos / Gradle Tasks Signed-off-by: Rohan Kumar --- gradle-plugin/kubernetes/pom.xml | 7 + .../jkube/gradle/plugin/KubernetesPlugin.java | 3 + .../task/KubernetesHelmInstallTask.java | 36 +++++ .../gradle/plugin/KubernetesPluginTest.java | 5 +- .../task/KubernetesHelmInstallTaskTest.java | 123 +++++++++++++++++ gradle-plugin/openshift/pom.xml | 7 + .../jkube/gradle/plugin/OpenShiftPlugin.java | 3 + .../plugin/task/OpenShiftHelmInstallTask.java | 26 ++++ .../gradle/plugin/OpenShiftPluginTest.java | 5 +- .../task/OpenShiftHelmInstallTaskTest.java | 123 +++++++++++++++++ kubernetes-maven-plugin/plugin/pom.xml | 6 + .../plugin/mojo/build/HelmInstallMojo.java | 27 ++++ .../mojo/GeneratedPluginDescriptorTest.java | 3 +- .../mojo/build/HelmInstallMojoTest.java | 130 ++++++++++++++++++ openshift-maven-plugin/plugin/pom.xml | 15 ++ .../mojo/build/OpenshiftHelmInstallMojo.java | 52 +++++++ ...penShiftGeneratedPluginDescriptorTest.java | 3 +- .../build/OpenshiftHelmInstallMojoTest.java | 130 ++++++++++++++++++ 18 files changed, 698 insertions(+), 6 deletions(-) create mode 100644 gradle-plugin/kubernetes/src/main/java/org/eclipse/jkube/gradle/plugin/task/KubernetesHelmInstallTask.java create mode 100644 gradle-plugin/kubernetes/src/test/java/org/eclipse/jkube/gradle/plugin/task/KubernetesHelmInstallTaskTest.java create mode 100644 gradle-plugin/openshift/src/main/java/org/eclipse/jkube/gradle/plugin/task/OpenShiftHelmInstallTask.java create mode 100644 gradle-plugin/openshift/src/test/java/org/eclipse/jkube/gradle/plugin/task/OpenShiftHelmInstallTaskTest.java create mode 100644 kubernetes-maven-plugin/plugin/src/main/java/org/eclipse/jkube/maven/plugin/mojo/build/HelmInstallMojo.java create mode 100644 kubernetes-maven-plugin/plugin/src/test/java/org/eclipse/jkube/maven/plugin/mojo/build/HelmInstallMojoTest.java create mode 100644 openshift-maven-plugin/plugin/src/main/java/org/eclipse/jkube/maven/plugin/mojo/build/OpenshiftHelmInstallMojo.java create mode 100644 openshift-maven-plugin/plugin/src/test/java/org/eclipse/jkube/maven/plugin/mojo/build/OpenshiftHelmInstallMojoTest.java diff --git a/gradle-plugin/kubernetes/pom.xml b/gradle-plugin/kubernetes/pom.xml index 89cd2880df..6c9a94d251 100644 --- a/gradle-plugin/kubernetes/pom.xml +++ b/gradle-plugin/kubernetes/pom.xml @@ -167,6 +167,13 @@ + + org.eclipse.jkube + jkube-kit-common + test + test-jar + + io.fabric8 mockwebserver diff --git a/gradle-plugin/kubernetes/src/main/java/org/eclipse/jkube/gradle/plugin/KubernetesPlugin.java b/gradle-plugin/kubernetes/src/main/java/org/eclipse/jkube/gradle/plugin/KubernetesPlugin.java index 8596cea26e..d7f8b1e0a0 100644 --- a/gradle-plugin/kubernetes/src/main/java/org/eclipse/jkube/gradle/plugin/KubernetesPlugin.java +++ b/gradle-plugin/kubernetes/src/main/java/org/eclipse/jkube/gradle/plugin/KubernetesPlugin.java @@ -24,6 +24,7 @@ import org.eclipse.jkube.gradle.plugin.task.KubernetesConfigViewTask; import org.eclipse.jkube.gradle.plugin.task.KubernetesDebugTask; import org.eclipse.jkube.gradle.plugin.task.KubernetesHelmDependencyUpdateTask; +import org.eclipse.jkube.gradle.plugin.task.KubernetesHelmInstallTask; import org.eclipse.jkube.gradle.plugin.task.KubernetesHelmLintTask; import org.eclipse.jkube.gradle.plugin.task.KubernetesHelmPushTask; import org.eclipse.jkube.gradle.plugin.task.KubernetesHelmTask; @@ -53,6 +54,7 @@ public Map>> getTaskPrecedence() { ret.put("k8sHelmPush", Collections.singletonList(KubernetesHelmTask.class)); ret.put("k8sHelmLint", Collections.singletonList(KubernetesHelmTask.class)); ret.put("k8sHelmDependencyUpdate", Collections.singletonList(KubernetesHelmTask.class)); + ret.put("k8sHelmInstall", Collections.singletonList(KubernetesHelmTask.class)); return ret; } @@ -70,6 +72,7 @@ protected void jKubeApply(Project project) { register(project, "k8sHelmPush", KubernetesHelmPushTask.class); register(project, "k8sHelmLint", KubernetesHelmLintTask.class); register(project, "k8sHelmDependencyUpdate", KubernetesHelmDependencyUpdateTask.class); + register(project, "k8sHelmInstall", KubernetesHelmInstallTask.class); register(project, "k8sRemoteDev", KubernetesRemoteDevTask.class); register(project, "k8sWatch", KubernetesWatchTask.class); } diff --git a/gradle-plugin/kubernetes/src/main/java/org/eclipse/jkube/gradle/plugin/task/KubernetesHelmInstallTask.java b/gradle-plugin/kubernetes/src/main/java/org/eclipse/jkube/gradle/plugin/task/KubernetesHelmInstallTask.java new file mode 100644 index 0000000000..2911c8b0c3 --- /dev/null +++ b/gradle-plugin/kubernetes/src/main/java/org/eclipse/jkube/gradle/plugin/task/KubernetesHelmInstallTask.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2019 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at: + * + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.jkube.gradle.plugin.task; + +import org.eclipse.jkube.gradle.plugin.KubernetesExtension; + +import javax.inject.Inject; + +public class KubernetesHelmInstallTask extends AbstractHelmTask { + @Inject + public KubernetesHelmInstallTask(Class extensionClass) { + super(extensionClass); + setDescription("Installs a helm chart onto Kubernetes cluster"); + } + + @Override + public void run() { + try { + jKubeServiceHub.getHelmService().install(kubernetesExtension.helm); + } catch (Exception exp) { + kitLogger.error("Error performing helm install", exp); + throw new IllegalStateException(exp.getMessage(), exp); + } + } +} diff --git a/gradle-plugin/kubernetes/src/test/java/org/eclipse/jkube/gradle/plugin/KubernetesPluginTest.java b/gradle-plugin/kubernetes/src/test/java/org/eclipse/jkube/gradle/plugin/KubernetesPluginTest.java index faa1605f48..ebe8735210 100644 --- a/gradle-plugin/kubernetes/src/test/java/org/eclipse/jkube/gradle/plugin/KubernetesPluginTest.java +++ b/gradle-plugin/kubernetes/src/test/java/org/eclipse/jkube/gradle/plugin/KubernetesPluginTest.java @@ -81,7 +81,7 @@ void getTaskPrecedence_withValidProject_shouldReturnTaskPrecedence() { final Map>> result = new KubernetesPlugin().getTaskPrecedence(); // Then assertThat(result) - .hasSize(7) + .hasSize(8) .containsEntry("k8sApply", Collections.singletonList(KubernetesResourceTask.class)) .containsEntry("k8sDebug", Arrays.asList(KubernetesBuildTask.class, KubernetesResourceTask.class, KubernetesApplyTask.class)) @@ -89,6 +89,7 @@ void getTaskPrecedence_withValidProject_shouldReturnTaskPrecedence() { .containsEntry("k8sHelm", Collections.singletonList(KubernetesResourceTask.class)) .containsEntry("k8sHelmDependencyUpdate", Collections.singletonList(KubernetesHelmTask.class)) .containsEntry("k8sHelmPush", Collections.singletonList(KubernetesHelmTask.class)) - .containsEntry("k8sHelmLint", Collections.singletonList(KubernetesHelmTask.class)); + .containsEntry("k8sHelmLint", Collections.singletonList(KubernetesHelmTask.class)) + .containsEntry("k8sHelmInstall", Collections.singletonList(KubernetesHelmTask.class)); } } diff --git a/gradle-plugin/kubernetes/src/test/java/org/eclipse/jkube/gradle/plugin/task/KubernetesHelmInstallTaskTest.java b/gradle-plugin/kubernetes/src/test/java/org/eclipse/jkube/gradle/plugin/task/KubernetesHelmInstallTaskTest.java new file mode 100644 index 0000000000..5668a5268c --- /dev/null +++ b/gradle-plugin/kubernetes/src/test/java/org/eclipse/jkube/gradle/plugin/task/KubernetesHelmInstallTaskTest.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2019 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at: + * + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.jkube.gradle.plugin.task; + +import com.marcnuri.helm.Helm; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.server.mock.EnableKubernetesMockClient; +import io.fabric8.kubernetes.client.server.mock.KubernetesMockServer; +import org.apache.commons.io.FileUtils; +import org.eclipse.jkube.gradle.plugin.KubernetesExtension; +import org.eclipse.jkube.gradle.plugin.TestKubernetesExtension; +import org.eclipse.jkube.kit.common.access.ClusterConfiguration; +import org.eclipse.jkube.kit.resource.helm.HelmConfig; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; + +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; +import static org.eclipse.jkube.kit.common.util.KubernetesMockServerUtil.prepareMockWebServerExpectationsForAggregatedDiscoveryEndpoints; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@EnableKubernetesMockClient(crud = true) +class KubernetesHelmInstallTaskTest { + @RegisterExtension + private final TaskEnvironmentExtension taskEnvironment = new TaskEnvironmentExtension(); + private KubernetesClient kubernetesClient; + private KubernetesMockServer server; + private TestKubernetesExtension extension; + + @BeforeEach + void setUp() throws IOException { + extension = new TestKubernetesExtension(); + // Remove after https://github.com/fabric8io/kubernetes-client/issues/6062 is fixed + prepareMockWebServerExpectationsForAggregatedDiscoveryEndpoints(server); + Helm.create().withDir(taskEnvironment.getRoot().toPath()).withName("empty-project").call(); + Path helmChartOutputDir = taskEnvironment.getRoot().toPath().resolve("build").resolve("jkube").resolve("helm"); + Files.createDirectories(helmChartOutputDir.resolve("kubernetes")); + FileUtils.copyDirectory(taskEnvironment.getRoot().toPath().resolve("empty-project").toFile(), helmChartOutputDir.resolve("kubernetes").toFile()); + Files.write(helmChartOutputDir.resolve("kubernetes").resolve("Chart.yaml"), + ("\ndependencies:\n" + + " - name: the-dependency\n" + + " version: 0.1.0\n" + + " repository: file://../../../../the-dependency\n").getBytes(StandardCharsets.UTF_8), + StandardOpenOption.APPEND); + System.setProperty("jkube.kubernetesTemplate", taskEnvironment.getRoot().getAbsolutePath()); + extension.helm = HelmConfig.builder() + .chartExtension("tgz") + .installDependencyUpdate(true) + .disableOpenAPIValidation(true) + .outputDir(helmChartOutputDir.toString()).build(); + extension.access = ClusterConfiguration.from(kubernetesClient.getConfiguration()).build(); + extension.isUseColor = false; + when(taskEnvironment.project.getName()).thenReturn("empty-project"); + when(taskEnvironment.project.getVersion()).thenReturn("0.1.0"); + when(taskEnvironment.project.getExtensions().getByType(KubernetesExtension.class)).thenReturn(extension); + } + + @AfterEach + void tearDown() { + System.clearProperty("jkube.kubernetesTemplate"); + } + + @Test + void runTask_withHelmDependencyPresent_shouldSucceed() { + // Given + KubernetesHelmInstallTask kubernetesHelmInstallTask = new KubernetesHelmInstallTask(KubernetesExtension.class); + Helm.create().withName("the-dependency").withDir(taskEnvironment.getRoot().toPath()).call(); + + // When + kubernetesHelmInstallTask.runTask(); + // Then + verify(taskEnvironment.logger).lifecycle("k8s: NAME : empty-project"); + verify(taskEnvironment.logger).lifecycle("k8s: NAMESPACE : "); + verify(taskEnvironment.logger).lifecycle("k8s: STATUS : deployed"); + verify(taskEnvironment.logger).lifecycle("k8s: REVISION : 1"); + verify(taskEnvironment.logger).lifecycle("k8s: Saving 1 charts"); + verify(taskEnvironment.logger).lifecycle("k8s: Deleting outdated charts"); + } + + @Test + void runTask_withHelmDependencyAbsent_shouldThrowException() { + // Given + KubernetesHelmInstallTask kubernetesHelmInstallTask = new KubernetesHelmInstallTask(KubernetesExtension.class); + // When + Then + assertThatIllegalStateException() + .isThrownBy(kubernetesHelmInstallTask::runTask) + .withMessageContaining("the-dependency not found"); + } + + @Test + void runTask_withInstallDependencyUpdateDisabled_shouldThrowException() { + // Given + extension.helm = extension.helm.toBuilder() + .installDependencyUpdate(false) + .build(); + when(taskEnvironment.project.getExtensions().getByType(KubernetesExtension.class)).thenReturn(extension); + KubernetesHelmInstallTask kubernetesHelmInstallTask = new KubernetesHelmInstallTask(KubernetesExtension.class); + // When + Then + assertThatIllegalStateException() + .isThrownBy(kubernetesHelmInstallTask::runTask) + .withMessage("An error occurred while checking for chart dependencies. " + + "You may need to run `helm dependency build` to fetch missing dependencies: found in Chart.yaml, but missing in charts/ directory: the-dependency"); + } +} diff --git a/gradle-plugin/openshift/pom.xml b/gradle-plugin/openshift/pom.xml index d906d5fc38..8538c21d3b 100644 --- a/gradle-plugin/openshift/pom.xml +++ b/gradle-plugin/openshift/pom.xml @@ -64,6 +64,13 @@ + + org.eclipse.jkube + jkube-kit-common + test + test-jar + + io.fabric8 mockwebserver diff --git a/gradle-plugin/openshift/src/main/java/org/eclipse/jkube/gradle/plugin/OpenShiftPlugin.java b/gradle-plugin/openshift/src/main/java/org/eclipse/jkube/gradle/plugin/OpenShiftPlugin.java index 150df93110..2425ab206c 100644 --- a/gradle-plugin/openshift/src/main/java/org/eclipse/jkube/gradle/plugin/OpenShiftPlugin.java +++ b/gradle-plugin/openshift/src/main/java/org/eclipse/jkube/gradle/plugin/OpenShiftPlugin.java @@ -28,6 +28,7 @@ import org.eclipse.jkube.gradle.plugin.task.OpenShiftBuildTask; import org.eclipse.jkube.gradle.plugin.task.OpenShiftDebugTask; import org.eclipse.jkube.gradle.plugin.task.OpenShiftHelmDependencyUpdateTask; +import org.eclipse.jkube.gradle.plugin.task.OpenShiftHelmInstallTask; import org.eclipse.jkube.gradle.plugin.task.OpenShiftHelmLintTask; import org.eclipse.jkube.gradle.plugin.task.OpenShiftHelmPushTask; import org.eclipse.jkube.gradle.plugin.task.OpenShiftHelmTask; @@ -57,6 +58,7 @@ public Map>> getTaskPrecedence() { ret.put("ocHelmPush", Arrays.asList(KubernetesHelmTask.class, OpenShiftHelmTask.class)); ret.put("ocHelmLint", Arrays.asList(KubernetesHelmTask.class, OpenShiftHelmTask.class)); ret.put("ocHelmDependencyUpdate", Arrays.asList(KubernetesHelmTask.class, OpenShiftHelmTask.class)); + ret.put("ocHelmInstall", Arrays.asList(KubernetesHelmTask.class, OpenShiftHelmTask.class)); return ret; } @@ -74,6 +76,7 @@ protected void jKubeApply(Project project) { register(project, "ocHelmPush", OpenShiftHelmPushTask.class); register(project, "ocHelmLint", OpenShiftHelmLintTask.class); register(project, "ocHelmDependencyUpdate", OpenShiftHelmDependencyUpdateTask.class); + register(project, "ocHelmInstall", OpenShiftHelmInstallTask.class); register(project, "ocRemoteDev", OpenShiftRemoteDevTask.class); register(project, "ocWatch", OpenShiftWatchTask.class); } diff --git a/gradle-plugin/openshift/src/main/java/org/eclipse/jkube/gradle/plugin/task/OpenShiftHelmInstallTask.java b/gradle-plugin/openshift/src/main/java/org/eclipse/jkube/gradle/plugin/task/OpenShiftHelmInstallTask.java new file mode 100644 index 0000000000..c7930dfdde --- /dev/null +++ b/gradle-plugin/openshift/src/main/java/org/eclipse/jkube/gradle/plugin/task/OpenShiftHelmInstallTask.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2019 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at: + * + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.jkube.gradle.plugin.task; + +import org.eclipse.jkube.gradle.plugin.OpenShiftExtension; + +import javax.inject.Inject; + +public class OpenShiftHelmInstallTask extends KubernetesHelmInstallTask implements OpenShiftJKubeTask { + @Inject + public OpenShiftHelmInstallTask(Class extensionClass) { + super(extensionClass); + setDescription("Installs a helm chart onto OpenShift cluster"); + } +} diff --git a/gradle-plugin/openshift/src/test/java/org/eclipse/jkube/gradle/plugin/OpenShiftPluginTest.java b/gradle-plugin/openshift/src/test/java/org/eclipse/jkube/gradle/plugin/OpenShiftPluginTest.java index 6fdfbc512e..ce30318314 100644 --- a/gradle-plugin/openshift/src/test/java/org/eclipse/jkube/gradle/plugin/OpenShiftPluginTest.java +++ b/gradle-plugin/openshift/src/test/java/org/eclipse/jkube/gradle/plugin/OpenShiftPluginTest.java @@ -39,7 +39,7 @@ void configurePrecedence_withValidProject_shouldReturnTaskPrecedence() { final Map>> result = new OpenShiftPlugin().getTaskPrecedence(); // Then assertThat(result) - .hasSize(7) + .hasSize(8) .containsEntry("ocApply", Arrays.asList(KubernetesResourceTask.class, OpenShiftResourceTask.class)) .containsEntry("ocDebug", Arrays.asList(KubernetesBuildTask.class, OpenShiftBuildTask.class, KubernetesResourceTask.class, OpenShiftResourceTask.class, KubernetesApplyTask.class, OpenShiftApplyTask.class)) @@ -47,6 +47,7 @@ void configurePrecedence_withValidProject_shouldReturnTaskPrecedence() { .containsEntry("ocHelm", Arrays.asList(KubernetesResourceTask.class, OpenShiftResourceTask.class)) .containsEntry("ocHelmDependencyUpdate", Arrays.asList(KubernetesHelmTask.class, OpenShiftHelmTask.class)) .containsEntry("ocHelmPush", Arrays.asList(KubernetesHelmTask.class, OpenShiftHelmTask.class)) - .containsEntry("ocHelmLint", Arrays.asList(KubernetesHelmTask.class, OpenShiftHelmTask.class)); + .containsEntry("ocHelmLint", Arrays.asList(KubernetesHelmTask.class, OpenShiftHelmTask.class)) + .containsEntry("ocHelmInstall", Arrays.asList(KubernetesHelmTask.class, OpenShiftHelmTask.class)); } } diff --git a/gradle-plugin/openshift/src/test/java/org/eclipse/jkube/gradle/plugin/task/OpenShiftHelmInstallTaskTest.java b/gradle-plugin/openshift/src/test/java/org/eclipse/jkube/gradle/plugin/task/OpenShiftHelmInstallTaskTest.java new file mode 100644 index 0000000000..836a7c8a1e --- /dev/null +++ b/gradle-plugin/openshift/src/test/java/org/eclipse/jkube/gradle/plugin/task/OpenShiftHelmInstallTaskTest.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2019 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at: + * + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.jkube.gradle.plugin.task; + +import com.marcnuri.helm.Helm; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.server.mock.EnableKubernetesMockClient; +import io.fabric8.kubernetes.client.server.mock.KubernetesMockServer; +import org.apache.commons.io.FileUtils; +import org.eclipse.jkube.gradle.plugin.OpenShiftExtension; +import org.eclipse.jkube.gradle.plugin.TestOpenShiftExtension; +import org.eclipse.jkube.kit.common.access.ClusterConfiguration; +import org.eclipse.jkube.kit.resource.helm.HelmConfig; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; + +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; +import static org.eclipse.jkube.kit.common.util.KubernetesMockServerUtil.prepareMockWebServerExpectationsForAggregatedDiscoveryEndpoints; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@EnableKubernetesMockClient(crud = true) +class OpenShiftHelmInstallTaskTest { + @RegisterExtension + private final TaskEnvironmentExtension taskEnvironment = new TaskEnvironmentExtension(); + private KubernetesClient kubernetesClient; + private KubernetesMockServer server; + private TestOpenShiftExtension extension; + + @BeforeEach + void setUp() throws IOException { + extension = new TestOpenShiftExtension(); + // Remove after https://github.com/fabric8io/kubernetes-client/issues/6062 is fixed + prepareMockWebServerExpectationsForAggregatedDiscoveryEndpoints(server); + Helm.create().withDir(taskEnvironment.getRoot().toPath()).withName("empty-project").call(); + Path helmChartOutputDir = taskEnvironment.getRoot().toPath().resolve("build").resolve("jkube").resolve("helm"); + Files.createDirectories(helmChartOutputDir.resolve("openshift")); + FileUtils.copyDirectory(taskEnvironment.getRoot().toPath().resolve("empty-project").toFile(), helmChartOutputDir.resolve("openshift").toFile()); + Files.write(helmChartOutputDir.resolve("openshift").resolve("Chart.yaml"), + ("\ndependencies:\n" + + " - name: the-dependency\n" + + " version: 0.1.0\n" + + " repository: file://../../../../the-dependency\n").getBytes(StandardCharsets.UTF_8), + StandardOpenOption.APPEND); + System.setProperty("jkube.kubernetesTemplate", taskEnvironment.getRoot().getAbsolutePath()); + extension.helm = HelmConfig.builder() + .chartExtension("tgz") + .installDependencyUpdate(true) + .disableOpenAPIValidation(true) + .outputDir(helmChartOutputDir.toString()).build(); + extension.access = ClusterConfiguration.from(kubernetesClient.getConfiguration()).build(); + extension.isUseColor = false; + when(taskEnvironment.project.getName()).thenReturn("empty-project"); + when(taskEnvironment.project.getVersion()).thenReturn("0.1.0"); + when(taskEnvironment.project.getExtensions().getByType(OpenShiftExtension.class)).thenReturn(extension); + } + + @AfterEach + void tearDown() { + System.clearProperty("jkube.kubernetesTemplate"); + } + + @Test + void runTask_withHelmDependencyPresent_shouldSucceed() { + // Given + OpenShiftHelmInstallTask openShiftHelmInstallTask = new OpenShiftHelmInstallTask(OpenShiftExtension.class); + Helm.create().withName("the-dependency").withDir(taskEnvironment.getRoot().toPath()).call(); + + // When + openShiftHelmInstallTask.runTask(); + // Then + verify(taskEnvironment.logger).lifecycle("oc: NAME : empty-project"); + verify(taskEnvironment.logger).lifecycle("oc: NAMESPACE : "); + verify(taskEnvironment.logger).lifecycle("oc: STATUS : deployed"); + verify(taskEnvironment.logger).lifecycle("oc: REVISION : 1"); + verify(taskEnvironment.logger).lifecycle("oc: Saving 1 charts"); + verify(taskEnvironment.logger).lifecycle("oc: Deleting outdated charts"); + } + + @Test + void runTask_withHelmDependencyAbsent_shouldThrowException() { + // Given + OpenShiftHelmInstallTask openShiftHelmInstallTask = new OpenShiftHelmInstallTask(OpenShiftExtension.class); + // When + Then + assertThatIllegalStateException() + .isThrownBy(openShiftHelmInstallTask::runTask) + .withMessageContaining("the-dependency not found"); + } + + @Test + void runTask_withInstallDependencyUpdateDisabled_shouldThrowException() { + // Given + extension.helm = extension.helm.toBuilder() + .installDependencyUpdate(false) + .build(); + when(taskEnvironment.project.getExtensions().getByType(OpenShiftExtension.class)).thenReturn(extension); + OpenShiftHelmInstallTask openShiftHelmInstallTask = new OpenShiftHelmInstallTask(OpenShiftExtension.class); + // When + Then + assertThatIllegalStateException() + .isThrownBy(openShiftHelmInstallTask::runTask) + .withMessage("An error occurred while checking for chart dependencies. " + + "You may need to run `helm dependency build` to fetch missing dependencies: found in Chart.yaml, but missing in charts/ directory: the-dependency"); + } +} diff --git a/kubernetes-maven-plugin/plugin/pom.xml b/kubernetes-maven-plugin/plugin/pom.xml index 7c0b9acd2f..0d681a1c3c 100644 --- a/kubernetes-maven-plugin/plugin/pom.xml +++ b/kubernetes-maven-plugin/plugin/pom.xml @@ -157,6 +157,12 @@ jkube-kit-remote-dev + + org.eclipse.jkube + jkube-kit-common + test + test-jar + io.fabric8 mockwebserver diff --git a/kubernetes-maven-plugin/plugin/src/main/java/org/eclipse/jkube/maven/plugin/mojo/build/HelmInstallMojo.java b/kubernetes-maven-plugin/plugin/src/main/java/org/eclipse/jkube/maven/plugin/mojo/build/HelmInstallMojo.java new file mode 100644 index 0000000000..27855f929f --- /dev/null +++ b/kubernetes-maven-plugin/plugin/src/main/java/org/eclipse/jkube/maven/plugin/mojo/build/HelmInstallMojo.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2019 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at: + * + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.jkube.maven.plugin.mojo.build; + +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.ResolutionScope; + +@Mojo(name = "helm-install", defaultPhase = LifecyclePhase.INSTALL, requiresDependencyResolution = ResolutionScope.COMPILE) +public class HelmInstallMojo extends AbstractHelmMojo { + @Override + public void executeInternal() throws MojoExecutionException { + jkubeServiceHub.getHelmService().install(getHelm()); + } +} diff --git a/kubernetes-maven-plugin/plugin/src/test/java/org/eclipse/jkube/maven/plugin/mojo/GeneratedPluginDescriptorTest.java b/kubernetes-maven-plugin/plugin/src/test/java/org/eclipse/jkube/maven/plugin/mojo/GeneratedPluginDescriptorTest.java index e5f7601e5e..e3e94a1f37 100644 --- a/kubernetes-maven-plugin/plugin/src/test/java/org/eclipse/jkube/maven/plugin/mojo/GeneratedPluginDescriptorTest.java +++ b/kubernetes-maven-plugin/plugin/src/test/java/org/eclipse/jkube/maven/plugin/mojo/GeneratedPluginDescriptorTest.java @@ -56,7 +56,8 @@ static Stream data() { arguments("helm", "", "pre-integration-test"), arguments("helm-dependency-update", "compile", "integration-test"), arguments("helm-push", "compile", "install"), - arguments("helm-lint", "compile", "integration-test")); + arguments("helm-lint", "compile", "integration-test"), + arguments("helm-install", "compile", "install")); } @DisplayName("verify, phase and required dependency resolution") diff --git a/kubernetes-maven-plugin/plugin/src/test/java/org/eclipse/jkube/maven/plugin/mojo/build/HelmInstallMojoTest.java b/kubernetes-maven-plugin/plugin/src/test/java/org/eclipse/jkube/maven/plugin/mojo/build/HelmInstallMojoTest.java new file mode 100644 index 0000000000..1fddbb4497 --- /dev/null +++ b/kubernetes-maven-plugin/plugin/src/test/java/org/eclipse/jkube/maven/plugin/mojo/build/HelmInstallMojoTest.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2019 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at: + * + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.jkube.maven.plugin.mojo.build; + +import com.marcnuri.helm.Helm; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.server.mock.EnableKubernetesMockClient; +import io.fabric8.kubernetes.client.server.mock.KubernetesMockServer; +import org.apache.commons.io.FileUtils; +import org.apache.maven.project.MavenProject; +import org.apache.maven.settings.Settings; +import org.eclipse.jkube.kit.common.access.ClusterConfiguration; +import org.eclipse.jkube.kit.resource.helm.HelmConfig; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; +import static org.eclipse.jkube.kit.common.util.KubernetesMockServerUtil.prepareMockWebServerExpectationsForAggregatedDiscoveryEndpoints; + +@EnableKubernetesMockClient(crud = true) +class HelmInstallMojoTest { + @TempDir + private Path projectDir; + private PrintStream originalPrintStream; + private ByteArrayOutputStream outputStream; + private HelmInstallMojo helmInstallMojo; + private KubernetesClient kubernetesClient; + private KubernetesMockServer server; + + @BeforeEach + void setUp() throws Exception { + originalPrintStream = System.out; + // Remove after https://github.com/fabric8io/kubernetes-client/issues/6062 is fixed + prepareMockWebServerExpectationsForAggregatedDiscoveryEndpoints(server); + outputStream = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outputStream)); + Helm.create().withDir(projectDir).withName("empty-project").call(); + Path helmChartOutputDir = projectDir.resolve("target").resolve("jkube").resolve("helm"); + Files.createDirectories(helmChartOutputDir.resolve("kubernetes")); + FileUtils.copyDirectory(projectDir.resolve("empty-project").toFile(), helmChartOutputDir.resolve("kubernetes").toFile()); + Files.write(helmChartOutputDir.resolve("kubernetes").resolve("Chart.yaml"), + ("\ndependencies:\n" + + " - name: the-dependency\n" + + " version: 0.1.0\n" + + " repository: file://../../../../the-dependency\n").getBytes(StandardCharsets.UTF_8), + StandardOpenOption.APPEND); + System.setProperty("jkube.kubernetesTemplate", projectDir.toFile().getAbsolutePath()); + helmInstallMojo = new HelmInstallMojo(); + helmInstallMojo.helm = HelmConfig.builder().chartExtension("tgz") + .outputDir(helmChartOutputDir.toString()) + .installDependencyUpdate(true) + .disableOpenAPIValidation(true) + .build(); + helmInstallMojo.access = ClusterConfiguration.from(kubernetesClient.getConfiguration()).build(); + helmInstallMojo.interpolateTemplateParameters = true; + helmInstallMojo.settings = new Settings(); + helmInstallMojo.project = new MavenProject(); + helmInstallMojo.project.setVersion("0.1.0"); + helmInstallMojo.project.getBuild() + .setOutputDirectory(projectDir.resolve("target").resolve("classes").toFile().getAbsolutePath()); + helmInstallMojo.project.getBuild().setDirectory(projectDir.resolve("target").toFile().getAbsolutePath()); + helmInstallMojo.project.setFile(projectDir.resolve("target").toFile()); + } + + @AfterEach + void tearDown() { + System.setOut(originalPrintStream); + System.clearProperty("jkube.kubernetesTemplate"); + helmInstallMojo = null; + } + + @Test + void execute_withInstallDependencyUpdateDisabled_shouldThrowException() { + // Given + helmInstallMojo.helm = helmInstallMojo.helm.toBuilder() + .installDependencyUpdate(false) + .build(); + // When + Then + assertThatIllegalStateException() + .isThrownBy(helmInstallMojo::execute) + .withMessage("An error occurred while checking for chart dependencies. " + + "You may need to run `helm dependency build` to fetch missing dependencies: found in Chart.yaml, but missing in charts/ directory: the-dependency"); + } + + @Test + void execute_withHelmDependencyPresent_shouldSucceed() throws Exception { + // Given + Helm.create().withName("the-dependency").withDir(projectDir).call(); + // When + helmInstallMojo.execute(); + // Then + assertThat(outputStream.toString()) + .contains("NAME : empty-project") + .contains("NAMESPACE : ") + .contains("STATUS : deployed") + .contains("REVISION : 1") + .contains("LAST DEPLOYED : ") + .contains("Saving 1 charts") + .contains("Deleting outdated charts"); + } + + @Test + void execute_withHelmDependencyAbsent_shouldThrowException() { + // When + Then + assertThatIllegalStateException() + .isThrownBy(() -> helmInstallMojo.execute()) + .withMessageContaining("the-dependency not found"); + } +} diff --git a/openshift-maven-plugin/plugin/pom.xml b/openshift-maven-plugin/plugin/pom.xml index a984e8f78a..6c00443965 100644 --- a/openshift-maven-plugin/plugin/pom.xml +++ b/openshift-maven-plugin/plugin/pom.xml @@ -41,6 +41,21 @@ ${jkube.kit.version} + + org.eclipse.jkube + jkube-kit-common + test + test-jar + + + io.fabric8 + mockwebserver + + + + io.fabric8 + openshift-server-mock + org.junit.jupiter junit-jupiter-engine diff --git a/openshift-maven-plugin/plugin/src/main/java/org/eclipse/jkube/maven/plugin/mojo/build/OpenshiftHelmInstallMojo.java b/openshift-maven-plugin/plugin/src/main/java/org/eclipse/jkube/maven/plugin/mojo/build/OpenshiftHelmInstallMojo.java new file mode 100644 index 0000000000..1fbc850c53 --- /dev/null +++ b/openshift-maven-plugin/plugin/src/main/java/org/eclipse/jkube/maven/plugin/mojo/build/OpenshiftHelmInstallMojo.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2019 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at: + * + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.jkube.maven.plugin.mojo.build; + +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.plugins.annotations.ResolutionScope; +import org.eclipse.jkube.kit.resource.helm.HelmConfig; +import org.eclipse.jkube.maven.plugin.mojo.OpenShift; + +import java.io.File; + +@Mojo(name = "helm-install", defaultPhase = LifecyclePhase.INSTALL, requiresDependencyResolution = ResolutionScope.COMPILE) +public class OpenshiftHelmInstallMojo extends HelmInstallMojo { + /** + * One of: + *
    + *
  • A directory containing OpenShift Templates to use as Helm parameters.
  • + *
  • A file containing a Kubernetes List with OpenShift Template entries to be used as Helm parameters.
  • + *
+ */ + @Parameter(property = "jkube.openshiftTemplate", defaultValue = "${basedir}/target/classes/META-INF/jkube/openshift") + private File openShiftTemplate; + + + @Override + protected File getKubernetesTemplate() { + return openShiftTemplate; + } + + @Override + protected HelmConfig.HelmType getDefaultHelmType() { + return HelmConfig.HelmType.OPENSHIFT; + } + + @Override + protected String getLogPrefix() { + return OpenShift.DEFAULT_LOG_PREFIX; + } +} diff --git a/openshift-maven-plugin/plugin/src/test/java/org/eclipse/jkube/maven/plugin/mojo/OpenShiftGeneratedPluginDescriptorTest.java b/openshift-maven-plugin/plugin/src/test/java/org/eclipse/jkube/maven/plugin/mojo/OpenShiftGeneratedPluginDescriptorTest.java index a04a9c94bc..da9ce060ca 100644 --- a/openshift-maven-plugin/plugin/src/test/java/org/eclipse/jkube/maven/plugin/mojo/OpenShiftGeneratedPluginDescriptorTest.java +++ b/openshift-maven-plugin/plugin/src/test/java/org/eclipse/jkube/maven/plugin/mojo/OpenShiftGeneratedPluginDescriptorTest.java @@ -55,7 +55,8 @@ static Stream data() { arguments("helm", "", "pre-integration-test"), arguments("helm-dependency-update", "compile", "integration-test"), arguments("helm-push", "compile", "install"), - arguments("helm-lint", "compile", "integration-test") + arguments("helm-lint", "compile", "integration-test"), + arguments("helm-install", "compile", "install") ); } diff --git a/openshift-maven-plugin/plugin/src/test/java/org/eclipse/jkube/maven/plugin/mojo/build/OpenshiftHelmInstallMojoTest.java b/openshift-maven-plugin/plugin/src/test/java/org/eclipse/jkube/maven/plugin/mojo/build/OpenshiftHelmInstallMojoTest.java new file mode 100644 index 0000000000..7705e5b1a0 --- /dev/null +++ b/openshift-maven-plugin/plugin/src/test/java/org/eclipse/jkube/maven/plugin/mojo/build/OpenshiftHelmInstallMojoTest.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2019 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at: + * + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.jkube.maven.plugin.mojo.build; + +import com.marcnuri.helm.Helm; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.server.mock.EnableKubernetesMockClient; +import io.fabric8.kubernetes.client.server.mock.KubernetesMockServer; +import org.apache.commons.io.FileUtils; +import org.apache.maven.project.MavenProject; +import org.apache.maven.settings.Settings; +import org.eclipse.jkube.kit.common.access.ClusterConfiguration; +import org.eclipse.jkube.kit.resource.helm.HelmConfig; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; +import static org.eclipse.jkube.kit.common.util.KubernetesMockServerUtil.prepareMockWebServerExpectationsForAggregatedDiscoveryEndpoints; + +@EnableKubernetesMockClient(crud = true) +class OpenshiftHelmInstallMojoTest { + @TempDir + private Path projectDir; + private PrintStream originalPrintStream; + private ByteArrayOutputStream outputStream; + private OpenshiftHelmInstallMojo openShiftHelmInstallMojo; + private KubernetesClient kubernetesClient; + private KubernetesMockServer server; + + @BeforeEach + void setUp() throws Exception { + originalPrintStream = System.out; + outputStream = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outputStream)); + Helm.create().withDir(projectDir).withName("empty-project").call(); + Path helmChartOutputDir = projectDir.resolve("target").resolve("jkube").resolve("helm"); + Files.createDirectories(helmChartOutputDir.resolve("openshift")); + FileUtils.copyDirectory(projectDir.resolve("empty-project").toFile(), helmChartOutputDir.resolve("openshift").toFile()); + Files.write(helmChartOutputDir.resolve("openshift").resolve("Chart.yaml"), + ("\ndependencies:\n" + + " - name: the-dependency\n" + + " version: 0.1.0\n" + + " repository: file://../../../../the-dependency\n").getBytes(StandardCharsets.UTF_8), + StandardOpenOption.APPEND); + System.setProperty("jkube.kubernetesTemplate", projectDir.toFile().getAbsolutePath()); + openShiftHelmInstallMojo = new OpenshiftHelmInstallMojo(); + openShiftHelmInstallMojo.helm = HelmConfig.builder().chartExtension("tgz") + .outputDir(helmChartOutputDir.toString()) + .installDependencyUpdate(true) + .disableOpenAPIValidation(true) + .build(); + openShiftHelmInstallMojo.interpolateTemplateParameters = true; + openShiftHelmInstallMojo.access = ClusterConfiguration.from(kubernetesClient.getConfiguration()).build(); + openShiftHelmInstallMojo.settings = new Settings(); + openShiftHelmInstallMojo.project = new MavenProject(); + openShiftHelmInstallMojo.project.setVersion("0.1.0"); + openShiftHelmInstallMojo.project.getBuild() + .setOutputDirectory(projectDir.resolve("target").resolve("classes").toFile().getAbsolutePath()); + openShiftHelmInstallMojo.project.getBuild().setDirectory(projectDir.resolve("target").toFile().getAbsolutePath()); + openShiftHelmInstallMojo.project.setFile(projectDir.resolve("target").toFile()); + // Remove after https://github.com/fabric8io/kubernetes-client/issues/6062 is fixed + prepareMockWebServerExpectationsForAggregatedDiscoveryEndpoints(server); + } + + @AfterEach + void tearDown() { + System.setOut(originalPrintStream); + System.clearProperty("jkube.kubernetesTemplate"); + openShiftHelmInstallMojo = null; + } + + @Test + void execute_withInstallDependencyUpdateDisabled_shouldThrowException() { + // Given + openShiftHelmInstallMojo.helm = openShiftHelmInstallMojo.helm.toBuilder() + .installDependencyUpdate(false) + .build(); + // When + Then + assertThatIllegalStateException() + .isThrownBy(openShiftHelmInstallMojo::execute) + .withMessage("An error occurred while checking for chart dependencies. " + + "You may need to run `helm dependency build` to fetch missing dependencies: found in Chart.yaml, but missing in charts/ directory: the-dependency"); + } + + @Test + void execute_withHelmDependencyPresent_shouldSucceed() throws Exception { + // Given + Helm.create().withName("the-dependency").withDir(projectDir).call(); + // When + openShiftHelmInstallMojo.execute(); + // Then + assertThat(outputStream.toString()) + .contains("NAME : empty-project") + .contains("NAMESPACE : ") + .contains("STATUS : deployed") + .contains("REVISION : 1") + .contains("LAST DEPLOYED : ") + .contains("Saving 1 charts") + .contains("Deleting outdated charts"); + } + + @Test + void execute_withHelmDependencyAbsent_shouldThrowException() { + // When + Then + assertThatIllegalStateException() + .isThrownBy(() -> openShiftHelmInstallMojo.execute()) + .withMessageContaining("the-dependency not found"); + } +}