diff --git a/pom.xml b/pom.xml index 3e1a70926..d021bab11 100644 --- a/pom.xml +++ b/pom.xml @@ -35,6 +35,7 @@ 8 2.9 2.12 + 2.25 @@ -57,7 +58,7 @@ org.jenkins-ci.plugins.workflow workflow-cps - 2.25 + ${workflow-cps-plugin.version} org.jenkins-ci.plugins.workflow @@ -103,6 +104,13 @@ tests test + + org.jenkins-ci.plugins.workflow + workflow-cps + ${workflow-cps-plugin.version} + tests + test + org.jenkins-ci.plugins.workflow workflow-job diff --git a/src/main/java/org/jenkinsci/plugins/docker/workflow/RegistryEndpointStep.java b/src/main/java/org/jenkinsci/plugins/docker/workflow/RegistryEndpointStep.java index d74ecfdd7..47ced2eb5 100644 --- a/src/main/java/org/jenkinsci/plugins/docker/workflow/RegistryEndpointStep.java +++ b/src/main/java/org/jenkinsci/plugins/docker/workflow/RegistryEndpointStep.java @@ -28,17 +28,24 @@ import hudson.Extension; import hudson.FilePath; import hudson.Launcher; +import hudson.Util; import hudson.model.Job; import hudson.model.Node; import hudson.model.TaskListener; import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.TreeMap; import javax.annotation.CheckForNull; import javax.annotation.Nonnull; import org.jenkinsci.plugins.docker.commons.credentials.DockerRegistryEndpoint; import org.jenkinsci.plugins.docker.commons.credentials.KeyMaterialFactory; import org.jenkinsci.plugins.docker.commons.tools.DockerTool; +import org.jenkinsci.plugins.structs.describable.UninstantiatedDescribable; import org.jenkinsci.plugins.workflow.steps.AbstractStepDescriptorImpl; import org.jenkinsci.plugins.workflow.steps.AbstractStepImpl; +import org.jenkinsci.plugins.workflow.steps.Step; import org.jenkinsci.plugins.workflow.steps.StepContextParameter; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.DataBoundSetter; @@ -62,7 +69,7 @@ public String getToolName() { } @DataBoundSetter public void setToolName(String toolName) { - this.toolName = toolName; + this.toolName = Util.fixEmpty(toolName); } public static class Execution extends AbstractEndpointStepExecution { @@ -105,6 +112,29 @@ public DescriptorImpl() { return true; } + @Override public UninstantiatedDescribable uninstantiate(Step step) throws UnsupportedOperationException { + RegistryEndpointStep s = (RegistryEndpointStep) step; + Map args = new TreeMap<>(); + args.put("url", s.registry.getUrl()); + args.put("credentialsId", s.registry.getCredentialsId()); + args.put("toolName", s.toolName); + args.values().removeAll(Collections.singleton(null)); + return new UninstantiatedDescribable(args); + } + + @Override public Step newInstance(Map arguments) throws Exception { + arguments = new HashMap<>(arguments); + if (arguments.containsKey("url") || arguments.containsKey("credentialsId")) { + if (arguments.containsKey("registry")) { + throw new IllegalArgumentException("cannot mix url/credentialsId with registry"); + } + arguments.put("registry", new DockerRegistryEndpoint((String) arguments.remove("url"), (String) arguments.remove("credentialsId"))); + } else if (!arguments.containsKey("registry")) { + throw new IllegalArgumentException("must specify url/credentialsId (or registry)"); + } + return super.newInstance(arguments); + } + } } diff --git a/src/main/java/org/jenkinsci/plugins/docker/workflow/ServerEndpointStep.java b/src/main/java/org/jenkinsci/plugins/docker/workflow/ServerEndpointStep.java index 4bbe854bb..8ddf52e15 100644 --- a/src/main/java/org/jenkinsci/plugins/docker/workflow/ServerEndpointStep.java +++ b/src/main/java/org/jenkinsci/plugins/docker/workflow/ServerEndpointStep.java @@ -85,6 +85,8 @@ public DescriptorImpl() { return true; } + // TODO allow DockerServerEndpoint fields to be inlined, as in RegistryEndpointStep, so Docker.groovy can say simply: script.withDockerServer(uri: uri, credentialsId: credentialsId) {…} + } } diff --git a/src/main/resources/org/jenkinsci/plugins/docker/workflow/Docker.groovy b/src/main/resources/org/jenkinsci/plugins/docker/workflow/Docker.groovy index 45159f9f1..97dad6030 100644 --- a/src/main/resources/org/jenkinsci/plugins/docker/workflow/Docker.groovy +++ b/src/main/resources/org/jenkinsci/plugins/docker/workflow/Docker.groovy @@ -37,7 +37,7 @@ class Docker implements Serializable { public V withRegistry(String url, String credentialsId = null, Closure body) { node { script.withEnv(["DOCKER_REGISTRY_URL=${url}"]) { - script.withDockerRegistry(registry: [url: url, credentialsId: credentialsId], toolName: script.env.DOCKER_TOOL_NAME) { + script.withDockerRegistry(url: url, credentialsId: credentialsId, toolName: script.env.DOCKER_TOOL_NAME) { body() } } diff --git a/src/test/java/org/jenkinsci/plugins/docker/workflow/RegistryEndpointStepTest.java b/src/test/java/org/jenkinsci/plugins/docker/workflow/RegistryEndpointStepTest.java index eda6e270f..1e5590444 100644 --- a/src/test/java/org/jenkinsci/plugins/docker/workflow/RegistryEndpointStepTest.java +++ b/src/test/java/org/jenkinsci/plugins/docker/workflow/RegistryEndpointStepTest.java @@ -28,45 +28,59 @@ import com.cloudbees.plugins.credentials.common.IdCredentials; import com.cloudbees.plugins.credentials.domains.Domain; import com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl; -import java.util.Collections; -import java.util.Map; -import java.util.TreeMap; import org.jenkinsci.plugins.docker.commons.credentials.DockerRegistryEndpoint; +import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; +import org.jenkinsci.plugins.workflow.cps.SnippetizerTester; +import org.jenkinsci.plugins.workflow.job.WorkflowJob; import org.jenkinsci.plugins.workflow.steps.StepConfigTester; -import org.jenkinsci.plugins.workflow.structs.DescribableHelper; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; -import org.junit.runners.model.Statement; -import org.jvnet.hudson.test.BuildWatcher; -import org.jvnet.hudson.test.RestartableJenkinsRule; +import org.jvnet.hudson.test.Issue; +import org.jvnet.hudson.test.JenkinsRule; public class RegistryEndpointStepTest { - @ClassRule public static BuildWatcher buildWatcher = new BuildWatcher(); - @Rule public RestartableJenkinsRule story = new RestartableJenkinsRule(); + @Rule public JenkinsRule r = new JenkinsRule(); - @Test public void configRoundTrip() { - story.addStep(new Statement() { - @Override public void evaluate() throws Throwable { - IdCredentials registryCredentials = new UsernamePasswordCredentialsImpl(CredentialsScope.GLOBAL, "registryCreds", null, "me", "pass"); - CredentialsProvider.lookupStores(story.j.jenkins).iterator().next().addCredentials(Domain.global(), registryCredentials); - StepConfigTester sct = new StepConfigTester(story.j); - Map registryConfig = new TreeMap(); - registryConfig.put("url", "https://docker.my.com/"); - registryConfig.put("credentialsId", registryCredentials.getId()); - Map config = Collections.singletonMap("registry", registryConfig); - RegistryEndpointStep step = DescribableHelper.instantiate(RegistryEndpointStep.class, config); - step = sct.configRoundTrip(step); - DockerRegistryEndpoint registry = step.getRegistry(); - assertNotNull(registry); - assertEquals("https://docker.my.com/", registry.getUrl()); - assertEquals(registryCredentials.getId(), registry.getCredentialsId()); - assertEquals(config, DescribableHelper.uninstantiate(step)); - } - }); + @Issue("JENKINS-51395") + @Test public void configRoundTrip() throws Exception { + { // Recommended syntax. + SnippetizerTester st = new SnippetizerTester(r); + RegistryEndpointStep step = new RegistryEndpointStep(new DockerRegistryEndpoint("https://myreg/", null)); + step.setToolName(""); + st.assertRoundTrip(step, "withDockerRegistry(url: 'https://myreg/') {\n // some block\n}"); + step = new RegistryEndpointStep(new DockerRegistryEndpoint(null, "hubcreds")); + st.assertRoundTrip(step, "withDockerRegistry(credentialsId: 'hubcreds') {\n // some block\n}"); + step = new RegistryEndpointStep(new DockerRegistryEndpoint("https://myreg/", "mycreds")); + step.setToolName("ce"); + st.assertRoundTrip(step, "withDockerRegistry(credentialsId: 'mycreds', toolName: 'ce', url: 'https://myreg/') {\n // some block\n}"); + } + { // Older syntax. + WorkflowJob p = r.createProject(WorkflowJob.class, "p"); + p.setDefinition(new CpsFlowDefinition("node {withDockerRegistry(registry: [url: 'https://docker.my.com/'], toolName: 'irrelevant') {}}", true)); + r.buildAndAssertSuccess(p); + p.setDefinition(new CpsFlowDefinition("node {withDockerRegistry(registry: [url: 'https://docker.my.com/']) {}}", true)); + r.buildAndAssertSuccess(p); + p.setDefinition(new CpsFlowDefinition("node {withDockerRegistry([url: 'https://docker.my.com/']) {}}", true)); + r.buildAndAssertSuccess(p); + // and new, just in case SnippetizerTester is faking it: + p.setDefinition(new CpsFlowDefinition("node {withDockerRegistry(url: 'https://docker.my.com/') {}}", true)); + r.buildAndAssertSuccess(p); + } + { // UI form. + IdCredentials registryCredentials = new UsernamePasswordCredentialsImpl(CredentialsScope.GLOBAL, "registryCreds", null, "me", "pass"); + CredentialsProvider.lookupStores(r.jenkins).iterator().next().addCredentials(Domain.global(), registryCredentials); + StepConfigTester sct = new StepConfigTester(r); + RegistryEndpointStep step = new RegistryEndpointStep(new DockerRegistryEndpoint("https://docker.my.com/", "registryCreds")); + step = sct.configRoundTrip(step); + DockerRegistryEndpoint registry = step.getRegistry(); + assertNotNull(registry); + assertEquals("https://docker.my.com/", registry.getUrl()); + assertEquals("registryCreds", registry.getCredentialsId()); + // TODO check toolName + } } } \ No newline at end of file