Skip to content

Commit

Permalink
Add support for classifiers.
Browse files Browse the repository at this point in the history
  • Loading branch information
tecarter94 committed Jun 3, 2024
1 parent a96d86a commit ab5069c
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import java.util.Objects;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;
import org.cyclonedx.exception.ParseException;
import org.cyclonedx.model.Bom;
import org.cyclonedx.model.Component;
Expand Down Expand Up @@ -76,23 +77,31 @@ public static Bom generateSBom(Set<TrackingData> trackingData, InputStream exist

Component component = existingIds.get(new Identifier(name, group, version));
List<Property> properties = new ArrayList<>();
Map<String, String> attributes = i.getAttributes();
if (component == null) {
component = new Component();
bom.getComponents().add(component);
component.setType(Component.Type.LIBRARY);
component.setGroup(group);
component.setName(name);
component.setVersion(version);
component.setPurl(String.format("pkg:maven/%s/%s@%s", group, name, version));
String purl = String.format("pkg:maven/%s/%s@%s", group, name, version);
String classifier = attributes.get("classifier");
if (StringUtils.isNotBlank(classifier)) {
purl += String.format("?classifier=%s", classifier);
}
component.setPurl(purl);
} else if (component.getProperties() != null) {
properties.addAll(component.getProperties());
}
component.setPublisher(i.source);
for (var e : i.getAttributes().entrySet()) {
Property property = new Property();
property.setName("java:" + e.getKey());
property.setValue(e.getValue());
properties.add(property);
if (!e.getKey().equals("classifier")) {
Property property = new Property();
property.setName("java:" + e.getKey());
property.setValue(e.getValue());
properties.add(property);

Check warning on line 103 in java-components/build-request-processor/src/main/java/com/redhat/hacbs/container/analyser/dependencies/SBomGenerator.java

View check run for this annotation

Codecov / codecov/patch

java-components/build-request-processor/src/main/java/com/redhat/hacbs/container/analyser/dependencies/SBomGenerator.java#L100-L103

Added lines #L100 - L103 were not covered by tests
}
}

Property packageTypeProperty = new Property();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.redhat.hacbs.container.deploy;

import static com.redhat.hacbs.classfile.tracker.TrackingData.extractClassifier;
import static org.apache.commons.lang3.ObjectUtils.isNotEmpty;

import java.io.File;
Expand Down Expand Up @@ -226,13 +227,21 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO
try {
String fileName = file.getFileName().toString();
Path temp = file.getParent().resolve(fileName + ".temp");
String classifier = extractClassifier(gav.getArtifactId(), gav.getVersion(), fileName);
Map<String, String> attributes;
if (StringUtils.isNotBlank(classifier)) {
attributes = Map.of("scm-uri", scmUri, "scm-commit", commit, "hermetic",
Boolean.toString(hermetic), BUILD_ID, buildId, "classifier", classifier);
} else {
attributes = Map.of("scm-uri", scmUri, "scm-commit", commit, "hermetic",
Boolean.toString(hermetic), BUILD_ID, buildId);
}
ClassFileTracker.addTrackingDataToJar(Files.newInputStream(file),
new TrackingData(
gav.getGroupId() + ":" + gav.getArtifactId() + ":"
+ gav.getVersion(),
"rebuilt",
Map.of("scm-uri", scmUri, "scm-commit", commit, "hermetic",
Boolean.toString(hermetic), BUILD_ID, buildId)),
attributes),
Files.newOutputStream(temp), false);
Files.delete(file);
Files.move(temp, file);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public void testSbomMerge() {
var sbom = SBomGenerator.generateSBom(
Set.of(
new TrackingData("commons-digester:commons-digester:2.1", "rebuilt", Map.of()),
new TrackingData("com.test:test:1.0", "central", Map.of())),
new TrackingData("com.test:test:1.0", "central", Map.of("classifier", "foo"))),
getClass().getClassLoader().getResourceAsStream("syft-sbom.json"));
Component test = null;
Component digester = null;
Expand All @@ -49,6 +49,6 @@ public void testSbomMerge() {
Assertions.assertEquals("rebuilt", digester.getPublisher());
Assertions.assertNotNull(digester.getBomRef());
Assertions.assertEquals("central", test.getPublisher());

Assertions.assertEquals("pkg:maven/com.test/[email protected]?classifier=foo", test.getPurl());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,15 @@
public class DeployContaminateTest {
private static final String GROUP = "com.company.foo";
private static final String VERSION = "3.25.8";
public static final String FOO_BAR = "foo-bar";
public static final String FOO_BAZ = "foo-baz";
public static final String FOOBAR = "foobar";
public static final String FOOBAZ = "foobaz";
private static final String DOT = ".";
private static final String SHA_1 = "sha1";
private static final String COMMIT = "3cf2d99b47f0a05466d1d0a2e09d8740faeda149";
private static final String REPO = "https://github.com/foo/bar";
private static final Map<String, String> ARTIFACT_FILE_MAP = Map.of(
FOO_BAR, "foobar-" + VERSION + "-tests.jar",
FOO_BAZ, "foobaz-" + VERSION + ".jar");
FOOBAR, "foobar-" + VERSION + "-tests.jar",
FOOBAZ, "foobaz-" + VERSION + ".jar");

@Inject
ResultsUpdater resultsUpdater;
Expand Down Expand Up @@ -103,8 +103,9 @@ public void testDeployWithContaminated()
testDeployment.run();
List<LogRecord> logRecords = LogCollectingTestResource.current().getRecords();

assertThat(logRecords).map(LogCollectingTestResource::format).contains("foobar-3.25.8-tests.jar was contaminated by org.jboss.metadata:jboss-metadata-common:9.0.0.Final from central", "GAVs to deploy: [com.company.foo:foo-bar:3.25.8, com.company.foo:foo-baz:3.25.8]");
assertThat(logRecords).map(LogCollectingTestResource::format).contains("foobar-3.25.8-tests.jar was contaminated by org.jboss.metadata:jboss-metadata-common:9.0.0.Final from central", "GAVs to deploy: [com.company.foo:foobaz:3.25.8, com.company.foo:foobar:3.25.8]");
assertThat(logRecords).extracting("message").doesNotContain("Removing");
assertThat(logRecords).map(LogCollectingTestResource::format).contains("Extracted classifier 'tests' for artifact 'foobar' and version '3.25.8'");
}

@Test
Expand All @@ -130,7 +131,7 @@ private Path createDeploymentRepo()
Files.createDirectories(testDir);
Path testFile = Paths.get(testDir.toString(), artifactFile.getValue());

if (artifactFile.getKey().contains(FOO_BAR)) {
if (artifactFile.getKey().contains(FOOBAR)) {
JarOutputStream jar = new JarOutputStream(new FileOutputStream(testFile.toString()));
Path iconClass = Paths.get(getClass().getResource("/").toURI())
.resolve("../../../cli/src/test/resources/Icon.class").normalize();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.redhat.hacbs.artifactcache.services;

import static com.redhat.hacbs.classfile.tracker.TrackingData.extractClassifier;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
Expand All @@ -10,7 +12,6 @@
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.security.MessageDigest;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
Expand All @@ -21,6 +22,8 @@
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;

import org.apache.commons.lang3.StringUtils;

import com.redhat.hacbs.classfile.tracker.ClassFileTracker;
import com.redhat.hacbs.classfile.tracker.HashingOutputStream;
import com.redhat.hacbs.classfile.tracker.TrackingData;
Expand Down Expand Up @@ -100,7 +103,8 @@ public Optional<ArtifactResult> getArtifactFile(String group, String artifact, S
String targetFile = group.replace('.', File.separatorChar) + File.separator + artifact
+ File.separator + version + File.separator + target;
return handleFile(targetFile, group.replace(File.separatorChar, '.') + ":" + artifact + ":" + version,
(c) -> c.getArtifactFile(group, artifact, version, target), tracked, cacheOnly);
(c) -> c.getArtifactFile(group, artifact, version, target), tracked, cacheOnly,
extractClassifier(artifact, version, target));
}
}

Expand All @@ -114,7 +118,8 @@ public Optional<ArtifactResult> getMetadataFile(String group, String target) {
}

private Optional<ArtifactResult> handleFile(String targetFile, String gav,
Function<RepositoryClient, Optional<ArtifactResult>> clientInvocation, boolean tracked, boolean cacheOnly) {
Function<RepositoryClient, Optional<ArtifactResult>> clientInvocation, boolean tracked, boolean cacheOnly,
String classifier) {
try {
var check = inProgressDownloads.get(targetFile);
if (check != null) {
Expand All @@ -129,7 +134,7 @@ private Optional<ArtifactResult> handleFile(String targetFile, String gav,
if (check != null) {
check.awaitReady();
}
return handleDownloadedFile(actual, trackedFile, tracked, gav);
return handleDownloadedFile(actual, trackedFile, tracked, gav, classifier);
}
if (cacheOnly) {
return Optional.empty();
Expand All @@ -143,12 +148,12 @@ private Optional<ArtifactResult> handleFile(String targetFile, String gav,
//if the file is not there it may mean that the sha1 was wrong
//so we never cache it
if (Files.exists(actual)) {
return handleDownloadedFile(actual, trackedFile, tracked, gav);
return handleDownloadedFile(actual, trackedFile, tracked, gav, classifier);

Check warning on line 151 in java-components/cache/src/main/java/com/redhat/hacbs/artifactcache/services/RepositoryCache.java

View check run for this annotation

Codecov / codecov/patch

java-components/cache/src/main/java/com/redhat/hacbs/artifactcache/services/RepositoryCache.java#L151

Added line #L151 was not covered by tests
}
existing = inProgressDownloads.putIfAbsent(targetFile, newFile);
}
return newFile.download(clientInvocation, repository.getClient(), actual, trackedFile,
tempDownloads, tracked, gav);
tempDownloads, tracked, gav, classifier);

} catch (Exception e) {
throw new RuntimeException(e);
Expand Down Expand Up @@ -188,7 +193,8 @@ public void deleteGav(String gav) {

}

private Optional<ArtifactResult> handleDownloadedFile(Path downloaded, Path trackedFileTarget, boolean tracked, String gav)
private Optional<ArtifactResult> handleDownloadedFile(Path downloaded, Path trackedFileTarget, boolean tracked, String gav,
String classifier)
throws IOException, InterruptedException {
var lock = new GavLock(gav);
try {
Expand Down Expand Up @@ -242,8 +248,11 @@ private Optional<ArtifactResult> handleDownloadedFile(Path downloaded, Path trac
Files.createDirectories(trackedJarFile.getParent());
try (OutputStream out = Files.newOutputStream(trackedJarFile); var in = Files.newInputStream(downloaded)) {
HashingOutputStream hashingOutputStream = new HashingOutputStream(out);
Map<String, String> attributes = StringUtils.isNotBlank(classifier) ? Map.of("classifier", classifier)
: Map.of();
ClassFileTracker.addTrackingDataToJar(in,
new TrackingData(gav, repository.getName(), Collections.emptyMap()), hashingOutputStream,
new TrackingData(gav, repository.getName(), attributes),
hashingOutputStream,
overwriteExistingBytecodeMarkers);
hashingOutputStream.close();

Expand Down Expand Up @@ -326,7 +335,8 @@ Optional<ArtifactResult> download(Function<RepositoryClient, Optional<ArtifactRe
Path trackedFile,
StorageManager downloadTempDir,
boolean tracked,
String gav) {
String gav,
String classifier) {
GavLock lock = new GavLock(gav);
try {
Optional<ArtifactResult> result = clientInvocation.apply(repositoryClient);
Expand Down Expand Up @@ -365,8 +375,11 @@ Optional<ArtifactResult> download(Function<RepositoryClient, Optional<ArtifactRe
"transformed", ".part");
try (var inFromFile = Files.newInputStream(tempFile);
var transformedOut = Files.newOutputStream(tempTransformedFile)) {
Map<String, String> attributes = StringUtils.isNotBlank(classifier)
? Map.of("classifier", classifier)
: Map.of();

Check warning on line 380 in java-components/cache/src/main/java/com/redhat/hacbs/artifactcache/services/RepositoryCache.java

View check run for this annotation

Codecov / codecov/patch

java-components/cache/src/main/java/com/redhat/hacbs/artifactcache/services/RepositoryCache.java#L379-L380

Added lines #L379 - L380 were not covered by tests
ClassFileTracker.addTrackingDataToJar(inFromFile,
new TrackingData(gav, repository.getName(), Collections.emptyMap()),
new TrackingData(gav, repository.getName(), attributes),

Check warning on line 382 in java-components/cache/src/main/java/com/redhat/hacbs/artifactcache/services/RepositoryCache.java

View check run for this annotation

Codecov / codecov/patch

java-components/cache/src/main/java/com/redhat/hacbs/artifactcache/services/RepositoryCache.java#L382

Added line #L382 was not covered by tests
transformedOut, overwriteExistingBytecodeMarkers);
}
Files.delete(tempFile);
Expand Down Expand Up @@ -411,7 +424,7 @@ Optional<ArtifactResult> download(Function<RepositoryClient, Optional<ArtifactRe
downloadTarget.getParent().resolve(downloadTarget.getFileName().toString() + HEADERS))) {
p.store(out, "");
}
return handleDownloadedFile(downloadTarget, trackedFile, tracked, gav);
return handleDownloadedFile(downloadTarget, trackedFile, tracked, gav, classifier);
}
return Optional.empty();
} catch (Throwable e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import com.redhat.hacbs.classfile.tracker.ClassFileTracker;
import com.redhat.hacbs.classfile.tracker.TrackingData;
import com.redhat.hacbs.resources.util.HashUtil;

import io.micrometer.core.instrument.composite.CompositeMeterRegistry;
Expand Down Expand Up @@ -102,19 +104,23 @@ public void testHashRequestedFirstTrackedArtifact() throws Exception {
current = new ArtifactResult(
null, new ByteArrayInputStream(jarFile), jarFile.length, Optional.of(HashUtil.sha1(jarFile)),
Map.of());
localCache.getArtifactFile("default", "test", "test", "1.0", "test.jar.sha1", true);
var result = localCache.getArtifactFile("default", "test", "test", "1.0", "test.jar", true);
localCache.getArtifactFile("default", "test", "test", "1.0", "test-1.0-dummy.jar.sha1", true);
var result = localCache.getArtifactFile("default", "test", "test", "1.0", "test-1.0-dummy.jar", true);
try (ZipInputStream in = new ZipInputStream(result.get().getData())) {
var entry = in.getNextEntry();
boolean found = false;
TrackingData trackingData = null;
while (entry != null) {
System.out.println(entry);
if (entry.getName().equals(getClass().getName().replace(".", "/") + ".class")) {
found = true;
trackingData = ClassFileTracker.readTrackingInformationFromClass(in.readAllBytes());
}
entry = in.getNextEntry();
}
Assertions.assertTrue(found);
Assertions.assertNotNull(trackingData);
Assertions.assertEquals("dummy", trackingData.getAttributes().get("classifier"));
}

} catch (Exception e) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package com.redhat.hacbs.classfile.tracker;

import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import java.util.Map;
import java.util.Objects;

public class TrackingData {

public static final Logger LOGGER = System.getLogger(TrackingData.class.getName());

public final String gav;
public final String source;
private final Map<String, String> attributes;
Expand Down Expand Up @@ -51,4 +55,19 @@ public String toString() {
", attributes=" + attributes +
'}';
}

public static String extractClassifier(String artifact, String version, String target) {
String jarExtension = ".jar";
String classifier = null;
String artifactAndVersionPrefix = artifact + "-" + version + "-";

Check warning on line 62 in java-components/classfile-tracker/src/main/java/com/redhat/hacbs/classfile/tracker/TrackingData.java

View check run for this annotation

Codecov / codecov/patch

java-components/classfile-tracker/src/main/java/com/redhat/hacbs/classfile/tracker/TrackingData.java#L60-L62

Added lines #L60 - L62 were not covered by tests
// Has classifier due to presence of '-' after version
if (target.startsWith(artifactAndVersionPrefix) && target.endsWith(jarExtension)) {
classifier = target.substring(artifactAndVersionPrefix.length(), target.length() - jarExtension.length());

Check warning on line 65 in java-components/classfile-tracker/src/main/java/com/redhat/hacbs/classfile/tracker/TrackingData.java

View check run for this annotation

Codecov / codecov/patch

java-components/classfile-tracker/src/main/java/com/redhat/hacbs/classfile/tracker/TrackingData.java#L65

Added line #L65 was not covered by tests
if (LOGGER.isLoggable(Level.DEBUG)) {
LOGGER.log(Level.DEBUG, "Extracted classifier '%s' for artifact '%s' and version '%s'".formatted(classifier,

Check warning on line 67 in java-components/classfile-tracker/src/main/java/com/redhat/hacbs/classfile/tracker/TrackingData.java

View check run for this annotation

Codecov / codecov/patch

java-components/classfile-tracker/src/main/java/com/redhat/hacbs/classfile/tracker/TrackingData.java#L67

Added line #L67 was not covered by tests
artifact, version));
}
}
return classifier;

Check warning on line 71 in java-components/classfile-tracker/src/main/java/com/redhat/hacbs/classfile/tracker/TrackingData.java

View check run for this annotation

Codecov / codecov/patch

java-components/classfile-tracker/src/main/java/com/redhat/hacbs/classfile/tracker/TrackingData.java#L71

Added line #L71 was not covered by tests
}
}

0 comments on commit ab5069c

Please sign in to comment.