Skip to content

Commit

Permalink
feat : Helm Install functionality exposed via Maven Mojos / Gradle Tasks
Browse files Browse the repository at this point in the history
Signed-off-by: Rohan Kumar <rohaan@redhat.com>
  • Loading branch information
rohanKanojia committed Jun 20, 2024
1 parent 8b3ddfa commit 01539d1
Show file tree
Hide file tree
Showing 19 changed files with 1,021 additions and 6 deletions.
9 changes: 9 additions & 0 deletions gradle-plugin/kubernetes/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,15 @@
</dependency>

<!-- test -->
<dependency>
<groupId>io.fabric8</groupId>
<artifactId>mockwebserver</artifactId>
</dependency>

<dependency>
<groupId>io.fabric8</groupId>
<artifactId>openshift-server-mock</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -53,6 +54,7 @@ public Map<String, Collection<Class<? extends Task>>> 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;
}

Expand All @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* 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 org.eclipse.jkube.kit.resource.helm.HelmConfig;

import javax.inject.Inject;

import static org.eclipse.jkube.kit.resource.helm.HelmServiceUtil.initHelmConfig;

public class KubernetesHelmInstallTask extends AbstractJKubeTask {
@Inject
public KubernetesHelmInstallTask(Class<? extends KubernetesExtension> extensionClass) {
super(extensionClass);
setDescription("Installs a helm chart onto Kubernetes cluster");
}

@Override
public void run() {
if (kubernetesExtension.getSkipOrDefault()) {
return;
}
try {
final HelmConfig helm = initHelmConfig(kubernetesExtension.getDefaultHelmType(), kubernetesExtension.javaProject,
kubernetesExtension.getKubernetesTemplateOrDefault(),
kubernetesExtension.helm)
.build();
jKubeServiceHub.getHelmService().install(helm);
} catch (Exception exp) {
kitLogger.error("Error performing helm dependency update", exp);
throw new IllegalStateException(exp.getMessage(), exp);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,15 @@ void getTaskPrecedence_withValidProject_shouldReturnTaskPrecedence() {
final Map<String, Collection<Class<? extends Task>>> 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))
.containsEntry("k8sPush", Collections.singletonList(KubernetesBuildTask.class))
.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));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
/*
* 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.api.model.APIGroupBuilder;
import io.fabric8.kubernetes.api.model.APIGroupList;
import io.fabric8.kubernetes.api.model.APIGroupListBuilder;
import io.fabric8.kubernetes.api.model.APIResourceBuilder;
import io.fabric8.kubernetes.api.model.APIResourceList;
import io.fabric8.kubernetes.api.model.APIResourceListBuilder;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.VersionInfo;
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.config.access.ClusterAccess;
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 org.mockito.MockedConstruction;

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.mockito.Mockito.mockConstruction;
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 MockedConstruction<ClusterAccess> clusterAccessMockedConstruction;
private TestKubernetesExtension extension;

@BeforeEach
void setUp() throws IOException {
extension = new TestKubernetesExtension();
clusterAccessMockedConstruction = mockConstruction(ClusterAccess.class, (mock, ctx) -> when(mock.createDefaultClient()).thenReturn(kubernetesClient));
// Should be removed once https://github.com/fabric8io/kubernetes-client/issues/6062 is fixed
prepareMockWebServerExpectationsForAggregatedDiscoveryEndpoints();
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.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");
clusterAccessMockedConstruction.close();
}

@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() throws Exception {
// 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");
}

private void prepareMockWebServerExpectationsForAggregatedDiscoveryEndpoints() {
server.expect().get()
.withPath("/version?timeout=32s")
.andReturn(200, new VersionInfo.Builder()
.withMajor("1")
.withMinor("30")
.build())
.always();
server.expect().get().withPath("/api?timeout=32s")
.andReturn(200, String.format("{\"kind\":\"APIVersions\",\"versions\":[\"v1\"],\"serverAddressByClientCIDRs\":[{\"clientCIDR\":\"0.0.0.0/0\",\"serverAddress\":\"%s:%d\"}]}", server.getHostName(), server.getPort()))
.always();
server.expect().get().withPath("/apis?timeout=32s")
.andReturn(200, createNewAPIGroupList())
.always();
server.expect().get().withPath("/api/v1?timeout=32s")
.andReturn(200, createNewAPIResourceList())
.always();
server.expect().get().withPath("/apis/apps/v1?timeout=32s")
.andReturn(200, createNewAPIResourceList())
.always();
}

private APIResourceList createNewAPIResourceList() {
APIResourceListBuilder apiResourceListBuilder = new APIResourceListBuilder();
apiResourceListBuilder.addToResources(new APIResourceBuilder()
.withNamespaced()
.withKind("Service")
.withName("services")
.withSingularName("service")
.build());
apiResourceListBuilder.addToResources(new APIResourceBuilder()
.withName("deployments")
.withKind("Deployment")
.withSingularName("deployment")
.withNamespaced()
.build());
apiResourceListBuilder.addToResources(new APIResourceBuilder()
.withName("serviceaccounts")
.withKind("ServiceAccount")
.withSingularName("serviceaccount")
.withNamespaced()
.build());
apiResourceListBuilder.addToResources(new APIResourceBuilder()
.withName("secrets")
.withKind("Secret")
.withSingularName("secret")
.withNamespaced()
.build());
return apiResourceListBuilder.build();
}

private APIGroupList createNewAPIGroupList() {
return new APIGroupListBuilder()
.addToGroups(new APIGroupBuilder()
.withName("apps")
.addNewVersion()
.withGroupVersion("apps/v1")
.withVersion("v1")
.endVersion()
.withNewPreferredVersion()
.withGroupVersion("apps/v1")
.withVersion("v1")
.endPreferredVersion()
.build())
.build();
}
}
9 changes: 9 additions & 0 deletions gradle-plugin/openshift/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,15 @@
</dependency>

<!-- test -->
<dependency>
<groupId>io.fabric8</groupId>
<artifactId>mockwebserver</artifactId>
</dependency>

<dependency>
<groupId>io.fabric8</groupId>
<artifactId>openshift-server-mock</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -57,6 +58,7 @@ public Map<String, Collection<Class<? extends Task>>> 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;
}

Expand All @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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<? extends OpenShiftExtension> extensionClass) {
super(extensionClass);
setDescription("Installs a helm chart onto Kubernetes cluster");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,15 @@ void configurePrecedence_withValidProject_shouldReturnTaskPrecedence() {
final Map<String, Collection<Class<? extends Task>>> 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))
.containsEntry("ocPush", Arrays.asList(KubernetesBuildTask.class, OpenShiftBuildTask.class))
.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));
}
}
Loading

0 comments on commit 01539d1

Please sign in to comment.