Skip to content

Commit

Permalink
feat(templates): reimplement custom event templates from mounted volu…
Browse files Browse the repository at this point in the history
…me (#445)
  • Loading branch information
andrewazores authored May 9, 2024
1 parent 68e5187 commit 319437a
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 41 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ nb-configuration.xml

.quinoa/
truststore/
templates/
certs/*.p12
certs/*.pass
*.jfr
Expand Down
3 changes: 3 additions & 0 deletions compose/cryostat.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ services:
volumes:
- ${XDG_RUNTIME_DIR}/podman/podman.sock:/run/user/1000/podman/podman.sock:Z
- jmxtls_cfg:/truststore:U
- templates:/opt/cryostat.d/templates.d:U
security_opt:
- label:disable
hostname: cryostat3
Expand Down Expand Up @@ -47,3 +48,5 @@ services:
volumes:
jmxtls_cfg:
external: true
templates:
external: true
11 changes: 11 additions & 0 deletions smoketest.bash
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,8 @@ cleanup() {
fi
${container_engine} rm jmxtls_cfg_helper || true
${container_engine} volume rm jmxtls_cfg || true
${container_engine} rm templates_helper || true
${container_engine} volume rm templates || true
truncate -s 0 "${HOSTSFILE}"
for i in "${PIDS[@]}"; do
kill -0 "${i}" && kill "${i}"
Expand Down Expand Up @@ -225,6 +227,15 @@ createJmxTlsCertVolume() {
}
createJmxTlsCertVolume

createEventTemplateVolume() {
"${container_engine}" volume create templates
"${container_engine}" container create --name templates_helper -v templates:/templates busybox
if [ -d "${DIR}/templates" ]; then
"${container_engine}" cp "${DIR}/templates" templates_helper:/templates
fi
}
createEventTemplateVolume

setupUserHosts() {
# This requires https://github.com/figiel/hosts to work. See README.
truncate -s 0 "${HOSTSFILE}"
Expand Down
1 change: 1 addition & 0 deletions src/main/java/io/cryostat/ConfigProperties.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,6 @@ public class ConfigProperties {
"storage.transient-archives.enabled";
public static final String STORAGE_TRANSIENT_ARCHIVES_TTL = "storage.transient-archives.ttl";

public static final String TEMPLATES_DIR = "templates-dir";
public static final String SSL_TRUSTSTORE_DIR = "ssl.truststore.dir";
}
131 changes: 90 additions & 41 deletions src/main/java/io/cryostat/events/S3TemplateService.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.text.ParseException;
import java.util.ArrayList;
import java.util.List;
Expand Down Expand Up @@ -82,6 +84,9 @@ public class S3TemplateService implements MutableTemplateService {
@ConfigProperty(name = ConfigProperties.AWS_BUCKET_NAME_EVENT_TEMPLATES)
String bucket;

@ConfigProperty(name = ConfigProperties.TEMPLATES_DIR)
Path dir;

@Inject S3Client storage;
@Inject StorageBuckets storageBuckets;

Expand All @@ -95,6 +100,28 @@ public class S3TemplateService implements MutableTemplateService {

void onStart(@Observes StartupEvent evt) {
storageBuckets.createIfNecessary(bucket);
if (!checkDir()) {
return;
}
try {
Files.walk(dir)
.filter(Files::isRegularFile)
.filter(Files::isReadable)
.forEach(
p -> {
try (var is = Files.newInputStream(p)) {
logger.debugv(
"Uploading template from {0} to S3", p.toString());
addTemplate(is);
} catch (IOException
| InvalidXmlException
| InvalidEventTemplateException e) {
logger.warn(e);
}
});
} catch (IOException e) {
logger.warn(e);
}
}

@Override
Expand Down Expand Up @@ -192,59 +219,30 @@ private InputStream getModel(String name) {
return storage.getObject(req);
}

private XMLModel parseXml(InputStream inputStream) throws IOException, ParseException {
try (inputStream) {
var model = EventConfiguration.createModel(inputStream);
model.checkErrors();

for (XMLValidationResult result : model.getResults()) {
if (result.isError()) {
throw new IllegalArgumentException(
new InvalidEventTemplateException(result.getText()));
}
}
return model;
}
}

@Override
public Template addTemplate(InputStream stream)
throws InvalidXmlException, InvalidEventTemplateException, IOException {
try (stream) {
XMLModel model = parseXml(stream);

XMLTagInstance configuration = model.getRoot();
XMLAttributeInstance labelAttr = null;
for (XMLAttributeInstance attr : configuration.getAttributeInstances()) {
if (attr.getAttribute().getName().equals("label")) {
labelAttr = attr;
break;
}
}

if (labelAttr == null) {
var model = parseXml(stream);
var template = createTemplate(model);
var existing = getTemplates();
if (existing.stream().anyMatch(t -> Objects.equals(t.getName(), template.getName()))) {
throw new IllegalArgumentException(
new InvalidEventTemplateException(
"Template has no configuration label attribute"));
String.format("Duplicate event template name: %s", template.getName()));
}

String templateName = labelAttr.getExplicitValue().replaceAll("[\\W]+", "_");

XMLTagInstance root = model.getRoot();
root.setValue(JFCGrammar.ATTRIBUTE_LABEL_MANDATORY, templateName);

String description = getAttributeValue(root, "description");
String provider = getAttributeValue(root, "provider");
storage.putObject(
PutObjectRequest.builder()
.bucket(bucket)
.key(templateName)
.key(template.getName())
.contentType(MediaType.APPLICATION_XML)
.tagging(createTemplateTagging(templateName, description, provider))
.tagging(
createTemplateTagging(
template.getName(),
template.getDescription(),
template.getProvider()))
.build(),
RequestBody.fromString(model.toString()));

var template = new Template(templateName, description, provider, TemplateType.CUSTOM);
bus.publish(
MessagingServer.class.getName(),
new Notification(EVENT_TEMPLATE_CREATED, Map.of("template", template)));
Expand All @@ -255,6 +253,8 @@ public Template addTemplate(InputStream stream)
throw new IllegalArgumentException("Unable to parse XML stream", ioe);
} catch (ParseException | IllegalArgumentException e) {
throw new IllegalArgumentException(new InvalidEventTemplateException("Invalid XML", e));
} catch (FlightRecorderException e) {
throw new IOException(e);
}
}

Expand Down Expand Up @@ -303,7 +303,56 @@ private Tagging createTemplateTagging(
return Tagging.builder().tagSet(tags).build();
}

protected String getAttributeValue(XMLTagInstance node, String valueKey) {
private boolean checkDir() {
return Files.exists(dir)
&& Files.isReadable(dir)
&& Files.isExecutable(dir)
&& Files.isDirectory(dir);
}

private XMLModel parseXml(InputStream inputStream) throws IOException, ParseException {
try (inputStream) {
var model = EventConfiguration.createModel(inputStream);
model.checkErrors();

for (XMLValidationResult result : model.getResults()) {
if (result.isError()) {
throw new IllegalArgumentException(
new InvalidEventTemplateException(result.getText()));
}
}
return model;
}
}

private Template createTemplate(XMLModel model) throws IOException, ParseException {
XMLTagInstance configuration = model.getRoot();
XMLAttributeInstance labelAttr = null;
for (XMLAttributeInstance attr : configuration.getAttributeInstances()) {
if (attr.getAttribute().getName().equals("label")) {
labelAttr = attr;
break;
}
}

if (labelAttr == null) {
throw new IllegalArgumentException(
new InvalidEventTemplateException(
"Template has no configuration label attribute"));
}

String templateName = labelAttr.getExplicitValue().replaceAll("[\\W]+", "_");

XMLTagInstance root = model.getRoot();
root.setValue(JFCGrammar.ATTRIBUTE_LABEL_MANDATORY, templateName);

String description = getAttributeValue(root, "description");
String provider = getAttributeValue(root, "provider");

return new Template(templateName, description, provider, TemplateType.CUSTOM);
}

private String getAttributeValue(XMLTagInstance node, String valueKey) {
return node.getAttributeInstances().stream()
.filter(i -> Objects.equals(valueKey, i.getAttribute().getName()))
.map(i -> i.getValue())
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ cryostat.http.proxy.port=${quarkus.http.port}
cryostat.http.proxy.path=/

conf-dir=/opt/cryostat.d
templates-dir=${conf-dir}/templates.d
ssl.truststore=${conf-dir}/truststore.p12
ssl.truststore.dir=/truststore
ssl.truststore.pass-file=${conf-dir}/truststore.pass
Expand Down

0 comments on commit 319437a

Please sign in to comment.