From 965ec7b127a2b4c28012c3542844fce79f91f805 Mon Sep 17 00:00:00 2001 From: Vasi Vasireddy Date: Wed, 18 Oct 2023 15:51:59 -0700 Subject: [PATCH] Initial commit, Add base for testcase --- terraform/testcases/otlp_logs/otconfig.tpl | 30 ++++++ .../testcases/otlp_logs/parameters.tfvars | 5 + validator/build.gradle | 1 + .../PredefinedExpectedTemplate.java | 1 + .../amazon/aoc/validators/CWLogValidator.java | 98 +++++++++++++++++++ .../aoc/validators/ValidatorFactory.java | 4 + .../validations/spark-otel-log-validation.yml | 6 ++ 7 files changed, 145 insertions(+) create mode 100644 terraform/testcases/otlp_logs/otconfig.tpl create mode 100644 terraform/testcases/otlp_logs/parameters.tfvars create mode 100644 validator/src/main/java/com/amazon/aoc/validators/CWLogValidator.java create mode 100644 validator/src/main/resources/validations/spark-otel-log-validation.yml diff --git a/terraform/testcases/otlp_logs/otconfig.tpl b/terraform/testcases/otlp_logs/otconfig.tpl new file mode 100644 index 000000000..729e86f52 --- /dev/null +++ b/terraform/testcases/otlp_logs/otconfig.tpl @@ -0,0 +1,30 @@ +extensions: + pprof: + endpoint: 0.0.0.0:1777 +receivers: + otlp: + protocols: + grpc: + endpoint: 0.0.0.0:${grpc_port} + +processors: + batch: + +exporters: + logging: + verbosity: detailed + awscloudwatchlogs: + log_group_name: "otlp-receiver" + log_stream_name: "otlp-logs" + region: ${region} + +service: + pipelines: + metrics: + receivers: [otlp] + processors: [batch] + exporters: [logging, awscloudwatchlogs] + extensions: [pprof] + telemetry: + logs: + level: ${log_level} diff --git a/terraform/testcases/otlp_logs/parameters.tfvars b/terraform/testcases/otlp_logs/parameters.tfvars new file mode 100644 index 000000000..7c62a1b91 --- /dev/null +++ b/terraform/testcases/otlp_logs/parameters.tfvars @@ -0,0 +1,5 @@ +validation_config = "spark-otel-log-validation.yml" + +sample_app = "spark" + +sample_app_image = "public.ecr.aws/aws-otel-test/aws-otel-java-spark:latest" diff --git a/validator/build.gradle b/validator/build.gradle index 02350df2b..c0b39fcc4 100644 --- a/validator/build.gradle +++ b/validator/build.gradle @@ -73,6 +73,7 @@ dependencies { // k8s client implementation "io.kubernetes:client-java-extended:18.0.1" + testImplementation("com.github.rholder:guava-retrying:2.0.0") } application { diff --git a/validator/src/main/java/com/amazon/aoc/fileconfigs/PredefinedExpectedTemplate.java b/validator/src/main/java/com/amazon/aoc/fileconfigs/PredefinedExpectedTemplate.java index e4819066e..eacd63b25 100644 --- a/validator/src/main/java/com/amazon/aoc/fileconfigs/PredefinedExpectedTemplate.java +++ b/validator/src/main/java/com/amazon/aoc/fileconfigs/PredefinedExpectedTemplate.java @@ -77,6 +77,7 @@ public enum PredefinedExpectedTemplate implements FileConfig { CONTAINER_INSIGHT_ECS_LOG("/expected-data-template/container-insight/ecs/ecs-instance"), CONTAINER_INSIGHT_ECS_PROMETHEUS_LOG("/expected-data-template/container-insight/ecs/prometheus"), CONTAINER_INSIGHT_FARGATE_EKS_LOG("/expected-data-template/container-insight/eks/fargate"), + DEFAULT_EXPECTED_LOG("/expected-data-template/otlpExpectedLog.mustache"), ; private String path; diff --git a/validator/src/main/java/com/amazon/aoc/validators/CWLogValidator.java b/validator/src/main/java/com/amazon/aoc/validators/CWLogValidator.java new file mode 100644 index 000000000..a51271d3f --- /dev/null +++ b/validator/src/main/java/com/amazon/aoc/validators/CWLogValidator.java @@ -0,0 +1,98 @@ +package com.amazon.aoc.validators; + +import com.amazon.aoc.callers.ICaller; +import com.amazon.aoc.fileconfigs.FileConfig; +import com.amazon.aoc.models.Context; +import com.amazon.aoc.models.ValidationConfig; +import com.amazonaws.services.logs.CloudWatchLogsClient; +import com.amazonaws.services.logs.model.GetLogEventsRequest; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.awaitility.core.RetryerBuilder; +import org.awaitility.core.StopStrategies; +import org.awaitility.core.WaitStrategies; +import org.opentest4j.AssertionFailedError; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.time.Duration; +import java.time.Instant; +import java.util.HashSet; +import java.util.Objects; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +public class CWLogValidator implements IValidator { + + + +// String getJsonSchemaMappingKey(JsonNode jsonNode) { +// // Your implementation for getting the JSON schema mapping key +// return null; +// } + + @Override + public void init(Context context, ValidationConfig validationConfig, ICaller caller, FileConfig expectedDataTemplate) throws Exception { + + } + + @Override + public void validate() throws Exception { + var lines = new HashSet(); + InputStream inputStream = getClass().getResourceAsStream("/logs/testingJSON.log"); + + try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) { + String line; + while ((line = reader.readLine()) != null) { + lines.add(line); + } + } catch (IOException e) { + throw new RuntimeException("Error reading from the file: " + inputStream, e); + } + + var cwClient = CloudWatchLogsClient.builder().build(); + var objectMapper = new ObjectMapper(); + + RetryerBuilder.newBuilder() + .retryIfException() + .retryIfRuntimeException() + .retryIfExceptionOfType(AssertionFailedError.class) + .withWaitStrategy(WaitStrategies.fixedWait(10, TimeUnit.SECONDS)) + .withStopStrategy(StopStrategies.stopAfterAttempt(5)) + .build() + .call(() -> { + var now = Instant.now(); + var start = now.minus(Duration.ofMinutes(2)); + var end = now.plus(Duration.ofMinutes(2)); + var response = cwClient.getLogEvents(GetLogEventsRequest.builder() + .logGroupName("adot-testbed/logs-component-testing/logs") + .logStreamName(testLogStreamName) + .startTime(start.toEpochMilli()) + .endTime(end.toEpochMilli()) + .build()); + + var events = response.events(); + var receivedMessages = events.stream().map(x -> x.message()).collect(Collectors.toSet()); + + // Extract the "body" field from each received message that is received from CloudWatch in JSON Format + var messageToValidate = receivedMessages.stream() + .map(message -> { + try { + JsonNode jsonNode = objectMapper.readTree(message); + return jsonNode.get("body").asText(); + } catch (Exception e) { + return null; + } + }) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + + // Validate the body field in JSON-messageToValidate with actual log lines from the log file. + assertThat(messageToValidate.containsAll(lines)).isTrue(); + assertThat(messageToValidate).containsExactlyInAnyOrderElementsOf(lines); + return null; + }); + } +} diff --git a/validator/src/main/java/com/amazon/aoc/validators/ValidatorFactory.java b/validator/src/main/java/com/amazon/aoc/validators/ValidatorFactory.java index e0da615c8..7b348625d 100644 --- a/validator/src/main/java/com/amazon/aoc/validators/ValidatorFactory.java +++ b/validator/src/main/java/com/amazon/aoc/validators/ValidatorFactory.java @@ -51,6 +51,10 @@ public IValidator launchValidator(ValidationConfig validationConfig) throws Exce validator = new LoadBalancingValidator(); break; case "cw-metric": + validator = new CWLogValidator(); + expectedData = validationConfig.getExpectedMetricTemplate(); + break; + case "cw-logs": validator = new CWMetricValidator(); expectedData = validationConfig.getExpectedMetricTemplate(); break; diff --git a/validator/src/main/resources/validations/spark-otel-log-validation.yml b/validator/src/main/resources/validations/spark-otel-log-validation.yml new file mode 100644 index 000000000..8dd609033 --- /dev/null +++ b/validator/src/main/resources/validations/spark-otel-log-validation.yml @@ -0,0 +1,6 @@ +- + validationType: "cw-logs" + httpPath: "/outgoing-http-call" + httpMethod: "get" + callingType: "http" + expectedMetricTemplate: "DEFAULT_EXPECTED_LOG" \ No newline at end of file