Skip to content

Commit

Permalink
feat(artifactory): add build to artifact metadata (#428)
Browse files Browse the repository at this point in the history
Artifactory is capable of keeping up with information about your build:
https://github.com/JFrog/build-info

Here we are grabbing the latest Artifactory build info related to an item if it exists, and adding it to the artifact metadata.
  • Loading branch information
claymccoy authored and jkschneider committed Apr 25, 2019
1 parent 58c70c4 commit 8548f55
Show file tree
Hide file tree
Showing 5 changed files with 168 additions and 95 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import com.netflix.discovery.DiscoveryClient;
import com.netflix.spectator.api.Registry;
import com.netflix.spinnaker.igor.IgorConfigurationProperties;
import com.netflix.spinnaker.igor.artifactory.model.ArtifactoryArtifact;
import com.netflix.spinnaker.igor.artifactory.model.ArtifactoryItem;
import com.netflix.spinnaker.igor.artifactory.model.ArtifactoryRepositoryType;
import com.netflix.spinnaker.igor.artifactory.model.ArtifactorySearch;
import com.netflix.spinnaker.igor.config.ArtifactoryProperties;
Expand Down Expand Up @@ -120,7 +120,7 @@ protected ArtifactPollingDelta generateDelta(PollContext ctx) {
"*\"}," +
"\"name\": {\"$match\":\"" +
"*.pom\"}" +
"}).include(\"path\",\"repo\",\"name\")";
"}).include(\"path\",\"repo\",\"name\", \"artifact.module.build\")";

ArtifactoryRequest aqlRequest = new ArtifactoryRequestImpl()
.method(ArtifactoryRequest.Method.POST)
Expand All @@ -132,7 +132,7 @@ protected ArtifactPollingDelta generateDelta(PollContext ctx) {
try {
ArtifactoryResponse aqlResponse = client.restCall(aqlRequest);
if (aqlResponse.isSuccessResponse()) {
List<ArtifactoryArtifact> results = aqlResponse.parseBody(ArtifactoryQueryResults.class).getResults();
List<ArtifactoryItem> results = aqlResponse.parseBody(ArtifactoryQueryResults.class).getResults();
return new ArtifactPollingDelta(search.getName(), search.getPartitionName(), Collections.singletonList(
new ArtifactDelta(System.currentTimeMillis(), search.getRepoType(), results)));
}
Expand All @@ -151,15 +151,15 @@ protected ArtifactPollingDelta generateDelta(PollContext ctx) {
protected void commitDelta(ArtifactPollingDelta delta, boolean sendEvents) {
for (ArtifactDelta artifactDelta : delta.items) {
if (sendEvents) {
for (ArtifactoryArtifact artifact : artifactDelta.getArtifacts()) {
for (ArtifactoryItem artifact : artifactDelta.getArtifacts()) {
postEvent(artifactDelta.getType(), artifact, delta.getName());
log.debug("{} event posted", artifact);
}
}
}
}

private void postEvent(ArtifactoryRepositoryType repoType, ArtifactoryArtifact artifact, String name) {
private void postEvent(ArtifactoryRepositoryType repoType, ArtifactoryItem artifact, String name) {
if (!echoService.isPresent()) {
log.warn("Cannot send build notification: Echo is not configured");
registry.counter(missedNotificationId.withTag("monitor", ArtifactoryBuildMonitor.class.getSimpleName())).increment();
Expand Down Expand Up @@ -187,11 +187,11 @@ static class ArtifactPollingDelta implements PollingDelta<ArtifactDelta> {
static class ArtifactDelta implements DeltaItem {
private final long searchTimestamp;
private final ArtifactoryRepositoryType type;
private final List<ArtifactoryArtifact> artifacts;
private final List<ArtifactoryItem> artifacts;
}

@Data
private static class ArtifactoryQueryResults {
List<ArtifactoryArtifact> results;
List<ArtifactoryItem> results;
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* Copyright 2019 Pivotal, 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.igor.artifactory.model;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.netflix.spinnaker.kork.artifacts.model.Artifact;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.annotation.Nullable;
import java.util.*;

@Data
public class ArtifactoryItem {
private String repo;
private String path;
private List<ArtifactoryArtifact> artifacts;

@Nullable
public Artifact toMatchableArtifact(ArtifactoryRepositoryType repoType) {
switch(repoType) {
case Maven:
String[] pathParts = path.split("/");
String version = pathParts[pathParts.length - 1];
String artifactId = pathParts[pathParts.length - 2];

String[] groupParts = Arrays.copyOfRange(pathParts, 0, pathParts.length - 2);
String group = String.join(".", groupParts);

final Artifact.ArtifactBuilder artifactBuilder = Artifact.builder().type("maven/file")
.reference(group + ":" + artifactId + ":" + version)
.name(group + ":" + artifactId)
.version(version)
.provenance(repo);

if (artifacts != null && !artifacts.isEmpty()) {
final ArtifactoryArtifact artifact = artifacts.get(0);
if (artifact.modules != null && !artifact.modules.isEmpty()) {
final ArtifactoryModule module = artifact.modules.get(0);
if (module.builds != null && !module.builds.isEmpty()) {
module.builds.sort((o1, o2) -> o2.created.compareTo(o1.created));
final ArtifactoryBuild build = module.builds.get(0);
final Map<String, Object> metadata = new HashMap<>();
metadata.put("build", build);
artifactBuilder.metadata(metadata);
}
}
}

return artifactBuilder.build();
}
return null;
}

@Data
@NoArgsConstructor
@AllArgsConstructor
public static class ArtifactoryArtifact {
private List<ArtifactoryModule> modules;
}

@Data
@NoArgsConstructor
@AllArgsConstructor
public static class ArtifactoryModule {
private List<ArtifactoryBuild> builds;
}

@Data
@NoArgsConstructor
@AllArgsConstructor
public static class ArtifactoryBuild {
@JsonProperty("build.created") private String created;
@JsonProperty("build.name") private String name;
@JsonProperty("build.number") private String number;
@JsonProperty("build.url") private String url;
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright 2019 Pivotal, 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.igor.artifactory.model;

import com.netflix.spinnaker.igor.artifactory.model.ArtifactoryItem.*;
import com.netflix.spinnaker.kork.artifacts.model.Artifact;
import org.junit.jupiter.api.Test;

import java.util.ArrayList;
import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;

class ArtifactoryItemTest {
@Test
void toMatchableArtifact() {
ArtifactoryItem artifact = new ArtifactoryItem();
artifact.setPath("io/pivotal/spinnaker/demo/0.1.0-dev.20+d9a14fb");
artifact.setRepo("libs-demo-local");

Artifact matchableArtifact = artifact.toMatchableArtifact(ArtifactoryRepositoryType.Maven);
assertThat(matchableArtifact).isNotNull();
assertThat(matchableArtifact.getType()).isEqualTo("maven/file");
assertThat(matchableArtifact.getReference()).isEqualTo("io.pivotal.spinnaker:demo:0.1.0-dev.20+d9a14fb");
assertThat(matchableArtifact.getVersion()).isEqualTo("0.1.0-dev.20+d9a14fb");
assertThat(matchableArtifact.getName()).isEqualTo("io.pivotal.spinnaker:demo");
}

@Test
void toMatchableArtifactWithBuild() {
ArtifactoryItem artifact = new ArtifactoryItem();
artifact.setPath("io/pivotal/spinnaker/demo/0.1.0-dev.20+d9a14fb");
artifact.setRepo("libs-demo-local");

final ArtifactoryBuild expectedBuild = new ArtifactoryBuild("2019-04-25T01:04:15.980Z",
"artifactory_build_info_maven", "3", "http://localhost:7080/job/artifactory_build_info_maven/3/");

final List<ArtifactoryBuild> builds = new ArrayList<>();
builds.add(new ArtifactoryBuild("2019-04-24T19:36:35.486Z", "artifactory_build_info_maven",
"1", "http://localhost:7080/job/artifactory_build_info_maven/1/"));
builds.add(expectedBuild);
builds.add(new ArtifactoryBuild("2019-04-25T00:56:26.723Z", "artifactory_build_info_maven",
"2", "http://localhost:7080/job/artifactory_build_info_maven/2/"));
final List<ArtifactoryModule> modules = new ArrayList<>();
modules.add(new ArtifactoryModule(builds));
final List<ArtifactoryArtifact> artifacts = new ArrayList<>();
artifacts.add(new ArtifactoryArtifact(modules));
artifact.setArtifacts(artifacts);

Artifact matchableArtifact = artifact.toMatchableArtifact(ArtifactoryRepositoryType.Maven);
assertThat(matchableArtifact.getMetadata().get("build")).isEqualTo(expectedBuild);
}
}

0 comments on commit 8548f55

Please sign in to comment.