@Override public String getUrlName() { return isRestartEnabled() ? "restart" : null; }
@Override public String getDisplayName() { return Messages.Restart_from_Stage(); }
/** * Alternative constructor for copying an existing {@link DeclarativeJobPropertyTrackerAction}'s contents directly. * * @param copyFrom a non-null {@link DeclarativeJobPropertyTrackerAction} */ public DeclarativeJobPropertyTrackerAction(@Nonnull DeclarativeJobPropertyTrackerAction copyFrom) { this.jobProperties.addAll(copyFrom.getJobProperties()); this.triggers.addAll(copyFrom.getTriggers()); this.parameters.addAll(copyFrom.getParameters()); this.options.addAll(copyFrom.getOptions()); }
public Queue.Item run(String stageName) { if (stageName == null || stageName.equals("")) { throw new IllegalStateException(Messages.RestartDeclarativePipelineAction_NullStageName()); } if (!run.hasPermission(Item.BUILD) || !run.getParent().isBuildable()) { throw new IllegalStateException(Messages.RestartDeclarativePipelineAction_ProjectNotBuildable(run.getParent().getFullName())); } if (run.isBuilding()) { throw new IllegalStateException(Messages.RestartDeclarativePipelineAction_OriginRunIncomplete(run.getFullDisplayName())); } ExecutionModelAction execAction = run.getAction(ExecutionModelAction.class); if (execAction == null) { throw new IllegalStateException(Messages.RestartDeclarativePipelineAction_OriginWasNotDeclarative(run.getFullDisplayName())); } if (!getRestartableStages().contains(stageName)) { throw new IllegalStateException(Messages.RestartDeclarativePipelineAction_StageNameNotPresent(stageName, run.getFullDisplayName())); } List<Action> actions = new ArrayList<>(); CpsFlowExecution execution = getExecution(); if (execution == null) { throw new IllegalStateException(Messages.RestartDeclarativePipelineAction_OriginRunMissingExecution(run.getFullDisplayName())); } actions.add(new RestartFlowFactoryAction(run.getExternalizableId())); actions.add(new CauseAction(new Cause.UserIdCause(), new RestartDeclarativePipelineCause(run, stageName))); return ParameterizedJobMixIn.scheduleBuild2(run.getParent(), 0, actions.toArray(new Action[actions.size()])); }
@Issue("JENKINS-45455") @Test public void emptyStageName() throws Exception { WorkflowRun original = expect(Result.FAILURE, "restart", "simpleRestart") .logContains("Odd numbered build, failing", "This shouldn't show up on second run") .go(); RestartDeclarativePipelineAction action = original.getAction(RestartDeclarativePipelineAction.class); assertNotNull(action); assertTrue(action.isRestartEnabled()); Exception runFailure = null; try { Queue.Item queueItem = action.run(""); // Because we should definitely have thrown an exception in run. assertNull(queueItem); } catch (Exception e) { runFailure = e; } assertNotNull(runFailure); assertTrue(runFailure instanceof IllegalStateException); assertEquals(Messages.RestartDeclarativePipelineAction_NullStageName(), runFailure.getMessage()); }
@Issue("JENKINS-45455") @Test public void notPresentStageName() throws Exception { WorkflowRun original = expect(Result.FAILURE, "restart", "simpleRestart") .logContains("Odd numbered build, failing", "This shouldn't show up on second run") .go(); RestartDeclarativePipelineAction action = original.getAction(RestartDeclarativePipelineAction.class); assertNotNull(action); assertTrue(action.isRestartEnabled()); Exception runFailure = null; try { Queue.Item queueItem = action.run("not-present"); // Because we should definitely have thrown an exception in run. assertNull(queueItem); } catch (Exception e) { runFailure = e; } assertNotNull(runFailure); assertTrue(runFailure instanceof IllegalStateException); assertEquals(Messages.RestartDeclarativePipelineAction_StageNameNotPresent("not-present", original.getFullDisplayName()), runFailure.getMessage()); }
@Issue("JENKINS-45455") @Test public void projectNotBuildable() throws Exception { WorkflowRun original = expect(Result.FAILURE, "restart", "simpleRestart") .logContains("Odd numbered build, failing", "This shouldn't show up on second run") .go(); RestartDeclarativePipelineAction action = original.getAction(RestartDeclarativePipelineAction.class); assertNotNull(action); assertTrue(action.isRestartEnabled()); WorkflowJob p = original.getParent(); p.setDisabled(true); Exception runFailure = null; try { Queue.Item queueItem = action.run("restart"); // Because we should definitely have thrown an exception in run. assertNull(queueItem); } catch (Exception e) { runFailure = e; } assertNotNull(runFailure); assertTrue(runFailure instanceof IllegalStateException); assertEquals(Messages.RestartDeclarativePipelineAction_ProjectNotBuildable(p.getFullName()), runFailure.getMessage()); }
public FormValidation doCheckStageName(@QueryParameter String value) { String s = Util.fixEmptyAndTrim(value); if (s == null || s.equals("")) { return FormValidation.error(Messages.RestartDeclarativePipelineAction_NullStageName()); } if (!getRestartableStages().contains(s)) { return FormValidation.error(Messages.RestartDeclarativePipelineAction_StageNameNotPresent(s, run.getFullDisplayName())); } return FormValidation.ok(); }
@Override protected int run() throws Exception { Run<?,?> run = number <= 0 ? job.getLastBuild() : job.getBuildByNumber(number); if (run == null) { throw new AbortException("No such build"); } RestartDeclarativePipelineAction action = run.getAction(RestartDeclarativePipelineAction.class); if (action == null) { throw new AbortException("Not a Declarative Pipeline build"); } if (!action.isRestartEnabled()) { throw new AbortException("Not authorized to restart builds of this job"); } if (!action.getRestartableStages().contains(stage)) { throw new AbortException("Stage " + stage + " either does not exist in the build, or did not run in the build, and so cannot be restarted."); } action.run(stage); return 0; }
@Restricted(NoExternalUse.class) @RequirePOST public HttpResponse doRestartPipeline(@QueryParameter String stageName) { Map<String, String> result = new HashMap<>(); result.put("success", "false"); if (isRestartEnabled()) { try { run(stageName); result.put("success", "true"); result.put("message", "ok"); } catch (IllegalStateException ise) { result.put("message", "Failure restarting from stage: " + ise); } } else { result.put("message", "not allowed to restart"); } return HttpResponses.okJSON(result); }
private HtmlPage restartFromStageInUI(@Nonnull WorkflowRun original, @Nonnull String stageName) throws Exception { RestartDeclarativePipelineAction action = original.getAction(RestartDeclarativePipelineAction.class); assertNotNull(action); assertTrue(action.isRestartEnabled()); HtmlPage page = j.createWebClient().getPage(original, action.getUrlName()); HtmlForm form = page.getFormByName("restart"); HtmlSelect select = form.getSelectByName("stageName"); select.getOptionByValue(stageName).setSelected(true); return j.submit(form); }
@Exported public List<String> getRestartableStages() { List<String> stages = new ArrayList<>(); FlowExecution execution = getExecution(); if (execution != null) { ExecutionModelAction execAction = run.getAction(ExecutionModelAction.class); if (execAction != null) { if (execAction.getStages() != null) { for (ModelASTStage s : execAction.getStages().getStages()) { if (!Utils.stageHasStatusOf(s.getName(), execution, StageStatus.getSkippedForFailure(), StageStatus.getSkippedForUnstable())) { stages.add(s.getName()); } } } } } return stages; }
public String getCheckUrl() { return Jenkins.getInstance().getRootUrl() + run.getUrl() + getUrlName() + "/" + "checkStageName"; }
@Override @Nonnull public Collection<? extends Action> createFor(@Nonnull WorkflowRun run) { return Collections.<Action>singleton(new RestartDeclarativePipelineAction(run)); } }
@Exported public boolean isRestartEnabled() { ExecutionModelAction executionModelAction = run.getAction(ExecutionModelAction.class); return executionModelAction != null && !run.isBuilding() && run.hasPermission(Item.BUILD) && run.getParent().isBuildable() && getExecution() != null; }
@Issue("JENKINS-45455") @Test public void actionPresentOnDeclarative() throws Exception { WorkflowRun r = expect("simplePipeline").go(); assertNotNull(r.getAction(RestartDeclarativePipelineAction.class)); }
@Issue("JENKINS-45455") @Test public void nullStageName() throws Exception { WorkflowRun original = expect(Result.FAILURE, "restart", "simpleRestart") .logContains("Odd numbered build, failing", "This shouldn't show up on second run") .go(); RestartDeclarativePipelineAction action = original.getAction(RestartDeclarativePipelineAction.class); assertNotNull(action); assertTrue(action.isRestartEnabled()); Exception runFailure = null; try { Queue.Item queueItem = action.run(null); // Because we should definitely have thrown an exception in run. assertNull(queueItem); } catch (Exception e) { runFailure = e; } assertNotNull(runFailure); assertTrue(runFailure instanceof IllegalStateException); assertEquals(Messages.RestartDeclarativePipelineAction_NullStageName(), runFailure.getMessage()); }
@Restricted(DoNotUse.class) @RequirePOST public void doRestart(StaplerRequest req, StaplerResponse rsp) throws ServletException, IOException { if (!isRestartEnabled()) { throw new AccessDeniedException("not allowed to restart"); // AccessDeniedException2 requires us to look up the specific Permission } JSONObject form = req.getSubmittedForm(); String stageName = Util.fixEmpty(form.getString("stageName")); try { run(stageName); } catch (IllegalStateException ise) { throw new Failure("Failure restarting from stage: " + ise); } rsp.sendRedirect("../.."); // back to WorkflowJob; new build might not start instantly so cannot redirect to it }
@Override public String getIconFileName() { return isRestartEnabled() ? "plugin/pipeline-model-definition/images/24x24/restart-stage.png" : null; }
@Override public Boolean call() throws RuntimeException { return a.isRestartEnabled(); } });