Skip to content

Commit

Permalink
Fix eclipse-jkube#1109: Implement k8s:watch equivalent functionalit…
Browse files Browse the repository at this point in the history
…y to Kubernetes Gradle Plugin

Add KuberntesWatchTask to Kubernetes Gradle Plugin

Signed-off-by: Rohan Kumar <rohaan@redhat.com>
  • Loading branch information
rohanKanojia committed Nov 25, 2021
1 parent 2c239db commit 3534a06
Show file tree
Hide file tree
Showing 4 changed files with 230 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import org.eclipse.jkube.gradle.plugin.task.KubernetesResourceTask;

import org.eclipse.jkube.gradle.plugin.task.KubernetesUndeployTask;
import org.eclipse.jkube.gradle.plugin.task.KubernetesWatchTask;
import org.gradle.api.Project;
import org.gradle.api.Task;

Expand Down Expand Up @@ -62,6 +63,7 @@ protected void jKubeApply(Project project) {
register(project, "k8sUndeploy", KubernetesUndeployTask.class);
register(project, "k8sHelm", KubernetesHelmTask.class);
register(project, "k8sHelmPush", KubernetesHelmPushTask.class);
register(project, "k8sWatch", KubernetesWatchTask.class);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/**
* 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 io.fabric8.kubernetes.api.model.HasMetadata;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClientException;
import org.eclipse.jkube.gradle.plugin.KubernetesExtension;
import org.eclipse.jkube.kit.build.core.GavLabel;
import org.eclipse.jkube.kit.build.service.docker.ServiceHub;
import org.eclipse.jkube.kit.build.service.docker.ServiceHubFactory;
import org.eclipse.jkube.kit.build.service.docker.access.log.LogDispatcher;
import org.eclipse.jkube.kit.build.service.docker.watch.WatchContext;
import org.eclipse.jkube.kit.common.util.KubernetesHelper;
import org.eclipse.jkube.kit.common.util.ResourceUtil;
import org.eclipse.jkube.kit.config.resource.ProcessorConfig;
import org.eclipse.jkube.kit.config.service.JKubeServiceHub;
import org.eclipse.jkube.kit.enricher.api.util.KubernetesResourceUtil;
import org.eclipse.jkube.kit.profile.ProfileUtil;
import org.eclipse.jkube.watcher.api.WatcherContext;
import org.eclipse.jkube.watcher.api.WatcherManager;

import javax.inject.Inject;
import java.io.IOException;
import java.net.URL;
import java.util.List;

import static org.eclipse.jkube.kit.common.util.BuildReferenceDateUtil.getBuildTimestamp;

public class KubernetesWatchTask extends AbstractJKubeTask {
@Inject
public KubernetesWatchTask(Class<? extends KubernetesExtension> extensionClass) {
super(extensionClass);
setDescription("Used to automatically rebuild Docker images and restart containers in case of updates.");
}

@Override
protected JKubeServiceHub.JKubeServiceHubBuilder initJKubeServiceHubBuilder() {
return TaskUtil.addDockerServiceHubToJKubeServiceHubBuilder(
super.initJKubeServiceHubBuilder(), kubernetesExtension, kitLogger)
.buildServiceConfig(TaskUtil.buildServiceConfigBuilder(kubernetesExtension).build());
}

@Override
public void run() {
try (KubernetesClient kubernetesClient = jKubeServiceHub.getClient()) {
URL masterUrl = kubernetesClient.getMasterUrl();
KubernetesResourceUtil.validateKubernetesMasterUrl(masterUrl);

try {
List<HasMetadata> resources = KubernetesHelper.loadResources(getManifest(kubernetesClient));
WatcherContext context = createWatcherContext();

WatcherManager.watch(resolvedImages, resources, context);
} catch (KubernetesClientException kubernetesClientException) {
KubernetesResourceUtil.handleKubernetesClientException(kubernetesClientException, kitLogger);
} catch (Exception ioException) {
throw new IllegalStateException("An error has occurred while while trying to watch the resources", ioException);
}
}
}

private WatcherContext createWatcherContext() throws IOException {
WatchContext watchContext = jKubeServiceHub.getDockerServiceHub() != null ? getWatchContext() : null;
return WatcherContext.builder()
.buildContext(jKubeServiceHub.getConfiguration())
.watchContext(watchContext)
.config(extractWatcherConfig())
.logger(kitLogger)
.newPodLogger(createLogger("[[C]][NEW][[C]] "))
.oldPodLogger(createLogger("[[R]][OLD][[R]] "))
.useProjectClasspath(kubernetesExtension.getUseProjectClassPathOrDefault())
.jKubeServiceHub(jKubeServiceHub)
.build();
}

private ProcessorConfig extractWatcherConfig() {
try {
return ProfileUtil.blendProfileWithConfiguration(ProfileUtil.WATCHER_CONFIG, kubernetesExtension.getProfileOrNull(), ResourceUtil.getFinalResourceDir(kubernetesExtension.getResourceSourceDirectoryOrDefault(), kubernetesExtension.getResourceEnvironmentOrNull()), kubernetesExtension.watcher);
} catch (IOException e) {
throw new IllegalArgumentException("Cannot extract watcher config: " + e, e);
}
}

private WatchContext getWatchContext() throws IOException {
final ServiceHub hub = jKubeServiceHub.getDockerServiceHub();
return WatchContext.builder()
.watchInterval(kubernetesExtension.getWatchIntervalOrDefault())
.watchMode(kubernetesExtension.getWatchModeOrDefault())
.watchPostExec(kubernetesExtension.getWatchPostExecOrNull())
.autoCreateCustomNetworks(kubernetesExtension.getWatchAutoCreateCustomNetworksOrDefault())
.keepContainer(kubernetesExtension.getWatchKeepContainerOrDefault())
.keepRunning(kubernetesExtension.getWatchKeepRunningOrDefault())
.removeVolumes(kubernetesExtension.getWatchRemoveVolumesOrDefault())
.containerNamePattern(kubernetesExtension.getWatchContainerNamePatternOrDefault())
.buildTimestamp(getBuildTimestamp(null, null, kubernetesExtension.javaProject.getBuildDirectory().getAbsolutePath(), DOCKER_BUILD_TIMESTAMP))
.gavLabel(new GavLabel(kubernetesExtension.javaProject.getGroupId(), kubernetesExtension.javaProject.getArtifactId(), kubernetesExtension.javaProject.getVersion()))
.buildContext(jKubeServiceHub.getConfiguration())
.follow(kubernetesExtension.getWatchFollowOrDefault())
.showLogs(kubernetesExtension.getWatchShowLogsOrNull())
.serviceHubFactory(new ServiceHubFactory())
.hub(hub)
.dispatcher(new LogDispatcher(hub.getDockerAccess()))
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.eclipse.jkube.gradle.plugin.task.KubernetesResourceTask;
import org.eclipse.jkube.gradle.plugin.task.KubernetesUndeployTask;

import org.eclipse.jkube.gradle.plugin.task.KubernetesWatchTask;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.junit.Before;
Expand Down Expand Up @@ -54,7 +55,8 @@ public static Collection<Object[]> data() {
new Object[] { "k8sResource", KubernetesResourceTask.class },
new Object[] { "k8sUndeploy", KubernetesUndeployTask.class },
new Object[] { "k8sHelm", KubernetesHelmTask.class },
new Object[] { "k8sHelmPush", KubernetesHelmPushTask.class });
new Object[] { "k8sHelmPush", KubernetesHelmPushTask.class },
new Object[] { "k8sWatch", KubernetesWatchTask.class});
}

@Parameterized.Parameter
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/**
* 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 io.fabric8.kubernetes.client.KubernetesClient;
import org.assertj.core.api.AssertionsForClassTypes;
import org.eclipse.jkube.gradle.plugin.KubernetesExtension;
import org.eclipse.jkube.gradle.plugin.TestKubernetesExtension;
import org.eclipse.jkube.kit.build.service.docker.DockerAccessFactory;
import org.eclipse.jkube.kit.build.service.docker.access.DockerAccess;
import org.eclipse.jkube.kit.config.access.ClusterAccess;
import org.eclipse.jkube.kit.config.service.kubernetes.DockerBuildService;
import org.eclipse.jkube.watcher.api.WatcherManager;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.MockedConstruction;
import org.mockito.MockedStatic;

import java.io.IOException;
import java.net.URL;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockConstruction;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.when;

public class KubernetesWatchTaskTest {

@Rule
public TaskEnvironment taskEnvironment = new TaskEnvironment();

private MockedConstruction<DockerAccessFactory> dockerAccessFactoryMockedConstruction;
private MockedConstruction<DockerBuildService> dockerBuildServiceMockedConstruction;
private MockedConstruction<ClusterAccess> clusterAccessMockedConstruction;
private MockedStatic<WatcherManager> watcherManagerMockedStatic;
private TestKubernetesExtension extension;

@Before
public void setUp() throws IOException {
// Mock required for environments with no DOCKER available (don't remove)
dockerAccessFactoryMockedConstruction = mockConstruction(DockerAccessFactory.class,
(mock, ctx) -> when(mock.createDockerAccess(any())).thenReturn(mock(DockerAccess.class)));
dockerBuildServiceMockedConstruction = mockConstruction(DockerBuildService.class, (mock, ctx) -> {
when(mock.isApplicable()).thenReturn(true);
});
clusterAccessMockedConstruction = mockConstruction(ClusterAccess.class, (mock, ctx) -> {
final KubernetesClient kubernetesClient = mock(KubernetesClient.class);
when(kubernetesClient.getMasterUrl()).thenReturn(new URL("http://kubernetes-cluster"));
when(mock.createDefaultClient()).thenReturn(kubernetesClient);
});
watcherManagerMockedStatic = mockStatic(WatcherManager.class);
extension = new TestKubernetesExtension();
when(taskEnvironment.project.getExtensions().getByType(KubernetesExtension.class)).thenReturn(extension);
extension.isFailOnNoKubernetesJson = false;
}

@After
public void tearDown() {
watcherManagerMockedStatic.close();
clusterAccessMockedConstruction.close();
dockerBuildServiceMockedConstruction.close();
dockerAccessFactoryMockedConstruction.close();
}

@Test
public void runTask_withNoManifest_shouldThrowException() {
// Given
extension.isFailOnNoKubernetesJson = true;
final KubernetesWatchTask watchTask = new KubernetesWatchTask(KubernetesExtension.class);
// When
final IllegalStateException result = assertThrows(IllegalStateException.class, watchTask::runTask);
// Then
AssertionsForClassTypes.assertThat(watchTask.jKubeServiceHub.getBuildService()).isNotNull()
.isInstanceOf(DockerBuildService.class);
assertThat(result)
.hasMessageContaining("An error has occurred while while trying to watch the resources");
}

@Test
public void runTask_withManifest_shouldWatchEntities() throws Exception {
// Given
taskEnvironment.withKubernetesManifest();
final KubernetesWatchTask watchTask = new KubernetesWatchTask(KubernetesExtension.class);
// When
watchTask.runTask();
// Then
AssertionsForClassTypes.assertThat(watchTask.jKubeServiceHub.getBuildService()).isNotNull()
.isInstanceOf(DockerBuildService.class);
watcherManagerMockedStatic.verify(() -> WatcherManager.watch(any(), any(), any()), times(1));
}
}

0 comments on commit 3534a06

Please sign in to comment.