Skip to content

Commit

Permalink
feat(codebuild): Add getArtifacts API for AWS CodeBuild (#624)
Browse files Browse the repository at this point in the history
* feat(codebuild): Add getArtifacts API for AWS CodeBuild

This API will be called by orca to fetch S3 artifacts produced by CodeBuild
so that these artifacts could be binded and consumed by downstream stages.

* feat(codebuild): Add name field in artifact

Co-authored-by: Clare Liguori <[email protected]>
  • Loading branch information
Kaixiang-AWS and clareliguori authored Feb 12, 2020
1 parent f0a8517 commit d08f63e
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,13 @@
import com.amazonaws.services.codebuild.AWSCodeBuildClientBuilder;
import com.amazonaws.services.codebuild.model.BatchGetBuildsRequest;
import com.amazonaws.services.codebuild.model.Build;
import com.amazonaws.services.codebuild.model.BuildArtifacts;
import com.amazonaws.services.codebuild.model.StartBuildRequest;
import com.amazonaws.services.codebuild.model.StopBuildRequest;
import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClient;
import com.netflix.spinnaker.kork.artifacts.model.Artifact;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
Expand Down Expand Up @@ -60,6 +64,11 @@ public Build getBuild(String buildId) {
return client.batchGetBuilds(new BatchGetBuildsRequest().withIds(buildId)).getBuilds().get(0);
}

public List<Artifact> getArtifacts(String buildId) {
Build build = getBuild(buildId);
return extractArtifactsFromBuild(build);
}

public Build stopBuild(String buildId) {
return client.stopBuild(new StopBuildRequest().withId(buildId)).getBuild();
}
Expand All @@ -78,4 +87,28 @@ private String getRoleArn(String accountId, String assumeRole) {
}
return assumeRoleValue;
}

private List<Artifact> extractArtifactsFromBuild(Build build) {
ArrayList<Artifact> artifactsList = new ArrayList<>();
BuildArtifacts primaryArtifacts = build.getArtifacts();
List<BuildArtifacts> secondaryArtifacts = build.getSecondaryArtifacts();
if (primaryArtifacts != null) {
artifactsList.add(getS3Artifact(primaryArtifacts.getLocation()));
}
if (secondaryArtifacts != null) {
secondaryArtifacts.forEach(
artifacts -> artifactsList.add(getS3Artifact(artifacts.getLocation())));
}
return artifactsList;
}

private Artifact getS3Artifact(String s3Arn) {
String reference = getS3ArtifactReference(s3Arn);
return Artifact.builder().type("s3/object").reference(reference).name(reference).build();
}

