diff --git a/src/main/java/org/jenkinsci/plugins/workflow/steps/RetryStep.java b/src/main/java/org/jenkinsci/plugins/workflow/steps/RetryStep.java index b26aa998..a2f74cca 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/steps/RetryStep.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/steps/RetryStep.java @@ -24,30 +24,49 @@ package org.jenkinsci.plugins.workflow.steps; -import hudson.Extension; -import hudson.model.TaskListener; +import java.io.Serializable; +import java.util.Collection; import java.util.Collections; import java.util.Set; + +import org.jenkinsci.plugins.workflow.support.steps.retry.RetryDelay; import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.DataBoundSetter; + +import hudson.Extension; +import hudson.model.Descriptor; +import hudson.model.TaskListener; /** * Executes the body up to N times. * * @author Kohsuke Kawaguchi */ -public class RetryStep extends Step { +public class RetryStep extends Step implements Serializable { private final int count; + private RetryDelay delay = null; + + public int left; @DataBoundConstructor public RetryStep(int count) { this.count = count; + this.left = count; } public int getCount() { return count; } + @DataBoundSetter public void setDelay(RetryDelay delay) { + this.delay = delay; + } + + public RetryDelay getDelay() { + return delay; + } + @Override public DescriptorImpl getDescriptor() { return (DescriptorImpl)super.getDescriptor(); @@ -55,7 +74,7 @@ public DescriptorImpl getDescriptor() { @Override public StepExecution start(StepContext context) throws Exception { - return new RetryStepExecution(count, context); + return new RetryStepExecution(this, context); } @Extension @@ -81,6 +100,10 @@ public Set> getRequiredContext() { return Collections.singleton(TaskListener.class); } + public Collection> getApplicableDescriptors() { + return RetryDelay.RetryDelayDescriptor.all(); + } } + private static final long serialVersionUID = 1L; } diff --git a/src/main/java/org/jenkinsci/plugins/workflow/steps/RetryStepExecution.java b/src/main/java/org/jenkinsci/plugins/workflow/steps/RetryStepExecution.java index 0490aec7..b6b83ba6 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/steps/RetryStepExecution.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/steps/RetryStepExecution.java @@ -1,53 +1,155 @@ package org.jenkinsci.plugins.workflow.steps; +import com.google.common.base.Function; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.AbortException; import hudson.Functions; import hudson.model.Run; +import hudson.Util; import hudson.model.TaskListener; import jenkins.model.CauseOfInterruption; +import java.util.UUID; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import javax.annotation.Nonnull; + +import jenkins.util.Timer; + /** * @author Kohsuke Kawaguchi */ public class RetryStepExecution extends AbstractStepExecutionImpl { - - @SuppressFBWarnings(value="SE_TRANSIENT_FIELD_NOT_RESTORED", justification="Only used when starting.") + + @SuppressFBWarnings(value = "SE_TRANSIENT_FIELD_NOT_RESTORED", justification = "Only used when starting.") + private transient final RetryStep step; + @SuppressFBWarnings(value = "SE_TRANSIENT_FIELD_NOT_RESTORED", justification = "Only used when starting.") private transient final int count; + private transient volatile ScheduledFuture task; + /** Used to track whether this is timing out on inactivity without needing to reference {@link #step}. */ + private boolean executing = false; + /** Token for {@link #executing} callbacks. */ + private final String id = UUID.randomUUID().toString(); + + + @Deprecated RetryStepExecution(int count, StepContext context) { super(context); - this.count = count; + this.count =count; + this.step = null; + } + + RetryStepExecution(@Nonnull RetryStep step, StepContext context) { + super(context); + this.step = step; + this.count = step.getCount(); } - @Override - public boolean start() throws Exception { + @Override public boolean start() throws Exception { StepContext context = getContext(); - context.newBodyInvoker() - .withCallback(new Callback(count)) - .start(); + if(step == null) { + context.newBodyInvoker() + .withCallback(new Callback(count)) + .start(); + } else { + executing = true; + context.newBodyInvoker() + .withCallback(new Callback(id,step)) + .start(); + } return false; // execution is asynchronous } + + @Override public void stop(Throwable cause) throws Exception { + if (task != null) { + task.cancel(false); + } + super.stop(cause); + } + + @Override public void onResume() { + if (!executing && step != null) { + // Restarted while waiting for the timer to go off. Rerun now. + getContext().newBodyInvoker().withCallback(new Callback(id, step)).start(); + executing = true; + } // otherwise we are in the middle of the body already, so let it run + } + + private static void retry(final String id, final StepContext context) { + StepExecution.applyAll(RetryStepExecution.class, new Function() { + @Override public Void apply(@Nonnull RetryStepExecution execution) { + if (execution.id.equals(id)) { + execution.retry(context); + } + return null; + } + }); + } + + private void retry(StepContext perBodyContext) { + executing = false; + getContext().saveState(); + + try { + TaskListener l = getContext().get(TaskListener.class); + if(step.left>0) { + long delay = step.getDelay().computeRetryDelay(); + l.getLogger().println( + "Will try again after " + + Util.getTimeSpanString(delay)); + task = Timer.get().schedule(new Runnable() { + @Override public void run() { + task = null; + try { + l.getLogger().println("Retrying"); + } catch (Exception x) { + getContext().onFailure(x); + return; + } + getContext().newBodyInvoker().withCallback(new Callback(id,step)).start(); + executing = true; + } + }, delay, TimeUnit.MILLISECONDS); + } + } catch (Throwable p) { + getContext().onFailure(p); + } + } - @Override public void onResume() {} + @Override public String getStatus() { + if (executing) { + return "running body"; + } else if (task == null) { + return "no body, no task, not sure what happened"; + } else if (task.isDone()) { + return "scheduled task is done, but no body"; + } else if (task.isCancelled()) { + return "scheduled task was cancelled"; + } else { + return "waiting to rerun; next recurrence period will be calculated ms " + + "during the next run." ; + } + } private static class Callback extends BodyExecutionCallback { + private final RetryStep step; private int left; + private final String id; + @Deprecated Callback(int count) { left = count; + this.step = null; + this.id = "-1"; } - /* Could be added, but seems unnecessary, given the message already printed in onFailure: - @Override public void onStart(StepContext context) { - try { - context.get(TaskListener.class).getLogger().println(left + " tries left"); - } catch (Exception x) { - context.onFailure(x); - } + Callback(String id, RetryStep step) { + this.id = id; + this.step = step; + left = step.getCount(); } - */ @Override public void onSuccess(StepContext context, Object result) { @@ -61,8 +163,15 @@ public void onFailure(StepContext context, Throwable t) { context.onFailure(t); return; } - left--; - if (left>0) { + int remaining = 0; + if(step != null) { + step.left--; + remaining = step.left; + } else { + left--; + remaining = left; + } + if (remaining>0) { TaskListener l = context.get(TaskListener.class); if (t instanceof AbortException) { l.error(t.getMessage()); @@ -72,8 +181,12 @@ public void onFailure(StepContext context, Throwable t) { } else { Functions.printStackTrace(t, l.error("Execution failed")); } - l.getLogger().println("Retrying"); - context.newBodyInvoker().withCallback(this).start(); + if(step != null && step.getDelay() == null) { + l.getLogger().println("Retrying"); + context.newBodyInvoker().withCallback(this).start(); + } else { + RetryStepExecution.retry(id, context); + } } else { // No need to print anything in this case, since it will be thrown up anyway. context.onFailure(t); diff --git a/src/main/java/org/jenkinsci/plugins/workflow/support/steps/retry/ExponentialDelay.java b/src/main/java/org/jenkinsci/plugins/workflow/support/steps/retry/ExponentialDelay.java new file mode 100644 index 00000000..73c679ed --- /dev/null +++ b/src/main/java/org/jenkinsci/plugins/workflow/support/steps/retry/ExponentialDelay.java @@ -0,0 +1,94 @@ +package org.jenkinsci.plugins.workflow.support.steps.retry; + +import java.io.Serializable; + +import javax.annotation.Nonnull; + +import org.jenkinsci.Symbol; +import org.kohsuke.stapler.DataBoundConstructor; + +import hudson.Extension; + +/** + * {@link ExponentialDelay} allows the delay to exponentially grow + * larger for issues that cannot be immediately fixed. The delay + * starts out at the min and then increases 2^x * 1 {@link java.util.concurrent.TimeUnit} + * each round after until it reaches the max, then the max for the + * remaining rounds. + */ +@Extension +public class ExponentialDelay extends RetryDelay implements Serializable { + + private static final int base = 2; + + private final long min; + private final long max; + private final int multiplier; + private int lastMultiplier = 0; + + public ExponentialDelay() { + super(); + this.multiplier = 0; + this.max = 0; + this.min = 0; + } + + @DataBoundConstructor + public ExponentialDelay(int multiplier, long min, long max) { + this.multiplier = multiplier; + this.max = max; + this.min = min; + } + + public int getMultiplier() { + return multiplier; + } + + public long getMin() { + return min; + } + + public long getMax() { + return max; + } + + @Override + public long computeRetryDelay() { + if(lastMultiplier > 0) { + lastMultiplier += 1; + } else { + lastMultiplier = multiplier; + } + long delay = powerN(base, lastMultiplier) + min; + + // Check to see if greater than max + if(delay > max) { + delay = max; + } + return unit.toMillis(delay); + } + + protected static long powerN(long number, int power){ + long res = 1; + long sq = number; + while(power > 0){ + if(power % 2 == 1){ + res *= sq; + } + sq = sq * sq; + power /= 2; + } + return res; + } + + @Extension @Symbol("exponential") + public static class DescriptorImpl extends RetryDelayDescriptor { + @Override + @Nonnull + public String getDisplayName() { + return "Exponential"; + } + private static final long serialVersionUID = 1L; + } + private static final long serialVersionUID = 1L; +} \ No newline at end of file diff --git a/src/main/java/org/jenkinsci/plugins/workflow/support/steps/retry/FixedDelay.java b/src/main/java/org/jenkinsci/plugins/workflow/support/steps/retry/FixedDelay.java new file mode 100644 index 00000000..9b628c8f --- /dev/null +++ b/src/main/java/org/jenkinsci/plugins/workflow/support/steps/retry/FixedDelay.java @@ -0,0 +1,46 @@ +package org.jenkinsci.plugins.workflow.support.steps.retry; + +import java.io.Serializable; + +import javax.annotation.Nonnull; + +import org.jenkinsci.Symbol; +import org.kohsuke.stapler.DataBoundConstructor; + +import hudson.Extension; + +@Extension +public class FixedDelay extends RetryDelay implements Serializable { + + private final long time; + + public FixedDelay() { + super(); + this.time = 0; + } + + @DataBoundConstructor + public FixedDelay(long time) { + this.time = time; + } + + public long getTime() { + return time; + } + + @Override + public long computeRetryDelay() { + return unit.toMillis(time); + } + + @Extension @Symbol("fixed") + public static class DescriptorImpl extends RetryDelayDescriptor { + @Override + @Nonnull + public String getDisplayName() { + return "Fixed"; + } + private static final long serialVersionUID = 1L; + } + private static final long serialVersionUID = 1L; +} \ No newline at end of file diff --git a/src/main/java/org/jenkinsci/plugins/workflow/support/steps/retry/IncrementalDelay.java b/src/main/java/org/jenkinsci/plugins/workflow/support/steps/retry/IncrementalDelay.java new file mode 100644 index 00000000..e6f3b702 --- /dev/null +++ b/src/main/java/org/jenkinsci/plugins/workflow/support/steps/retry/IncrementalDelay.java @@ -0,0 +1,80 @@ +package org.jenkinsci.plugins.workflow.support.steps.retry; + +import java.io.Serializable; + +import javax.annotation.Nonnull; + +import org.jenkinsci.Symbol; +import org.kohsuke.stapler.DataBoundConstructor; + +import hudson.Extension; + +/** + * {@link IncrementalDelay} allows the delay to gradually get + * larger for issues that cannot be immediately fixed. The delay + * starts out at the min and then each round after that the + * increment is added to it until it reaches the max, and then + * the max for the remaining rounds. + */ +@Extension +public class IncrementalDelay extends RetryDelay implements Serializable { + + private final long min; + private final long max; + private final long increment; + private long lastDelay = 0; + + public IncrementalDelay() { + super(); + this.increment = 0; + this.max = 0; + this.min = 0; + } + + @DataBoundConstructor + public IncrementalDelay(long increment, long min, long max) { + this.increment = increment; + this.max = max; + this.min = min; + } + + public long getIncrement() { + return increment; + } + + public long getMin() { + return min; + } + + public long getMax() { + return max; + } + + @Override + public long computeRetryDelay() { + long delay = min; + if(lastDelay == 0) { + lastDelay = min; + } else { + delay = lastDelay; + lastDelay = delay + increment; + } + + // Check to see if greater than max + if(lastDelay > max) { + lastDelay = max; + } + return unit.toMillis(lastDelay); + } + + @Extension @Symbol("incremental") + public static class DescriptorImpl extends RetryDelayDescriptor { + @Override + @Nonnull + public String getDisplayName() { + return "Incremental"; + } + private static final long serialVersionUID = 1L; + } + private static final long serialVersionUID = 1L; +} \ No newline at end of file diff --git a/src/main/java/org/jenkinsci/plugins/workflow/support/steps/retry/RandomDelay.java b/src/main/java/org/jenkinsci/plugins/workflow/support/steps/retry/RandomDelay.java new file mode 100644 index 00000000..1919c3b4 --- /dev/null +++ b/src/main/java/org/jenkinsci/plugins/workflow/support/steps/retry/RandomDelay.java @@ -0,0 +1,66 @@ +package org.jenkinsci.plugins.workflow.support.steps.retry; + +import java.io.Serializable; +import java.util.Random; + +import javax.annotation.Nonnull; + +import org.jenkinsci.Symbol; +import org.kohsuke.stapler.DataBoundConstructor; + +import hudson.Extension; + +/** + * {@link RandomDelay} allows the delay to vary between the min and max range. + */ +@Extension +public class RandomDelay extends RetryDelay implements Serializable { + + private static final Random RANDOM = new Random(); + + private final int min; + private final int max; + + public RandomDelay() { + super(); + this.max = 0; + this.min = 0; + } + + @DataBoundConstructor + public RandomDelay(int min, int max) { + this.max = max; + this.min = min; + } + + public long getMin() { + return min; + } + + public long getMax() { + return max; + } + + @Override + public long computeRetryDelay() { + long delay; + if(min == max) { + delay = min; + } else { + delay = RANDOM.nextInt(max-min) + min; + } + + return unit.toMillis(delay); + } + + @Extension @Symbol("random") + public static class DescriptorImpl extends RetryDelayDescriptor { + @Override + @Nonnull + public String getDisplayName() { + return "Random"; + } + private static final long serialVersionUID = 1L; + } + private static final long serialVersionUID = 1L; +} \ No newline at end of file diff --git a/src/main/java/org/jenkinsci/plugins/workflow/support/steps/retry/RandomExponentialDelay.java b/src/main/java/org/jenkinsci/plugins/workflow/support/steps/retry/RandomExponentialDelay.java new file mode 100644 index 00000000..9b842714 --- /dev/null +++ b/src/main/java/org/jenkinsci/plugins/workflow/support/steps/retry/RandomExponentialDelay.java @@ -0,0 +1,83 @@ +package org.jenkinsci.plugins.workflow.support.steps.retry; + +import java.io.Serializable; +import java.util.Random; + +import javax.annotation.Nonnull; + +import org.jenkinsci.Symbol; +import org.kohsuke.stapler.DataBoundConstructor; + +import hudson.Extension; + +/** + * {@link RandomExponentialDelay} The delay starts out at the + * 2^0 {@link java.util.concurrent.TimeUnit} and then increases 2^x * 1 {@link java.util.concurrent.TimeUnit} + * each round after until it reaches the max, then each round after + * it reaches the max, the delay is a random number between 1 and max. + */ +@Extension +public class RandomExponentialDelay extends RetryDelay implements Serializable { + + private static final int base = 2; + private static final Random RANDOM = new Random(); + + private final int multiplier; + private final int max; + private boolean largerThanMax = false; + private int lastMultiplier = 0; + + public RandomExponentialDelay() { + super(); + this.multiplier = 0; + this.max = 0; + } + + @DataBoundConstructor + public RandomExponentialDelay(int multiplier, int max) { + this.multiplier = multiplier; + this.max = max; + } + + public int getMultiplier() { + return multiplier; + } + + public long getMax() { + return max; + } + + @Override + public long computeRetryDelay() { + long delay; + if(largerThanMax) { + delay = 1 + RANDOM.nextInt(max); + } else { + if(lastMultiplier > 0) { + lastMultiplier += 1; + } else { + lastMultiplier = multiplier; + } + delay = ExponentialDelay.powerN(base, lastMultiplier); + + // Check to see if greater than max + if(delay > max) { + delay = max; + largerThanMax = true; + } + } + + return unit.toMillis(delay); + } + + @Extension @Symbol("randomExponential") + public static class DescriptorImpl extends RetryDelayDescriptor { + @Override + @Nonnull + public String getDisplayName() { + return "Random Exponential"; + } + private static final long serialVersionUID = 1L; + } + private static final long serialVersionUID = 1L; +} \ No newline at end of file diff --git a/src/main/java/org/jenkinsci/plugins/workflow/support/steps/retry/RetryDelay.java b/src/main/java/org/jenkinsci/plugins/workflow/support/steps/retry/RetryDelay.java new file mode 100644 index 00000000..0b8004fb --- /dev/null +++ b/src/main/java/org/jenkinsci/plugins/workflow/support/steps/retry/RetryDelay.java @@ -0,0 +1,69 @@ +package org.jenkinsci.plugins.workflow.support.steps.retry; + +import java.io.Serializable; +import java.util.Comparator; +import java.util.List; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import javax.annotation.Nonnull; + +import org.jenkinsci.plugins.structs.SymbolLookup; +import org.kohsuke.stapler.DataBoundSetter; + +import hudson.ExtensionList; +import hudson.ExtensionPoint; +import hudson.model.AbstractDescribableImpl; +import hudson.model.Descriptor; +import hudson.util.ListBoxModel; + +public abstract class RetryDelay extends AbstractDescribableImpl implements ExtensionPoint { + + @Nonnull protected TimeUnit unit = TimeUnit.SECONDS; + + @DataBoundSetter public void setUnit(@Nonnull TimeUnit unit) { + this.unit = unit; + } + + @Nonnull public TimeUnit getUnit() { + return unit; + } + + /** + * Computes the delay in MILLISECONDS + * @return Returns an integer for the time delay in MILLISECONDS + */ + public abstract long computeRetryDelay(); + + public static abstract class RetryDelayDescriptor extends Descriptor implements Serializable { + + public @Nonnull String getName() { + Set symbolValues = SymbolLookup.getSymbolValue(this); + if (symbolValues.isEmpty()) { + throw new IllegalArgumentException("Retry Delay descriptor class " + this.getClass().getName() + + " does not have a @Symbol and does not override getName()."); + } + return symbolValues.iterator().next(); + } + + /** + * Get all {@link RetryDelayDescriptor}s. + * + * @return a List of {@link RetryDelayDescriptor}s + */ + public static List all() { + ExtensionList descs = ExtensionList.lookup(RetryDelayDescriptor.class); + return descs.stream().sorted(Comparator.comparing(RetryDelayDescriptor::getName)).collect(Collectors.toList()); + } + + public ListBoxModel doFillUnitItems() { + ListBoxModel r = new ListBoxModel(); + for (TimeUnit unit : TimeUnit.values()) { + r.add(unit.name()); + } + return r; + } + private static final long serialVersionUID = 1L; + } +} \ No newline at end of file diff --git a/src/main/resources/org/jenkinsci/plugins/workflow/steps/RetryStep/config.jelly b/src/main/resources/org/jenkinsci/plugins/workflow/steps/RetryStep/config.jelly index a734bff4..0cce5faa 100644 --- a/src/main/resources/org/jenkinsci/plugins/workflow/steps/RetryStep/config.jelly +++ b/src/main/resources/org/jenkinsci/plugins/workflow/steps/RetryStep/config.jelly @@ -28,4 +28,8 @@ THE SOFTWARE. + + + + diff --git a/src/main/resources/org/jenkinsci/plugins/workflow/steps/RetryStep/help-count.html b/src/main/resources/org/jenkinsci/plugins/workflow/steps/RetryStep/help-count.html new file mode 100644 index 00000000..c6b0eca4 --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/workflow/steps/RetryStep/help-count.html @@ -0,0 +1,3 @@ +
+ The number of times to retry if an exception happens dury its body of execution. +
diff --git a/src/main/resources/org/jenkinsci/plugins/workflow/steps/RetryStep/help-useRetryDelay.html b/src/main/resources/org/jenkinsci/plugins/workflow/steps/RetryStep/help-useRetryDelay.html new file mode 100644 index 00000000..4ed1dac9 --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/workflow/steps/RetryStep/help-useRetryDelay.html @@ -0,0 +1,3 @@ +
+ Use a time delay in between retries. +
diff --git a/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/ExponentialDelay/config.jelly b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/ExponentialDelay/config.jelly new file mode 100644 index 00000000..e3227ec8 --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/ExponentialDelay/config.jelly @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/ExponentialDelay/help-max.html b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/ExponentialDelay/help-max.html new file mode 100644 index 00000000..dbe59fc3 --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/ExponentialDelay/help-max.html @@ -0,0 +1,3 @@ +
+ The maximum time delay that will be used in between retries. The default unit is 'SECONDS'. +
diff --git a/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/ExponentialDelay/help-min.html b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/ExponentialDelay/help-min.html new file mode 100644 index 00000000..42b1424e --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/ExponentialDelay/help-min.html @@ -0,0 +1,3 @@ +
+ The minimum time delay that will be used in between retries. The default unit is 'SECONDS'. +
diff --git a/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/ExponentialDelay/help-multiplier.html b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/ExponentialDelay/help-multiplier.html new file mode 100644 index 00000000..3bf6a749 --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/ExponentialDelay/help-multiplier.html @@ -0,0 +1,3 @@ +
+ The exponent for calculate the additional delay in 2^x * 1 TimeUnit +
diff --git a/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/ExponentialDelay/help-unit.html b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/ExponentialDelay/help-unit.html new file mode 100644 index 00000000..7501d2e5 --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/ExponentialDelay/help-unit.html @@ -0,0 +1,3 @@ +
+ The unit of time to be applied to the time delay. +
diff --git a/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/ExponentialDelay/help.html b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/ExponentialDelay/help.html new file mode 100644 index 00000000..1861f9ef --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/ExponentialDelay/help.html @@ -0,0 +1,15 @@ +
+ Exponential Delay allows the delay between retries to exponentially grow + larger for issues that cannot be immediately fixed. The delay starts out + at the min and then increases 2^x * 1 TimeUnit each round + after until it reaches the max, then the max for the remaining rounds. + An example of the exponential(multiplier, min, max, unit) + usage in a pipeline retry step looks like: + + retry(count: 4, + delay: exponential(multiplier: 2, min: 10, max: 50000, unit: 'SECONDS'), + useRetryDelay: true) { + error("oops") + } + +
diff --git a/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/FixedDelay/config.jelly b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/FixedDelay/config.jelly new file mode 100644 index 00000000..c32c7db2 --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/FixedDelay/config.jelly @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/FixedDelay/help-time.html b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/FixedDelay/help-time.html new file mode 100644 index 00000000..d5bb6757 --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/FixedDelay/help-time.html @@ -0,0 +1,3 @@ +
+ The time delay that will be used in between retries. The default unit is 'SECONDS'. +
diff --git a/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/FixedDelay/help-unit.html b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/FixedDelay/help-unit.html new file mode 100644 index 00000000..7501d2e5 --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/FixedDelay/help-unit.html @@ -0,0 +1,3 @@ +
+ The unit of time to be applied to the time delay. +
diff --git a/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/FixedDelay/help.html b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/FixedDelay/help.html new file mode 100644 index 00000000..8cbe8df4 --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/FixedDelay/help.html @@ -0,0 +1,12 @@ +
+ Fixed Delay is a Retry Delay option that allows the you to configure the Retry + so that there is a fixed amount of time TimeUnitbefore the system + tries to execute an action in which an exception was thrown in the previous + execution. An example of the usage of fixed(delay,unit) in a + pipeline retry step looks like: + + retry(count: 4, delay: fixed(time: 2, unit: 'SECONDS'), useRetryDelay: true) { + error("oops") + } + +
diff --git a/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/IncrementalDelay/config.jelly b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/IncrementalDelay/config.jelly new file mode 100644 index 00000000..37d19b0b --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/IncrementalDelay/config.jelly @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/IncrementalDelay/help-increment.html b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/IncrementalDelay/help-increment.html new file mode 100644 index 00000000..f17c4688 --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/IncrementalDelay/help-increment.html @@ -0,0 +1,4 @@ +
+ The amount of time in TimeUnit the delay will be increased between + each recurring retry. +
diff --git a/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/IncrementalDelay/help-max.html b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/IncrementalDelay/help-max.html new file mode 100644 index 00000000..dbe59fc3 --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/IncrementalDelay/help-max.html @@ -0,0 +1,3 @@ +
+ The maximum time delay that will be used in between retries. The default unit is 'SECONDS'. +
diff --git a/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/IncrementalDelay/help-min.html b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/IncrementalDelay/help-min.html new file mode 100644 index 00000000..42b1424e --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/IncrementalDelay/help-min.html @@ -0,0 +1,3 @@ +
+ The minimum time delay that will be used in between retries. The default unit is 'SECONDS'. +
diff --git a/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/IncrementalDelay/help-unit.html b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/IncrementalDelay/help-unit.html new file mode 100644 index 00000000..7501d2e5 --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/IncrementalDelay/help-unit.html @@ -0,0 +1,3 @@ +
+ The unit of time to be applied to the time delay. +
diff --git a/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/IncrementalDelay/help.html b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/IncrementalDelay/help.html new file mode 100644 index 00000000..e20b659a --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/IncrementalDelay/help.html @@ -0,0 +1,15 @@ +
+ Incremental Delay allows the delay to be increased by a specific amount + of time TimeUnit between reach recurring retry. The delay + starts out at the min and then increases by the increment each round + after until it reaches the max, then the max for the remaining rounds. + An example of the incremental(increment, min, max, unit) + usage in a pipeline retry step looks like: + + retry(count: 4, + delay: incremental(increment: 60, min: 10, max: 50000, unit: 'SECONDS'), + useRetryDelay: true) { + error("oops") + } + +
diff --git a/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/RandomDelay/config.jelly b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/RandomDelay/config.jelly new file mode 100644 index 00000000..d38e8eb1 --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/RandomDelay/config.jelly @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/RandomDelay/help-max.html b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/RandomDelay/help-max.html new file mode 100644 index 00000000..dbe59fc3 --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/RandomDelay/help-max.html @@ -0,0 +1,3 @@ +
+ The maximum time delay that will be used in between retries. The default unit is 'SECONDS'. +
diff --git a/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/RandomDelay/help-min.html b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/RandomDelay/help-min.html new file mode 100644 index 00000000..42b1424e --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/RandomDelay/help-min.html @@ -0,0 +1,3 @@ +
+ The minimum time delay that will be used in between retries. The default unit is 'SECONDS'. +
diff --git a/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/RandomDelay/help-unit.html b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/RandomDelay/help-unit.html new file mode 100644 index 00000000..7501d2e5 --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/RandomDelay/help-unit.html @@ -0,0 +1,3 @@ +
+ The unit of time to be applied to the time delay. +
diff --git a/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/RandomDelay/help.html b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/RandomDelay/help.html new file mode 100644 index 00000000..58156e36 --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/RandomDelay/help.html @@ -0,0 +1,16 @@ +
+ Random Delay allows for jitter between retries to help in cases where there + might be contention with external forces. A random number will be chosen + between the min and max values and will be used as the time in + TimeUnit delay. An example of the usage of + random(min, max, unit) in a + pipeline retry step looks like: + + + retry(count: 4, delay: + random(min: 4, max: 60, unit: 'SECONDS'), + useRetryDelay: true) { + error("oops") + } + +
diff --git a/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/RandomExponentialDelay/config.jelly b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/RandomExponentialDelay/config.jelly new file mode 100644 index 00000000..d0a41b3e --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/RandomExponentialDelay/config.jelly @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/RandomExponentialDelay/help-max.html b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/RandomExponentialDelay/help-max.html new file mode 100644 index 00000000..dbe59fc3 --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/RandomExponentialDelay/help-max.html @@ -0,0 +1,3 @@ +
+ The maximum time delay that will be used in between retries. The default unit is 'SECONDS'. +
diff --git a/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/RandomExponentialDelay/help-multiplier.html b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/RandomExponentialDelay/help-multiplier.html new file mode 100644 index 00000000..3bf6a749 --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/RandomExponentialDelay/help-multiplier.html @@ -0,0 +1,3 @@ +
+ The exponent for calculate the additional delay in 2^x * 1 TimeUnit +
diff --git a/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/RandomExponentialDelay/help.html b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/RandomExponentialDelay/help.html new file mode 100644 index 00000000..834f14e2 --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/workflow/support/steps/retry/RandomExponentialDelay/help.html @@ -0,0 +1,16 @@ +
+ Random Exponential Delay starts out at the 2^0 {@TimeUnit} and then + increases 2^x * 1 {@TimeUnit} each recurring failure. The x is + incremented after each round giving you larger delay until it reaches + the max delay. At that point a random number between one and max in + TimeUnit is generated for the delay foreach remaining retry. + An example of the randomExponential(multiplier, max, unit) + usage in a pipeline retry step looks like: + + retry(count: 4, + delay: randomExponential(max: 50000, multiplier: 2, unit: 'SECONDS'), + useRetryDelay: true) { + error("oops") + } + +
diff --git a/src/test/java/org/jenkinsci/plugins/workflow/steps/RetryStepTest.java b/src/test/java/org/jenkinsci/plugins/workflow/steps/RetryStepTest.java index 9191fa59..2508ec72 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/steps/RetryStepTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/steps/RetryStepTest.java @@ -225,4 +225,5 @@ public void shouldRetryAfterInnerTimeout() throws Exception { r.assertLogContains("try 1", run); r.assertLogContains("try 2", run); } + } diff --git a/src/test/java/org/jenkinsci/plugins/workflow/support/steps/retry/RetryStepDelayTest.java b/src/test/java/org/jenkinsci/plugins/workflow/support/steps/retry/RetryStepDelayTest.java new file mode 100644 index 00000000..af77c2f3 --- /dev/null +++ b/src/test/java/org/jenkinsci/plugins/workflow/support/steps/retry/RetryStepDelayTest.java @@ -0,0 +1,222 @@ +package org.jenkinsci.plugins.workflow.support.steps.retry; + +import static org.junit.Assert.assertTrue; + +import java.util.concurrent.TimeUnit; + +import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; +import org.jenkinsci.plugins.workflow.job.WorkflowJob; +import org.jenkinsci.plugins.workflow.job.WorkflowRun; +import org.jenkinsci.plugins.workflow.steps.RetryStep; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.jvnet.hudson.test.BuildWatcher; +import org.jvnet.hudson.test.JenkinsRule; + +import hudson.model.queue.QueueTaskFuture; + +/** + * Tests {@link RetryStep}. + */ +public class RetryStepDelayTest { + + @ClassRule + public static BuildWatcher buildWatcher = new BuildWatcher(); + @Rule + public JenkinsRule r = new JenkinsRule(); + + @Test + public void retryFixedDelay() throws Exception { + WorkflowJob p = r.jenkins.createProject(WorkflowJob.class, "p"); + p.setDefinition(new CpsFlowDefinition( + "int i = 0;\n" + + "retry(count: 3, delay: fixed(time: 10, unit: 'SECONDS')) {\n" + + " println 'Trying!'\n" + + " if (i++ < 2) error('oops');\n" + + " println 'Done!'\n" + + "}\n" + + "println 'Over!'" + , true)); + + long before = System.currentTimeMillis(); + QueueTaskFuture f = p.scheduleBuild2(0); + long after = System.currentTimeMillis(); + long difference = after - before; + long minimumTimeMilliseconds = TimeUnit.SECONDS.convert(20, TimeUnit.MILLISECONDS); + assertTrue(difference > minimumTimeMilliseconds); + WorkflowRun b = r.assertBuildStatusSuccess(f); + + String log = JenkinsRule.getLog(b); + r.assertLogNotContains("\tat ", b); + + int idx = 0; + for (String msg : new String[] { + "Trying!", + "oops", + "Will try again after 10 sec", + "Retrying", + "Trying!", + "oops", + "Will try again after 10 sec", + "Retrying", + "Trying!", + "Done!", + "Over!", + }) { + idx = log.indexOf(msg, idx + 1); + assertTrue(msg + " not found", idx != -1); + } + + idx = 0; + for (String msg : new String[] { + "[Pipeline] Start of Pipeline", + "[Pipeline] retry", + "[Pipeline] {", + "[Pipeline] echo", + "[Pipeline] error", + "[Pipeline] }", + "[Pipeline] {", + "[Pipeline] echo", + "[Pipeline] error", + "[Pipeline] }", + "[Pipeline] {", + "[Pipeline] echo", + "[Pipeline] echo", + "[Pipeline] }", + "[Pipeline] // retry", + }) { + idx = log.indexOf(msg, idx + 1); + assertTrue(msg + " not found", idx != -1); + } + } + + @Test + public void stackTraceOnErrorWithRetry() throws Exception { + WorkflowJob p = r.jenkins.createProject(WorkflowJob.class, "p"); + p.setDefinition( new CpsFlowDefinition( + "int i = 0\n" + + "retry(count: 2, delay: fixed(time: 5, unit: 'SECONDS')) {\n" + + " i += 1\n" + + " echo 'Try #' + i\n" + + " if (i == 1) {\n" + + " throw new Exception('foo')\n" + + " }\n" + + " echo 'Done!'\n" + + "}\n", + true)); + + WorkflowRun run = r.buildAndAssertSuccess(p); + r.assertLogContains("Try #1", run); + r.assertLogContains("ERROR: Execution failed", run); + r.assertLogContains("java.lang.Exception: foo", run); + r.assertLogContains("\tat ", run); + r.assertLogContains("Try #2", run); + r.assertLogContains("Done!", run); + } + + @Test + public void retryRandomDelay() throws Exception { + WorkflowJob p = r.jenkins.createProject(WorkflowJob.class, "p"); + p.setDefinition( new CpsFlowDefinition( + "int i = 0\n" + + "retry(count: 4, delay: random(max: 15, min: 5, unit: 'SECONDS')) {\n" + + " if (i++ < 3) {\n" + + " echo 'Try #' + i\n" + + " error('oops');\n" + + " }\n" + + " println 'Done!'\n" + + "}\n", + true)); + + WorkflowRun run = r.buildAndAssertSuccess(p); + r.assertLogContains("Try #1", run); + r.assertLogContains("ERROR: oops", run); + r.assertLogContains("Will try again after", run); + r.assertLogContains("Retrying", run); + r.assertLogContains("Try #2", run); + r.assertLogContains("Try #3", run); + r.assertLogContains("Done!", run); + } + + @Test + public void retryExponentialDelay() throws Exception { + WorkflowJob p = r.jenkins.createProject(WorkflowJob.class, "p"); + p.setDefinition( new CpsFlowDefinition( + "int i = 0\n" + + "retry(count: 4, delay: exponential(max: 20, min: 1, multiplier: 2, unit: 'SECONDS')) {\n" + + " if (i++ < 3) {\n" + + " echo 'Try #' + i\n" + + " error('oops');\n" + + " }\n" + + " println 'Done!'\n" + + "}\n", + true)); + + WorkflowRun run = r.buildAndAssertSuccess(p); + r.assertLogContains("Try #1", run); + r.assertLogContains("ERROR: oops", run); + r.assertLogContains("Will try again after 5 sec", run); + r.assertLogContains("Retrying", run); + r.assertLogContains("Try #2", run); + r.assertLogContains("Will try again after 9 sec", run); + r.assertLogContains("Try #3", run); + r.assertLogContains("Will try again after 17 sec", run); + r.assertLogContains("Done!", run); + } + + @Test + public void retryIncrementalDelay() throws Exception { + WorkflowJob p = r.jenkins.createProject(WorkflowJob.class, "p"); + p.setDefinition( new CpsFlowDefinition( + "int i = 0\n" + + "retry(count: 4, delay: incremental(increment: 2, max: 10, min: 1, unit: 'SECONDS')) {\n" + + " if (i++ < 3) {\n" + + " echo 'Try #' + i\n" + + " error('oops');\n" + + " }\n" + + " println 'Done!'\n" + + "}\n", + true)); + + WorkflowRun run = r.buildAndAssertSuccess(p); + r.assertLogContains("Try #1", run); + r.assertLogContains("ERROR: oops", run); + r.assertLogContains("Will try again after 1 sec", run); + r.assertLogContains("Retrying", run); + r.assertLogContains("Try #2", run); + r.assertLogContains("Will try again after 3 sec", run); + r.assertLogContains("Try #3", run); + r.assertLogContains("Will try again after 5 sec", run); + r.assertLogContains("Done!", run); + } + + @Test + public void retryRandomExponentialDelay() throws Exception { + WorkflowJob p = r.jenkins.createProject(WorkflowJob.class, "p"); + p.setDefinition( new CpsFlowDefinition( + "int i = 0\n" + + "retry(count: 6, delay: randomExponential(max: 10, multiplier: 2, unit: 'SECONDS')) {\n" + + " if (i++ < 5) {\n" + + " echo 'Try #' + i\n" + + " error('oops');\n" + + " }\n" + + " println 'Done!'\n" + + "}\n", + true)); + + WorkflowRun run = r.buildAndAssertSuccess(p); + + r.assertLogContains("Try #1", run); + r.assertLogContains("ERROR: oops", run); + r.assertLogContains("Will try again after 4 sec", run); + r.assertLogContains("Retrying", run); + r.assertLogContains("Try #2", run); + r.assertLogContains("Will try again after 8 sec", run); + r.assertLogContains("Try #3", run); + r.assertLogContains("Will try again after 10 sec", run); + r.assertLogContains("Try #4", run); + r.assertLogContains("Try #5", run); + r.assertLogContains("Done!", run); + } +} \ No newline at end of file diff --git a/src/test/resources/org/jenkinsci/plugins/workflow/support/RetryStepDelayTest/pipelineOptionsRetryExponentialDelay.groovy b/src/test/resources/org/jenkinsci/plugins/workflow/support/RetryStepDelayTest/pipelineOptionsRetryExponentialDelay.groovy new file mode 100644 index 00000000..0d1c8da6 --- /dev/null +++ b/src/test/resources/org/jenkinsci/plugins/workflow/support/RetryStepDelayTest/pipelineOptionsRetryExponentialDelay.groovy @@ -0,0 +1,19 @@ +pipeline { + agent any + options { + retry(count: 4, delay: exponential(max: 20, min: 1, multiplier: 2, unit: 'SECONDS')) + } + stages { + stage('x') { + steps { + echo 'Trying!' + error('oops') + } + } + } + post { + always { + echo 'Done!' + } + } +} \ No newline at end of file diff --git a/src/test/resources/org/jenkinsci/plugins/workflow/support/RetryStepDelayTest/pipelineOptionsRetryFixedDelay.groovy b/src/test/resources/org/jenkinsci/plugins/workflow/support/RetryStepDelayTest/pipelineOptionsRetryFixedDelay.groovy new file mode 100644 index 00000000..f0dd199f --- /dev/null +++ b/src/test/resources/org/jenkinsci/plugins/workflow/support/RetryStepDelayTest/pipelineOptionsRetryFixedDelay.groovy @@ -0,0 +1,19 @@ +pipeline { + agent any + options { + retry(count: 3, delay: fixed(time: 10, unit: 'SECONDS')) + } + stages { + stage('x') { + steps { + echo 'Trying!' + error('oops') + } + } + } + post { + always { + echo 'Done!' + } + } +} \ No newline at end of file diff --git a/src/test/resources/org/jenkinsci/plugins/workflow/support/RetryStepDelayTest/pipelineOptionsRetryRandomExponentialDelay.groovy b/src/test/resources/org/jenkinsci/plugins/workflow/support/RetryStepDelayTest/pipelineOptionsRetryRandomExponentialDelay.groovy new file mode 100644 index 00000000..f0dd199f --- /dev/null +++ b/src/test/resources/org/jenkinsci/plugins/workflow/support/RetryStepDelayTest/pipelineOptionsRetryRandomExponentialDelay.groovy @@ -0,0 +1,19 @@ +pipeline { + agent any + options { + retry(count: 3, delay: fixed(time: 10, unit: 'SECONDS')) + } + stages { + stage('x') { + steps { + echo 'Trying!' + error('oops') + } + } + } + post { + always { + echo 'Done!' + } + } +} \ No newline at end of file diff --git a/src/test/resources/org/jenkinsci/plugins/workflow/support/RetryStepDelayTest/pipelineRetryStepFixedDelay.groovy b/src/test/resources/org/jenkinsci/plugins/workflow/support/RetryStepDelayTest/pipelineRetryStepFixedDelay.groovy new file mode 100644 index 00000000..98069380 --- /dev/null +++ b/src/test/resources/org/jenkinsci/plugins/workflow/support/RetryStepDelayTest/pipelineRetryStepFixedDelay.groovy @@ -0,0 +1,18 @@ +pipeline { + agent any + stages { + stage('x') { + steps { + retry(count: 3, delay: fixed(time: 10, unit: 'SECONDS')) { + echo 'Trying!' + error('oops') + } + } + } + } + post { + always { + echo 'Done!' + } + } +} \ No newline at end of file diff --git a/src/test/resources/org/jenkinsci/plugins/workflow/support/RetryStepDelayTest/pipelineRetryStepIncrementalDelay.groovy b/src/test/resources/org/jenkinsci/plugins/workflow/support/RetryStepDelayTest/pipelineRetryStepIncrementalDelay.groovy new file mode 100644 index 00000000..24813a04 --- /dev/null +++ b/src/test/resources/org/jenkinsci/plugins/workflow/support/RetryStepDelayTest/pipelineRetryStepIncrementalDelay.groovy @@ -0,0 +1,18 @@ +pipeline { + agent any + stages { + stage('x') { + steps { + retry(count: 4, delay: incremental(increment: 2, max: 10, min: 1, unit: 'SECONDS')) { + echo 'Trying!' + error('oops') + } + } + } + } + post { + always { + echo 'Done!' + } + } +} \ No newline at end of file diff --git a/src/test/resources/org/jenkinsci/plugins/workflow/support/RetryStepDelayTest/stageOptionsRetryStepFixedDelay.groovy b/src/test/resources/org/jenkinsci/plugins/workflow/support/RetryStepDelayTest/stageOptionsRetryStepFixedDelay.groovy new file mode 100644 index 00000000..37892bb3 --- /dev/null +++ b/src/test/resources/org/jenkinsci/plugins/workflow/support/RetryStepDelayTest/stageOptionsRetryStepFixedDelay.groovy @@ -0,0 +1,19 @@ +pipeline { + agent any + stages { + stage('x') { + options { + retry(count: 3, delay: fixed(time: 10, unit: 'SECONDS')) + } + steps { + echo 'Trying!' + error('oops') + } + } + } + post { + always { + echo 'Done!' + } + } +} \ No newline at end of file diff --git a/src/test/resources/org/jenkinsci/plugins/workflow/support/RetryStepDelayTest/stageOptionsRetryStepRandomDelay.groovy b/src/test/resources/org/jenkinsci/plugins/workflow/support/RetryStepDelayTest/stageOptionsRetryStepRandomDelay.groovy new file mode 100644 index 00000000..1ebeb6c9 --- /dev/null +++ b/src/test/resources/org/jenkinsci/plugins/workflow/support/RetryStepDelayTest/stageOptionsRetryStepRandomDelay.groovy @@ -0,0 +1,19 @@ +pipeline { + agent any + stages { + stage('x') { + options { + retry(count: 4, delay: random(max: 15, min: 5, unit: 'SECONDS')) + } + steps { + echo 'Trying!' + error('oops') + } + } + } + post { + always { + echo 'Done!' + } + } +} \ No newline at end of file