diff --git a/igor-web/src/main/java/com/netflix/spinnaker/igor/codebuild/AwsCodeBuildAccount.java b/igor-web/src/main/java/com/netflix/spinnaker/igor/codebuild/AwsCodeBuildAccount.java index 34feb7f57..7d752e0a7 100644 --- a/igor-web/src/main/java/com/netflix/spinnaker/igor/codebuild/AwsCodeBuildAccount.java +++ b/igor-web/src/main/java/com/netflix/spinnaker/igor/codebuild/AwsCodeBuildAccount.java @@ -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; @@ -60,6 +64,11 @@ public Build getBuild(String buildId) { return client.batchGetBuilds(new BatchGetBuildsRequest().withIds(buildId)).getBuilds().get(0); } + public List getArtifacts(String buildId) { + Build build = getBuild(buildId); + return extractArtifactsFromBuild(build); + } + public Build stopBuild(String buildId) { return client.stopBuild(new StopBuildRequest().withId(buildId)).getBuild(); } @@ -78,4 +87,28 @@ private String getRoleArn(String accountId, String assumeRole) { } return assumeRoleValue; } + + private List extractArtifactsFromBuild(Build build) { + ArrayList artifactsList = new ArrayList<>(); + BuildArtifacts primaryArtifacts = build.getArtifacts(); + List 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]; + } } diff --git a/igor-web/src/main/java/com/netflix/spinnaker/igor/codebuild/AwsCodeBuildController.java b/igor-web/src/main/java/com/netflix/spinnaker/igor/codebuild/AwsCodeBuildController.java index effc6ad0c..3194c1ce5 100644 --- a/igor-web/src/main/java/com/netflix/spinnaker/igor/codebuild/AwsCodeBuildController.java +++ b/igor-web/src/main/java/com/netflix/spinnaker/igor/codebuild/AwsCodeBuildController.java @@ -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; @@ -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 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); diff --git a/igor-web/src/test/groovy/com/netflix/spinnaker/igor/codebuild/AwsCodeBuildAccountSpec.groovy b/igor-web/src/test/groovy/com/netflix/spinnaker/igor/codebuild/AwsCodeBuildAccountSpec.groovy index 6b556b61b..863a103dc 100644 --- a/igor-web/src/test/groovy/com/netflix/spinnaker/igor/codebuild/AwsCodeBuildAccountSpec.groovy +++ b/igor-web/src/test/groovy/com/netflix/spinnaker/igor/codebuild/AwsCodeBuildAccountSpec.groovy @@ -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 @@ -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);