Skip to content

Commit

Permalink
feat(codebuild): Stop build when cancelling stage (#3412)
Browse files Browse the repository at this point in the history
* feat(codebuild): Stop build when cancelling stage

* Remove unnecessary imports
  • Loading branch information
Kaixiang-AWS authored Feb 5, 2020
1 parent 31e15e7 commit 17cd044
Show file tree
Hide file tree
Showing 7 changed files with 169 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ AwsCodeBuildExecution startAwsCodeBuild(
AwsCodeBuildExecution getAwsCodeBuildExecution(
@Path("account") String account, @Path("buildId") String buildId);

@POST("/codebuild/builds/stop/{account}/{buildId}")
AwsCodeBuildExecution stopAwsCodeBuild(
@Path("account") String account, @Path("buildId") String buildId);

@GET("/delivery-config/manifest")
Map<String, Object> getDeliveryConfigManifest(
@Query("scmType") String repoType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public enum Status {
FAILED(ExecutionStatus.TERMINAL),
FAULT(ExecutionStatus.TERMINAL),
TIMED_OUT(ExecutionStatus.TERMINAL),
STOPPED(ExecutionStatus.CANCELED),
STOPPED(ExecutionStatus.TERMINAL),
UNKNOWN(ExecutionStatus.TERMINAL);

@Getter private ExecutionStatus executionStatus;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,16 @@
*/
package com.netflix.spinnaker.orca.igor.pipeline;

import com.netflix.spinnaker.orca.CancellableStage;
import com.netflix.spinnaker.orca.TaskResult;
import com.netflix.spinnaker.orca.igor.tasks.MonitorAwsCodeBuildTask;
import com.netflix.spinnaker.orca.igor.tasks.StartAwsCodeBuildTask;
import com.netflix.spinnaker.orca.igor.tasks.StopAwsCodeBuildTask;
import com.netflix.spinnaker.orca.pipeline.StageDefinitionBuilder;
import com.netflix.spinnaker.orca.pipeline.TaskNode;
import com.netflix.spinnaker.orca.pipeline.model.Stage;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Nonnull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -28,11 +33,35 @@
@Component
@RequiredArgsConstructor
@Slf4j
public class AwsCodeBuildStage implements StageDefinitionBuilder {
public class AwsCodeBuildStage implements StageDefinitionBuilder, CancellableStage {
private final StopAwsCodeBuildTask stopAwsCodeBuildTask;

@Override
public void taskGraph(@Nonnull Stage stage, @Nonnull TaskNode.Builder builder) {
builder
.withTask("startAwsCodeBuildTask", StartAwsCodeBuildTask.class)
.withTask("monitorAwsCodeBuildTask", MonitorAwsCodeBuildTask.class);
}

@Override
public Result cancel(Stage stage) {
log.info(
String.format(
"Cancelling stage (stageId: %s, executionId: %s context: %s)",
stage.getId(), stage.getExecution().getId(), stage.getContext()));

try {
TaskResult result = stopAwsCodeBuildTask.execute(stage);
Map<String, Object> context = new HashMap<>();
context.put("buildInfo", result.getContext().get("buildInfo"));
stage.setContext(context);
} catch (Exception e) {
log.error(
String.format(
"Failed to cancel stage (stageId: %s, executionId: %s), e: %s",
stage.getId(), stage.getExecution().getId(), e.getMessage()),
e);
}
return new Result(stage, stage.getContext());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.netflix.spinnaker.orca.igor.tasks;

import com.netflix.spinnaker.orca.ExecutionStatus;
import com.netflix.spinnaker.orca.Task;
import com.netflix.spinnaker.orca.TaskResult;
import com.netflix.spinnaker.orca.igor.IgorService;
import com.netflix.spinnaker.orca.igor.model.AwsCodeBuildExecution;
import com.netflix.spinnaker.orca.igor.model.AwsCodeBuildStageDefinition;
import com.netflix.spinnaker.orca.pipeline.model.Stage;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Nonnull;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
public class StopAwsCodeBuildTask implements Task {
private final IgorService igorService;

@Override
@Nonnull
public TaskResult execute(@Nonnull Stage stage) {
AwsCodeBuildStageDefinition stageDefinition = stage.mapTo(AwsCodeBuildStageDefinition.class);
AwsCodeBuildExecution execution = stageDefinition.getBuildInfo();
if (execution != null) {
AwsCodeBuildExecution latestDetails =
igorService.stopAwsCodeBuild(
stageDefinition.getAccount(), getBuildId(execution.getArn()));
Map<String, Object> context = new HashMap<>();
context.put("buildInfo", latestDetails);
return TaskResult.builder(ExecutionStatus.SUCCEEDED).context(context).build();
}
return TaskResult.SUCCEEDED;
}

private String getBuildId(String arn) {
return arn.split("/")[1];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.netflix.spinnaker.orca.igor.pipeline

import com.netflix.spinnaker.orca.igor.tasks.MonitorAwsCodeBuildTask
import com.netflix.spinnaker.orca.igor.tasks.StartAwsCodeBuildTask
import spock.lang.Specification

Expand Down Expand Up @@ -45,4 +46,25 @@ class AwsCodeBuildStageSpec extends Specification {
it.implementingClass == StartAwsCodeBuildTask
}.size() == 1
}

def "should wait for completion"() {
given:
def awsCodeBuildStage = new AwsCodeBuildStage()

def stage = stage {
type = "awsCodeBuild"
context = [
account: ACCOUNT,
projectName: PROJECT_NAME,
]
}

when:
def tasks = awsCodeBuildStage.buildTaskGraph(stage)

then:
tasks.findAll {
it.implementingClass == MonitorAwsCodeBuildTask
}.size() == 1
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ class MonitorAwsCodeBuildTaskSpec extends Specification {
"FAILED" | ExecutionStatus.TERMINAL
"FAULT" | ExecutionStatus.TERMINAL
"TIMED_OUT" | ExecutionStatus.TERMINAL
"STOPPED" | ExecutionStatus.CANCELED
"STOPPED" | ExecutionStatus.TERMINAL
"UNKNOWN" | ExecutionStatus.TERMINAL
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright 2020 Amazon.com, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License")
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.netflix.spinnaker.orca.igor.tasks

import com.netflix.spinnaker.orca.ExecutionStatus
import com.netflix.spinnaker.orca.TaskResult
import com.netflix.spinnaker.orca.igor.IgorService
import com.netflix.spinnaker.orca.igor.model.AwsCodeBuildExecution
import com.netflix.spinnaker.orca.pipeline.model.Execution
import com.netflix.spinnaker.orca.pipeline.model.Stage
import spock.lang.Specification
import spock.lang.Subject

class StopAwsCodeBuildTaskSpec extends Specification {
def ACCOUNT = "codebuild-account"
def PROJECT_NAME = "test"
def ARN = "arn:aws:codebuild:us-west-2:123456789012:build/test:c7715bbf-5c12-44d6-87ef-8149473e02f7"

Execution execution = Mock(Execution)
IgorService igorService = Mock(IgorService)

@Subject
StopAwsCodeBuildTask task = new StopAwsCodeBuildTask(igorService)

def "should stop a build"() {
given:
def igorResponse = new AwsCodeBuildExecution(ARN, null, null)
def stage = new Stage(execution, "awsCodeBuild", [
account: ACCOUNT,
buildInfo: [
arn: ARN
],
])

when:
TaskResult result = task.execute(stage)

then:
1 * igorService.stopAwsCodeBuild(ACCOUNT, _) >> igorResponse
result.status == ExecutionStatus.SUCCEEDED
result.context.buildInfo.arn == igorResponse.arn
}

def "should do nothing if buildInfo doesn't exist"() {
given:
def igorResponse = new AwsCodeBuildExecution(ARN, null, null)
def stage = new Stage(execution, "awsCodeBuild", [account: ACCOUNT, projectName: PROJECT_NAME])

when:
TaskResult result = task.execute(stage)

then:
0 * igorService.stopAwsCodeBuild(ACCOUNT, _)
result.status == ExecutionStatus.SUCCEEDED
result.context.buildInfo == null
}
}

0 comments on commit 17cd044

Please sign in to comment.