Skip to content

Commit

Permalink
Add TLS support
Browse files Browse the repository at this point in the history
Add configuration constants for property names
Move constants to a holder class
Port HttpClientOptionsConsumer from Quarkus
Modify HCOP to use ConfigProperties
General code cleanup
  • Loading branch information
jasondlee committed Nov 18, 2024
1 parent b302e29 commit 5159b9c
Show file tree
Hide file tree
Showing 10 changed files with 320 additions and 168 deletions.
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package io.smallrye.opentelemetry.implementation.exporters;

import static io.smallrye.opentelemetry.implementation.exporters.OtlpExporterUtil.OTEL_EXPORTER_OTLP_ENDPOINT;
import static io.smallrye.opentelemetry.implementation.exporters.OtlpExporterUtil.OTLP_GRPC_ENDPOINT;
import static io.smallrye.opentelemetry.implementation.exporters.OtlpExporterUtil.OTLP_HTTP_PROTOBUF_ENDPOINT;
import static io.smallrye.opentelemetry.implementation.exporters.OtlpExporterUtil.PROTOCOL_GRPC;
import static io.smallrye.opentelemetry.implementation.exporters.Constants.MIMETYPE_PROTOBUF;
import static io.smallrye.opentelemetry.implementation.exporters.Constants.OTEL_EXPORTER_OTLP_ENDPOINT;
import static io.smallrye.opentelemetry.implementation.exporters.Constants.OTEL_EXPORTER_OTLP_SIGNAL_ENDPOINT;
import static io.smallrye.opentelemetry.implementation.exporters.Constants.OTEL_EXPORTER_VERTX_CDI_QUALIFIER;
import static io.smallrye.opentelemetry.implementation.exporters.Constants.OTLP_GRPC_ENDPOINT;
import static io.smallrye.opentelemetry.implementation.exporters.Constants.OTLP_HTTP_PROTOBUF_ENDPOINT;
import static io.smallrye.opentelemetry.implementation.exporters.OtlpExporterUtil.getCompression;
import static io.smallrye.opentelemetry.implementation.exporters.OtlpExporterUtil.getOtlpEndpoint;
import static io.smallrye.opentelemetry.implementation.exporters.OtlpExporterUtil.getTimeout;

import java.net.URI;
import java.net.URISyntaxException;
import java.time.Duration;
import java.util.logging.Level;
import java.util.logging.Logger;

Expand All @@ -25,26 +29,11 @@
import io.vertx.core.Vertx;

