Skip to content

Commit

Permalink
Merge pull request #69 from jglick/guice
Browse files Browse the repository at this point in the history
Stop using Guice to inject `InputStep` & context parameters
  • Loading branch information
car-roll committed Jan 19, 2022
2 parents 5462272 + a060d36 commit f27b0b8
Show file tree
Hide file tree
Showing 15 changed files with 198 additions and 37 deletions.
1 change: 1 addition & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-step-api</artifactId>
<version>622.vb_8e7c15b_c95a_</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import hudson.Util;
import hudson.model.ParameterDefinition;
import hudson.model.PasswordParameterDefinition;
import hudson.model.Run;
import hudson.model.TaskListener;
import hudson.util.Secret;
import java.io.Serializable;
import java.util.Collections;
Expand All @@ -20,9 +22,12 @@
import org.jenkinsci.plugins.structs.describable.CustomDescribableModel;
import org.jenkinsci.plugins.structs.describable.DescribableModel;
import org.jenkinsci.plugins.structs.describable.UninstantiatedDescribable;
import org.jenkinsci.plugins.workflow.steps.AbstractStepDescriptorImpl;
import org.jenkinsci.plugins.workflow.graph.FlowNode;
import org.jenkinsci.plugins.workflow.steps.AbstractStepImpl;
import org.jenkinsci.plugins.workflow.steps.Step;
import org.jenkinsci.plugins.workflow.steps.StepContext;
import org.jenkinsci.plugins.workflow.steps.StepDescriptor;
import org.jenkinsci.plugins.workflow.steps.StepExecution;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.DataBoundSetter;

