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

CAMEL-21710 camel-jbang-plugin-kubernetes - automatic cluster detection #17216

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -1296,7 +1296,9 @@ camel kubernetes run route.yaml --image-registry=kind
----

When connecting to a local Kubernetes cluster, you may need to specify the image registry where the application container image gets pushed to.
The run command is able to automatically configure the local registry when using predefined names such as `kind` or `minikube`.
The run command is able to automatically configure the local registry when using predefined names such as `kind` or `minikube`, there is a detection mechanism to set some properties accordingly to the cluster type, currently it can auto detect Openshift and Minikube. If you want to disable the cluster detection, you have to set the `--disable-auto` cli parameter.

NOTE: When running minikube, the easiest way to push the image to the minikube container registry, is to enable the registry addon in minikube and to run `eval $(minikube docker-env)`.

Use the `--image-group` or the `--image` option to customize the container image.

Expand Down
11 changes: 11 additions & 0 deletions dsl/camel-jbang/camel-jbang-plugin-kubernetes/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,12 @@
<scope>test</scope>
<type>test-jar</type>
</dependency>
<dependency>
<groupId>org.junit-pioneer</groupId>
<artifactId>junit-pioneer</artifactId>
<version>${junit-pioneer-version}</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>io.fabric8</groupId>
Expand All @@ -107,6 +113,11 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<!-- This is required for the @SetEnvironmentVariable usage of junit-pioneer in KubernetesRunCustomTest -->
<argLine>
--add-opens java.base/java.util=ALL-UNNAMED
--add-opens java.base/java.lang=ALL-UNNAMED
</argLine>
<systemPropertyVariables>
<!-- Used in unit tests to resolve dependencies via Maven downloader -->
<camel.version>${project.version}</camel.version>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
Expand All @@ -31,10 +32,12 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.json.JsonMapper;
import io.fabric8.kubernetes.api.model.GenericKubernetesResource;
import io.fabric8.kubernetes.api.model.GenericKubernetesResourceList;
import io.fabric8.kubernetes.api.model.Pod;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClientBuilder;
import org.apache.camel.dsl.jbang.core.commands.CommandHelper;
import io.fabric8.kubernetes.client.dsl.base.ResourceDefinitionContext;
import org.apache.camel.dsl.jbang.core.common.YamlHelper;
import org.apache.camel.util.FileUtil;
import org.apache.camel.util.StringHelper;
Expand Down Expand Up @@ -76,7 +79,6 @@ private KubernetesHelper() {
public static KubernetesClient getKubernetesClient() {
if (kubernetesClient == null) {
kubernetesClient = new KubernetesClientBuilder().build();
printClientInfo(kubernetesClient);
}

return kubernetesClient;
Expand All @@ -91,19 +93,9 @@ public static KubernetesClient getKubernetesClient(String config) {
}

var client = new KubernetesClientBuilder().withConfig(config).build();
printClientInfo(client);
return clients.put(config, client);
}

private static void printClientInfo(KubernetesClient client) {
var printer = CommandHelper.getPrinter();
if (printer != null) {
var serverUrl = client.getConfiguration().getMasterUrl();
var info = client.getKubernetesVersion();
printer.println(String.format("Kubernetes v%s.%s %s", info.getMajor(), info.getMinor(), serverUrl));
}
}

/**
* Creates new Yaml instance. The implementation provided by Snakeyaml is not thread-safe. It is better to create a
* fresh instance for every YAML stream.
Expand All @@ -125,6 +117,67 @@ public static ObjectMapper json() {
return OBJECT_MAPPER;
}

/**
* Verify what cluster this shell console is connected to, currently it tests for Openshift and Minikube, in case of
* errors or no connected cluster, it defaults to Kubernetes.
*
* @return The cluster type may be: Openshift, Minikube or Kubernetes.
*/
static ClusterType discoverClusterType() {
ClusterType cluster = ClusterType.KUBERNETES;
// in case it's not connected to a cluster, don't waste time waiting for a response.
System.setProperty("kubernetes.connection.timeout", "2000");
System.setProperty("kubernetes.request.timeout", "2000");
System.setProperty("kubernetes.request.retry.backoffLimit", "1");
if (isConnectedToOpenshift()) {
cluster = ClusterType.OPENSHIFT;
} else if (isConnectedToMinikube()) {
cluster = ClusterType.MINIKUBE;
}
return cluster;
}

private static boolean isConnectedToOpenshift() {
boolean ocp = false;
try {
// set to openshift if there is clusterversions.config.openshift.io/version
ResourceDefinitionContext ocpVersion = new ResourceDefinitionContext.Builder()
.withGroup("config.openshift.io")
.withVersion("v1")
.withKind("ClusterVersion")
.withNamespaced(false)
.build();
GenericKubernetesResource versioncr
= getKubernetesClient().genericKubernetesResources(ocpVersion).withName("version").get();
ocp = versioncr != null;
} catch (RuntimeException e) {
// ignore it, since we try to discover the cluster and don't want the caller to handle any error
}
return ocp;
}

private static boolean isConnectedToMinikube() {
boolean minikube = false;
try {
ResourceDefinitionContext nodecrd = new ResourceDefinitionContext.Builder()
.withVersion("v1")
.withKind("Node")
.withNamespaced(false)
.build();
// if there is a node with minikube label, then it's minikube
GenericKubernetesResourceList list = getKubernetesClient().genericKubernetesResources(nodecrd)
.withLabels(Collections.singletonMap("minikube.k8s.io/name", null)).list();
minikube = list.getItems().size() > 0;
// thse env properties are set when running eval $(minikube docker-env) in the console
// this is important for the docker builder to actually build the image in the exposed docker from the minikube registry
minikube = minikube && System.getenv("MINIKUBE_ACTIVE_DOCKERD") != null
&& System.getenv("DOCKER_TLS_VERIFY") != null;
} catch (Exception e) {
// ignore it, since we try to discover the cluster and don't want the caller to handle any error
}
return minikube;
}

/**
* Sanitize given name to meet Kubernetes resource naming requirements.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,9 @@ public class KubernetesRun extends KubernetesBaseCommand {
description = "Maven/Gradle build properties, ex. --build-property=prop1=foo")
List<String> buildProperties = new ArrayList<>();

@CommandLine.Option(names = { "--disable-auto" }, description = "Disable automatic cluster type detection.")
boolean disableAuto = false;

// DevMode/Reload state
private CamelContext devModeContext;
private Thread devModeShutdownTask;
Expand Down Expand Up @@ -350,6 +353,7 @@ private String getIndexedWorkingDir(String projectName) {
}

private KubernetesExport configureExport(String workingDir) {
detectCluster();
KubernetesExport.ExportConfigurer configurer = new KubernetesExport.ExportConfigurer(
runtime,
quarkusVersion,
Expand Down Expand Up @@ -664,6 +668,23 @@ private Integer deployProject(String workingDir, boolean reload) throws Exceptio
return 0;
}

private void detectCluster() {
if (!disableAuto) {
if (verbose) {
printer().print("Automatic kubernetes cluster detection... ");
}
ClusterType cluster = KubernetesHelper.discoverClusterType();
this.clusterType = cluster.name();
if (ClusterType.MINIKUBE == cluster) {
this.imageBuilder = "docker";
this.imagePush = false;
}
if (verbose) {
printer().println(this.clusterType);
}
}
}

@Override
protected Printer printer() {
if (quiet || output != null) {
Expand All @@ -674,7 +695,6 @@ protected Printer printer() {
CommandHelper.setPrinter(quietPrinter);
return quietPrinter;
}

return super.printer();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,7 @@
public class KubernetesBaseTest {

private KubernetesMockServer k8sServer;

protected KubernetesClient kubernetesClient;

protected StringPrinter printer;

public static boolean isDockerAvailable() {
Expand Down Expand Up @@ -74,11 +72,11 @@ public void cleanup() {
k8sServer.destroy();
}

protected InputStream getKubernetesManifestAsStream(String printerOutput) {
protected static InputStream getKubernetesManifestAsStream(String printerOutput) {
return getKubernetesManifestAsStream(printerOutput, "yaml");
}

protected InputStream getKubernetesManifestAsStream(String printerOutput, String output) {
protected static InputStream getKubernetesManifestAsStream(String printerOutput, String output) {
if (output.equals("yaml")) {
String manifest = StringHelper.after(printerOutput, "---");
if (manifest == null) {
Expand Down
Loading
Loading