public abstract class AbstractVertxExporterProvider<T extends Marshaler> {
private static final String OTEL_EXPORTER_OTLP_PROTOCOL = "otel.exporter.otlp.protocol";
private static final String OTEL_EXPORTER_OTLP_SIGNAL_PROTOCOL = "otel.exporter.otlp.%s.protocol";

private static final String OTEL_EXPORTER_OTLP_TIMEOUT = "otel.exporter.otlp.timeout";
private static final String OTEL_EXPORTER_OTLP_SIGNAL_TIMEOUT = "otel.exporter.otlp.%s.timeout";

private static final String OTEL_EXPORTER_OTLP_SIGNAL_ENDPOINT = "otel.exporter.otlp.%s.endpoint";

private static final String OTEL_EXPORTER_OTLP_COMPRESSION = "otel.exporter.otlp.compression";
private static final String OTEL_EXPORTER_OTLP_SIGNAL_COMPRESSION = "otel.exporter.otlp.%s.compression";

private static final String MIMETYPE_PROTOBUF = "application/x-protobuf";

private static final String OTEL_EXPORTER_VERTX_CDI_QUALIFIER = "otel.exporter.vertx.cdi.identifier";

private static final Logger logger = Logger.getLogger(AbstractVertxExporterProvider.class.getName());

private final String signalType;
private final String exporterName;

private static final Logger logger = Logger.getLogger(AbstractVertxExporterProvider.class.getName());

public AbstractVertxExporterProvider(String signalType, String exporterName) {
this.signalType = signalType;
this.exporterName = exporterName;
Expand Down Expand Up @@ -88,23 +77,28 @@ private Vertx getVertx(ConfigProperties config) {
}

protected VertxGrpcSender<T> createGrpcSender(ConfigProperties config, String grpcEndpointPath) throws URISyntaxException {
URI baseUri = new URI(getOtlpEndpoint(config, OTLP_GRPC_ENDPOINT, signalType));
return new VertxGrpcSender<>(
new URI(getOtlpEndpoint(config, OTLP_GRPC_ENDPOINT)),
signalType,
baseUri,
grpcEndpointPath,
getCompression(config),
getTimeout(config),
getCompression(config, signalType),
getTimeout(config, signalType),
OtlpExporterUtil.populateTracingExportHttpHeaders(),
new HttpClientOptionsConsumer(config, baseUri, signalType),
getVertx(config));
}

protected VertxHttpSender createHttpSender(ConfigProperties config, String httpEndpointPath) throws URISyntaxException {
URI baseUri = new URI(getOtlpEndpoint(config, OTLP_HTTP_PROTOBUF_ENDPOINT, signalType));
return new VertxHttpSender(
new URI(getOtlpEndpoint(config, OTLP_HTTP_PROTOBUF_ENDPOINT)),
baseUri,
httpEndpointPath,
getCompression(config),
getTimeout(config),
getCompression(config, signalType),
getTimeout(config, signalType),
OtlpExporterUtil.populateTracingExportHttpHeaders(),
MIMETYPE_PROTOBUF,
new HttpClientOptionsConsumer(config, baseUri, signalType),
getVertx(config));
}

Expand All @@ -114,80 +108,4 @@ protected IllegalArgumentException buildUnsupportedProtocolException(String prot
return new IllegalArgumentException(String.format("Unsupported OTLP protocol %s specified. ", protocol) +
String.format("Please check the `%s` and/or '%s' properties", signalProperty, OTEL_EXPORTER_OTLP_ENDPOINT));
}

/**
* Given the OpenTelemetry config, lookup a value using the given keys, stopping with the first non-null value. If
* no keys are found, return the defaultValue. Since the OpenTelemetry API offers signal-specific settings, as well
* as one overarching property key for a variety of configuration options, this allows the caller to specify
* signal-specific keys to search, then defaulting to the "top-level" property, then finally defaulting to a
* SmallRye-specific default value.
*
* @param config OpenTelemetry config
* @param defaultValue The default value for the property.
* @param keys The keys to iterate over
* @return either the configured or default value
*/
protected String getConfig(ConfigProperties config, String defaultValue, String... keys) {
String value = null;
for (String key : keys) {
value = config.getString(key);
if (value != null) {
break;
}
}

return value != null ? value : defaultValue;
}

/**
* Determine the wire protocol for sending signal data to the remote
*
* @param config OpenTelemetry configuration
* @return either the configured or default value
*/
protected String getProtocol(ConfigProperties config) {
// The otel API uses "span" and "traces" in various places, so we need to modify that case here
String signalKey = signalType.replace("span", "traces");

return getConfig(config, PROTOCOL_GRPC,
String.format(OTEL_EXPORTER_OTLP_SIGNAL_PROTOCOL, signalKey), OTEL_EXPORTER_OTLP_PROTOCOL);
}

/**
* Determine whether to enable compression
*
* @param config OpenTelemetry configuration
* @return either the configured or default value
*/
protected boolean getCompression(ConfigProperties config) {
return Boolean.parseBoolean(getConfig(config, "true",
String.format(OTEL_EXPORTER_OTLP_SIGNAL_COMPRESSION, signalType),
OTEL_EXPORTER_OTLP_COMPRESSION));
}

/**
* Return timeout, in seconds, for sending data to the remote
*
* @param config OpenTelemetry configuration
* @return either the configured or default value
*/
protected Duration getTimeout(ConfigProperties config) {
return Duration.ofSeconds(Integer.parseInt(
getConfig(config, "10",
String.format(OTEL_EXPORTER_OTLP_SIGNAL_TIMEOUT, signalType), OTEL_EXPORTER_OTLP_TIMEOUT)));
}

/**
* Gets the OTLP traces endpoint, if defined. If it is not, it returns the OTLP endpoint. If that is not defined,
* it returns defaultEndpoint.
*
* @param config OpenTelemetry configuration
* @param defaultEndpoint The default endpoint for the desired protocol
* @return either the configured or default value
*/
protected String getOtlpEndpoint(ConfigProperties config, String defaultEndpoint) {
return getConfig(config, defaultEndpoint,
String.format(OTEL_EXPORTER_OTLP_SIGNAL_ENDPOINT, signalType),
OTEL_EXPORTER_OTLP_ENDPOINT);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package io.smallrye.opentelemetry.implementation.exporters;

public class Constants {
private Constants() {
}

public static final String PROTOCOL_GRPC = "grpc";
public static final String PROTOCOL_HTTP_PROTOBUF = "http/protobuf";

public static final String OTLP_GRPC_ENDPOINT = "http://localhost:4317";
public static final String OTLP_HTTP_PROTOBUF_ENDPOINT = "http://localhost:4318";

public static final String OTEL_EXPORTER_OTLP_ENDPOINT = "otel.exporter.otlp.endpoint";
public static final String OTEL_EXPORTER_OTLP_TRACES_PROTOCOL = "otel.exporter.otlp.traces.protocol";

static final String OTEL_EXPORTER_VERTX_CDI_QUALIFIER = "otel.exporter.vertx.cdi.identifier";

static final String OTEL_EXPORTER_OTLP_CERTIFICATE = "otel.exporter.otlp.certificate";
static final String OTEL_EXPORTER_OTLP_SIGNAL_CERTIFICATE = "otel.exporter.otlp.%s.certificate";

static final String OTEL_EXPORTER_OTLP_CLIENT_KEY = "otel.exporter.otlp.client.key";
static final String OTEL_EXPORTER_OTLP_SIGNAL_CLIENT_KEY = "otel.exporter.otlp.%s.client.key";

static final String OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE = "otel.exporter.otlp.client.certificate";
static final String OTEL_EXPORTER_OTLP_SIGNAL_CLIENT_CERTIFICATE = "otel.exporter.otlp.%s.client.certificate";

static final String OTEL_EXPORTER_OTLP_PROTOCOL = "otel.exporter.otlp.protocol";
static final String OTEL_EXPORTER_OTLP_SIGNAL_PROTOCOL = "otel.exporter.otlp.%s.protocol";

static final String OTEL_EXPORTER_OTLP_TIMEOUT = "otel.exporter.otlp.timeout";
static final String OTEL_EXPORTER_OTLP_SIGNAL_TIMEOUT = "otel.exporter.otlp.%s.timeout";

static final String OTEL_EXPORTER_OTLP_SIGNAL_ENDPOINT = "otel.exporter.otlp.%s.endpoint";

static final String OTEL_EXPORTER_OTLP_COMPRESSION = "otel.exporter.otlp.compression";
static final String OTEL_EXPORTER_OTLP_SIGNAL_COMPRESSION = "otel.exporter.otlp.%s.compression";

static final String MIMETYPE_PROTOBUF = "application/x-protobuf";

static final String SROTEL_TLS_TRUST_ALL = "otel.exporter.tls.trustAll";
// Proxy options
static final String SROTEL_PROXY_ENABLED = "otel.exporter.proxy.enabled";
static final String SROTEL_PROXY_USERNAME = "otel.exporter.proxy.username";
static final String SROTEL_PROXY_PASSWORD = "otel.exporter.proxy.password";
static final String SROTEL_PROXY_HOST = "otel.exporter.proxy.host";
static final String SROTEL_PROXY_PORT = "otel.exporter.proxy.port";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package io.smallrye.opentelemetry.implementation.exporters;

import static io.smallrye.opentelemetry.implementation.exporters.Constants.OTEL_EXPORTER_OTLP_CERTIFICATE;
import static io.smallrye.opentelemetry.implementation.exporters.Constants.OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE;
import static io.smallrye.opentelemetry.implementation.exporters.Constants.OTEL_EXPORTER_OTLP_CLIENT_KEY;
import static io.smallrye.opentelemetry.implementation.exporters.Constants.OTEL_EXPORTER_OTLP_SIGNAL_CERTIFICATE;
import static io.smallrye.opentelemetry.implementation.exporters.Constants.OTEL_EXPORTER_OTLP_SIGNAL_CLIENT_CERTIFICATE;
import static io.smallrye.opentelemetry.implementation.exporters.Constants.OTEL_EXPORTER_OTLP_SIGNAL_CLIENT_KEY;
import static io.smallrye.opentelemetry.implementation.exporters.Constants.SROTEL_PROXY_ENABLED;
import static io.smallrye.opentelemetry.implementation.exporters.Constants.SROTEL_PROXY_HOST;
import static io.smallrye.opentelemetry.implementation.exporters.Constants.SROTEL_PROXY_PASSWORD;
import static io.smallrye.opentelemetry.implementation.exporters.Constants.SROTEL_PROXY_PORT;
import static io.smallrye.opentelemetry.implementation.exporters.Constants.SROTEL_PROXY_USERNAME;
import static io.smallrye.opentelemetry.implementation.exporters.Constants.SROTEL_TLS_TRUST_ALL;
import static io.smallrye.opentelemetry.implementation.exporters.OtlpExporterUtil.getConfig;

import java.net.URI;
import java.util.function.Consumer;

import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.vertx.core.http.HttpClientOptions;
import io.vertx.core.net.PemKeyCertOptions;
import io.vertx.core.net.PemTrustOptions;
import io.vertx.core.net.ProxyOptions;

class HttpClientOptionsConsumer implements Consumer<HttpClientOptions> {
private final ConfigProperties config;
private final URI baseUri;
private final String signalType;

public HttpClientOptionsConsumer(ConfigProperties config, URI baseUri, String signalType) {
this.config = config;
this.baseUri = baseUri;
this.signalType = signalType;
}

@Override
public void accept(HttpClientOptions options) {
configureTLS(options);

if (Boolean.parseBoolean(getConfig(config, "false", SROTEL_PROXY_ENABLED))) {
configureProxyOptions(options);
}
}

private void configureTLS(HttpClientOptions options) {
configureKeyCertOptions(options);
configureTrustOptions(options);

if (OtlpExporterUtil.isHttps(baseUri)) {
options.setSsl(true);
options.setUseAlpn(true);
}

if (Boolean.parseBoolean(getConfig(config, "false", SROTEL_TLS_TRUST_ALL))) {
options.setTrustAll(true);
options.setVerifyHost(false);
}
}

private void configureProxyOptions(HttpClientOptions options) {
var proxyHost = getConfig(config, "", SROTEL_PROXY_HOST);
if (!proxyHost.isBlank()) {
ProxyOptions proxyOptions = new ProxyOptions()
.setHost(proxyHost);
var proxyPort = getConfig(config, "", SROTEL_PROXY_PORT);
var proxyUsername = getConfig(config, "", SROTEL_PROXY_USERNAME);
var proxyPassword = getConfig(config, "", SROTEL_PROXY_PASSWORD);

if (!proxyPort.isBlank()) {
proxyOptions.setPort(Integer.parseInt(proxyPort));
}
if (!proxyUsername.isBlank()) {
proxyOptions.setUsername(proxyUsername);
}
if (!proxyPassword.isBlank()) {
proxyOptions.setPassword(proxyPassword);
}
options.setProxyOptions(proxyOptions);
} else {
configureProxyOptionsFromJDKSysProps(options);
}
}

private void configureProxyOptionsFromJDKSysProps(HttpClientOptions options) {
var proxyHost = options.isSsl()
? System.getProperty("https.proxyHost", "none")
: System.getProperty("http.proxyHost", "none");
var proxyPortAsString = options.isSsl()
? System.getProperty("https.proxyPort", "443")
: System.getProperty("http.proxyPort", "80");
var proxyPort = Integer.parseInt(proxyPortAsString);

if (!"none".equals(proxyHost)) {
ProxyOptions proxyOptions = new ProxyOptions().setHost(proxyHost).setPort(proxyPort);
var proxyUser = options.isSsl()
? System.getProperty("https.proxyUser")
: System.getProperty("http.proxyUser");
if (proxyUser != null && !proxyUser.isBlank()) {
proxyOptions.setUsername(proxyUser);
}
var proxyPassword = options.isSsl()
? System.getProperty("https.proxyPassword")
: System.getProperty("http.proxyPassword");
if (proxyPassword != null && !proxyPassword.isBlank()) {
proxyOptions.setPassword(proxyPassword);
}
options.setProxyOptions(proxyOptions);
}
}

private void configureKeyCertOptions(HttpClientOptions options) {
var pemKeyCertOptions = new PemKeyCertOptions();

var certificate = getConfig(config, "",
String.format(OTEL_EXPORTER_OTLP_SIGNAL_CLIENT_CERTIFICATE, signalType),
OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE);
var key = getConfig(config, "",
String.format(OTEL_EXPORTER_OTLP_SIGNAL_CLIENT_KEY, signalType), OTEL_EXPORTER_OTLP_CLIENT_KEY);

if (!certificate.isEmpty()) {
pemKeyCertOptions.addCertPath(certificate);
}

if (!key.isEmpty()) {
pemKeyCertOptions.addKeyPath(key);
}
options.setKeyCertOptions(pemKeyCertOptions);
}

private void configureTrustOptions(HttpClientOptions options) {
var certificate = getConfig(config, "",
String.format(OTEL_EXPORTER_OTLP_SIGNAL_CERTIFICATE, signalType), OTEL_EXPORTER_OTLP_CERTIFICATE);

if (!certificate.isEmpty()) {
var pemTrustOptions = new PemTrustOptions()
.addCertPath(certificate);
options.setPemTrustOptions(pemTrustOptions);
}
}
}
Loading

0 comments on commit 5159b9c

Please sign in to comment.