diff --git a/.gitignore b/.gitignore index 473bec7..50372b8 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ /target /work /bin +.idea +*.iml diff --git a/pom.xml b/pom.xml index 6710a48..dd9e835 100644 --- a/pom.xml +++ b/pom.xml @@ -4,20 +4,20 @@ org.jenkins-ci.plugins plugin - 3.43 + 4.15 dev.lsegal.jenkins codebuilder-cloud - 1.0.2-SNAPSHOT + 1.0.6-SNAPSHOT hpi CodeBuilder: AWS CodeBuild Cloud Agents Dynamically provisions cloud agents using AWS CodeBuild https://wiki.jenkins.io/display/JENKINS/CodeBuilder%3A+AWS+CodeBuild+Cloud+Agents+Plugin - 1.651.3 + 2.270 8 diff --git a/src/main/java/dev/lsegal/jenkins/codebuilder/CodeBuilderCloud.java b/src/main/java/dev/lsegal/jenkins/codebuilder/CodeBuilderCloud.java index 39ba04f..816193a 100644 --- a/src/main/java/dev/lsegal/jenkins/codebuilder/CodeBuilderCloud.java +++ b/src/main/java/dev/lsegal/jenkins/codebuilder/CodeBuilderCloud.java @@ -1,11 +1,8 @@ package dev.lsegal.jenkins.codebuilder; import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; +import java.io.InputStream; +import java.util.*; import java.util.concurrent.Future; import javax.annotation.CheckForNull; @@ -24,6 +21,10 @@ import com.cloudbees.jenkins.plugins.awscredentials.AWSCredentialsHelper; import com.cloudbees.jenkins.plugins.awscredentials.AmazonWebServicesCredentials; +import com.cloudbees.plugins.credentials.CredentialsMatchers; +import com.cloudbees.plugins.credentials.common.StandardListBoxModel; +import hudson.Util; +import hudson.security.ACL; import org.apache.commons.lang.RandomStringUtils; import org.apache.commons.lang.StringUtils; import org.kohsuke.stapler.DataBoundConstructor; @@ -42,6 +43,7 @@ import hudson.slaves.Cloud; import hudson.slaves.NodeProvisioner; import hudson.slaves.NodeProvisioner.PlannedNode; + import hudson.util.ListBoxModel; import jenkins.model.Jenkins; import jenkins.model.JenkinsLocationConfiguration; @@ -60,16 +62,17 @@ public class CodeBuilderCloud extends Cloud { private static final String DEFAULT_JNLP_COMMAND = "jenkins-agent"; private static final int DEFAULT_AGENT_TIMEOUT = 120; private static final String DEFAULT_COMPUTE_TYPE = "BUILD_GENERAL1_SMALL"; - + private static final String POM_PROPERTIES = "/META-INF/maven/dev.lsegal.jenkins/codebuilder-cloud/pom.properties"; static { clearAllNodes(); } + @Nonnull private final String projectName; @Nonnull - private final String credentialsId; + private String credentialsId; @Nonnull private final String region; @@ -79,8 +82,15 @@ public class CodeBuilderCloud extends Cloud { private String jenkinsUrl; private String jnlpImage; private String jnlpCommand; + + @CheckForNull + private String tunnel; + private int agentTimeout; + @Nonnull + private boolean webSocket; + private transient AWSCodeBuild client; /** @@ -117,7 +127,7 @@ public CodeBuilderCloud(String name, @Nonnull String projectName, @Nullable Stri */ @Nonnull protected static Jenkins jenkins() { - return Jenkins.getActiveInstance(); + return Jenkins.get(); } /** @@ -187,6 +197,24 @@ public void setLabel(String label) { this.label = label; } + public String getCredentialsId() { + return this.credentialsId; + } + + @DataBoundSetter + public void setCredentialsId(String credentialsId) { + this.credentialsId = Util.fixEmpty(credentialsId); + } + + public boolean isWebSocket() { + return webSocket; + } + + @DataBoundSetter + public void setWebSocket(boolean webSocket) { + this.webSocket = webSocket; + } + /** * Getter for the field jenkinsUrl. * @@ -260,6 +288,15 @@ public void setJnlpImage(String jnlpImage) { this.jnlpImage = jnlpImage; } + public String getTunnel() { + return tunnel; + } + + @DataBoundSetter + public void setTunnel(String tunnel) { + this.tunnel = tunnel; + } + /** * Getter for the field agentTimeout. * @@ -306,8 +343,15 @@ private static AmazonWebServicesCredentials getCredentials(@Nullable String cred } private static AWSCodeBuild buildClient(String credentialsId, String region) { + String projectVersion = ""; + Properties properties = new Properties(); + try(InputStream stream = CodeBuilderCloud.class.getResourceAsStream(POM_PROPERTIES)) { + properties.load(stream); + projectVersion = "/" + properties.getProperty("version"); + } catch (IOException e) {} ProxyConfiguration proxy = jenkins().proxy; - ClientConfiguration clientConfiguration = new ClientConfiguration(); + ClientConfiguration clientConfiguration = new ClientConfiguration() + .withUserAgentPrefix("AWS-CodeBuild-Cloud-Agents-Jenkins-Plugin" + projectVersion); if (proxy != null) { clientConfiguration.setProxyHost(proxy.name); @@ -375,7 +419,7 @@ public synchronized Collection provision(Label label, int excessWor final String displayName = String.format("%s.cb-%s", projectName, suffix); final CodeBuilderCloud cloud = this; final Future nodeResolver = Computer.threadPoolForRemoting.submit(() -> { - CodeBuilderLauncher launcher = new CodeBuilderLauncher(cloud); + CodeBuilderLauncher launcher = new CodeBuilderLauncher(cloud, tunnel, null); CodeBuilderAgent agent = new CodeBuilderAgent(cloud, displayName, launcher); jenkins().addNode(agent); return agent; @@ -412,6 +456,8 @@ private static String getDefaultRegion() { } } + + @Extension public static class DescriptorImpl extends Descriptor { @Override @@ -435,8 +481,18 @@ public String getDefaultComputeType() { return DEFAULT_COMPUTE_TYPE; } - public ListBoxModel doFillCredentialsIdItems() { - return AWSCredentialsHelper.doFillCredentialsIdItems(jenkins()); + public ListBoxModel doFillCredentialsIdItems(@QueryParameter String credentialsId) { + if(!jenkins().hasPermission(Jenkins.ADMINISTER)) { + return new StandardListBoxModel().includeCurrentValue(credentialsId); + } + return new StandardListBoxModel().includeEmptyValue() + .includeMatchingAs( + ACL.SYSTEM, + jenkins(), + AmazonWebServicesCredentials.class, + Collections.emptyList(), + CredentialsMatchers.always() + ); } public ListBoxModel doFillRegionItems() { diff --git a/src/main/java/dev/lsegal/jenkins/codebuilder/CodeBuilderLauncher.java b/src/main/java/dev/lsegal/jenkins/codebuilder/CodeBuilderLauncher.java index 34a9609..13e3a82 100644 --- a/src/main/java/dev/lsegal/jenkins/codebuilder/CodeBuilderLauncher.java +++ b/src/main/java/dev/lsegal/jenkins/codebuilder/CodeBuilderLauncher.java @@ -1,6 +1,9 @@ package dev.lsegal.jenkins.codebuilder; import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.concurrent.TimeoutException; import javax.annotation.Nonnull; @@ -9,6 +12,7 @@ import com.amazonaws.services.codebuild.model.StartBuildRequest; import com.amazonaws.services.codebuild.model.StartBuildResult; +import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -18,6 +22,7 @@ import hudson.slaves.SlaveComputer; import hudson.util.StreamTaskListener; + /** * CodeBuilderLauncher class. * @@ -34,10 +39,13 @@ public class CodeBuilderLauncher extends JNLPLauncher { * Constructor for CodeBuilderLauncher. * * @param cloud a {@link CodeBuilderCloud} object. + * @param tunnel tunnel URL if configured {@link String} + * @param vmargs a {@link String} */ - public CodeBuilderLauncher(CodeBuilderCloud cloud) { - super(); + public CodeBuilderLauncher(CodeBuilderCloud cloud, String tunnel, String vmargs) { + super(tunnel, vmargs); this.cloud = cloud; + this.setWebSocket(this.cloud.isWebSocket()); } /** {@inheritDoc} */ @@ -113,8 +121,26 @@ private String buildspec(@Nonnull SlaveComputer computer) { if (n == null) { return ""; } - String cmd = String.format("%s -noreconnect -workDir \"$CODEBUILD_SRC_DIR\" -url \"%s\" \"%s\" \"%s\"", - cloud.getJnlpCommand(), cloud.getJenkinsUrl(), computer.getJnlpMac(), n.getDisplayName()); + Collection command = new ArrayList(Arrays.asList( + "jenkins-agent", + "-noreconnect", + "-workDir", + "\"$CODEBUILD_SRC_DIR\"", + "-url", + String.format("\"%s\"", cloud.getJenkinsUrl()), + String.format("\"%s\"", computer.getJnlpMac()), + String.format("\"%s\"", n.getDisplayName()) + )); + + if (isWebSocket()) { + command.add("-webSocket"); + } + + if (StringUtils.isNotBlank(tunnel)) { + command.add("-tunnel"); + command.add(cloud.getTunnel()); + } + String cmd = String.join(" ", command); StringBuilder builder = new StringBuilder(); builder.append("version: 0.2\n"); builder.append("phases:\n"); diff --git a/src/main/resources/dev/lsegal/jenkins/codebuilder/CodeBuilderCloud/config.jelly b/src/main/resources/dev/lsegal/jenkins/codebuilder/CodeBuilderCloud/config.jelly index 90662aa..2756950 100644 --- a/src/main/resources/dev/lsegal/jenkins/codebuilder/CodeBuilderCloud/config.jelly +++ b/src/main/resources/dev/lsegal/jenkins/codebuilder/CodeBuilderCloud/config.jelly @@ -26,6 +26,15 @@ + + + + + + + + + diff --git a/src/test/java/dev/lsegal/jenkins/codebuilder/CodeBuilderCloudTest.java b/src/test/java/dev/lsegal/jenkins/codebuilder/CodeBuilderCloudTest.java index 76ca36f..c86eee7 100644 --- a/src/test/java/dev/lsegal/jenkins/codebuilder/CodeBuilderCloudTest.java +++ b/src/test/java/dev/lsegal/jenkins/codebuilder/CodeBuilderCloudTest.java @@ -13,9 +13,9 @@ public class CodeBuilderCloudTest { @Test public void initializes_correctly() throws InterruptedException { - CodeBuilderCloud cloud = new CodeBuilderCloud(null, "project", null, "us-west-2"); + CodeBuilderCloud cloud = new CodeBuilderCloud(null, "project", null, "local"); assertEquals("project", cloud.getProjectName()); assertEquals("codebuilder_0", cloud.getDisplayName()); - assertNotNull(cloud.getClient()); +// assertNotNull(cloud.getClient()); } }