Expand Down Expand Up @@ -62,6 +67,7 @@ public class InputStep extends AbstractStepImpl implements Serializable {

@DataBoundConstructor
public InputStep(String message) {
super(true);
if (message==null)
message = "Pipeline has paused and needs your input before proceeding";
this.message = message;
Expand Down Expand Up @@ -151,18 +157,18 @@ public boolean canSettle(Authentication a) {
return false;
}

@Override public StepExecution start(StepContext context) throws Exception {
return new InputStepExecution(this, context);
}


@Override
public DescriptorImpl getDescriptor() {
return (DescriptorImpl)super.getDescriptor();
}

@Extension
public static class DescriptorImpl extends AbstractStepDescriptorImpl implements CustomDescribableModel {

public DescriptorImpl() {
super(InputStepExecution.class);
}
public static class DescriptorImpl extends StepDescriptor implements CustomDescribableModel {

@Override
public String getFunctionName() {
Expand All @@ -174,6 +180,12 @@ public String getDisplayName() {
return Messages.wait_for_interactive_input();
}

@Override public Set<? extends Class<?>> getRequiredContext() {
Set<Class<?>> context = new HashSet<>();
Collections.addAll(context, Run.class, TaskListener.class, FlowNode.class);
return Collections.unmodifiableSet(context);
}

/**
* Compatibility hack for JENKINS-63516.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import com.cloudbees.plugins.credentials.CredentialsParameterValue;
import com.cloudbees.plugins.credentials.builds.CredentialsParameterBinder;
import com.google.inject.Inject;
import hudson.FilePath;
import hudson.Util;
import hudson.console.HyperlinkNote;
Expand All @@ -19,7 +18,6 @@
import hudson.security.ACL;
import hudson.security.ACLContext;
import hudson.security.SecurityRealm;
import hudson.security.Permission;
import hudson.util.HttpResponses;
import jenkins.model.IdStrategy;
import jenkins.model.Jenkins;
Expand All @@ -31,7 +29,6 @@
import org.jenkinsci.plugins.workflow.steps.AbstractStepExecutionImpl;
import org.jenkinsci.plugins.workflow.support.actions.PauseAction;
import org.jenkinsci.plugins.workflow.graph.FlowNode;
import org.jenkinsci.plugins.workflow.steps.StepContextParameter;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.interceptor.RequirePOST;
Expand All @@ -50,6 +47,7 @@
import java.util.logging.Logger;
import jenkins.util.Timer;
import org.jenkinsci.plugins.workflow.steps.FlowInterruptedException;
import org.jenkinsci.plugins.workflow.steps.StepContext;

/**
* @author Kohsuke Kawaguchi
Expand All @@ -58,21 +56,24 @@ public class InputStepExecution extends AbstractStepExecutionImpl implements Mod

private static final Logger LOGGER = Logger.getLogger(InputStepExecution.class.getName());

@StepContextParameter private transient Run run;

@StepContextParameter private transient TaskListener listener;

@StepContextParameter private transient FlowNode node;

/**
* Result of the input.
*/
private Outcome outcome;

@Inject(optional=true) InputStep input;
final InputStep input;

InputStepExecution(InputStep input, StepContext context) {
super(context);
this.input = input;
}

@Override
public boolean start() throws Exception {
Run<?, ?> run = getRun();
TaskListener listener = getListener();
FlowNode node = getNode();

// record this input
getPauseAction().add(this);

Expand Down Expand Up @@ -101,6 +102,8 @@ public void stop(Throwable cause) throws Exception {
@Override public void run() {
try (ACLContext context = ACL.as(ACL.SYSTEM)) {
doAbort();
} catch (IOException | InterruptedException x) {
LOGGER.log(Level.WARNING, "failed to abort " + getContext(), x);
}
}
});
Expand All @@ -114,8 +117,16 @@ public InputStep getInput() {
return input;
}

public Run getRun() {
return run;
public Run<?, ?> getRun() throws IOException, InterruptedException {
return getContext().get(Run.class);
}

private FlowNode getNode() throws InterruptedException, IOException {
return getContext().get(FlowNode.class);
}

private TaskListener getListener() throws IOException, InterruptedException {
return getContext().get(TaskListener.class);
}

/**
Expand All @@ -128,7 +139,8 @@ public boolean isSettled() {
/**
* Gets the {@link InputAction} that this step should be attached to.
*/
private InputAction getPauseAction() {
private InputAction getPauseAction() throws IOException, InterruptedException {
Run<?, ?> run = getRun();
InputAction a = run.getAction(InputAction.class);
if (a==null)
run.addAction(a=new InputAction());
Expand Down Expand Up @@ -175,15 +187,15 @@ public HttpResponse doProceed(StaplerRequest request) throws IOException, Servle
* @param params A map that represents the parameters sent in the request
* @return A HttpResponse object that represents Status code (200) indicating the request succeeded normally.
*/
public HttpResponse proceed(@CheckForNull Map<String,Object> params) {
public HttpResponse proceed(@CheckForNull Map<String,Object> params) throws IOException, InterruptedException {
User user = User.current();
String approverId = null;
if (user != null){
approverId = user.getId();
run.addAction(new ApproverAction(approverId));
listener.getLogger().println("Approved by " + hudson.console.ModelHyperlinkNote.encodeTo(user));
getRun().addAction(new ApproverAction(approverId));
getListener().getLogger().println("Approved by " + hudson.console.ModelHyperlinkNote.encodeTo(user));
}
node.addAction(new InputSubmittedAction(approverId, params));
getNode().addAction(new InputSubmittedAction(approverId, params));

Object v;
if (params != null && params.size() == 1) {
Expand All @@ -200,7 +212,7 @@ public HttpResponse proceed(@CheckForNull Map<String,Object> params) {

@Deprecated
@SuppressWarnings("unchecked")
public HttpResponse proceed(Object v) {
public HttpResponse proceed(Object v) throws IOException, InterruptedException {
if (v instanceof Map) {
return proceed(new HashMap<String,Object>((Map) v));
} else if (v == null) {
Expand All @@ -214,7 +226,7 @@ public HttpResponse proceed(Object v) {
* Used from the Proceed hyperlink when no parameters are defined.
*/
@RequirePOST
public HttpResponse doProceedEmpty() throws IOException {
public HttpResponse doProceedEmpty() throws IOException, InterruptedException {
preSubmissionCheck();

return proceed(null);
Expand All @@ -224,7 +236,7 @@ public HttpResponse doProceedEmpty() throws IOException {
* REST endpoint to abort the workflow.
*/
@RequirePOST
public HttpResponse doAbort() {
public HttpResponse doAbort() throws IOException, InterruptedException {
preAbortCheck();

FlowInterruptedException e = new FlowInterruptedException(Result.ABORTED, new Rejection(User.current()));
Expand All @@ -240,7 +252,7 @@ public HttpResponse doAbort() {
/**
* Check if the current user can abort/cancel the run from the input.
*/
private void preAbortCheck() {
private void preAbortCheck() throws IOException, InterruptedException {
if (isSettled()) {
throw new Failure("This input has been already given");
} if (!canCancel() && !canSubmit()) {
Expand All @@ -255,7 +267,7 @@ private void preAbortCheck() {
/**
* Check if the current user can submit the input.
*/
public void preSubmissionCheck() {
public void preSubmissionCheck() throws IOException, InterruptedException {
if (isSettled())
throw new Failure("This input has been already given");
if (!canSubmit()) {
Expand All @@ -267,38 +279,39 @@ public void preSubmissionCheck() {
}
}

private void postSettlement() {
private void postSettlement() throws IOException, InterruptedException {
try {
getPauseAction().remove(this);
run.save();
getRun().save();
} catch (IOException | InterruptedException | TimeoutException x) {
LOGGER.log(Level.WARNING, "failed to remove InputAction from " + run, x);
LOGGER.log(Level.WARNING, "failed to remove InputAction from " + getContext(), x);
} finally {
FlowNode node = getNode();
if (node != null) {
try {
PauseAction.endCurrentPause(node);
} catch (IOException x) {
LOGGER.log(Level.WARNING, "failed to end PauseAction in " + run, x);
LOGGER.log(Level.WARNING, "failed to end PauseAction in " + getContext(), x);
}
} else {
LOGGER.log(Level.WARNING, "cannot set pause end time for {0} in {1}", new Object[] {getId(), run});
LOGGER.log(Level.WARNING, "cannot set pause end time for {0} in {1}", new Object[] {getId(), getContext()});
}
}
}

private boolean canCancel() {
private boolean canCancel() throws IOException, InterruptedException {
return !Jenkins.get().isUseSecurity() || getRun().getParent().hasPermission(Job.CANCEL);
}

private boolean canSubmit() {
private boolean canSubmit() throws IOException, InterruptedException {
Authentication a = Jenkins.getAuthentication();
return canSettle(a);
}

/**
* Checks if the given user can settle this input.
*/
private boolean canSettle(Authentication a) {
private boolean canSettle(Authentication a) throws IOException, InterruptedException {
String submitter = input.getSubmitter();
if (submitter==null)
return getRun().getParent().hasPermission(Job.BUILD);
Expand Down Expand Up @@ -369,6 +382,7 @@ private Map<String,Object> parseValue(StaplerRequest request) throws ServletExce
}
}

Run<?, ?> run = getRun();
CredentialsParameterBinder binder = CredentialsParameterBinder.getOrCreate(run);
String userId = Jenkins.getAuthentication().getName();
for (ParameterValue val : vals) {
Expand All @@ -394,7 +408,7 @@ private Map<String,Object> parseValue(StaplerRequest request) throws ServletExce
private Object convert(String name, ParameterValue v) throws IOException, InterruptedException {
if (v instanceof FileParameterValue) {
FileParameterValue fv = (FileParameterValue) v;
FilePath fp = new FilePath(run.getRootDir()).child(name);
FilePath fp = new FilePath(getRun().getRootDir()).child(name);
fp.copyFrom(fv.getFile());
return fp;
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
import com.cloudbees.plugins.credentials.CredentialsScope;
import com.cloudbees.plugins.credentials.domains.Domain;
import com.gargoylesoftware.htmlunit.ElementNotFoundException;
import com.gargoylesoftware.htmlunit.HttpMethod;
import com.gargoylesoftware.htmlunit.WebRequest;
import com.gargoylesoftware.htmlunit.html.HtmlAnchor;
import com.gargoylesoftware.htmlunit.html.HtmlElementUtil;
import com.gargoylesoftware.htmlunit.html.HtmlForm;
Expand Down Expand Up @@ -76,6 +78,7 @@
import org.jvnet.hudson.test.MockAuthorizationStrategy;

import edu.umd.cs.findbugs.annotations.Nullable;
import org.jvnet.hudson.test.recipes.LocalData;

/**
* @author Kohsuke Kawaguchi
Expand Down Expand Up @@ -472,6 +475,15 @@ public void passwordParameters() throws Exception {
j.assertLogContains("Password is mySecret", b);
}

@LocalData
@Test public void serialForm() throws Exception {
WorkflowJob p = j.jenkins.getItemByFullName("p", WorkflowJob.class);
WorkflowRun b = p.getBuildByNumber(1);
JenkinsRule.WebClient wc = j.createWebClient();
wc.getPage(new WebRequest(wc.createCrumbedUrl("job/p/1/input/9edfbbe09847e1bfee4f8d2b0abfd1c3/proceedEmpty"), HttpMethod.POST));
j.assertBuildStatusSuccess(j.waitForCompletion(b));
}

private void selectUserCredentials(JenkinsRule.WebClient wc, WorkflowRun run, CpsFlowExecution execution, String credentialsId, String username, String inputId) throws Exception {
while (run.getAction(InputAction.class) == null) {
execution.waitForSuspension();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?xml version='1.1' encoding='UTF-8'?>
<flow-build>
<actions>
<org.jenkinsci.plugins.workflow.support.steps.input.InputAction>
<ids class="java.util.concurrent.CopyOnWriteArrayList">
<string>9edfbbe09847e1bfee4f8d2b0abfd1c3</string>
</ids>
</org.jenkinsci.plugins.workflow.support.steps.input.InputAction>
</actions>
<queueId>1</queueId>
<timestamp>1641319957316</timestamp>
<startTime>1641319957331</startTime>
<duration>0</duration>
<charset>UTF-8</charset>
<keepLog>false</keepLog>
<execution class="org.jenkinsci.plugins.workflow.cps.CpsFlowExecution">
<result>SUCCESS</result>
<script>input &apos;please approve&apos;</script>
<loadedScripts class="map"/>
<durabilityHint>MAX_SURVIVABILITY</durabilityHint>
<timings class="map">
<entry>
<string>flowNode</string>
<long>40682675</long>
</entry>
<entry>
<string>classLoad</string>
<long>115296295</long>
</entry>
<entry>
<string>run</string>
<long>178764716</long>
</entry>
<entry>
<string>parse</string>
<long>228948941</long>
</entry>
<entry>
<string>saveProgram</string>
<long>30298839</long>
</entry>
</timings>
<sandbox>true</sandbox>
<iota>3</iota>
<head>1:3</head>
<start>2</start>
<done>false</done>
<resumeBlocked>false</resumeBlocked>
</execution>
<completed>false</completed>
<checkouts class="hudson.util.PersistedList"/>
</flow-build>
Loading

0 comments on commit f27b0b8

Please sign in to comment.