{
diff --git a/src/main/java/com/redhat/exhort/api/serialization/package-info.java b/src/main/java/com/redhat/exhort/api/serialization/package-info.java
index ccc9a59c..866be540 100644
--- a/src/main/java/com/redhat/exhort/api/serialization/package-info.java
+++ b/src/main/java/com/redhat/exhort/api/serialization/package-info.java
@@ -1,2 +1,2 @@
-/** Package hosting various the Exhort API implementation. **/
+/** Package hosting various the Exhort API implementation. * */
package com.redhat.exhort.api.serialization;
diff --git a/src/main/java/com/redhat/exhort/image/Image.java b/src/main/java/com/redhat/exhort/image/Image.java
index 1eee5044..a3ba9297 100644
--- a/src/main/java/com/redhat/exhort/image/Image.java
+++ b/src/main/java/com/redhat/exhort/image/Image.java
@@ -19,7 +19,6 @@
* Contents in this file are from:
* https://github.com/fabric8io/docker-maven-plugin/blob/6eeb78a9b074328ef5817c1c91392d8d8350984e/src/main/java/io/fabric8/maven/docker/util/ImageName.java
*/
-
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -30,18 +29,19 @@
* Helper class for parsing docker repository/image names:
*
*
- * - If the first part before the slash contains a "." or a ":" it is considered to be a registry URL
- * - A last part starting with a ":" is considered to be a tag
- * - The rest is considered the repository name (which might be separated via slashes)
+ * - If the first part before the slash contains a "." or a ":" it is considered to be a
+ * registry URL
+ *
- A last part starting with a ":" is considered to be a tag
+ *
- The rest is considered the repository name (which might be separated via slashes)
*
- *
- * Example of valid names:
+ *
+ *
Example of valid names:
*
*
- * - consol/tomcat-8.0
- * - consol/tomcat-8.0:8.0.9
- * - docker.consol.de:5000/tomcat-8.0
- * - docker.consol.de:5000/jolokia/tomcat-8.0:8.0.9
+ * - consol/tomcat-8.0
+ *
- consol/tomcat-8.0:8.0.9
+ *
- docker.consol.de:5000/tomcat-8.0
+ *
- docker.consol.de:5000/jolokia/tomcat-8.0:8.0.9
*
*
* @author roland
@@ -53,13 +53,17 @@ public class Image {
// https://github.com/docker/docker/blob/04da4041757370fb6f85510c8977c5a18ddae380/vendor/github.com/docker/distribution/reference/regexp.go#L18
private final String nameComponentRegexp = "[a-z0-9]+(?:(?:(?:[._]|__|[-]*)[a-z0-9]+)+)?";
// https://github.com/docker/docker/blob/04da4041757370fb6f85510c8977c5a18ddae380/vendor/github.com/docker/distribution/reference/regexp.go#L25
- private final String domainComponentRegexp = "(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])";
+ private final String domainComponentRegexp =
+ "(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])";
// https://github.com/docker/docker/blob/04da4041757370fb6f85510c8977c5a18ddae380/vendor/github.com/docker/distribution/reference/regexp.go#L18
private final Pattern NAME_COMP_REGEXP = Pattern.compile(nameComponentRegexp);
// https://github.com/docker/docker/blob/04da4041757370fb6f85510c8977c5a18ddae380/vendor/github.com/docker/distribution/reference/regexp.go#L53
- private final Pattern IMAGE_NAME_REGEXP = Pattern.compile(nameComponentRegexp + "(?:(?:/" + nameComponentRegexp + ")+)?");
+ private final Pattern IMAGE_NAME_REGEXP =
+ Pattern.compile(nameComponentRegexp + "(?:(?:/" + nameComponentRegexp + ")+)?");
// https://github.com/docker/docker/blob/04da4041757370fb6f85510c8977c5a18ddae380/vendor/github.com/docker/distribution/reference/regexp.go#L31
- private final Pattern DOMAIN_REGEXP = Pattern.compile("^" + domainComponentRegexp + "(?:\\." + domainComponentRegexp + ")*(?::[0-9]+)?$");
+ private final Pattern DOMAIN_REGEXP =
+ Pattern.compile(
+ "^" + domainComponentRegexp + "(?:\\." + domainComponentRegexp + ")*(?::[0-9]+)?$");
// https://github.com/docker/docker/blob/04da4041757370fb6f85510c8977c5a18ddae380/vendor/github.com/docker/distribution/reference/regexp.go#L37
private final Pattern TAG_REGEXP = Pattern.compile("^[\\w][\\w.-]{0,127}$");
private final Pattern DIGEST_REGEXP = Pattern.compile("^sha256:[a-z0-9]{32,}$");
@@ -108,7 +112,8 @@ public Image(String fullName, String givenTag) {
Pattern tagPattern = Pattern.compile("^(.+?)(?::([^:/]+))?$");
Matcher matcher = tagPattern.matcher(fullName);
if (!matcher.matches()) {
- throw new IllegalArgumentException(fullName + " is not a proper image name ([registry/][repo][:port]");
+ throw new IllegalArgumentException(
+ fullName + " is not a proper image name ([registry/][repo][:port]");
}
// extract tag if it exists
tag = givenTag != null ? givenTag : matcher.group(2);
@@ -130,10 +135,10 @@ public Image(String fullName, String givenTag) {
}
/**
- * Check whether the given name validates agains the Docker rules for names
+ * Check whether the given name validates against the Docker rules for names
*
- * @param image image name to validate
- * d@throws IllegalArgumentException if the name doesnt validate
+ * @param image image name to validate d@throws IllegalArgumentException if the name doesnt
+ * validate
*/
public static void validate(String image) {
// Validation will be triggered during construction
@@ -167,11 +172,11 @@ public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Image image = (Image) o;
- return Objects.equals(repository, image.repository) &&
- Objects.equals(registry, image.registry) &&
- Objects.equals(tag, image.tag) &&
- Objects.equals(digest, image.digest) &&
- Objects.equals(user, image.user);
+ return Objects.equals(repository, image.repository)
+ && Objects.equals(registry, image.registry)
+ && Objects.equals(tag, image.tag)
+ && Objects.equals(digest, image.digest)
+ && Objects.equals(user, image.user);
}
@Override
@@ -204,8 +209,8 @@ private boolean isRegistry(String part) {
}
/**
- * Get the full name of this image, including the registry but without
- * any tag (e.g. privateregistry:fabric8io/java
)
+ * Get the full name of this image, including the registry but without any tag (e.g.
+ * privateregistry:fabric8io/java
)
*
* @return full name with the original registry
*/
@@ -214,13 +219,13 @@ public String getNameWithoutTag() {
}
/**
- * Get the full name of this image like {@link #getNameWithoutTag()} does, but allow
- * an optional registry. This registry is used when this image does not already
- * contain a registry.
+ * Get the full name of this image like {@link #getNameWithoutTag()} does, but allow an optional
+ * registry. This registry is used when this image does not already contain a registry.
*
- * @param optionalRegistry optional registry to use when this image does not provide
- * a registry. Can be null in which case no optional registry is used*
- * @return full name with original registry (if set) or optional registry (if not null
)
+ * @param optionalRegistry optional registry to use when this image does not provide a registry.
+ * Can be null in which case no optional registry is used*
+ * @return full name with original registry (if set) or optional registry (if not null
+ *
)
*/
public String getNameWithoutTag(String optionalRegistry) {
StringBuilder ret = new StringBuilder();
@@ -238,8 +243,8 @@ public String getNameWithoutTag(String optionalRegistry) {
// https://github.com/docker/docker/blob/04da4041757370fb6f85510c8977c5a18ddae380/vendor/github.com/docker/distribution/reference/reference.go
/**
- * Get the full name of this image, including the registry and tag
- * (e.g. privateregistry:fabric8io/java:7u53
)
+ * Get the full name of this image, including the registry and tag (e.g.
+ * privateregistry:fabric8io/java:7u53
)
*
* @return full name with the original registry and the original tag given (if any).
*/
@@ -248,13 +253,14 @@ public String getFullName() {
}
/**
- * Get the full name of this image like {@link #getFullName(String)} does, but allow
- * an optional registry. This registry is used when this image does not already
- * contain a registry. If no tag was provided in the initial name, latest
is used.
+ * Get the full name of this image like {@link #getFullName(String)} does, but allow an optional
+ * registry. This registry is used when this image does not already contain a registry. If no tag
+ * was provided in the initial name, latest
is used.
*
- * @param optionalRegistry optional registry to use when this image does not provide
- * a registry. Can be null in which case no optional registry is used*
- * @return full name with original registry (if set) or optional registry (if not null
).
+ * @param optionalRegistry optional registry to use when this image does not provide a registry.
+ * Can be null in which case no optional registry is used*
+ * @return full name with original registry (if set) or optional registry (if not null
+ *
).
*/
public String getFullName(String optionalRegistry) {
String fullName = getNameWithoutTag(optionalRegistry);
@@ -270,8 +276,8 @@ public String getFullName(String optionalRegistry) {
// ==========================================================
/**
- * Get the user (or "project") part of the image name. This is the part after the registry and before
- * the image name
+ * Get the user (or "project") part of the image name. This is the part after the registry and
+ * before the image name
*
* @return user part or null
if no user is present in the name
*/
@@ -305,20 +311,22 @@ private void doValidate() {
List errors = new ArrayList<>();
// Strip off user from repository name
String image = user != null ? repository.substring(user.length() + 1) : repository;
- Object[] checks = new Object[]{
- "registry", DOMAIN_REGEXP, registry,
- "image", IMAGE_NAME_REGEXP, image,
- "user", NAME_COMP_REGEXP, user,
- "tag", TAG_REGEXP, tag,
- "digest", DIGEST_REGEXP, digest
- };
+ Object[] checks =
+ new Object[] {
+ "registry", DOMAIN_REGEXP, registry,
+ "image", IMAGE_NAME_REGEXP, image,
+ "user", NAME_COMP_REGEXP, user,
+ "tag", TAG_REGEXP, tag,
+ "digest", DIGEST_REGEXP, digest
+ };
for (int i = 0; i < checks.length; i += 3) {
String value = (String) checks[i + 2];
Pattern checkPattern = (Pattern) checks[i + 1];
- if (value != null &&
- !checkPattern.matcher(value).matches()) {
- errors.add(String.format("%s part '%s' doesn't match allowed pattern '%s'",
- checks[i], value, checkPattern.pattern()));
+ if (value != null && !checkPattern.matcher(value).matches()) {
+ errors.add(
+ String.format(
+ "%s part '%s' doesn't match allowed pattern '%s'",
+ checks[i], value, checkPattern.pattern()));
}
}
if (errors.size() > 0) {
diff --git a/src/main/java/com/redhat/exhort/image/ImageRef.java b/src/main/java/com/redhat/exhort/image/ImageRef.java
index 92e71dec..265ce9b1 100644
--- a/src/main/java/com/redhat/exhort/image/ImageRef.java
+++ b/src/main/java/com/redhat/exhort/image/ImageRef.java
@@ -15,17 +15,16 @@
*/
package com.redhat.exhort.image;
+import static com.redhat.exhort.image.ImageUtils.getImageDigests;
+import static com.redhat.exhort.image.ImageUtils.getImagePlatform;
+
import com.fasterxml.jackson.core.JsonProcessingException;
import com.github.packageurl.MalformedPackageURLException;
import com.github.packageurl.PackageURL;
-
import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;
-import static com.redhat.exhort.image.ImageUtils.getImageDigests;
-import static com.redhat.exhort.image.ImageUtils.getImagePlatform;
-
public class ImageRef {
public static final String OCI_TYPE = "oci";
@@ -109,10 +108,7 @@ public int hashCode() {
@Override
public String toString() {
- return "ImageRef{" +
- "image='" + image + '\'' +
- ", platform='" + platform + '\'' +
- '}';
+ return "ImageRef{" + "image='" + image + '\'' + ", platform='" + platform + '\'' + '}';
}
void checkImageDigest() {
@@ -132,7 +128,8 @@ void checkImageDigest() {
throw new RuntimeException("Failed to get image platform for image digest");
}
if (!digests.containsKey(this.platform)) {
- throw new RuntimeException(String.format("Failed to get image digest for platform %s", this.platform));
+ throw new RuntimeException(
+ String.format("Failed to get image digest for platform %s", this.platform));
}
this.image.setDigest(digests.get(this.platform));
}
@@ -162,11 +159,12 @@ public PackageURL getPackageURL() throws MalformedPackageURLException {
qualifiers.put(TAG_QUALIFIER, tag);
}
- return new PackageURL(OCI_TYPE,
- null,
- this.image.getSimpleName().toLowerCase(),
- image.getDigest().toLowerCase(),
- qualifiers,
- null);
+ return new PackageURL(
+ OCI_TYPE,
+ null,
+ this.image.getSimpleName().toLowerCase(),
+ image.getDigest().toLowerCase(),
+ qualifiers,
+ null);
}
}
diff --git a/src/main/java/com/redhat/exhort/image/ImageUtils.java b/src/main/java/com/redhat/exhort/image/ImageUtils.java
index 75b5f822..8268393e 100644
--- a/src/main/java/com/redhat/exhort/image/ImageUtils.java
+++ b/src/main/java/com/redhat/exhort/image/ImageUtils.java
@@ -13,9 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package com.redhat.exhort.image;
+import static com.redhat.exhort.image.Platform.EMPTY_PLATFORM;
+import static com.redhat.exhort.impl.ExhortApi.getStringValueEnvironment;
+
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -24,7 +26,6 @@
import com.github.packageurl.MalformedPackageURLException;
import com.redhat.exhort.logging.LoggersFactory;
import com.redhat.exhort.tools.Operations;
-
import java.io.File;
import java.io.IOException;
import java.util.AbstractMap;
@@ -39,9 +40,6 @@
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
-import static com.redhat.exhort.image.Platform.EMPTY_PLATFORM;
-import static com.redhat.exhort.impl.ExhortApi.getStringValueEnvironment;
-
public class ImageUtils {
static final String EXHORT_SYFT_CONFIG_PATH = "EXHORT_SYFT_CONFIG_PATH";
@@ -52,45 +50,49 @@ public class ImageUtils {
static final String EXHORT_IMAGE_VARIANT = "EXHORT_IMAGE_VARIANT";
static final String EXHORT_SKOPEO_CONFIG_PATH = "EXHORT_SKOPEO_CONFIG_PATH";
static final String EXHORT_IMAGE_SERVICE_ENDPOINT = "EXHORT_IMAGE_SERVICE_ENDPOINT";
- private static final String MEDIA_TYPE_DOCKER2_MANIFEST = "application/vnd.docker.distribution.manifest.v2+json";
- private static final String MEDIA_TYPE_DOCKER2_MANIFEST_LIST = "application/vnd.docker.distribution.manifest.list.v2+json";
- private static final String MEDIA_TYPE_OCI1_MANIFEST = "application/vnd.oci.image.manifest.v1+json";
- private static final String MEDIA_TYPE_OCI1_MANIFEST_LIST = "application/vnd.oci.image.index.v1+json";
+ private static final String MEDIA_TYPE_DOCKER2_MANIFEST =
+ "application/vnd.docker.distribution.manifest.v2+json";
+ private static final String MEDIA_TYPE_DOCKER2_MANIFEST_LIST =
+ "application/vnd.docker.distribution.manifest.list.v2+json";
+ private static final String MEDIA_TYPE_OCI1_MANIFEST =
+ "application/vnd.oci.image.manifest.v1+json";
+ private static final String MEDIA_TYPE_OCI1_MANIFEST_LIST =
+ "application/vnd.oci.image.index.v1+json";
private static final Logger logger = LoggersFactory.getLogger(ImageUtils.class.getName());
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
- private static final Map archMapping = Map.ofEntries(
- new AbstractMap.SimpleEntry<>("amd64", "amd64"),
- new AbstractMap.SimpleEntry<>("x86_64", "amd64"),
- new AbstractMap.SimpleEntry<>("armv5tl", "arm"),
- new AbstractMap.SimpleEntry<>("armv5tel", "arm"),
- new AbstractMap.SimpleEntry<>("armv5tejl", "arm"),
- new AbstractMap.SimpleEntry<>("armv6l", "arm"),
- new AbstractMap.SimpleEntry<>("armv7l", "arm"),
- new AbstractMap.SimpleEntry<>("armv7ml", "arm"),
- new AbstractMap.SimpleEntry<>("arm64", "arm64"),
- new AbstractMap.SimpleEntry<>("aarch64", "arm64"),
- new AbstractMap.SimpleEntry<>("i386", "386"),
- new AbstractMap.SimpleEntry<>("i486", "386"),
- new AbstractMap.SimpleEntry<>("i586", "386"),
- new AbstractMap.SimpleEntry<>("i686", "386"),
- new AbstractMap.SimpleEntry<>("mips64le", "mips64le"),
- new AbstractMap.SimpleEntry<>("ppc64le", "ppc64le"),
- new AbstractMap.SimpleEntry<>("riscv64", "riscv64"),
- new AbstractMap.SimpleEntry<>("s390x", "s390x")
- );
- private static final Map variantMapping = Map.ofEntries(
- new AbstractMap.SimpleEntry<>("armv5tl", "v5"),
- new AbstractMap.SimpleEntry<>("armv5tel", "v5"),
- new AbstractMap.SimpleEntry<>("armv5tejl", "v5"),
- new AbstractMap.SimpleEntry<>("armv6l", "v6"),
- new AbstractMap.SimpleEntry<>("armv7l", "v7"),
- new AbstractMap.SimpleEntry<>("armv7ml", "v7"),
- new AbstractMap.SimpleEntry<>("arm64", "v8"),
- new AbstractMap.SimpleEntry<>("aarch64", "v8")
- );
+ private static final Map archMapping =
+ Map.ofEntries(
+ new AbstractMap.SimpleEntry<>("amd64", "amd64"),
+ new AbstractMap.SimpleEntry<>("x86_64", "amd64"),
+ new AbstractMap.SimpleEntry<>("armv5tl", "arm"),
+ new AbstractMap.SimpleEntry<>("armv5tel", "arm"),
+ new AbstractMap.SimpleEntry<>("armv5tejl", "arm"),
+ new AbstractMap.SimpleEntry<>("armv6l", "arm"),
+ new AbstractMap.SimpleEntry<>("armv7l", "arm"),
+ new AbstractMap.SimpleEntry<>("armv7ml", "arm"),
+ new AbstractMap.SimpleEntry<>("arm64", "arm64"),
+ new AbstractMap.SimpleEntry<>("aarch64", "arm64"),
+ new AbstractMap.SimpleEntry<>("i386", "386"),
+ new AbstractMap.SimpleEntry<>("i486", "386"),
+ new AbstractMap.SimpleEntry<>("i586", "386"),
+ new AbstractMap.SimpleEntry<>("i686", "386"),
+ new AbstractMap.SimpleEntry<>("mips64le", "mips64le"),
+ new AbstractMap.SimpleEntry<>("ppc64le", "ppc64le"),
+ new AbstractMap.SimpleEntry<>("riscv64", "riscv64"),
+ new AbstractMap.SimpleEntry<>("s390x", "s390x"));
+ private static final Map variantMapping =
+ Map.ofEntries(
+ new AbstractMap.SimpleEntry<>("armv5tl", "v5"),
+ new AbstractMap.SimpleEntry<>("armv5tel", "v5"),
+ new AbstractMap.SimpleEntry<>("armv5tejl", "v5"),
+ new AbstractMap.SimpleEntry<>("armv6l", "v6"),
+ new AbstractMap.SimpleEntry<>("armv7l", "v7"),
+ new AbstractMap.SimpleEntry<>("armv7ml", "v7"),
+ new AbstractMap.SimpleEntry<>("arm64", "v8"),
+ new AbstractMap.SimpleEntry<>("aarch64", "v8"));
static String updatePATHEnv(String execPath) {
String path = System.getenv("PATH");
@@ -101,7 +103,8 @@ static String updatePATHEnv(String execPath) {
}
}
- public static JsonNode generateImageSBOM(ImageRef imageRef) throws IOException, MalformedPackageURLException {
+ public static JsonNode generateImageSBOM(ImageRef imageRef)
+ throws IOException, MalformedPackageURLException {
var output = execSyft(imageRef);
if (!output.getError().isEmpty() || output.getExitCode() != 0) {
@@ -121,7 +124,8 @@ public static JsonNode generateImageSBOM(ImageRef imageRef) throws IOException,
}
}
- throw new RuntimeException(String.format("The generated SBOM of the image is invalid: %s", output.getOutput()));
+ throw new RuntimeException(
+ String.format("The generated SBOM of the image is invalid: %s", output.getOutput()));
}
static Operations.ProcessExecOutput execSyft(ImageRef imageRef) {
@@ -133,27 +137,57 @@ static Operations.ProcessExecOutput execSyft(ImageRef imageRef) {
var imageSource = getStringValueEnvironment(EXHORT_SYFT_IMAGE_SOURCE, "");
SyftImageSource.getImageSource(imageSource);
- var dockerPath = docker != null && docker.contains(File.separator) ?
- docker.substring(0, docker.lastIndexOf(File.separator) + 1) : "";
- var podmanPath = podman != null && podman.contains(File.separator) ?
- podman.substring(0, podman.lastIndexOf(File.separator) + 1) : "";
+ var dockerPath =
+ docker != null && docker.contains(File.separator)
+ ? docker.substring(0, docker.lastIndexOf(File.separator) + 1)
+ : "";
+ var podmanPath =
+ podman != null && podman.contains(File.separator)
+ ? podman.substring(0, podman.lastIndexOf(File.separator) + 1)
+ : "";
var envs = getSyftEnvs(dockerPath, podmanPath);
var scheme = imageRef.getImage().toString();
String[] cmd;
if (!imageSource.isEmpty()) {
- cmd = syftConfigPath.isEmpty() ?
- new String[]{syft, scheme, "--from", imageSource, "-s", "all-layers", "-o", "cyclonedx-json", "-q"} :
- new String[]{syft, scheme, "--from", imageSource, "-c", syftConfigPath, "-s", "all-layers", "-o", "cyclonedx-json", "-q"};
+ cmd =
+ syftConfigPath.isEmpty()
+ ? new String[] {
+ syft,
+ scheme,
+ "--from",
+ imageSource,
+ "-s",
+ "all-layers",
+ "-o",
+ "cyclonedx-json",
+ "-q"
+ }
+ : new String[] {
+ syft,
+ scheme,
+ "--from",
+ imageSource,
+ "-c",
+ syftConfigPath,
+ "-s",
+ "all-layers",
+ "-o",
+ "cyclonedx-json",
+ "-q"
+ };
} else {
- cmd = syftConfigPath.isEmpty() ?
- new String[]{syft, scheme, "-s", "all-layers", "-o", "cyclonedx-json", "-q"} :
- new String[]{syft, scheme, "-c", syftConfigPath, "-s", "all-layers", "-o", "cyclonedx-json", "-q"};
+ cmd =
+ syftConfigPath.isEmpty()
+ ? new String[] {syft, scheme, "-s", "all-layers", "-o", "cyclonedx-json", "-q"}
+ : new String[] {
+ syft, scheme, "-c", syftConfigPath, "-s", "all-layers", "-o", "cyclonedx-json", "-q"
+ };
}
- return Operations.runProcessGetFullOutput(null, cmd,
- envs.isEmpty() ? null : envs.toArray(new String[1]));
+ return Operations.runProcessGetFullOutput(
+ null, cmd, envs.isEmpty() ? null : envs.toArray(new String[1]));
}
static List getSyftEnvs(String dockerPath, String podmanPath) {
@@ -210,19 +244,21 @@ public static Platform getImagePlatform() {
static String hostInfo(String engine, String info) {
var exec = Operations.getCustomPathOrElse(engine);
- var cmd = new String[]{exec, "info"};
+ var cmd = new String[] {exec, "info"};
var output = Operations.runProcessGetFullOutput(null, cmd, null);
- if (output.getOutput().isEmpty() && (!output.getError().isEmpty() || output.getExitCode() != 0)) {
+ if (output.getOutput().isEmpty()
+ && (!output.getError().isEmpty() || output.getExitCode() != 0)) {
throw new RuntimeException(output.getError());
}
- return output.getOutput()
- .lines()
- .filter(line -> line.stripLeading().startsWith(info + ":"))
- .map(line -> line.strip().substring(info.length() + 1).strip())
- .findAny()
- .orElse("");
+ return output
+ .getOutput()
+ .lines()
+ .filter(line -> line.stripLeading().startsWith(info + ":"))
+ .map(line -> line.strip().substring(info.length() + 1).strip())
+ .findAny()
+ .orElse("");
}
static String dockerGetOs() {
@@ -261,7 +297,8 @@ static String dockerPodmanInfo(Supplier dockerSupplier, Supplier
return info;
}
- public static Map getImageDigests(ImageRef imageRef) throws JsonProcessingException {
+ public static Map getImageDigests(ImageRef imageRef)
+ throws JsonProcessingException {
var output = execSkopeoInspect(imageRef, true);
if (!output.getError().isEmpty() || output.getExitCode() != 0) {
@@ -293,23 +330,23 @@ static Map getMultiImageDigests(JsonNode node) {
var manifestsNode = node.get("manifests");
if (manifestsNode.isArray()) {
return StreamSupport.stream(manifestsNode.spliterator(), false)
- .filter(ImageUtils::filterMediaType)
- .filter(ImageUtils::filterDigest)
- .filter(ImageUtils::filterPlatform)
- .collect(Collectors.toMap(
- manifestNode -> {
- var platformNode = manifestNode.get("platform");
- var arch = platformNode.get("architecture").asText();
- var os = platformNode.get("os").asText();
- if (platformNode.hasNonNull("variant")) {
- var variant = platformNode.get("variant").asText();
- return new Platform(String.format("%s/%s/%s", os, arch, variant));
- } else {
- return new Platform(String.format("%s/%s", os, arch));
- }
- },
- manifestNode -> manifestNode.get("digest").asText()
- ));
+ .filter(ImageUtils::filterMediaType)
+ .filter(ImageUtils::filterDigest)
+ .filter(ImageUtils::filterPlatform)
+ .collect(
+ Collectors.toMap(
+ manifestNode -> {
+ var platformNode = manifestNode.get("platform");
+ var arch = platformNode.get("architecture").asText();
+ var os = platformNode.get("os").asText();
+ if (platformNode.hasNonNull("variant")) {
+ var variant = platformNode.get("variant").asText();
+ return new Platform(String.format("%s/%s/%s", os, arch, variant));
+ } else {
+ return new Platform(String.format("%s/%s", os, arch));
+ }
+ },
+ manifestNode -> manifestNode.get("digest").asText()));
}
}
return Collections.emptyMap();
@@ -320,7 +357,8 @@ static boolean filterMediaType(JsonNode manifestNode) {
var mediaTypeNode = manifestNode.get("mediaType");
if (mediaTypeNode.isTextual()) {
var mediaType = mediaTypeNode.asText();
- return MEDIA_TYPE_OCI1_MANIFEST.equals(mediaType) || MEDIA_TYPE_DOCKER2_MANIFEST.equals(mediaType);
+ return MEDIA_TYPE_OCI1_MANIFEST.equals(mediaType)
+ || MEDIA_TYPE_DOCKER2_MANIFEST.equals(mediaType);
}
}
return false;
@@ -346,7 +384,10 @@ static boolean filterPlatform(JsonNode manifestNode) {
var variantNode = platformNode.get("variant");
if (variantNode.isTextual()) {
try {
- new Platform(String.format("%s/%s/%s", osNode.asText(), architectureNode.asText(), variantNode.asText()));
+ new Platform(
+ String.format(
+ "%s/%s/%s",
+ osNode.asText(), architectureNode.asText(), variantNode.asText()));
} catch (IllegalArgumentException e) {
return false;
}
@@ -366,7 +407,8 @@ static boolean filterPlatform(JsonNode manifestNode) {
return false;
}
- static Map getSingleImageDigest(ImageRef imageRef) throws JsonProcessingException {
+ static Map getSingleImageDigest(ImageRef imageRef)
+ throws JsonProcessingException {
var output = execSkopeoInspect(imageRef, false);
if (!output.getError().isEmpty() || output.getExitCode() != 0) {
@@ -392,49 +434,74 @@ static Operations.ProcessExecOutput execSkopeoInspect(ImageRef imageRef, boolean
String[] cmd;
if (daemonHost.isEmpty()) {
- cmd = configPath.isEmpty() ?
- new String[]{skopeo, "inspect", raw ? "--raw" : "",
- String.format("docker://%s", imageRef.getImage().getFullName())} :
- new String[]{skopeo, "inspect", "--authfile", configPath, raw ? "--raw" : "",
- String.format("docker://%s", imageRef.getImage().getFullName())};
+ cmd =
+ configPath.isEmpty()
+ ? new String[] {
+ skopeo,
+ "inspect",
+ raw ? "--raw" : "",
+ String.format("docker://%s", imageRef.getImage().getFullName())
+ }
+ : new String[] {
+ skopeo,
+ "inspect",
+ "--authfile",
+ configPath,
+ raw ? "--raw" : "",
+ String.format("docker://%s", imageRef.getImage().getFullName())
+ };
} else {
- cmd = configPath.isEmpty() ?
- new String[]{skopeo, "inspect", "--daemon-host", daemonHost, raw ? "--raw" : "",
- String.format("docker-daemon:%s", imageRef.getImage().getFullName())} :
- new String[]{skopeo, "inspect", "--authfile", configPath, "--daemon-host", daemonHost, raw ? "--raw" : "",
- String.format("docker-daemon:%s", imageRef.getImage().getFullName())};
+ cmd =
+ configPath.isEmpty()
+ ? new String[] {
+ skopeo,
+ "inspect",
+ "--daemon-host",
+ daemonHost,
+ raw ? "--raw" : "",
+ String.format("docker-daemon:%s", imageRef.getImage().getFullName())
+ }
+ : new String[] {
+ skopeo,
+ "inspect",
+ "--authfile",
+ configPath,
+ "--daemon-host",
+ daemonHost,
+ raw ? "--raw" : "",
+ String.format("docker-daemon:%s", imageRef.getImage().getFullName())
+ };
}
return Operations.runProcessGetFullOutput(null, cmd, null);
}
private enum SyftImageSource {
- DEFAULT("",
- () -> dockerPodmanInfo(ImageUtils::dockerGetOs, ImageUtils::podmanGetOs),
- () -> dockerPodmanInfo(ImageUtils::dockerGetArch, ImageUtils::podmanGetArch),
- () -> dockerPodmanInfo(ImageUtils::dockerGetVariant, ImageUtils::podmanGetVariant)),
- REGISTRY("registry",
- () -> dockerPodmanInfo(ImageUtils::dockerGetOs, ImageUtils::podmanGetOs),
- () -> dockerPodmanInfo(ImageUtils::dockerGetArch, ImageUtils::podmanGetArch),
- () -> dockerPodmanInfo(ImageUtils::dockerGetVariant, ImageUtils::podmanGetVariant)),
- DOCKER("docker",
- ImageUtils::dockerGetOs,
- ImageUtils::dockerGetArch,
- ImageUtils::dockerGetVariant),
- PODMAN("podman",
- ImageUtils::podmanGetOs,
- ImageUtils::podmanGetArch,
- ImageUtils::podmanGetVariant);
+ DEFAULT(
+ "",
+ () -> dockerPodmanInfo(ImageUtils::dockerGetOs, ImageUtils::podmanGetOs),
+ () -> dockerPodmanInfo(ImageUtils::dockerGetArch, ImageUtils::podmanGetArch),
+ () -> dockerPodmanInfo(ImageUtils::dockerGetVariant, ImageUtils::podmanGetVariant)),
+ REGISTRY(
+ "registry",
+ () -> dockerPodmanInfo(ImageUtils::dockerGetOs, ImageUtils::podmanGetOs),
+ () -> dockerPodmanInfo(ImageUtils::dockerGetArch, ImageUtils::podmanGetArch),
+ () -> dockerPodmanInfo(ImageUtils::dockerGetVariant, ImageUtils::podmanGetVariant)),
+ DOCKER(
+ "docker", ImageUtils::dockerGetOs, ImageUtils::dockerGetArch, ImageUtils::dockerGetVariant),
+ PODMAN(
+ "podman", ImageUtils::podmanGetOs, ImageUtils::podmanGetArch, ImageUtils::podmanGetVariant);
private final String name;
private final Supplier osSupplier;
private final Supplier archSupplier;
private final Supplier variantSupplier;
- SyftImageSource(String name,
- Supplier osSupplier,
- Supplier archSupplier,
- Supplier variantSupplier) {
+ SyftImageSource(
+ String name,
+ Supplier osSupplier,
+ Supplier archSupplier,
+ Supplier variantSupplier) {
this.name = name;
this.osSupplier = osSupplier;
this.archSupplier = archSupplier;
@@ -443,9 +510,12 @@ private enum SyftImageSource {
static SyftImageSource getImageSource(String name) {
return EnumSet.allOf(SyftImageSource.class).stream()
- .filter(s -> s.name.equals(name))
- .findAny()
- .orElseThrow(() -> new IllegalArgumentException(String.format("The image source for syft is not valid: %s", name)));
+ .filter(s -> s.name.equals(name))
+ .findAny()
+ .orElseThrow(
+ () ->
+ new IllegalArgumentException(
+ String.format("The image source for syft is not valid: %s", name)));
}
String getOs() {
@@ -461,4 +531,3 @@ String getVariant() {
}
}
}
-
diff --git a/src/main/java/com/redhat/exhort/image/Platform.java b/src/main/java/com/redhat/exhort/image/Platform.java
index d2636a96..70a7deb6 100644
--- a/src/main/java/com/redhat/exhort/image/Platform.java
+++ b/src/main/java/com/redhat/exhort/image/Platform.java
@@ -22,7 +22,8 @@ public class Platform {
// $GOOS and $GOARCH
// https://github.com/docker-library/bashbrew/blob/v0.1.2/architecture/oci-platform.go#L14-L27
- private static final Set SUPPORTED_PLATFORMS = Set.of(
+ private static final Set SUPPORTED_PLATFORMS =
+ Set.of(
new Platform().os("linux").arch("amd64"),
new Platform().os("linux").arch("arm").variant("v5"),
new Platform().os("linux").arch("arm").variant("v6"),
@@ -33,9 +34,7 @@ public class Platform {
new Platform().os("linux").arch("ppc64le"),
new Platform().os("linux").arch("riscv64"),
new Platform().os("linux").arch("s390x"),
-
- new Platform().os("windows").arch("arm64")
- );
+ new Platform().os("windows").arch("arm64"));
public static final Platform EMPTY_PLATFORM = new Platform();
@@ -43,8 +42,7 @@ public class Platform {
private String architecture;
private String variant;
- private Platform() {
- }
+ private Platform() {}
public Platform(String platform) {
if (platform == null) {
@@ -68,7 +66,8 @@ public Platform(String platform) {
}
if (!SUPPORTED_PLATFORMS.contains(this)) {
- throw new IllegalArgumentException(String.format("Image platform is not supported: %s", platform));
+ throw new IllegalArgumentException(
+ String.format("Image platform is not supported: %s", platform));
}
}
@@ -91,7 +90,8 @@ public Platform(String os, String arch, String variant) {
}
if (!SUPPORTED_PLATFORMS.contains(this)) {
- throw new IllegalArgumentException(String.format("Image platform is not supported: %s/%s/%s", os, arch, variant));
+ throw new IllegalArgumentException(
+ String.format("Image platform is not supported: %s/%s/%s", os, arch, variant));
}
}
@@ -138,7 +138,9 @@ public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Platform platform = (Platform) o;
- return Objects.equals(os, platform.os) && Objects.equals(architecture, platform.architecture) && Objects.equals(variant, platform.variant);
+ return Objects.equals(os, platform.os)
+ && Objects.equals(architecture, platform.architecture)
+ && Objects.equals(variant, platform.variant);
}
@Override
diff --git a/src/main/java/com/redhat/exhort/impl/ExhortApi.java b/src/main/java/com/redhat/exhort/impl/ExhortApi.java
index 25f3012c..ddca1efe 100644
--- a/src/main/java/com/redhat/exhort/impl/ExhortApi.java
+++ b/src/main/java/com/redhat/exhort/impl/ExhortApi.java
@@ -15,6 +15,22 @@
*/
package com.redhat.exhort.impl;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.github.packageurl.MalformedPackageURLException;
+import com.github.packageurl.PackageURL;
+import com.redhat.exhort.Api;
+import com.redhat.exhort.Provider;
+import com.redhat.exhort.api.AnalysisReport;
+import com.redhat.exhort.image.ImageRef;
+import com.redhat.exhort.image.ImageUtils;
+import com.redhat.exhort.logging.LoggersFactory;
+import com.redhat.exhort.tools.Ecosystem;
+import jakarta.mail.MessagingException;
+import jakarta.mail.internet.MimeMultipart;
+import jakarta.mail.util.ByteArrayDataSource;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
@@ -43,35 +59,13 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.DeserializationFeature;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.github.packageurl.MalformedPackageURLException;
-import com.github.packageurl.PackageURL;
-import com.redhat.exhort.Api;
-import com.redhat.exhort.Provider;
-import com.redhat.exhort.api.AnalysisReport;
-import com.redhat.exhort.image.ImageRef;
-import com.redhat.exhort.image.ImageUtils;
-import com.redhat.exhort.logging.LoggersFactory;
-import com.redhat.exhort.tools.Ecosystem;
-
-import jakarta.mail.MessagingException;
-import jakarta.mail.internet.MimeMultipart;
-import jakarta.mail.util.ByteArrayDataSource;
-
-/**
- * Concrete implementation of the Exhort {@link Api} Service.
- **/
+/** Concrete implementation of the Exhort {@link Api} Service. */
public final class ExhortApi implements Api {
-// private static final System.Logger LOG = System.getLogger(ExhortApi.class.getName());
+ // private static final System.Logger LOG = System.getLogger(ExhortApi.class.getName());
private static final Logger LOG = LoggersFactory.getLogger(ExhortApi.class.getName());
-
-
public static final String DEFAULT_ENDPOINT = "https://rhda.rhcloud.com";
public static final String DEFAULT_ENDPOINT_DEV = "https://exhort.stage.devshift.net";
public static final String RHDA_TOKEN_HEADER = "rhda-token";
@@ -85,27 +79,31 @@ public String getEndpoint() {
return endpoint;
}
- public static final void main(String[] args) throws IOException, InterruptedException, ExecutionException {
+ public static final void main(String[] args)
+ throws IOException, InterruptedException, ExecutionException {
System.setProperty("EXHORT_DEV_MODE", "true");
- AnalysisReport analysisReport = new ExhortApi()
- .stackAnalysisMixed("/tmp/exhort_test_10582748308498949664/pom.xml").get().json;
-// ObjectMapper om = new ObjectMapper().configure(SerializationFeature.WRITE_EMPTY_JSON_ARRAYS, false);
-// System.out.println(om.writerWithDefaultPrettyPrinter().writeValueAsString(analysisReport));
-// AnalysisReport analysisReport = new ExhortApi()
-// byte[] analysisReport = new ExhortApi().
-// stackAnalysisHtml("/home/zgrinber/git/exhort-java-api/src/test/resources/tst_manifests/golang/go_mod_with_one_ignored_prefix_go/go.mod").get();
-// Path html = Files.createFile(Path.of("/","tmp", "golang0210.html"));
-// Files.write(html,analysisReport);
-
- }
-
- /**
- * Enum for identifying token environment variables and their
- * corresponding request headers.
- */
+ AnalysisReport analysisReport =
+ new ExhortApi()
+ .stackAnalysisMixed("/tmp/exhort_test_10582748308498949664/pom.xml")
+ .get()
+ .json;
+ // ObjectMapper om = new
+ // ObjectMapper().configure(SerializationFeature.WRITE_EMPTY_JSON_ARRAYS, false);
+ //
+ // System.out.println(om.writerWithDefaultPrettyPrinter().writeValueAsString(analysisReport));
+ // AnalysisReport analysisReport = new ExhortApi()
+ // byte[] analysisReport = new ExhortApi().
+ //
+ // stackAnalysisHtml("/home/zgrinber/git/exhort-java-api/src/test/resources/tst_manifests/golang/go_mod_with_one_ignored_prefix_go/go.mod").get();
+ // Path html = Files.createFile(Path.of("/","tmp", "golang0210.html"));
+ // Files.write(html,analysisReport);
+
+ }
+
+ /** Enum for identifying token environment variables and their corresponding request headers. */
private enum TokenProvider {
- SNYK, OSS_INDEX;
-
+ SNYK,
+ OSS_INDEX;
/**
* Get the expected environment variable name.
@@ -146,26 +144,35 @@ public ExhortApi() {
}
/**
- * Get the HTTP protocol Version set by client in environment variable, if not set, the default is HTTP Protocol Version 1.1
+ * Get the HTTP protocol Version set by client in environment variable, if not set, the default is
+ * HTTP Protocol Version 1.1
*
* @return i.e. HttpClient.Version.HTTP_1.1
*/
static HttpClient.Version getHttpVersion() {
- return (System.getenv("HTTP_VERSION_EXHORT_CLIENT") != null && System.getenv("HTTP_VERSION_EXHORT_CLIENT").contains("2")) ? HttpClient.Version.HTTP_2 : HttpClient.Version.HTTP_1_1;
+ return (System.getenv("HTTP_VERSION_EXHORT_CLIENT") != null
+ && System.getenv("HTTP_VERSION_EXHORT_CLIENT").contains("2"))
+ ? HttpClient.Version.HTTP_2
+ : HttpClient.Version.HTTP_1_1;
}
ExhortApi(final HttpClient client) {
-// // temp system property - as long as prod exhort url not implemented the multi-source v4 endpoint, this property needs to be true
-// System.setProperty("EXHORT_DEV_MODE","true");
+ // // temp system property - as long as prod exhort url not implemented the multi-source v4
+ // endpoint, this
+ // property needs to be true
+ // System.setProperty("EXHORT_DEV_MODE","true");
commonHookBeginning(true);
this.client = client;
this.mapper = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
// Take default from config.properties in case client didn't override DEV MODE
if (System.getProperty("EXHORT_DEV_MODE") == null) {
try {
- InputStream exhortConfig = this.getClass().getClassLoader().getResourceAsStream("config.properties");
+ InputStream exhortConfig =
+ this.getClass().getClassLoader().getResourceAsStream("config.properties");
if (exhortConfig == null) {
- LOG.info("config.properties not found on the class path, fallback to default DEV MODE = false");
+ LOG.info(
+ "config.properties not found on the class path, fallback to default DEV MODE ="
+ + " false");
System.setProperty("EXHORT_DEV_MODE", "false");
} else {
Properties properties = new Properties();
@@ -173,7 +180,11 @@ static HttpClient.Version getHttpVersion() {
System.setProperty("EXHORT_DEV_MODE", (String) properties.get("EXHORT_DEV_MODE"));
}
} catch (IOException e) {
- LOG.info(String.format("Error loading config.properties , fallback to set default property DEV MODE = false, Error message = %s", e.getMessage()));
+ LOG.info(
+ String.format(
+ "Error loading config.properties , fallback to set default property DEV MODE ="
+ + " false, Error message = %s",
+ e.getMessage()));
System.setProperty("EXHORT_DEV_MODE", "false");
}
}
@@ -182,14 +193,13 @@ static HttpClient.Version getHttpVersion() {
}
private String commonHookBeginning(boolean startOfApi) {
- if(startOfApi) {
+ if (startOfApi) {
generateClientRequestId();
if (debugLoggingIsNeeded()) {
LOG.info("Start of exhort-java-api client");
}
- }
- else {
- if(Objects.isNull(getClientRequestId())) {
+ } else {
+ if (Objects.isNull(getClientRequestId())) {
generateClientRequestId();
}
if (debugLoggingIsNeeded()) {
@@ -219,97 +229,159 @@ public String getExhortUrl() {
endpoint = DEFAULT_ENDPOINT;
}
if (debugLoggingIsNeeded()) {
- LOG.info(String.format("EXHORT_DEV_MODE=%s,DEV_EXHORT_BACKEND_URL=%s, Chosen Backend URL=%s , DEFAULT_ENDPOINT_DEV=%s , DEFAULT_ENDPOINT=%s", getBooleanValueEnvironment("EXHORT_DEV_MODE", "false"), getStringValueEnvironment("DEV_EXHORT_BACKEND_URL", DEFAULT_ENDPOINT_DEV), endpoint, DEFAULT_ENDPOINT_DEV, DEFAULT_ENDPOINT));
+ LOG.info(
+ String.format(
+ "EXHORT_DEV_MODE=%s,DEV_EXHORT_BACKEND_URL=%s, Chosen Backend URL=%s ,"
+ + " DEFAULT_ENDPOINT_DEV=%s , DEFAULT_ENDPOINT=%s",
+ getBooleanValueEnvironment("EXHORT_DEV_MODE", "false"),
+ getStringValueEnvironment("DEV_EXHORT_BACKEND_URL", DEFAULT_ENDPOINT_DEV),
+ endpoint,
+ DEFAULT_ENDPOINT_DEV,
+ DEFAULT_ENDPOINT));
}
return endpoint;
}
public static boolean getBooleanValueEnvironment(String key, String defaultValue) {
- String result = Objects.requireNonNullElse(System.getenv(key), Objects.requireNonNullElse(System.getProperty(key), defaultValue));
+ String result =
+ Objects.requireNonNullElse(
+ System.getenv(key), Objects.requireNonNullElse(System.getProperty(key), defaultValue));
return Boolean.parseBoolean(result.trim().toLowerCase());
}
public static String getStringValueEnvironment(String key, String defaultValue) {
- String result = Objects.requireNonNullElse(System.getenv(key), Objects.requireNonNullElse(System.getProperty(key), defaultValue));
+ String result =
+ Objects.requireNonNullElse(
+ System.getenv(key), Objects.requireNonNullElse(System.getProperty(key), defaultValue));
return result;
}
@Override
- public CompletableFuture stackAnalysisMixed(final String manifestFile) throws IOException {
+ public CompletableFuture stackAnalysisMixed(final String manifestFile)
+ throws IOException {
String exClientTraceId = commonHookBeginning(false);
- return this.client.sendAsync(this.buildStackRequest(manifestFile, MediaType.MULTIPART_MIXED), HttpResponse.BodyHandlers.ofByteArray()).thenApply(resp -> {
- RequestManager.getInstance().addClientTraceIdToRequest(exClientTraceId);
- if(debugLoggingIsNeeded()) {
- logExhortRequestId(resp);
- }
- if (resp.statusCode() == 200) {
- byte[] htmlPart = null;
- AnalysisReport jsonPart = null;
- var ds = new ByteArrayDataSource(resp.body(), MediaType.MULTIPART_MIXED.toString());
- try {
- var mp = new MimeMultipart(ds);
- for (var i = 0; i < mp.getCount(); i++) {
- if (Objects.isNull(htmlPart) && MediaType.TEXT_HTML.toString().equals(mp.getBodyPart(i).getContentType())) {
- htmlPart = mp.getBodyPart(i).getInputStream().readAllBytes();
- }
- if (Objects.isNull(jsonPart) && MediaType.APPLICATION_JSON.toString().equals(mp.getBodyPart(i).getContentType())) {
- jsonPart = this.mapper.readValue(mp.getBodyPart(i).getInputStream().readAllBytes(), AnalysisReport.class);
- }
- }
- } catch (IOException | MessagingException e) {
- throw new RuntimeException(e);
- }
- commonHookAfterExhortResponse();
- return new MixedReport(Objects.requireNonNull(htmlPart), Objects.requireNonNull(jsonPart));
- } else {
- LOG.severe(String.format("failed to invoke stackAnalysisMixed for getting the html and json reports, Http Response Status=%s , received message from server= %s ", resp.statusCode(), new String(resp.body())));
- return new MixedReport();
- }
- });
+ return this.client
+ .sendAsync(
+ this.buildStackRequest(manifestFile, MediaType.MULTIPART_MIXED),
+ HttpResponse.BodyHandlers.ofByteArray())
+ .thenApply(
+ resp -> {
+ RequestManager.getInstance().addClientTraceIdToRequest(exClientTraceId);
+ if (debugLoggingIsNeeded()) {
+ logExhortRequestId(resp);
+ }
+ if (resp.statusCode() == 200) {
+ byte[] htmlPart = null;
+ AnalysisReport jsonPart = null;
+ var ds = new ByteArrayDataSource(resp.body(), MediaType.MULTIPART_MIXED.toString());
+ try {
+ var mp = new MimeMultipart(ds);
+ for (var i = 0; i < mp.getCount(); i++) {
+ if (Objects.isNull(htmlPart)
+ && MediaType.TEXT_HTML
+ .toString()
+ .equals(mp.getBodyPart(i).getContentType())) {
+ htmlPart = mp.getBodyPart(i).getInputStream().readAllBytes();
+ }
+ if (Objects.isNull(jsonPart)
+ && MediaType.APPLICATION_JSON
+ .toString()
+ .equals(mp.getBodyPart(i).getContentType())) {
+ jsonPart =
+ this.mapper.readValue(
+ mp.getBodyPart(i).getInputStream().readAllBytes(),
+ AnalysisReport.class);
+ }
+ }
+ } catch (IOException | MessagingException e) {
+ throw new RuntimeException(e);
+ }
+ commonHookAfterExhortResponse();
+ return new MixedReport(
+ Objects.requireNonNull(htmlPart), Objects.requireNonNull(jsonPart));
+ } else {
+ LOG.severe(
+ String.format(
+ "failed to invoke stackAnalysisMixed for getting the html and json reports,"
+ + " Http Response Status=%s , received message from server= %s ",
+ resp.statusCode(), new String(resp.body())));
+ return new MixedReport();
+ }
+ });
}
@Override
public CompletableFuture stackAnalysisHtml(final String manifestFile) throws IOException {
String exClientTraceId = commonHookBeginning(false);
- return this.client.sendAsync(this.buildStackRequest(manifestFile, MediaType.TEXT_HTML), HttpResponse.BodyHandlers.ofByteArray()).thenApply(httpResponse -> {
- RequestManager.getInstance().addClientTraceIdToRequest(exClientTraceId);
- if(debugLoggingIsNeeded()) {
- logExhortRequestId(httpResponse);
- }
- if (httpResponse.statusCode() != 200) {
- LOG.severe(String.format("failed to invoke stackAnalysis for getting the html report, Http Response Status=%s , received message from server= %s ", httpResponse.statusCode(), new String(httpResponse.body())));
- }
- commonHookAfterExhortResponse();
- return httpResponse.body();
- }).exceptionally(exception -> {
- LOG.severe(String.format("failed to invoke stackAnalysis for getting the html report, received message= %s ", exception.getMessage()));
-// LOG.log(System.Logger.Level.ERROR, "Exception Entity", exception);
- commonHookAfterExhortResponse();
- return new byte[0];
- });
+ return this.client
+ .sendAsync(
+ this.buildStackRequest(manifestFile, MediaType.TEXT_HTML),
+ HttpResponse.BodyHandlers.ofByteArray())
+ .thenApply(
+ httpResponse -> {
+ RequestManager.getInstance().addClientTraceIdToRequest(exClientTraceId);
+ if (debugLoggingIsNeeded()) {
+ logExhortRequestId(httpResponse);
+ }
+ if (httpResponse.statusCode() != 200) {
+ LOG.severe(
+ String.format(
+ "failed to invoke stackAnalysis for getting the html report, Http Response"
+ + " Status=%s , received message from server= %s ",
+ httpResponse.statusCode(), new String(httpResponse.body())));
+ }
+ commonHookAfterExhortResponse();
+ return httpResponse.body();
+ })
+ .exceptionally(
+ exception -> {
+ LOG.severe(
+ String.format(
+ "failed to invoke stackAnalysis for getting the html report, received"
+ + " message= %s ",
+ exception.getMessage()));
+ // LOG.log(System.Logger.Level.ERROR, "Exception Entity", exception);
+ commonHookAfterExhortResponse();
+ return new byte[0];
+ });
}
@Override
- public CompletableFuture stackAnalysis(final String manifestFile) throws IOException {
+ public CompletableFuture stackAnalysis(final String manifestFile)
+ throws IOException {
String exClientTraceId = commonHookBeginning(false);
- return this.client.sendAsync(this.buildStackRequest(manifestFile, MediaType.APPLICATION_JSON), HttpResponse.BodyHandlers.ofString())
-// .thenApply(HttpResponse::body)
- .thenApply(response -> getAnalysisReportFromResponse(response, "StackAnalysis", "json",exClientTraceId)).exceptionally(exception -> {
- LOG.severe(String.format("failed to invoke stackAnalysis for getting the json report, received message= %s ", exception.getMessage()));
-// LOG.log(System.Logger.Level.ERROR, "Exception Entity", exception);
- return new AnalysisReport();
- });
- }
-
- private AnalysisReport getAnalysisReportFromResponse(HttpResponse response, String operation, String reportName,String exClientTraceId) {
+ return this.client
+ .sendAsync(
+ this.buildStackRequest(manifestFile, MediaType.APPLICATION_JSON),
+ HttpResponse.BodyHandlers.ofString())
+ // .thenApply(HttpResponse::body)
+ .thenApply(
+ response ->
+ getAnalysisReportFromResponse(response, "StackAnalysis", "json", exClientTraceId))
+ .exceptionally(
+ exception -> {
+ LOG.severe(
+ String.format(
+ "failed to invoke stackAnalysis for getting the json report, received"
+ + " message= %s ",
+ exception.getMessage()));
+ // LOG.log(System.Logger.Level.ERROR, "Exception Entity", exception);
+ return new AnalysisReport();
+ });
+ }
+
+ private AnalysisReport getAnalysisReportFromResponse(
+ HttpResponse response, String operation, String reportName, String exClientTraceId) {
RequestManager.getInstance().addClientTraceIdToRequest(exClientTraceId);
if (debugLoggingIsNeeded()) {
logExhortRequestId(response);
}
if (response.statusCode() == 200) {
if (debugLoggingIsNeeded()) {
- LOG.info(String.format("Response body received from exhort server : %s %s", System.lineSeparator(), response.body()));
-
+ LOG.info(
+ String.format(
+ "Response body received from exhort server : %s %s",
+ System.lineSeparator(), response.body()));
}
commonHookAfterExhortResponse();
try {
@@ -320,55 +392,77 @@ private AnalysisReport getAnalysisReportFromResponse(HttpResponse respon
}
} else {
- LOG.severe(String.format("failed to invoke %s for getting the %s report, Http Response Status=%s , received message from server= %s ", operation, reportName, response.statusCode(), response.body()));
+ LOG.severe(
+ String.format(
+ "failed to invoke %s for getting the %s report, Http Response Status=%s , received"
+ + " message from server= %s ",
+ operation, reportName, response.statusCode(), response.body()));
return new AnalysisReport();
}
-
}
private static void logExhortRequestId(HttpResponse response) {
- Optional headerExRequestId = response.headers().allValues(EXHORT_REQUEST_ID_HEADER_NAME).stream().findFirst();
- headerExRequestId.ifPresent(value -> LOG.info(String.format("Unique Identifier associated with this request ( Received from Exhort Backend ) - ex-request-id= : %s", value)));
+ Optional headerExRequestId =
+ response.headers().allValues(EXHORT_REQUEST_ID_HEADER_NAME).stream().findFirst();
+ headerExRequestId.ifPresent(
+ value ->
+ LOG.info(
+ String.format(
+ "Unique Identifier associated with this request ( Received from Exhort Backend"
+ + " ) - ex-request-id= : %s",
+ value)));
}
public static boolean debugLoggingIsNeeded() {
- return Boolean.parseBoolean(getStringValueEnvironment("EXHORT_DEBUG","false"));
+ return Boolean.parseBoolean(getStringValueEnvironment("EXHORT_DEBUG", "false"));
}
@Override
- public CompletableFuture componentAnalysis(final String manifestType, final byte[] manifestContent) throws IOException {
+ public CompletableFuture componentAnalysis(
+ final String manifestType, final byte[] manifestContent) throws IOException {
String exClientTraceId = commonHookBeginning(false);
var provider = Ecosystem.getProvider(manifestType);
var uri = URI.create(String.format("%s/api/v4/analysis", this.endpoint));
var content = provider.provideComponent(manifestContent);
commonHookAfterProviderCreatedSbomAndBeforeExhort();
- return getAnalysisReportForComponent(uri, content,exClientTraceId);
+ return getAnalysisReportForComponent(uri, content, exClientTraceId);
}
private void commonHookAfterProviderCreatedSbomAndBeforeExhort() {
- if(debugLoggingIsNeeded()) {
+ if (debugLoggingIsNeeded()) {
LOG.info("After Provider created sbom hook");
this.providerEndTime = LocalDateTime.now();
LOG.info(String.format("After Creating Sbom time: %s", this.startTime));
- LOG.info(String.format("Time took to create sbom file to be sent to exhort backend, in ms : %s, in seconds: %s",this.startTime.until(this.providerEndTime, ChronoUnit.MILLIS),(float)(this.startTime.until(this.providerEndTime, ChronoUnit.MILLIS) / 1000F)));
+ LOG.info(
+ String.format(
+ "Time took to create sbom file to be sent to exhort backend, in ms : %s, in seconds:"
+ + " %s",
+ this.startTime.until(this.providerEndTime, ChronoUnit.MILLIS),
+ (float) (this.startTime.until(this.providerEndTime, ChronoUnit.MILLIS) / 1000F)));
}
-
-
-
}
private void commonHookAfterExhortResponse() {
- if(debugLoggingIsNeeded()) {
+ if (debugLoggingIsNeeded()) {
this.endTime = LocalDateTime.now();
LOG.info(String.format("After got response from exhort time: %s", this.endTime));
- LOG.info(String.format("Time took to get response from exhort backend, in ms: %s, in seconds: %s",this.providerEndTime.until(this.endTime, ChronoUnit.MILLIS),this.providerEndTime.until(this.endTime, ChronoUnit.MILLIS) / 1000F));
- LOG.info(String.format("Total time took for complete analysis, in ms: %s, in seconds: %s",this.startTime.until(this.endTime, ChronoUnit.MILLIS),this.startTime.until(this.endTime, ChronoUnit.MILLIS) / 1000F));
-
+ LOG.info(
+ String.format(
+ "Time took to get response from exhort backend, in ms: %s, in seconds: %s",
+ this.providerEndTime.until(this.endTime, ChronoUnit.MILLIS),
+ this.providerEndTime.until(this.endTime, ChronoUnit.MILLIS) / 1000F));
+ LOG.info(
+ String.format(
+ "Total time took for complete analysis, in ms: %s, in seconds: %s",
+ this.startTime.until(this.endTime, ChronoUnit.MILLIS),
+ this.startTime.until(this.endTime, ChronoUnit.MILLIS) / 1000F));
}
RequestManager.getInstance().removeClientTraceIdFromRequest();
}
+
@Override
- public CompletableFuture componentAnalysis(String manifestFile) throws IOException {
+ public CompletableFuture componentAnalysis(String manifestFile)
+ throws IOException {
String exClientTraceId = commonHookBeginning(false);
var manifestPath = Paths.get(manifestFile);
var provider = Ecosystem.getProvider(manifestPath);
@@ -378,25 +472,39 @@ public CompletableFuture componentAnalysis(String manifestFile)
return getAnalysisReportForComponent(uri, content, exClientTraceId);
}
- private CompletableFuture getAnalysisReportForComponent(URI uri, Provider.Content content, String exClientTraceId) {
- return this.client.sendAsync(this.buildRequest(content, uri, MediaType.APPLICATION_JSON, "Component Analysis"), HttpResponse.BodyHandlers.ofString())
-// .thenApply(HttpResponse::body)
- .thenApply(response -> getAnalysisReportFromResponse(response, "Component Analysis", "json",exClientTraceId)).exceptionally(exception -> {
- LOG.severe( String.format("failed to invoke Component Analysis for getting the json report, received message= %s ", exception.getMessage()));
-// LOG.log(System.Logger.Level.ERROR, "Exception Entity", exception);
- return new AnalysisReport();
- });
+ private CompletableFuture getAnalysisReportForComponent(
+ URI uri, Provider.Content content, String exClientTraceId) {
+ return this.client
+ .sendAsync(
+ this.buildRequest(content, uri, MediaType.APPLICATION_JSON, "Component Analysis"),
+ HttpResponse.BodyHandlers.ofString())
+ // .thenApply(HttpResponse::body)
+ .thenApply(
+ response ->
+ getAnalysisReportFromResponse(
+ response, "Component Analysis", "json", exClientTraceId))
+ .exceptionally(
+ exception -> {
+ LOG.severe(
+ String.format(
+ "failed to invoke Component Analysis for getting the json report, received"
+ + " message= %s ",
+ exception.getMessage()));
+ // LOG.log(System.Logger.Level.ERROR, "Exception Entity", exception);
+ return new AnalysisReport();
+ });
}
/**
* Build an HTTP request wrapper for sending to the Backend API for Stack Analysis only.
*
* @param manifestFile the path for the manifest file
- * @param acceptType the type of requested content
+ * @param acceptType the type of requested content
* @return a HttpRequest ready to be sent to the Backend API
* @throws IOException when failed to load the manifest file
*/
- private HttpRequest buildStackRequest(final String manifestFile, final MediaType acceptType) throws IOException {
+ private HttpRequest buildStackRequest(final String manifestFile, final MediaType acceptType)
+ throws IOException {
var manifestPath = Paths.get(manifestFile);
var provider = Ecosystem.getProvider(manifestPath);
var uri = URI.create(String.format("%s/api/v4/analysis", this.endpoint));
@@ -407,56 +515,60 @@ private HttpRequest buildStackRequest(final String manifestFile, final MediaType
}
@Override
- public CompletableFuture