package com.intuit.tank.jenkins.publisher; /* * #%L * Intuit Tank Jenkins Plugin * %% * Copyright (C) 2011 - 2015 Intuit Inc. * %% * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * #L% */ import hudson.EnvVars; import hudson.Extension; import hudson.Launcher; import hudson.model.BuildListener; import hudson.model.Describable; import hudson.model.EnvironmentContributingAction; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; import hudson.tasks.BuildStepDescriptor; import hudson.tasks.BuildStepMonitor; import hudson.tasks.Publisher; import hudson.tasks.Recorder; import hudson.util.FormValidation; import java.io.IOException; import java.io.PrintStream; import java.io.Serializable; import javax.servlet.ServletException; import net.sf.json.JSONObject; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.StaplerRequest; import com.intuit.tank.jenkins.callables.CreateProxyCallable; import com.intuit.tank.jenkins.callables.ProxyRequest; import com.intuit.tank.jenkins.callables.StopProxyCallable; import com.intuit.tank.jenkins.constants.StringConstants; import com.intuit.tank.jenkins.exceptions.TankJenkinsPluginException; import com.intuit.tank.jenkins.printer.LogPrinter; /** * This is our main plug-in class. It extends from the Jenkins' Publisher * extension point, which gives it hooks into pre-build and post-build (via the * perform) method functionality. We don't care when the pre-build code * executes, but we do care when the post-build code executes - using the * Publisher extension satisfies these requirements. * * Through VirtualChannels, we issue commands to slaves (whether local or * remote). * * The only configurable entity of this plugin is the port from which we're * trying to start the proxy. * * @author bfiola * */ public class ProxyPlugin extends Recorder implements Describable<Publisher>, Serializable { private static final long serialVersionUID = 1L; private static final String PROXY_PORT_ENVIRONMENT_KEY = "tankProxyPort"; private Integer proxyPort; private boolean startedProxy; @DataBoundConstructor public ProxyPlugin(Integer proxyPort) { super(); this.proxyPort = proxyPort; } public Integer getProxyPort() { return this.proxyPort; } /** * This function executes after pre-build steps and arbitrarily before the * build itself is run. */ @Override public boolean prebuild(AbstractBuild<?, ?> build, BuildListener listener) { PrintStream logger = listener.getLogger(); try { LogPrinter.print("Sending remote command to start proxy on port " + proxyPort + ".", logger); Launcher launcher = build.getBuiltOn().createLauncher(listener); launcher.getChannel().call( new CreateProxyCallable(new ProxyRequest(listener, proxyPort, build.getExternalizableId(), build.getWorkspace(), getDescriptor()))); build.addAction(new AddEnvironmentVariableAction(PROXY_PORT_ENVIRONMENT_KEY, Integer.toString(proxyPort))); } catch (Exception e) { LogPrinter.print(e, logger); return false; } return true; } /** * Once we've finished building, this method gets called (regardless of * build result) to stop the proxy. */ @Override public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) { PrintStream logger = listener.getLogger(); try { LogPrinter.print("Sending remote command to stop proxy on port " + proxyPort + ".", logger); launcher.getChannel().call( new StopProxyCallable(new ProxyRequest(listener, proxyPort, build.getExternalizableId(), build.getWorkspace(), getDescriptor()))); } catch (Exception e) { LogPrinter.print(e, logger); return false; } return true; } @Override public BuildStepMonitor getRequiredMonitorService() { return BuildStepMonitor.NONE; } @Override public ProxyPluginDescriptor getDescriptor() { return (ProxyPluginDescriptor) super.getDescriptor(); } @Extension public static final class ProxyPluginDescriptor extends BuildStepDescriptor<Publisher> implements Serializable { private static final long serialVersionUID = 1L; public ProxyPluginDescriptor() { super(ProxyPlugin.class); load(); } public boolean isApplicable(Class<? extends AbstractProject> aClass) { return true; } @Override public String getDisplayName() { return StringConstants.DisplayName.PERSIST_SCRIPT; } public FormValidation doCheckProxyPort(@QueryParameter String proxyPort) throws IOException, ServletException { return validatePort(proxyPort); } // this is called at run-time to make sure someone isn't trying to run a // test that's misconfigured. public void testPort(Integer value) throws TankJenkinsPluginException { testPort(Integer.toString(value)); } // this is called when a user changes the build configuration private FormValidation validatePort(String current) { try { testPort(current); return FormValidation.ok(); } catch (Exception e) { return FormValidation.error(e.getMessage()); } } private void testPort(String value) throws TankJenkinsPluginException { testIsNumber(value); testPortRange(Integer.parseInt(value)); } private void testIsNumber(String value) throws TankJenkinsPluginException { try { Integer.parseInt(value); } catch (Exception e) { throw new TankJenkinsPluginException( StringConstants.ErrorMessages.MUST_BE_INTEGER); } } private void testPortRange(Integer value) throws TankJenkinsPluginException { if (value < 0 || value >= 65536) { throw new TankJenkinsPluginException( StringConstants.ErrorMessages.PORT_INTEGER_RANGE); } } @Override public boolean configure(StaplerRequest req, JSONObject formData) throws FormException { save(); return super.configure(req, formData); } } class AddEnvironmentVariableAction implements EnvironmentContributingAction { private String key; private String value; public AddEnvironmentVariableAction(String key, String value) { this.key = key; this.value = value; } public void buildEnvVars(AbstractBuild<?,?> build, EnvVars vars) { if(vars != null && key != null && value != null) { vars.put(key, value); } } public String getDisplayName() { return "Proxy Port Environment Variable"; } public String getIconFileName() { return null; } public String getUrlName() { return null; } } }