diff --git a/compose/cryostat.yml b/compose/cryostat.yml index e70122f27..24e509f92 100644 --- a/compose/cryostat.yml +++ b/compose/cryostat.yml @@ -10,6 +10,7 @@ services: - ${XDG_RUNTIME_DIR}/podman/podman.sock:/run/user/1000/podman/podman.sock:Z - jmxtls_cfg:/truststore:U - templates:/opt/cryostat.d/templates.d:U + - probes:/opt/cryostat.d/probes.d:U security_opt: - label:disable hostname: cryostat @@ -50,3 +51,5 @@ volumes: external: true templates: external: true + probes: + external: true diff --git a/smoketest.bash b/smoketest.bash index f6e2fd7d4..87ad9fc2b 100755 --- a/smoketest.bash +++ b/smoketest.bash @@ -266,6 +266,8 @@ cleanup() { ${container_engine} volume rm jmxtls_cfg || true ${container_engine} rm templates_helper || true ${container_engine} volume rm templates || true + ${container_engine} rm probes_helper || true + ${container_engine} volume rm probes || true truncate -s 0 "${HOSTSFILE}" for i in "${PIDS[@]}"; do kill -0 "${i}" && kill "${i}" @@ -334,6 +336,15 @@ createEventTemplateVolume() { } createEventTemplateVolume +createProbeTemplateVolume() { + "${container_engine}" volume create probes + "${container_engine}" container create --name probes_helper -v probes:/probes registry.access.redhat.com/ubi9/ubi-micro + if [ -d "${DIR}/probes" ]; then + "${container_engine}" cp "${DIR}/probes" probes_helper:/probes + fi +} +createProbeTemplateVolume + setupUserHosts() { # This requires https://github.com/figiel/hosts to work. See README. truncate -s 0 "${HOSTSFILE}" diff --git a/src/main/java/io/cryostat/ConfigProperties.java b/src/main/java/io/cryostat/ConfigProperties.java index b117d3965..322cf74be 100644 --- a/src/main/java/io/cryostat/ConfigProperties.java +++ b/src/main/java/io/cryostat/ConfigProperties.java @@ -57,6 +57,7 @@ public class ConfigProperties { public static final String CUSTOM_TEMPLATES_DIR = "templates-dir"; public static final String PRESET_TEMPLATES_DIR = "preset-templates-dir"; + public static final String PROBE_TEMPLATES_DIR = "probe-templates-dir"; public static final String SSL_TRUSTSTORE_DIR = "ssl.truststore.dir"; public static final String RULES_DIR = "rules-dir"; diff --git a/src/main/java/io/cryostat/jmcagent/S3ProbeTemplateService.java b/src/main/java/io/cryostat/jmcagent/S3ProbeTemplateService.java index a22588846..f72b73263 100644 --- a/src/main/java/io/cryostat/jmcagent/S3ProbeTemplateService.java +++ b/src/main/java/io/cryostat/jmcagent/S3ProbeTemplateService.java @@ -18,6 +18,8 @@ import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -58,6 +60,9 @@ public class S3ProbeTemplateService implements ProbeTemplateService { @ConfigProperty(name = ConfigProperties.AWS_BUCKET_NAME_PROBE_TEMPLATES) String bucket; + @ConfigProperty(name = ConfigProperties.PROBE_TEMPLATES_DIR) + Path dir; + @Inject S3Client storage; @Inject StorageBuckets storageBuckets; @@ -74,6 +79,36 @@ public class S3ProbeTemplateService implements ProbeTemplateService { void onStart(@Observes StartupEvent evt) { storageBuckets.createIfNecessary(bucket); + if (!checkDir()) { + return; + } + try { + Files.walk(dir) + .filter(Files::isRegularFile) + .filter(Files::isReadable) + .forEach( + path -> { + try (var is = Files.newInputStream(path)) { + logger.debugv( + "Uploading probe template from {0} to S3", + path.toString()); + addTemplate(is, path.toString()); + } catch (IOException | SAXException e) { + logger.error(e); + } catch (DuplicateProbeTemplateException e) { + logger.warn(e); + } + }); + } catch (IOException e) { + logger.warn(e); + } + } + + private boolean checkDir() { + return Files.exists(dir) + && Files.isReadable(dir) + && Files.isExecutable(dir) + && Files.isDirectory(dir); } @Override @@ -187,6 +222,11 @@ public ProbeTemplate addTemplate(InputStream stream, String fileName) ProbeTemplate template = new ProbeTemplate(); template.setFileName(fileName); template.deserialize(stream); + var existing = getTemplates(); + if (existing.stream() + .anyMatch(t -> Objects.equals(t.getFileName(), template.getFileName()))) { + throw new DuplicateProbeTemplateException(template.getFileName()); + } storage.putObject( PutObjectRequest.builder() .bucket(bucket) @@ -243,4 +283,10 @@ private Tagging createTemplateTagging( .toList()); return Tagging.builder().tagSet(tags).build(); } + + static class DuplicateProbeTemplateException extends IllegalArgumentException { + DuplicateProbeTemplateException(String templateName) { + super(String.format("Probe Template with name \"%s\" already exists", templateName)); + } + } } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index c9c684f94..7d05159d3 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -53,6 +53,7 @@ cryostat.agent.tls.required=true conf-dir=/opt/cryostat.d rules-dir=${conf-dir}/rules.d templates-dir=${conf-dir}/templates.d +probe-templates-dir=${conf-dir}/probes.d preset-templates-dir=${conf-dir}/presets.d ssl.truststore=${conf-dir}/truststore.p12 ssl.truststore.dir=/truststore