package hudson.plugins.build_timeout.impl; import static hudson.plugins.build_timeout.BuildTimeoutWrapper.MINIMUM_TIMEOUT_MILLISECONDS; import hudson.Extension; import hudson.model.AbstractBuild; import hudson.model.BuildListener; import hudson.model.Descriptor; import hudson.model.Result; import hudson.model.Run; import hudson.plugins.build_timeout.BuildTimeOutStrategy; import hudson.plugins.build_timeout.BuildTimeOutStrategyDescriptor; import hudson.util.ListBoxModel; import org.jenkinsci.plugins.tokenmacro.MacroEvaluationException; import org.kohsuke.stapler.DataBoundConstructor; import java.io.IOException; public class ElasticTimeOutStrategy extends BuildTimeOutStrategy { private final String timeoutPercentage; private final String numberOfBuilds; private final boolean failSafeTimeoutDuration; /** * The timeout to use if there are no valid builds in the build * history (ie, no successful or unstable builds) */ private final String timeoutMinutesElasticDefault; /** * @return how long percentage of the average duration to timeout. */ public String getTimeoutPercentage() { return timeoutPercentage; } /** * @return the number of last builds to use to calculate average duration. */ public String getNumberOfBuilds() { return numberOfBuilds; } /** * @return the default minutes to timeout used when failed to calculate average duration */ public String getTimeoutMinutesElasticDefault() { return timeoutMinutesElasticDefault; } /** * @return if fail-safe timeout needs to be used */ public boolean isFailSafeTimeoutDuration() { return failSafeTimeoutDuration; } @Deprecated public ElasticTimeOutStrategy(int timeoutPercentage, int timeoutMinutesElasticDefault, int numberOfBuilds) { this(Integer.toString(timeoutPercentage), Integer.toString(timeoutMinutesElasticDefault), Integer.toString(numberOfBuilds), false); } @Deprecated public ElasticTimeOutStrategy(String timeoutPercentage, String timeoutMinutesElasticDefault, String numberOfBuilds) { this(timeoutPercentage, timeoutMinutesElasticDefault, numberOfBuilds, false); } @DataBoundConstructor public ElasticTimeOutStrategy(String timeoutPercentage, String timeoutMinutesElasticDefault, String numberOfBuilds, boolean failSafeTimeoutDuration) { this.timeoutPercentage = timeoutPercentage; this.timeoutMinutesElasticDefault = timeoutMinutesElasticDefault; this.numberOfBuilds = numberOfBuilds; this.failSafeTimeoutDuration = failSafeTimeoutDuration; } @Override public long getTimeOut(AbstractBuild<?, ?> build, BuildListener listener) throws InterruptedException, MacroEvaluationException, IOException { double elasticTimeout = getElasticTimeout(Integer.parseInt(expandAll(build,listener,getTimeoutPercentage())), build, listener); if (elasticTimeout == 0) { return Math.max(MINIMUM_TIMEOUT_MILLISECONDS, Integer.parseInt(expandAll(build, listener, getTimeoutMinutesElasticDefault())) * MINUTES); } else { if (isFailSafeTimeoutDuration()) { return Math.max(Integer.parseInt(expandAll(build, listener, getTimeoutMinutesElasticDefault())) * MINUTES, (long) elasticTimeout); } else { return (long) Math.max(MINIMUM_TIMEOUT_MILLISECONDS, elasticTimeout); } } } private double getElasticTimeout(int timeoutPercentage, AbstractBuild<?, ?> build, BuildListener listener) throws InterruptedException, MacroEvaluationException, IOException { return timeoutPercentage * .01D * (timeoutPercentage > 0 ? averageDuration(build,listener) : 0); } private double averageDuration(AbstractBuild<?, ?> build, BuildListener listener) throws InterruptedException, MacroEvaluationException, IOException { int nonFailingBuilds = 0; int durationSum = 0; int numberOfBuilds = Integer.parseInt(expandAll(build, listener, getNumberOfBuilds())); while(build.getPreviousBuild() != null && nonFailingBuilds < numberOfBuilds) { build = build.getPreviousBuild(); if (build.getResult() != null && build.getResult().isBetterOrEqualTo(Result.UNSTABLE)) { durationSum += build.getDuration(); nonFailingBuilds++; } } return nonFailingBuilds > 0 ? ((double)durationSum) / nonFailingBuilds : 0; } public Descriptor<BuildTimeOutStrategy> getDescriptor() { return DESCRIPTOR; } @Extension public static final DescriptorImpl DESCRIPTOR = new DescriptorImpl(); public static class DescriptorImpl extends BuildTimeOutStrategyDescriptor { @Override public String getDisplayName() { return Messages.ElasticTimeOutStrategy_DisplayName(); } public int[] getPercentages() { return new int[] {150,200,250,300,350,400}; } public ListBoxModel doFillTimeoutPercentageItems() { ListBoxModel m = new ListBoxModel(); for (int option : getPercentages()) { String s = String.valueOf(option); m.add(s + "%", s); } return m; } } }