diff --git a/README.adoc b/README.adoc index 8147b58..b18140e 100755 --- a/README.adoc +++ b/README.adoc @@ -48,8 +48,12 @@ The following properties are available: | report-interval | A value in minutes which sets the reporting interval (defaults to 1 minute) | api-timeout | The api call and api attempt timeout in ms (if not set defaults to AWS SDK default) | metric enabled="{true/false}" | a metric name (from a list of metrics) which should be reported to CloudWatch -| report-raw-count-value | Report the raw value of count metrics instead of reporting only the count difference since the last report, default is false. -| zero-values-submission | POSTs all values to CloudWatch. Otherwise, the reporter does not POST values which are zero in order to save costs, default is false. +| report-raw-count-value | Report the raw value of count metrics instead of only reporting metric value changes. +The default is false. +| zero-values-submission | If enabled, the extension also POSTs zero value metric updates to CloudWatch. +Otherwise, the reporter does not POST zero values in order to save costs. +The default is false. +| cloudwatch-endpoint-override | Override the default cloudWatch endpoint where this extension pushes the metrics. |=== .Example Configuration diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3dd26bb..bf291ff 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,11 +8,11 @@ jaxb-api = "2.3.3" jaxb-impl = "2.3.9" jetbrains-annotations = "24.1.0" junit-jupiter = "5.10.0" +kotlin = "2.0.0" logback = "1.5.6" mockito = "5.12.0" -testcontainers = "1.19.8" okhttp = "4.12.0" -kotlin = "2.0.0" +testcontainers = "1.19.8" [libraries] awaitility = { module = "org.awaitility:awaitility", version.ref = "awaitility" } @@ -22,16 +22,16 @@ hivemq-mqttClient = { module = "com.hivemq:hivemq-mqtt-client", version.ref = "h jaxb-api = { module = "jakarta.xml.bind:jakarta.xml.bind-api", version.ref = "jaxb-api" } jaxb-impl = { module = "com.sun.xml.bind:jaxb-impl", version.ref = "jaxb-impl" } jetbrains-annotations = { module = "org.jetbrains:annotations", version.ref = "jetbrains-annotations" } +kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" } +logback-classic = { module = "ch.qos.logback:logback-classic", version.ref = "logback" } mockito = { module = "org.mockito:mockito-core", version.ref = "mockito" } +okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" } testcontainers-hivemq = { module = "org.testcontainers:hivemq", version.ref = "testcontainers" } testcontainers-junitJupiter = { module = "org.testcontainers:junit-jupiter", version.ref = "testcontainers" } -logback-classic = { module = "ch.qos.logback:logback-classic", version.ref = "logback" } -okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" } -kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" } testcontainers-localstack = { module = "org.testcontainers:localstack", version.ref = "testcontainers" } [plugins] -hivemq-extension = { id = "com.hivemq.extension", version = "3.1.0" } defaults = { id = "io.github.sgtsilvio.gradle.defaults", version = "0.2.0" } -license = { id = "com.github.hierynomus.license", version = "0.16.1" } +hivemq-extension = { id = "com.hivemq.extension", version = "3.1.0" } kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } +license = { id = "com.github.hierynomus.license", version = "0.16.1" } diff --git a/src/integrationTest/kotlin/com/hivemq/extensions/aws/cloudwatch/DockerImageNames.kt b/src/integrationTest/kotlin/com/hivemq/extensions/aws/cloudwatch/DockerImageNames.kt index c40e2c3..b2e29b1 100644 --- a/src/integrationTest/kotlin/com/hivemq/extensions/aws/cloudwatch/DockerImageNames.kt +++ b/src/integrationTest/kotlin/com/hivemq/extensions/aws/cloudwatch/DockerImageNames.kt @@ -17,7 +17,7 @@ package com.hivemq.extensions.aws.cloudwatch import org.testcontainers.utility.DockerImageName -val LOCALSTACK_DOCKER_IMAGE: DockerImageName = DockerImageName.parse("localstack/localstack:3.2.0") +val LOCALSTACK_DOCKER_IMAGE: DockerImageName = DockerImageName.parse("localstack/localstack:3.5.0") val HIVEMQ_DOCKER_IMAGE: DockerImageName = DockerImageName.parse("hivemq/hivemq4:4.28.2") diff --git a/src/integrationTest/kotlin/com/hivemq/extensions/aws/cloudwatch/EndToEndIT.kt b/src/integrationTest/kotlin/com/hivemq/extensions/aws/cloudwatch/EndToEndIT.kt index de56482..44b4bb1 100644 --- a/src/integrationTest/kotlin/com/hivemq/extensions/aws/cloudwatch/EndToEndIT.kt +++ b/src/integrationTest/kotlin/com/hivemq/extensions/aws/cloudwatch/EndToEndIT.kt @@ -37,10 +37,10 @@ import java.time.Duration import java.time.Instant import java.util.concurrent.TimeUnit -class EndToEndIT { +internal class EndToEndIT { @Test - @Timeout(value = 3, unit = TimeUnit.MINUTES) + @Timeout(value = 5, unit = TimeUnit.MINUTES) fun endToEnd() { val network = Network.newNetwork() @@ -66,68 +66,73 @@ class EndToEndIT { } hivemq.start() - val credentialsProvider = - StaticCredentialsProvider.create(AwsBasicCredentials.create(localStack.accessKey, localStack.secretKey)) + try { + val credentialsProvider = + StaticCredentialsProvider.create(AwsBasicCredentials.create(localStack.accessKey, localStack.secretKey)) - val cloudWatchClient = CloudWatchClient.builder() // - .credentialsProvider(credentialsProvider) // - .endpointOverride(localStack.getEndpointOverride(CLOUDWATCH)) // - .region(Region.of(localStack.region)) // - .build() + val cloudWatchClient = CloudWatchClient.builder() // + .credentialsProvider(credentialsProvider) // + .endpointOverride(localStack.getEndpointOverride(CLOUDWATCH)) // + .region(Region.of(localStack.region)) // + .build() - await().timeout(Duration.ofMinutes(2)).until { - cloudWatchClient.listMetrics().metrics().any { - it.metricName() == "com.hivemq.messages.incoming.publish.count" + await().timeout(Duration.ofMinutes(2)).until { + cloudWatchClient.listMetrics().metrics().any { + it.metricName() == "com.hivemq.messages.incoming.publish.count" + } } - } - - val metric = Metric.builder()// - .namespace("hivemq-metrics")// - .metricName("com.hivemq.messages.incoming.publish.count")// - .dimensions(emptyList())// - .build() - val metricStat = MetricStat.builder() - .stat(Statistic.MAXIMUM.toString()) - .period(60) - .metric(metric) - .build() + val metric = Metric.builder()// + .namespace("hivemq-metrics")// + .metricName("com.hivemq.messages.incoming.publish.count")// + .dimensions(emptyList())// + .build() - val metricDataQuery = MetricDataQuery.builder() - .id("m1") - .metricStat(metricStat) - .returnData(true) - .build() + val metricStat = MetricStat.builder() + .stat(Statistic.MAXIMUM.toString()) + .period(60) + .metric(metric) + .build() - await().timeout(Duration.ofMinutes(2)).until { - val request = GetMetricDataRequest.builder() - .startTime(Instant.now().minusSeconds(3600)) - .endTime(Instant.now()) - .metricDataQueries(listOf(metricDataQuery)) + val metricDataQuery = MetricDataQuery.builder() + .id("m1") + .metricStat(metricStat) + .returnData(true) .build() - val response = cloudWatchClient.getMetricData(request) - response.metricDataResults().maxOf { - it.values()[0] - } == 0.0 - } - val mqttClient = Mqtt5Client.builder().serverHost(hivemq.host).serverPort(hivemq.mqttPort).buildBlocking() - mqttClient.connect() - mqttClient.publishWith().topic("wabern").send() + await().timeout(Duration.ofMinutes(2)).until { + val request = GetMetricDataRequest.builder() + .startTime(Instant.now().minusSeconds(3600)) + .endTime(Instant.now()) + .metricDataQueries(listOf(metricDataQuery)) + .build() + val response = cloudWatchClient.getMetricData(request) + response.metricDataResults().maxOf { + it.values()[0] + } == 0.0 + } - await().timeout(Duration.ofMinutes(2)).until { - val request = GetMetricDataRequest.builder() - .startTime(Instant.now().minusSeconds(3600)) - .endTime(Instant.now()) - .metricDataQueries(listOf(metricDataQuery)) - .build() - val response = cloudWatchClient.getMetricData(request) - response.metricDataResults().maxOf { - it.values()[0] - } == 1.0 + val mqttClient = Mqtt5Client.builder().serverHost(hivemq.host).serverPort(hivemq.mqttPort).buildBlocking() + mqttClient.connect() + mqttClient.publishWith().topic("wabern").send() + + await().timeout(Duration.ofMinutes(5)).until { + val request = GetMetricDataRequest.builder() + .startTime(Instant.now().minusSeconds(3600)) + .endTime(Instant.now()) + .metricDataQueries(listOf(metricDataQuery)) + .build() + val response = cloudWatchClient.getMetricData(request) + response.metricDataResults().maxOf { + it.values()[0] + } == 1.0 + } + cloudWatchClient.close() + } finally { + hivemq.stop() + localStack.stop() + network.close() } - - cloudWatchClient.close() } } diff --git a/src/integrationTest/resources/extension-config.xml b/src/integrationTest/resources/extension-config.xml index 9676a5c..2f2244c 100644 --- a/src/integrationTest/resources/extension-config.xml +++ b/src/integrationTest/resources/extension-config.xml @@ -17,9 +17,9 @@ --> - http://localstack:4566 + http://localstack:4566 1 - true + true true com.hivemq.messages.incoming.publish.count diff --git a/src/main/java/com/hivemq/extensions/aws/cloudwatch/CloudWatchReporterService.java b/src/main/java/com/hivemq/extensions/aws/cloudwatch/CloudWatchReporterService.java index 02b0c7c..a546e1f 100755 --- a/src/main/java/com/hivemq/extensions/aws/cloudwatch/CloudWatchReporterService.java +++ b/src/main/java/com/hivemq/extensions/aws/cloudwatch/CloudWatchReporterService.java @@ -66,12 +66,12 @@ void startCloudWatchReporter( final Duration apiTimeout = cloudWatchConfig.getApiTimeout().map(Duration::ofMillis).orElse(null); final CloudWatchAsyncClientBuilder cloudWatchAsyncClientBuilder = CloudWatchAsyncClient.builder(); - if (configuration.getConfig().getAwsEndpointOverride() != null) { + if (configuration.getConfig().getCloudWatchEndpointOverride() != null) { cloudWatchAsyncClientBuilder.endpointOverride(URI.create(configuration.getConfig() - .getAwsEndpointOverride())); + .getCloudWatchEndpointOverride())); } - final CloudWatchAsyncClient cloudWatchAsync = + final CloudWatchAsyncClient cloudWatchAsyncClient = cloudWatchAsyncClientBuilder.credentialsProvider(DefaultCredentialsProvider.create()) .asyncConfiguration(ClientAsyncConfiguration.builder() .advancedOption(SdkAdvancedAsyncClientOption.FUTURE_COMPLETION_EXECUTOR, @@ -84,7 +84,7 @@ void startCloudWatchReporter( .build(); final CloudWatchReporter.Builder builder = - CloudWatchReporter.forRegistry(metricRegistry, cloudWatchAsync, METRIC_NAMESPACE); + CloudWatchReporter.forRegistry(metricRegistry, cloudWatchAsyncClient, METRIC_NAMESPACE); if (configuration.getConfig().getZeroValuesSubmission()) { builder.withZeroValuesSubmission(); @@ -92,11 +92,10 @@ void startCloudWatchReporter( if (configuration.getConfig().getReportRawCountValue()) { builder.withReportRawCountValue(); } - cloudWatchReporter = builder.withZeroValuesSubmission() - .withReportRawCountValue() + cloudWatchReporter = builder .filter(new ConfiguredMetricsFilter(configuration.getEnabledMetrics())) .build(); - cloudWatchReporter.start(cloudWatchConfig.getReportInterval(), TimeUnit.SECONDS); + cloudWatchReporter.start(cloudWatchConfig.getReportInterval(), TimeUnit.MINUTES); log.info("Started CloudWatchReporter for {} HiveMQ metrics", configuration.getEnabledMetrics().size()); } } diff --git a/src/main/java/com/hivemq/extensions/aws/cloudwatch/configuration/entities/Config.java b/src/main/java/com/hivemq/extensions/aws/cloudwatch/configuration/entities/Config.java index 0d98e1a..49bfbc9 100755 --- a/src/main/java/com/hivemq/extensions/aws/cloudwatch/configuration/entities/Config.java +++ b/src/main/java/com/hivemq/extensions/aws/cloudwatch/configuration/entities/Config.java @@ -17,7 +17,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import software.amazon.awssdk.services.cloudwatch.endpoints.internal.Value; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; @@ -57,8 +56,8 @@ public class Config { @XmlElement(name = "report-raw-count-value", defaultValue = "false") private final boolean reportRawCountValue = false; - @XmlElement(name = "aws-endpoint-override") - private final @Nullable String awsEndpointOverride = null; + @XmlElement(name = "cloudwatch-endpoint-override") + private final @Nullable String cloudWatchEndpointOverride = null; public final @NotNull List getMetrics() { return metrics; @@ -88,8 +87,8 @@ public boolean getZeroValuesSubmission() { return zeroValuesSubmission; } - public @Nullable String getAwsEndpointOverride() { - return awsEndpointOverride; + public @Nullable String getCloudWatchEndpointOverride() { + return cloudWatchEndpointOverride; } @Override