package org.csanchez.jenkins.plugins.kubernetes.pipeline; import java.io.Closeable; import java.io.IOException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; import java.util.logging.Logger; import org.csanchez.jenkins.plugins.kubernetes.KubernetesCloud; import org.jenkinsci.plugins.workflow.steps.AbstractStepExecutionImpl; import org.jenkinsci.plugins.workflow.steps.BodyExecutionCallback; import org.jenkinsci.plugins.workflow.steps.BodyInvoker; import org.jenkinsci.plugins.workflow.steps.StepContext; import hudson.AbortException; import hudson.FilePath; import hudson.LauncherDecorator; import hudson.model.TaskListener; import io.fabric8.kubernetes.client.KubernetesClient; import jenkins.model.Jenkins; public class ContainerStepExecution extends AbstractStepExecutionImpl { private static final long serialVersionUID = 7634132798345235774L; private static final transient Logger LOGGER = Logger.getLogger(ContainerStepExecution.class.getName()); private static final transient String HOSTNAME_FILE = "/etc/hostname"; private final ContainerStep step; private transient KubernetesClient client; private transient ContainerExecDecorator decorator; ContainerStepExecution(ContainerStep step, StepContext context) { super(context); this.step = step; } @Override public boolean start() throws Exception { LOGGER.log(Level.FINE, "Starting container step."); FilePath workspace = getContext().get(FilePath.class); String podName = workspace.child(HOSTNAME_FILE).readToString().trim(); String containerName = step.getName(); final AtomicBoolean podAlive = new AtomicBoolean(false); final CountDownLatch podStarted = new CountDownLatch(1); final CountDownLatch podFinished = new CountDownLatch(1); String cloudName = getContext().get(PodTemplateStep.class).getCloud(); KubernetesCloud cloud = (KubernetesCloud) Jenkins.getInstance().getCloud(cloudName); if (cloud == null) { throw new AbortException(String.format("Cloud does not exist: %s", cloudName)); } client = cloud.connect(); decorator = new ContainerExecDecorator(client, podName, containerName, workspace.getRemote(), podAlive, podStarted, podFinished); getContext().newBodyInvoker() .withContext(BodyInvoker .mergeLauncherDecorators(getContext().get(LauncherDecorator.class), decorator)) .withCallback(new ContainerExecCallback()) .start(); return false; } @Override public void stop(Throwable cause) throws Exception { LOGGER.log(Level.FINE, "Stopping container step."); closeQuietly(client, decorator); } private void closeQuietly(Closeable... closeables) { for (Closeable c : closeables) { if (c != null) { try { c.close(); } catch (IOException e) { try { getContext().get(TaskListener.class).error("Error while closing: [" + c + "]"); } catch (IOException | InterruptedException e1) { LOGGER.log(Level.WARNING, "Error writing to task listener", e); } } } } } private static class ContainerExecCallback extends BodyExecutionCallback { @Override public void onSuccess(StepContext context, Object result) { context.onSuccess(result); } @Override public void onFailure(StepContext context, Throwable t) { context.onFailure(t); } } }