Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat (jkube-kit/config/service) : DebugService throws exception in buildpacks build strategy (#2462) #2957

Merged
merged 1 commit into from
Apr 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Usage:
### 1.17-SNAPSHOT
* Fix #2335: Add support for configuring nodeSelector spec for controller via xml/groovy DSL configuration
* Fix #2459: Allow configuring Buildpacks build via ImageConfiguration
* Fix #2462: `k8s:debug` throws error when using `buildpacks` build strategy
* Fix #2662: Sanitize VCS remote URL used in `jkube.eclipse.org/git-url` annotation
* Fix #2860: Correctly pass Docker build-arg from the build configuration to the Openshift build strategy

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

import io.fabric8.kubernetes.api.model.HasMetadata;
import io.fabric8.kubernetes.client.KubernetesClient;
import org.eclipse.jkube.kit.config.service.DebugContext;
import org.gradle.api.GradleException;

public class KubernetesDebugTask extends AbstractJKubeTask {
Expand All @@ -40,10 +41,14 @@ public void run() {
try (KubernetesClient kubernetes = jKubeServiceHub.getClient()) {
final File manifest = getManifest(kubernetes);
final List<HasMetadata> entities = KubernetesHelper.loadResources(manifest);
jKubeServiceHub.getDebugService().debug(
kubernetesExtension.getNamespaceOrNull(), manifest.getName(), entities,
"" + kubernetesExtension.getLocalDebugPortOrDefault(), kubernetesExtension.getDebugSuspendOrDefault(),
createLogger("[[Y]][W][[Y]] [[s]]"));
jKubeServiceHub.getDebugService().debug(DebugContext.builder()
.namespace(kubernetesExtension.getNamespaceOrNull())
.fileName(manifest.getName())
.localDebugPort("" + kubernetesExtension.getLocalDebugPortOrDefault())
.debugSuspend(kubernetesExtension.getDebugSuspendOrDefault())
.podWaitLog(createLogger("[[Y]][W][[Y]] [[s]]"))
.jKubeBuildStrategy(kubernetesExtension.getBuildStrategyOrDefault())
.build(), entities);
} catch (IOException ex) {
throw new GradleException("Failure in debug task", ex);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,18 @@
import org.eclipse.jkube.gradle.plugin.GradleLogger;
import org.eclipse.jkube.gradle.plugin.KubernetesExtension;
import org.eclipse.jkube.gradle.plugin.TestKubernetesExtension;
import org.eclipse.jkube.kit.config.service.DebugContext;
import org.eclipse.jkube.kit.config.service.DebugService;

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.ArgumentCaptor;
import org.mockito.MockedConstruction;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mockConstruction;
import static org.mockito.Mockito.times;
Expand Down Expand Up @@ -71,12 +72,18 @@ void runTask_withManifest_shouldStartDebug() throws Exception {
// Given
taskEnvironment.withKubernetesManifest();
final KubernetesDebugTask debugTask = new KubernetesDebugTask(KubernetesExtension.class);
ArgumentCaptor<DebugContext> debugContextArgumentCaptor = ArgumentCaptor.forClass(DebugContext.class);
// When
debugTask.runTask();
// Then
assertThat(debugServiceMockedConstruction.constructed()).hasSize(1);
verify(debugServiceMockedConstruction.constructed().iterator().next(), times(1))
.debug(any(), eq("kubernetes.yml"), eq(Collections.emptyList()), eq("5005"), eq(false), any(GradleLogger.class));
.debug(debugContextArgumentCaptor.capture(), eq(Collections.emptyList()));
assertThat(debugContextArgumentCaptor.getValue())
.hasFieldOrPropertyWithValue("fileName", "kubernetes.yml")
.hasFieldOrPropertyWithValue("localDebugPort", "5005")
.hasFieldOrPropertyWithValue("debugSuspend", false)
.satisfies(d -> assertThat(d.getPodWaitLog()).isInstanceOf(GradleLogger.class));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@
import org.eclipse.jkube.gradle.plugin.OpenShiftExtension;
import org.eclipse.jkube.gradle.plugin.TestOpenShiftExtension;
import org.eclipse.jkube.kit.common.util.OpenshiftHelper;
import org.eclipse.jkube.kit.config.service.DebugContext;
import org.eclipse.jkube.kit.config.service.DebugService;
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.ArgumentCaptor;
import org.mockito.MockedConstruction;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
Expand Down Expand Up @@ -77,12 +79,18 @@ void runTask_withManifest_shouldStartDebug() throws Exception {
// Given
taskEnvironment.withOpenShiftManifest();
final OpenShiftDebugTask debugTask = new OpenShiftDebugTask(OpenShiftExtension.class);
ArgumentCaptor<DebugContext> debugContextArgumentCaptor = ArgumentCaptor.forClass(DebugContext.class);
// When
debugTask.runTask();
// Then
assertThat(debugServiceMockedConstruction.constructed()).hasSize(1);
verify(debugServiceMockedConstruction.constructed().iterator().next(), times(1))
.debug(any(), eq("openshift.yml"), eq(Collections.emptyList()), eq("5005"), eq(false), any(GradleLogger.class));
.debug(debugContextArgumentCaptor.capture(), eq(Collections.emptyList()));
assertThat(debugContextArgumentCaptor.getValue())
.hasFieldOrPropertyWithValue("fileName", "openshift.yml")
.hasFieldOrPropertyWithValue("localDebugPort", "5005")
.hasFieldOrPropertyWithValue("debugSuspend", false)
.satisfies(d -> assertThat(d.getPodWaitLog()).isInstanceOf(GradleLogger.class));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -28,22 +28,22 @@ public enum JKubeBuildStrategy {
/**
* S2i build with a binary source
*/
s2i("S2I", true),
s2i("S2I", true, true),

/**
* JIB build
*/
jib("Jib", true),
jib("Jib", true, true),

/**
* Docker build with a binary source
*/
docker("Docker", true),
docker("Docker", true, true),

/**
* BuildPacks
*/
buildpacks("Buildpacks", false);
buildpacks("Buildpacks", false, false);

// Source strategy elements
public enum SourceStrategy {
Expand All @@ -60,10 +60,12 @@ public String key() {

private final String label;
private final boolean supportsWatch;
private final boolean supportsDebug;

JKubeBuildStrategy(String label, boolean supportsWatch) {
JKubeBuildStrategy(String label, boolean supportsWatch, boolean supportsDebug) {
this.label = label;
this.supportsWatch = supportsWatch;
this.supportsDebug = supportsDebug;
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -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.kit.config.service;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.eclipse.jkube.kit.common.KitLogger;
import org.eclipse.jkube.kit.config.image.build.JKubeBuildStrategy;

@Builder(toBuilder = true)
@AllArgsConstructor
@NoArgsConstructor
@Getter
@EqualsAndHashCode
public class DebugContext {
private String namespace;
private String fileName;
private String localDebugPort;
private boolean debugSuspend;
private KitLogger podWaitLog;
private JKubeBuildStrategy jKubeBuildStrategy;
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@
import io.fabric8.kubernetes.client.NamespacedKubernetesClient;
import io.fabric8.kubernetes.client.dsl.PodResource;
import org.eclipse.jkube.kit.common.DebugConstants;
import org.eclipse.jkube.kit.common.JKubeException;
import org.eclipse.jkube.kit.common.KitLogger;
import org.eclipse.jkube.kit.common.util.KubernetesHelper;
import org.eclipse.jkube.kit.config.image.build.JKubeBuildStrategy;
import org.eclipse.jkube.kit.config.service.portforward.PortForwardPodWatcher;

import io.fabric8.kubernetes.api.model.Container;
Expand Down Expand Up @@ -71,16 +73,17 @@ public DebugService(KitLogger log, KubernetesClient kubernetesClient, PortForwar
this.applyService = applyService;
}

public void debug(
String namespace, String fileName, Collection<HasMetadata> entities, String localDebugPort, boolean debugSuspend, KitLogger podWaitLog
) {
public void debug(DebugContext debugContext, Collection<HasMetadata> entities) {
if (!isDebugApplicable(entities)) {
log.error("Unable to proceed with Debug. No application resource found running in the cluster");
return;
}
if (debugContext.getJKubeBuildStrategy() != null && !debugContext.getJKubeBuildStrategy().isSupportsDebug()) {
throw new JKubeException("Debug is not supported in " + debugContext.getJKubeBuildStrategy().getLabel() + " build strategy");
}
final NamespacedKubernetesClient nsClient;
if (namespace != null) {
nsClient = kubernetesClient.adapt(NamespacedKubernetesClient.class).inNamespace(namespace);
if (debugContext.getNamespace() != null) {
nsClient = kubernetesClient.adapt(NamespacedKubernetesClient.class).inNamespace(debugContext.getNamespace());
} else {
nsClient = kubernetesClient.adapt(NamespacedKubernetesClient.class);
}
Expand All @@ -89,13 +92,13 @@ public void debug(
if (firstSelector == null) {
firstSelector = extractPodLabelSelector(entity);
}
enableDebugging(entity, fileName, debugSuspend);
enableDebugging(entity, debugContext.getFileName(), debugContext.isDebugSuspend());
}
if (firstSelector == null) {
log.error("Debug is not applicable for the currently generated resources");
return;
}
startPortForward(nsClient, firstSelector, debugSuspend, localDebugPort, podWaitLog);
startPortForward(nsClient, firstSelector, debugContext.isDebugSuspend(), debugContext.getLocalDebugPort(), debugContext.getPodWaitLog());
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import com.fasterxml.jackson.databind.JsonNode;
import io.fabric8.kubernetes.api.model.APIGroupListBuilder;
import io.fabric8.kubernetes.api.model.ConfigMap;
import io.fabric8.kubernetes.api.model.HasMetadata;
import io.fabric8.kubernetes.api.model.LabelSelector;
import io.fabric8.kubernetes.api.model.LabelSelectorBuilder;
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
Expand Down Expand Up @@ -45,10 +46,12 @@
import okhttp3.mockwebserver.RecordedRequest;
import org.assertj.core.groups.Tuple;
import org.eclipse.jkube.kit.common.JKubeConfiguration;
import org.eclipse.jkube.kit.common.JKubeException;
import org.eclipse.jkube.kit.common.KitLogger;
import org.eclipse.jkube.kit.common.util.Serialization;
import org.eclipse.jkube.kit.config.access.ClusterAccess;
import org.eclipse.jkube.kit.config.access.ClusterConfiguration;
import org.eclipse.jkube.kit.config.image.build.JKubeBuildStrategy;
import org.eclipse.jkube.kit.config.resource.RuntimeMode;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
Expand All @@ -59,6 +62,7 @@
import java.net.Socket;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
Expand All @@ -67,6 +71,7 @@

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.startsWith;
import static org.mockito.Mockito.spy;
Expand All @@ -82,6 +87,7 @@ class DebugServiceTest {
private KubernetesMockServer mockServer;
private ExecutorService singleThreadExecutor;
private DebugService debugService;
private DebugContext debugContext;

@BeforeEach
void setUp() {
Expand All @@ -92,6 +98,12 @@ void setUp() {
.platformMode(RuntimeMode.KUBERNETES)
.clusterAccess(new ClusterAccess(ClusterConfiguration.from(kubernetesClient.getConfiguration()).build()))
.build();
debugContext = DebugContext.builder()
.namespace("namespace")
.fileName("file.name")
.debugSuspend(false)
.podWaitLog(logger)
.build();
singleThreadExecutor = Executors.newSingleThreadExecutor();
serviceHub.getApplyService().setNamespace(kubernetesClient.getNamespace());
debugService = new DebugService(logger, kubernetesClient, new PortForwardService(logger), serviceHub.getApplyService());
Expand Down Expand Up @@ -207,27 +219,46 @@ void enableDebuggingWithDeploymentConfig() {
}

@Test
@DisplayName("with buildpacks build strategy, should throw exception")
void debugWithBuildPacksBuildStrategyShouldThrowException() {
// Given
final Deployment deployment = withDeploymentRollout(initDeployment());
debugContext = debugContext.toBuilder().jKubeBuildStrategy(JKubeBuildStrategy.buildpacks).build();
List<HasMetadata> entities = Collections.singletonList(deployment);

// When + Then
assertThatExceptionOfType(JKubeException.class)
.isThrownBy(() -> debugService.debug(debugContext, entities))
.withMessage("Debug is not supported in Buildpacks build strategy");
}

@Test
@DisplayName("Empty Kubernetes resources list logs no application resource found error")
void debugWithNotApplicableEntitiesLogsError() {
// When
debugService.debug("namespace", "file.name", Collections.emptySet(), null, false, logger);
debugService.debug(debugContext, Collections.emptySet());
// Then
verify(logger, times(1))
.error("Unable to proceed with Debug. No application resource found running in the cluster");
}

@Test
@DisplayName("Valid Kubernetes resources list should initiate port forward in specified namespace")
void debugWithApplicableEntities() throws Exception {
// Given
final Deployment deployment = withDeploymentRollout(initDeployment());
debugContext = debugContext.toBuilder()
.namespace("test")
.localDebugPort("1337")
.build();
final CompletableFuture<RecordedRequest> portForwardRequest = new CompletableFuture<>();
mockServer.expect().get().withPath("/api/v1/namespaces/test/pods/pod-in-debug-mode/portforward?ports=5005")
.andReply(200, r -> {
portForwardRequest.complete(r);
return "";
}).always();
// When
singleThreadExecutor.submit(() -> debugService.debug( "test", "file.name",
Collections.singletonList(deployment), "1337", false, logger));
singleThreadExecutor.submit(() -> debugService.debug(debugContext, Collections.singletonList(deployment)));
verify(logger, timeout(10000L))
.info(startsWith("Now you can start a Remote debug session by using localhost"), anyInt());
try (final Socket ignored = new Socket(InetAddress.getLocalHost(), 1337)) {
Expand All @@ -238,8 +269,14 @@ void debugWithApplicableEntities() throws Exception {
}

@Test
@DisplayName("valid Kubernetes resource list with debug suspend enabled, should initiate port forward")
void debugWithApplicableEntitiesAndSuspend() throws Exception {
// Given
debugContext = debugContext.toBuilder()
.namespace("test")
.localDebugPort("1337")
.debugSuspend(true)
.build();
final Deployment deployment = withDeploymentRollout(initDeployment());
final CompletableFuture<RecordedRequest> portForwardRequest = new CompletableFuture<>();
mockServer.expect().get().withPath("/api/v1/namespaces/test/pods/pod-in-debug-mode/portforward?ports=5005")
Expand All @@ -248,8 +285,7 @@ void debugWithApplicableEntitiesAndSuspend() throws Exception {
return "";
}).always();
// When
singleThreadExecutor.submit(() -> debugService.debug( "test", "file.name",
Collections.singletonList(deployment), "1337", true, logger));
singleThreadExecutor.submit(() -> debugService.debug(debugContext, Collections.singletonList(deployment)));
verify(logger, timeout(10000L))
.info(startsWith("Now you can start a Remote debug session by using localhost"), anyInt());
try (final Socket ignored = new Socket(InetAddress.getLocalHost(), 1337)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

import java.util.Collection;

import org.eclipse.jkube.kit.config.service.DebugContext;
import org.eclipse.jkube.maven.plugin.mojo.build.ApplyMojo;

import io.fabric8.kubernetes.api.model.HasMetadata;
Expand All @@ -39,8 +40,14 @@ public class DebugMojo extends ApplyMojo {

@Override
protected void applyEntities(KubernetesClient kubernetes, String fileName, Collection<HasMetadata> entities) {
jkubeServiceHub.getDebugService().debug(
applyService.getNamespace(), fileName, entities, localDebugPort, debugSuspend, createLogger("[[Y]][W][[Y]] [[s]]"));
jkubeServiceHub.getDebugService().debug(DebugContext.builder()
.namespace(applyService.getNamespace())
.fileName(fileName)
.localDebugPort(localDebugPort)
.debugSuspend(debugSuspend)
.podWaitLog(createLogger("[[Y]][W][[Y]] [[s]]"))
.jKubeBuildStrategy(buildStrategy)
.build(), entities);
}

}
Expand Down
Loading