Skip to content

Commit

Permalink
[JENKINS-22068] Fail promotion builds instead of hanging when a promo…
Browse files Browse the repository at this point in the history
…tion is built/rebuilt directly (#107)

* Prevent RebuildAction from being added to future promotions

* Remove deserialized RebuildActions from Promotions

* Make promotion builds fail instead of hang when there is no target, and keep promotions from being rebuilt directly

* Keep related code grouped together

* Add test for PromotionRebuildValidator

* Add logging to post2 and move null check to start of method

* Add test asserting that rebuilding a promotion directly throws a 404
  • Loading branch information
dwnusbaum authored and oleg-nenashev committed Oct 19, 2017
1 parent 14cc62d commit 043d1a5
Show file tree
Hide file tree
Showing 5 changed files with 196 additions and 3 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Expand Up @@ -79,7 +79,7 @@
<dependency>
<groupId>com.sonyericsson.hudson.plugins.rebuild</groupId>
<artifactId>rebuild</artifactId>
<version>1.22</version>
<version>1.25</version>
<optional>true</optional>
</dependency>
<dependency>
Expand Down
21 changes: 19 additions & 2 deletions src/main/java/hudson/plugins/promoted_builds/Promotion.java
Expand Up @@ -55,6 +55,9 @@
import javax.annotation.Nonnull;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.stapler.HttpResponses;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;

/**
* Records a promotion process.
Expand Down Expand Up @@ -84,7 +87,7 @@ public Promotion(PromotionProcess project, File buildDir) throws IOException {
@Exported
public AbstractBuild<?,?> getTarget() {
PromotionTargetAction pta = getAction(PromotionTargetAction.class);
return pta.resolve(this);
return pta == null ? null : pta.resolve(this);
}

@Override public AbstractBuild<?,?> getRootBuild() {
Expand Down Expand Up @@ -285,8 +288,14 @@ public List<ParameterDefinition> getParameterDefinitionsWithValue(){
return definitions;
}

public void doRebuild(StaplerRequest req, StaplerResponse rsp) throws IOException {
throw HttpResponses.error(404, "Promotions may not be rebuilt directly");
}

public void run() {
getStatus().addPromotionAttempt(this);
if (getTarget() != null) {
getStatus().addPromotionAttempt(this);
}
run(new RunnerImpl(this));
}

Expand All @@ -299,6 +308,9 @@ protected class RunnerImpl extends AbstractRunner {

@Override
protected Lease decideWorkspace(Node n, WorkspaceList wsl) throws InterruptedException, IOException {
if (getTarget() == null) {
throw new IOException("No Promotion target, cannot retrieve workspace");
}
String customWorkspace = Promotion.this.getProject().getCustomWorkspace();
if (customWorkspace != null) {
final FilePath rootPath = n.getRootPath();
Expand Down Expand Up @@ -383,6 +395,11 @@ protected Result doRun(BuildListener listener) throws Exception {
}

protected void post2(BuildListener listener) throws Exception {
if (getTarget() == null) {
listener.error("No Promotion target, cannot save target or update status");
return;
}

if(getResult()== Result.SUCCESS)
getStatus().onSuccessfulPromotion(Promotion.this);
// persist the updated build record
Expand Down
@@ -0,0 +1,47 @@
/*
* The MIT License
*
* Copyright 2017 CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package hudson.plugins.promoted_builds;

import com.sonyericsson.rebuild.RebuildValidator;
import hudson.Extension;
import hudson.model.Run;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;

/**
* Disables the rebuild button for {@link Promotion} objects since rebuilding
* is handled separately for them.
*
* @see Status
*/
@Extension
@Restricted(NoExternalUse.class)
public class PromotionRebuildValidator extends RebuildValidator {

@Override
public boolean isApplicable(Run build) {
return build instanceof Promotion;
}

}
@@ -0,0 +1,62 @@
/*
* The MIT License
*
* Copyright 2017 CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package hudson.plugins.promoted_builds;

import com.sonyericsson.rebuild.RebuildAction;
import hudson.model.FreeStyleBuild;
import hudson.model.FreeStyleProject;
import hudson.plugins.promoted_builds.conditions.SelfPromotionCondition;
import org.junit.Test;
import static org.junit.Assert.*;
import org.junit.Rule;
import org.jvnet.hudson.test.JenkinsRule;

public class PromotionRebuildValidatorTest {

@Rule
public JenkinsRule r = new JenkinsRule();

@Test
public void testPromotionsDoNotHaveRebuildActions() throws Exception {
FreeStyleProject p = r.createFreeStyleProject();

// promote if the downstream passes
JobPropertyImpl promotion = new JobPropertyImpl(p);
p.addProperty(promotion);

PromotionProcess promo1 = promotion.addProcess("promo1");
promo1.conditions.add(new SelfPromotionCondition(false));

FreeStyleBuild b = r.assertBuildStatusSuccess(p.scheduleBuild2(0));
// internally, the promotion is still an asynchronous process. It just happens
// right away after the build is complete.
Thread.sleep(1000);

Promotion pb = promo1.getBuilds().iterator().next();
assertSame(pb.getTarget(), b);

assertNull(pb.getAction(RebuildAction.class));
}

}
67 changes: 67 additions & 0 deletions src/test/java/hudson/plugins/promoted_builds/PromotionTest.java
@@ -0,0 +1,67 @@
/*
* The MIT License
*
* Copyright 2017 CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package hudson.plugins.promoted_builds;

import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
import hudson.model.FreeStyleBuild;
import hudson.model.FreeStyleProject;
import hudson.plugins.promoted_builds.conditions.SelfPromotionCondition;
import org.junit.Test;
import static org.junit.Assert.*;
import org.junit.Rule;
import org.jvnet.hudson.test.JenkinsRule;

public class PromotionTest {

@Rule
public JenkinsRule r = new JenkinsRule();

@Test
public void testRebuildPromotionThrowsException() throws Exception {
FreeStyleProject p = r.createFreeStyleProject("proj1");

JobPropertyImpl promotion = new JobPropertyImpl(p);
p.addProperty(promotion);

PromotionProcess promo1 = promotion.addProcess("promo1");
promo1.conditions.add(new SelfPromotionCondition(false));

FreeStyleBuild b = r.assertBuildStatusSuccess(p.scheduleBuild2(0));
// internally, the promotion is still an asynchronous process. It just happens
// right away after the build is complete.
Thread.sleep(1000);

Promotion pb = promo1.getBuilds().getLastBuild();
assertSame(pb.getTarget(), b);

try {
r.createWebClient().goTo("job/"+p.getName()+"/1/promotion/"+promo1.getName()+"/promotionBuild/"+pb.getNumber()+"/rebuild");
fail("rebuilding a promotion directly should fail");
} catch (FailingHttpStatusCodeException x) {
assertEquals("wrong status code", 404, x.getStatusCode());
assertNotEquals("unexpected content", -1, x.getResponse().getContentAsString().indexOf("Promotions may not be rebuilt directly"));
}
}

}

0 comments on commit 043d1a5

Please sign in to comment.