Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,12 @@
<artifactId>quarkus-jacoco</artifactId>
<scope>test</scope>
</dependency>
<!-- YAML parser for DVC YAML files -->
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>2.3</version>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
Expand Down
6 changes: 6 additions & 0 deletions src/main/docker/Dockerfile.jvm
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ FROM registry.access.redhat.com/ubi9/openjdk-21:1.21

ENV LANGUAGE='en_US:en'

# Install DVC CLI and dependencies for data version control
USER 0
RUN microdnf install -y python3 python3-pip git && \
pip3 install --no-cache-dir dvc dvc-s3 && \
microdnf clean all
USER 185

# We make four distinct layers so if there are application changes the library layers can be re-used
COPY --chown=185 target/quarkus-app/lib/ /deployments/lib/
Expand Down
113 changes: 113 additions & 0 deletions src/main/java/com/redhat/sast/api/service/DvcService.java
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i would add some input validate, java is less forgiving compare to python

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package com.redhat.sast.api.service;

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

import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.yaml.snakeyaml.Yaml;

import jakarta.enterprise.context.ApplicationScoped;
import lombok.extern.slf4j.Slf4j;

@ApplicationScoped
@Slf4j
public class DvcService {

@ConfigProperty(name = "dvc.repo.url")
String dvcRepoUrl;

@ConfigProperty(name = "dvc.batch.yaml.path")
String batchYamlPath;

/**
* Get list of NVRs from DVC repository by version tag
* Fetches YAML file from DVC and extracts NVR list
*
* @param version DVC version tag (e.g., "1.0.0" or "v1.0.0")
* @return List of package NVR strings (empty list if no NVRs found)
* @throws Exception if DVC fetch fails or parsing fails
* @throws IllegalArgumentException if version is null or empty
*/
public List<String> getNvrListByVersion(String version) throws Exception {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe better exceptions

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// Validate version parameter
if (version == null || version.isBlank()) {
throw new IllegalArgumentException("DVC version cannot be null or empty");
}

LOGGER.info("Fetching NVR list from DVC repository: version={}", version);
LOGGER.debug("Fetching YAML from DVC: path={}", batchYamlPath);

String yamlContent = fetchFromDvc(batchYamlPath, version);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe a short comment on yaml expected structure

LOGGER.debug("Raw YAML content from DVC ({} bytes)", yamlContent.length());

// Parse YAML to extract NVRs
Yaml yaml = new Yaml();
Object data = yaml.load(yamlContent);

List<String> nvrList = new ArrayList<>();

if (data instanceof java.util.Map) {
// YAML has a map structure, find list of strings
java.util.Map<String, Object> map = (java.util.Map<String, Object>) data;
for (Object value : map.values()) {
if (value instanceof List) {
List<?> list = (List<?>) value;
if (!list.isEmpty() && list.get(0) instanceof String) {
nvrList = (List<String>) list;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if not items are strings it can cause issue
try nvrList.add((String) item)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

break;
}
}
}
} else if (data instanceof List) {
// YAML is just a list of NVRs
nvrList = (List<String>) data;
}

if (nvrList.isEmpty()) {
LOGGER.warn("No NVRs found in YAML for DVC version {}", version);
return nvrList;
}

LOGGER.info("Successfully retrieved {} NVRs from YAML (DVC version {})", nvrList.size(), version);
LOGGER.debug("NVR list: {}", nvrList);
return nvrList;
}

/**
* Fetches raw file content from DVC repository using DVC CLI
*
* @param filePath Path to file in DVC repo
* @param version DVC version tag
* @return File content as String
* @throws Exception if DVC command fails
*/
private String fetchFromDvc(String filePath, String version) throws Exception {
LOGGER.debug("Executing DVC get command: repo={}, path={}, version={}", dvcRepoUrl, filePath, version);

java.nio.file.Path tempFile = java.nio.file.Files.createTempFile("dvc-fetch-", ".tmp");
try {
ProcessBuilder processBuilder = new ProcessBuilder(
"dvc", "get", dvcRepoUrl, filePath, "--rev", version, "-o", tempFile.toString(), "--force");

Process process = processBuilder.start();

// Read stderr for error messages
String error = new String(process.getErrorStream().readAllBytes(), java.nio.charset.StandardCharsets.UTF_8);

int exitCode = process.waitFor();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add timeout so it won't get stuck

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


if (exitCode != 0) {
LOGGER.error("DVC command failed with exit code {}: {}", exitCode, error);
throw new Exception("Failed to fetch data from DVC: " + error);
}

// Read content from temp file - the nvrs content
String output = java.nio.file.Files.readString(tempFile, java.nio.charset.StandardCharsets.UTF_8);
LOGGER.debug("Successfully fetched {} bytes from DVC", output.length());
return output;
} finally {
// Clean up temp file
java.nio.file.Files.deleteIfExists(tempFile);
}
}
}
4 changes: 4 additions & 0 deletions src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ url-validation.request-timeout=30s
sast.ai.batch.job.polling.interval=5000
sast.ai.batch.job.timeout=3600000

# DVC configuration
dvc.repo.url=https://github.com/RHEcosystemAppEng/sast-ai-dvc
# default value - might change in future
dvc.batch.yaml.path=testing-data-nvrs.yaml
# OSH (Open Scan Hub) Integration Configuration
# Master toggle for OSH integration
osh.integration.enabled=false
Expand Down