Java tutorial
/* * Copyright 2016 Red Hat, Inc. * * Red Hat licenses this file to you under the Apache License, version * 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. See the License for the specific language governing * permissions and limitations under the License. */ package io.fabric8.maven.plugin; import io.fabric8.kubernetes.api.Controller; import io.fabric8.kubernetes.api.model.Container; import io.fabric8.kubernetes.api.model.EnvVar; import io.fabric8.kubernetes.api.model.EnvVarBuilder; import io.fabric8.kubernetes.api.model.HasMetadata; import io.fabric8.kubernetes.api.model.Pod; import io.fabric8.kubernetes.api.model.PodList; import io.fabric8.kubernetes.api.model.PodSpec; import io.fabric8.kubernetes.api.model.PodTemplateSpec; import io.fabric8.kubernetes.api.model.ReplicationController; import io.fabric8.kubernetes.api.model.ReplicationControllerSpec; import io.fabric8.kubernetes.api.model.extensions.Deployment; import io.fabric8.kubernetes.api.model.extensions.DeploymentSpec; import io.fabric8.kubernetes.api.model.extensions.LabelSelector; import io.fabric8.kubernetes.api.model.extensions.ReplicaSet; import io.fabric8.kubernetes.api.model.extensions.ReplicaSetSpec; import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.KubernetesClientException; import io.fabric8.kubernetes.client.Watch; import io.fabric8.kubernetes.client.Watcher; import io.fabric8.kubernetes.client.dsl.FilterWatchListDeletable; import io.fabric8.maven.core.util.ProcessUtil; import io.fabric8.openshift.api.model.DeploymentConfig; import io.fabric8.openshift.api.model.DeploymentConfigSpec; import io.fabric8.openshift.client.OpenShiftClient; import io.fabric8.utils.Objects; import io.fabric8.utils.Strings; 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.Parameter; import org.apache.maven.plugins.annotations.ResolutionScope; import java.io.File; import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.concurrent.CountDownLatch; import static io.fabric8.kubernetes.api.KubernetesHelper.getKind; import static io.fabric8.kubernetes.api.KubernetesHelper.getName; import static io.fabric8.kubernetes.api.KubernetesHelper.isPodRunning; import static io.fabric8.maven.core.util.ProcessUtil.processCommandAsync; import static io.fabric8.maven.plugin.AbstractInstallMojo.GOFABRIC8; import static org.json.XMLTokener.entity; /** * Ensures that the current app has debug enabled, then opens the debug port so that you can debug the latest pod * from your IDE */ @Mojo(name = "debug", requiresDependencyResolution = ResolutionScope.COMPILE, defaultPhase = LifecyclePhase.INSTALL) public class DebugMojo extends AbstractDeployMojo { public static final String ENV_VAR_JAVA_DEBUG = "JAVA_ENABLE_DEBUG"; public static final String ENV_VAR_JAVA_DEBUG_PORT = "JAVA_DEBUG_PORT"; public static final String ENV_VAR_JAVA_DEBUG_PORT_DEFAULT = "5005"; @Parameter(property = "fabric8.debug.port", defaultValue = "5005") private String localDebugPort; private String remoteDebugPort = ENV_VAR_JAVA_DEBUG_PORT_DEFAULT; private Watch podWatcher; private CountDownLatch terminateLatch = new CountDownLatch(1); private Pod foundPod; @Override protected void applyEntities(Controller controller, KubernetesClient kubernetes, String namespace, String fileName, Set<HasMetadata> entities) throws Exception { for (HasMetadata entity : entities) { String name = getName(entity); LabelSelector selector = null; if (entity instanceof Deployment) { Deployment resource = (Deployment) entity; DeploymentSpec spec = resource.getSpec(); if (spec != null) { if (enableDebugging(entity, spec.getTemplate())) { kubernetes.extensions().deployments().inNamespace(namespace).withName(name) .replace(resource); } selector = getPodLabelSelector(entity); } } else if (entity instanceof ReplicaSet) { ReplicaSet resource = (ReplicaSet) entity; ReplicaSetSpec spec = resource.getSpec(); if (spec != null) { if (enableDebugging(entity, spec.getTemplate())) { kubernetes.extensions().replicaSets().inNamespace(namespace).withName(name) .replace(resource); } selector = getPodLabelSelector(entity); } } else if (entity instanceof ReplicationController) { ReplicationController resource = (ReplicationController) entity; ReplicationControllerSpec spec = resource.getSpec(); if (spec != null) { if (enableDebugging(entity, spec.getTemplate())) { kubernetes.replicationControllers().inNamespace(namespace).withName(name).replace(resource); } selector = getPodLabelSelector(entity); } } else if (entity instanceof DeploymentConfig) { DeploymentConfig resource = (DeploymentConfig) entity; DeploymentConfigSpec spec = resource.getSpec(); if (spec != null) { if (enableDebugging(entity, spec.getTemplate())) { OpenShiftClient openshiftClient = new Controller(kubernetes).getOpenShiftClientOrNull(); if (openshiftClient == null) { log.warn("Ignoring DeploymentConfig " + name + " as not connected to an OpenShift cluster"); continue; } openshiftClient.deploymentConfigs().inNamespace(namespace).withName(name).replace(resource); } selector = getPodLabelSelector(entity); } } if (selector != null) { String podName = waitForRunningPodWithEnvVar(kubernetes, namespace, selector, ENV_VAR_JAVA_DEBUG, "true"); portForward(controller, podName); break; } } } private String waitForRunningPodWithEnvVar(final KubernetesClient kubernetes, final String namespace, LabelSelector selector, final String envVarName, final String envVarValue) throws MojoExecutionException { // wait for the newest pod to be ready with the given env var FilterWatchListDeletable<Pod, PodList, Boolean, Watch, Watcher<Pod>> pods = withSelector( kubernetes.pods().inNamespace(namespace), selector); log.info("Waiting for debug pod with selector " + selector + " and $" + envVarName + " = " + envVarValue); PodList list = pods.list(); if (list != null) { List<Pod> items = list.getItems(); Pod latestPod = getNewestPod(list.getItems()); if (latestPod != null && podHasEnvVarValue(latestPod, envVarName, envVarValue)) { return getName(latestPod); } } podWatcher = pods.watch(new Watcher<Pod>() { @Override public void eventReceived(Watcher.Action action, Pod pod) { if (action.equals(Action.ADDED) || action.equals(Action.MODIFIED) && isPodRunning(pod) && podHasEnvVarValue(pod, envVarName, envVarValue)) { foundPod = pod; terminateLatch.countDown(); } } @Override public void onClose(KubernetesClientException e) { // ignore } }); // now lets wait forever? while (terminateLatch.getCount() > 0) { try { terminateLatch.await(); } catch (InterruptedException e) { // ignore } if (foundPod != null) { return getName(foundPod); } } throw new MojoExecutionException("Could not find a running pod with $" + envVarName + " = " + envVarValue); } private boolean podHasEnvVarValue(Pod pod, String envVarName, String envVarValue) { PodSpec spec = pod.getSpec(); if (spec != null) { List<Container> containers = spec.getContainers(); if (containers != null && !containers.isEmpty()) { Container container = containers.get(0); List<EnvVar> env = container.getEnv(); if (env != null) { for (EnvVar envVar : env) { if (Objects.equal(envVar.getName(), envVarName) && Objects.equal(envVar.getValue(), envVarValue)) { return true; } } } } } return false; } private void portForward(Controller controller, String podName) throws MojoExecutionException { File file = getKubeCtlExecutable(controller); String command = file.getName(); log.info( "Port forwarding to port " + remoteDebugPort + " on pod " + podName + " using command: " + command); String arguments = " port-forward " + podName + " " + localDebugPort + ":" + remoteDebugPort; String commands = command + arguments; log.info("Executing command: " + commands); final String message = "port forward"; final Process process; try { process = Runtime.getRuntime().exec(file.getAbsolutePath() + arguments); Runtime.getRuntime().addShutdownHook(new Thread("mvn fabric8:run-interactive shutdown hook") { @Override public void run() { if (process != null) { log.info("Terminating port forward process:"); try { process.destroy(); } catch (Exception e) { log.error("Failed to terminate process " + message); } try { if (process != null && process.isAlive()) { process.destroyForcibly(); } } catch (Exception e) { log.error("Failed to forcibly terminate process " + message); } } } }); log.info(""); log.info("Now you can start a Remote debug execution in your IDE by using localhost and the debug port " + localDebugPort); log.info(""); processCommandAsync(process, createExternalProcessLogger(command + "> "), commands, message); } catch (Exception e) { throw new MojoExecutionException("Failed to execute process " + commands + " for " + message + ": " + e, e); } } private boolean enableDebugging(HasMetadata entity, PodTemplateSpec template) { if (template != null) { PodSpec podSpec = template.getSpec(); if (podSpec != null) { List<Container> containers = podSpec.getContainers(); if (containers.size() > 0) { Container container = containers.get(0); List<EnvVar> env = container.getEnv(); if (env == null) { env = new ArrayList<>(); } remoteDebugPort = getEnvVar(env, ENV_VAR_JAVA_DEBUG_PORT, ENV_VAR_JAVA_DEBUG_PORT_DEFAULT); if (setEnvVar(env, ENV_VAR_JAVA_DEBUG, "true")) { container.setEnv(env); log.info("Enabling debug on " + getKind(entity) + " " + getName(entity)); return true; } } } } return false; } private boolean setEnvVar(List<EnvVar> envVarList, String name, String value) { for (EnvVar envVar : envVarList) { String envVarName = envVar.getName(); if (Objects.equal(name, envVarName)) { String oldValue = envVar.getValue(); if (Objects.equal(value, oldValue)) { return false; } else { envVar.setValue(value); return true; } } } EnvVar env = new EnvVarBuilder().withName(name).withValue(value).build(); envVarList.add(env); return true; } private String getEnvVar(List<EnvVar> envVarList, String name, String defaultValue) { String answer = defaultValue; if (envVarList != null) { for (EnvVar envVar : envVarList) { String envVarName = envVar.getName(); if (Objects.equal(name, envVarName)) { String value = envVar.getValue(); if (Strings.isNotBlank(value)) { return value; } } } } return answer; } }