private String getS3ArtifactReference(String s3Arn) {
String[] s3ArnSplit = s3Arn.split(":");
return "s3://" + s3ArnSplit[s3ArnSplit.length - 1];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.amazonaws.services.codebuild.model.Build;
import com.amazonaws.services.codebuild.model.StartBuildRequest;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.netflix.spinnaker.kork.artifacts.model.Artifact;
import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor;
Expand Down Expand Up @@ -54,6 +55,11 @@ Build getBuild(@PathVariable String account, @PathVariable String buildId) {
return awsCodeBuildAccountRepository.getAccount(account).getBuild(buildId);
}

@RequestMapping(value = "/builds/artifacts/{account}/{buildId}", method = RequestMethod.GET)
List<Artifact> getArtifacts(@PathVariable String account, @PathVariable String buildId) {
return awsCodeBuildAccountRepository.getAccount(account).getArtifacts(buildId);
}

@RequestMapping(value = "/builds/stop/{account}/{buildId}", method = RequestMethod.POST)
Build stopBuild(@PathVariable String account, @PathVariable String buildId) {
return awsCodeBuildAccountRepository.getAccount(account).stopBuild(buildId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import com.amazonaws.services.codebuild.AWSCodeBuildClient
import com.amazonaws.services.codebuild.model.BatchGetBuildsRequest
import com.amazonaws.services.codebuild.model.BatchGetBuildsResult
import com.amazonaws.services.codebuild.model.Build
import com.amazonaws.services.codebuild.model.BuildArtifacts
import com.amazonaws.services.codebuild.model.StartBuildRequest
import com.amazonaws.services.codebuild.model.StartBuildResult
import spock.lang.Specification
Expand Down Expand Up @@ -55,6 +56,95 @@ class AwsCodeBuildAccountSpec extends Specification {
result == mockOutputBuild
}

def "getArtifacts returns empty list if no artifact exists"() {
given:
String buildId = "test:c7715bbf-5c12-44d6-87ef-8149473e02f7"
def inputRequest = getBatchGetBuildsInput(buildId)
def mockOutputBuild = getOutputBuild("test")

when:
def result = awsCodeBuildAccount.getArtifacts(buildId)

then:
1 * client.batchGetBuilds(inputRequest) >> new BatchGetBuildsResult().withBuilds(mockOutputBuild)
result.size() == 0
}

def "getArtifacts returns primary artifacts"() {
given:
String buildId = "test:c7715bbf-5c12-44d6-87ef-8149473e02f7"
def inputRequest = getBatchGetBuildsInput(buildId)
def mockOutputBuild = getOutputBuild("test")
mockOutputBuild.setArtifacts(
new BuildArtifacts()
.withLocation("arn:aws:s3:::bucket/path/file.zip")
)

when:
def result = awsCodeBuildAccount.getArtifacts(buildId)

then:
1 * client.batchGetBuilds(inputRequest) >> new BatchGetBuildsResult().withBuilds(mockOutputBuild)
result.size() == 1
result.get(0).getType() == "s3/object"
result.get(0).getReference() == "s3://bucket/path/file.zip"
result.get(0).getName() == "s3://bucket/path/file.zip"
}

def "getArtifacts returns secondary artifacts"() {
given:
String buildId = "test:c7715bbf-5c12-44d6-87ef-8149473e02f7"
def inputRequest = getBatchGetBuildsInput(buildId)
def mockOutputBuild = getOutputBuild("test")
mockOutputBuild.setSecondaryArtifacts([
new BuildArtifacts()
.withLocation("arn:aws:s3:::bucket/path/file.zip"),
new BuildArtifacts()
.withLocation("arn:aws:s3:::another-bucket/another/path/file.zip"),
])

when:
def result = awsCodeBuildAccount.getArtifacts(buildId)

then:
1 * client.batchGetBuilds(inputRequest) >> new BatchGetBuildsResult().withBuilds(mockOutputBuild)
result.size() == 2
result.get(0).getType() == "s3/object"
result.get(0).getReference() == "s3://bucket/path/file.zip"
result.get(0).getName() == "s3://bucket/path/file.zip"
result.get(1).getType() == "s3/object"
result.get(1).getReference() == "s3://another-bucket/another/path/file.zip"
result.get(1).getName() == "s3://another-bucket/another/path/file.zip"
}

def "getArtifacts returns both primary and secondary artifacts"() {
given:
String buildId = "test:c7715bbf-5c12-44d6-87ef-8149473e02f7"
def inputRequest = getBatchGetBuildsInput(buildId)
def mockOutputBuild = getOutputBuild("test")
mockOutputBuild.setArtifacts(
new BuildArtifacts()
.withLocation("arn:aws:s3:::bucket/path/file.zip")
)
mockOutputBuild.setSecondaryArtifacts([
new BuildArtifacts()
.withLocation("arn:aws:s3:::another-bucket/another/path/file.zip")
])

when:
def result = awsCodeBuildAccount.getArtifacts(buildId)

then:
1 * client.batchGetBuilds(inputRequest) >> new BatchGetBuildsResult().withBuilds(mockOutputBuild)
result.size() == 2
result.get(0).getType() == "s3/object"
result.get(0).getReference() == "s3://bucket/path/file.zip"
result.get(0).getName() == "s3://bucket/path/file.zip"
result.get(1).getType() == "s3/object"
result.get(1).getReference() == "s3://another-bucket/another/path/file.zip"
result.get(1).getName() == "s3://another-bucket/another/path/file.zip"
}

private static StartBuildRequest getStartBuildInput(String projectName) {
return new StartBuildRequest()
.withProjectName(projectName);
Expand Down

0 comments on commit d08f63e

Please sign in to comment.