From bb68f495c4312225fef1683414b19d5a466d506d Mon Sep 17 00:00:00 2001 From: "Alex E." <36134278+chusitoo@users.noreply.github.com> Date: Mon, 6 Jan 2025 11:38:34 -0500 Subject: [PATCH] [TEST] Functional tests for OTLP/gRPC with mutual TLS (#3227) --- .github/workflows/ci.yml | 9 + exporters/otlp/src/otlp_grpc_exporter.cc | 6 + functional/otlp/CMakeLists.txt | 6 + functional/otlp/Dockerfile | 1 + functional/otlp/func_grpc_main.cc | 821 ++++++++++++++++++ functional/otlp/otel-config-https-mtls.yaml | 40 + .../otlp/otel-docker-config-https-mtls.yaml | 39 + functional/otlp/run_test.sh | 55 +- functional/otlp/run_test_mode.sh | 18 +- 9 files changed, 984 insertions(+), 11 deletions(-) create mode 100644 functional/otlp/func_grpc_main.cc create mode 100644 functional/otlp/otel-config-https-mtls.yaml create mode 100644 functional/otlp/otel-docker-config-https-mtls.yaml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index efc01e2a00..438b2414bd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -452,6 +452,15 @@ jobs: run: | sudo ./ci/setup_grpc.sh ./ci/do_ci.sh cmake.exporter.otprotocol.test + - name: generate test cert + env: + CFSSL_VERSION: 1.6.3 + run: | + sudo -E ./tools/setup-cfssl.sh + (cd ./functional/cert; ./generate_cert.sh) + - name: run func test + run: | + (cd ./functional/otlp; ./run_test.sh) cmake_modern_protobuf_grpc_with_abseil_test: name: CMake test (with modern protobuf,grpc and abseil) diff --git a/exporters/otlp/src/otlp_grpc_exporter.cc b/exporters/otlp/src/otlp_grpc_exporter.cc index e4012c2ebf..53b6d5b8a3 100644 --- a/exporters/otlp/src/otlp_grpc_exporter.cc +++ b/exporters/otlp/src/otlp_grpc_exporter.cc @@ -150,6 +150,7 @@ sdk::common::ExportResult OtlpGrpcExporter::Export( else { #endif + const auto resource_spans_size = request->resource_spans_size(); grpc::Status status = OtlpGrpcClient::DelegateExport(trace_service_stub_.get(), std::move(context), std::move(arena), std::move(*request), response); @@ -160,6 +161,11 @@ sdk::common::ExportResult OtlpGrpcExporter::Export( << "\" error_message: \"" << status.error_message() << "\""); return sdk::common::ExportResult::kFailure; } + else + { + OTEL_INTERNAL_LOG_DEBUG("[OTLP TRACE GRPC Exporter] Export " << resource_spans_size + << " trace span(s) success"); + } #ifdef ENABLE_ASYNC_EXPORT } #endif diff --git a/functional/otlp/CMakeLists.txt b/functional/otlp/CMakeLists.txt index 39b25cc324..dd2c0c6130 100644 --- a/functional/otlp/CMakeLists.txt +++ b/functional/otlp/CMakeLists.txt @@ -3,6 +3,12 @@ include_directories(${CMAKE_SOURCE_DIR}/exporters/otlp/include) +if(WITH_OTLP_GRPC) + add_executable(func_otlp_grpc func_grpc_main.cc) + target_link_libraries(func_otlp_grpc ${CMAKE_THREAD_LIBS_INIT} + opentelemetry_trace opentelemetry_exporter_otlp_grpc) +endif() + if(WITH_OTLP_HTTP) add_executable(func_otlp_http func_http_main.cc) target_link_libraries(func_otlp_http ${CMAKE_THREAD_LIBS_INIT} diff --git a/functional/otlp/Dockerfile b/functional/otlp/Dockerfile index 79e09d61ba..8c593ad7e0 100644 --- a/functional/otlp/Dockerfile +++ b/functional/otlp/Dockerfile @@ -4,4 +4,5 @@ FROM otel/opentelemetry-collector COPY . . CMD ["--config", "/otel-cpp/otel-config.yaml"] +EXPOSE 4317 EXPOSE 4318 diff --git a/functional/otlp/func_grpc_main.cc b/functional/otlp/func_grpc_main.cc new file mode 100644 index 0000000000..e52fe12d84 --- /dev/null +++ b/functional/otlp/func_grpc_main.cc @@ -0,0 +1,821 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include +#include +#include +#include +#include +#include + +#include "opentelemetry/exporters/otlp/otlp_environment.h" +#include "opentelemetry/exporters/otlp/otlp_grpc_exporter_factory.h" +#include "opentelemetry/exporters/otlp/otlp_grpc_exporter_options.h" +#include "opentelemetry/nostd/shared_ptr.h" +#include "opentelemetry/nostd/string_view.h" +#include "opentelemetry/sdk/common/attribute_utils.h" +#include "opentelemetry/sdk/common/global_log_handler.h" +#include "opentelemetry/sdk/trace/processor.h" +#include "opentelemetry/sdk/trace/recordable.h" +#include "opentelemetry/sdk/trace/simple_processor_factory.h" +#include "opentelemetry/sdk/trace/tracer_provider.h" +#include "opentelemetry/sdk/trace/tracer_provider_factory.h" +#include "opentelemetry/trace/provider.h" +#include "opentelemetry/trace/span.h" +#include "opentelemetry/trace/span_id.h" +#include "opentelemetry/trace/tracer.h" +#include "opentelemetry/trace/tracer_provider.h" + +namespace trace = opentelemetry::trace; +namespace trace_sdk = opentelemetry::sdk::trace; +namespace otlp = opentelemetry::exporter::otlp; +namespace nostd = opentelemetry::nostd; + +namespace internal_log = opentelemetry::sdk::common::internal_log; + +const int TEST_PASSED = 0; +const int TEST_FAILED = 1; + +/* + Command line parameters. +*/ + +enum class TestMode : std::uint8_t +{ + kNone, + kHttp, + kHttps +}; + +bool opt_help = false; +bool opt_list = false; +bool opt_debug = false; +bool opt_secure = false; +// HTTPS by default +std::string opt_endpoint = "https://127.0.0.1:4317"; +std::string opt_cert_dir; +std::string opt_test_name; +TestMode opt_mode = TestMode::kNone; + +/* + Log parsing +*/ + +struct TestResult +{ + bool found_connection_failed = false; + bool found_export_error = false; + bool found_export_success = false; + bool deadline_exceeded = false; + bool empty_address_list = false; + + void reset() + { + found_connection_failed = false; + found_export_error = false; + found_export_success = false; + deadline_exceeded = false; + empty_address_list = false; + } +}; + +struct TestResult g_test_result; + +void parse_error_msg(TestResult *result, const std::string &msg) +{ + static std::string connection_failed("failed to connect to all addresses"); + + if (msg.find(connection_failed) != std::string::npos) + { + result->found_connection_failed = true; + } + + static std::string export_failed("Export() failed with status_code"); + + if (msg.find(export_failed) != std::string::npos) + { + result->found_export_error = true; + } + + static std::string deadline_exceeded("Deadline Exceeded"); + + if (msg.find(deadline_exceeded) != std::string::npos) + { + result->deadline_exceeded = true; + } + + static std::string empty_address_list("empty address list:"); + + if (msg.find(empty_address_list) != std::string::npos) + { + result->empty_address_list = true; + } +} + +void parse_warning_msg(TestResult * /* result */, const std::string & /* msg */) {} + +void parse_info_msg(TestResult * /* result */, const std::string & /* msg */) {} + +void parse_debug_msg(TestResult *result, const std::string &msg) +{ + static std::string export_success("Export 1 trace span(s) success"); + + if (msg.find(export_success) != std::string::npos) + { + result->found_export_success = true; + } +} + +class TestLogHandler : public opentelemetry::sdk::common::internal_log::LogHandler +{ +public: + void Handle(opentelemetry::sdk::common::internal_log::LogLevel level, + const char * /* file */, + int /* line */, + const char *msg, + const opentelemetry::sdk::common::AttributeMap & /* attributes */) noexcept override + { + if (msg == nullptr) + { + msg = ""; + } + + switch (level) + { + case opentelemetry::sdk::common::internal_log::LogLevel::None: + break; + case opentelemetry::sdk::common::internal_log::LogLevel::Error: + std::cout << " - [E] " << msg << '\n'; + parse_error_msg(&g_test_result, msg); + break; + case opentelemetry::sdk::common::internal_log::LogLevel::Warning: + std::cout << " - [W] " << msg << '\n'; + parse_warning_msg(&g_test_result, msg); + break; + case opentelemetry::sdk::common::internal_log::LogLevel::Info: + std::cout << " - [I] " << msg << '\n'; + parse_info_msg(&g_test_result, msg); + break; + case opentelemetry::sdk::common::internal_log::LogLevel::Debug: + std::cout << " - [D] " << msg << '\n'; + parse_debug_msg(&g_test_result, msg); + break; + } + } +}; + +void init(const otlp::OtlpGrpcExporterOptions &opts) +{ + // Create OTLP exporter instance + auto exporter = otlp::OtlpGrpcExporterFactory::Create(opts); + auto processor = trace_sdk::SimpleSpanProcessorFactory::Create(std::move(exporter)); + std::shared_ptr provider = + trace_sdk::TracerProviderFactory::Create(std::move(processor)); + // Set the global trace provider + trace::Provider::SetTracerProvider(provider); +} + +void payload() +{ + static const nostd::string_view k_tracer_name("func_test"); + static const nostd::string_view k_span_name("func_grpc_main"); + static const nostd::string_view k_attr_test_name("test_name"); + + auto provider = trace::Provider::GetTracerProvider(); + auto tracer = provider->GetTracer(k_tracer_name, "1.0"); + + auto span = tracer->StartSpan(k_span_name); + span->SetAttribute(k_attr_test_name, opt_test_name); + span->End(); +} + +void cleanup() +{ + std::shared_ptr none; + trace::Provider::SetTracerProvider(none); +} + +void instrumented_payload(const otlp::OtlpGrpcExporterOptions &opts) +{ + g_test_result.reset(); + init(opts); + payload(); + cleanup(); +} + +void usage(FILE *out) +{ + static const char *msg = + "Usage: func_otlp_grpc [options] test_name\n" + "Valid options are:\n" + " --help Print this help\n" + " --list List test names\n" + " --endpoint url OTLP gRPC endpoint (https://localhost:4317/ by default)\n" + " --cert-dir dir Directory that contains test ssl certificates\n" + " --mode mode Test server mode (used to verify expected results)\n" + " - none: no endpoint\n" + " - http: http endpoint\n" + " - https: https endpoint\n"; + fprintf(out, "%s", msg); +} + +int parse_args(int argc, char *argv[]) +{ + int remaining_argc = argc; + char **remaining_argv = argv; + + while (remaining_argc > 0) + { + if (strcmp(*remaining_argv, "--help") == 0) + { + opt_help = true; + return 0; + } + + if (strcmp(*remaining_argv, "--list") == 0) + { + opt_list = true; + return 0; + } + + if (strcmp(*remaining_argv, "--debug") == 0) + { + opt_debug = true; + remaining_argc--; + remaining_argv++; + continue; + } + + if (remaining_argc >= 2) + { + if (strcmp(*remaining_argv, "--cert-dir") == 0) + { + remaining_argc--; + remaining_argv++; + opt_cert_dir = *remaining_argv; + remaining_argc--; + remaining_argv++; + continue; + } + + if (strcmp(*remaining_argv, "--endpoint") == 0) + { + remaining_argc--; + remaining_argv++; + opt_endpoint = *remaining_argv; + remaining_argc--; + remaining_argv++; + continue; + } + + if (strcmp(*remaining_argv, "--mode") == 0) + { + remaining_argc--; + remaining_argv++; + std::string mode = *remaining_argv; + remaining_argc--; + remaining_argv++; + + if (mode == "none") + { + opt_mode = TestMode::kNone; + } + else if (mode == "http") + { + opt_mode = TestMode::kHttp; + } + else if (mode == "https") + { + opt_mode = TestMode::kHttps; + } + else + { + // Unknown mode + return 2; + } + + continue; + } + } + + opt_test_name = *remaining_argv; + remaining_argc--; + remaining_argv++; + + if (remaining_argc) + { + // Unknown option + return 1; + } + } + + return 0; +} + +typedef int (*test_func_t)(); + +struct test_case +{ + std::string m_name; + test_func_t m_func; +}; + +int test_basic(); + +int test_cert_not_found(); +int test_cert_invalid(); +int test_cert_unreadable(); +int test_client_cert_not_found(); +int test_client_cert_invalid(); +int test_client_cert_unreadable(); +int test_client_cert_no_key(); +int test_client_key_not_found(); +int test_client_key_invalid(); +int test_client_key_unreadable(); + +int test_mtls_ok(); + +static const test_case all_tests[] = {{"basic", test_basic}, + {"cert-not-found", test_cert_not_found}, + {"cert-invalid", test_cert_invalid}, + {"cert-unreadable", test_cert_unreadable}, +#ifdef ENABLE_OTLP_GRPC_SSL_MTLS_PREVIEW + {"client-cert-not-found", test_client_cert_not_found}, + {"client-cert-invalid", test_client_cert_invalid}, + {"client-cert-unreadable", test_client_cert_unreadable}, + {"client-cert-no-key", test_client_cert_no_key}, + {"client-key-not-found", test_client_key_not_found}, + {"client-key-invalid", test_client_key_invalid}, + {"client-key-unreadable", test_client_key_unreadable}, + {"mtls-ok", test_mtls_ok}, +#endif // ENABLE_OTLP_GRPC_SSL_MTLS_PREVIEW + {"", nullptr}}; + +void list_test_cases() +{ + const test_case *current = all_tests; + + while (current->m_func != nullptr) + { + std::cout << current->m_name << '\n'; + current++; + } +} + +int run_test_case(const std::string &name) +{ + const test_case *current = all_tests; + + while (current->m_func != nullptr) + { + if (current->m_name == name) + { + int rc = current->m_func(); + return rc; + } + current++; + } + + std::cerr << "Unknown test <" << name << ">" << '\n'; + return 1; +} + +int main(int argc, char *argv[]) +{ + // Program name + argc--; + argv++; + + int rc = parse_args(argc, argv); + + if (rc != 0) + { + usage(stderr); + return 1; + } + + if (opt_help) + { + usage(stdout); + return 0; + } + + if (opt_list) + { + list_test_cases(); + return 0; + } + + if (opt_endpoint.find("https:") != std::string::npos) + { + opt_secure = true; + } + else + { + opt_secure = false; + } + + opentelemetry::nostd::shared_ptr log_handler(new TestLogHandler); + + internal_log::GlobalLogHandler::SetLogHandler(log_handler); + internal_log::GlobalLogHandler::SetLogLevel(internal_log::LogLevel::Debug); + + rc = run_test_case(opt_test_name); + return rc; +} + +void set_common_opts(otlp::OtlpGrpcExporterOptions &opts) +{ + opts.endpoint = opt_endpoint; + opts.timeout = std::chrono::milliseconds{100}; + opts.use_ssl_credentials = (opt_mode == TestMode::kHttps); + +#ifdef ENABLE_ASYNC_EXPORT + // Work around, ASYNC export not working. + opts.max_concurrent_requests = 0; +#endif +} + +int expect_connection_failed() +{ + if (g_test_result.found_export_error && + (g_test_result.found_connection_failed || g_test_result.deadline_exceeded || + g_test_result.empty_address_list)) + { + return TEST_PASSED; + } + return TEST_FAILED; +} + +int expect_success() +{ + if (g_test_result.found_export_success) + { + return TEST_PASSED; + } + return TEST_FAILED; +} + +int expect_export_failed() +{ + /* + Can not test exact root cause: + - connect failed ? + - send request failed ? + - exact error message ? + so only verifying export failed. + */ + + if (g_test_result.found_export_error) + { + return TEST_PASSED; + } + return TEST_FAILED; +} + +int test_basic() +{ + otlp::OtlpGrpcExporterOptions opts; + + set_common_opts(opts); + + instrumented_payload(opts); + + if (opt_mode == TestMode::kNone) + { + return expect_connection_failed(); + } + + if (!opt_secure && (opt_mode == TestMode::kHttp)) + { + return expect_success(); + } + + if (!opt_secure && (opt_mode == TestMode::kHttps)) + { + return expect_export_failed(); + } + + if (opt_secure && (opt_mode == TestMode::kHttp)) + { + return expect_connection_failed(); + } + + return expect_connection_failed(); +} + +int test_cert_not_found() +{ + otlp::OtlpGrpcExporterOptions opts; + + set_common_opts(opts); + opts.ssl_credentials_cacert_path = opt_cert_dir + "/no-such-file.pem"; + + instrumented_payload(opts); + + if (opt_mode == TestMode::kNone) + { + return expect_connection_failed(); + } + + if (!opt_secure && (opt_mode == TestMode::kHttp)) + { + return expect_success(); + } + + if (!opt_secure && (opt_mode == TestMode::kHttps)) + { + return expect_export_failed(); + } + + return expect_connection_failed(); +} + +int test_cert_invalid() +{ + otlp::OtlpGrpcExporterOptions opts; + + set_common_opts(opts); + opts.ssl_credentials_cacert_path = opt_cert_dir + "/garbage.pem"; + + instrumented_payload(opts); + + if (opt_mode == TestMode::kNone) + { + return expect_connection_failed(); + } + + if (!opt_secure && (opt_mode == TestMode::kHttp)) + { + return expect_success(); + } + + if (!opt_secure && (opt_mode == TestMode::kHttps)) + { + return expect_export_failed(); + } + + return expect_connection_failed(); +} + +int test_cert_unreadable() +{ + otlp::OtlpGrpcExporterOptions opts; + + set_common_opts(opts); + opts.ssl_credentials_cacert_path = opt_cert_dir + "/unreadable.pem"; + + instrumented_payload(opts); + + if (opt_mode == TestMode::kNone) + { + return expect_connection_failed(); + } + + if (!opt_secure && (opt_mode == TestMode::kHttp)) + { + return expect_success(); + } + + if (!opt_secure && (opt_mode == TestMode::kHttps)) + { + return expect_export_failed(); + } + + return expect_connection_failed(); +} + +#ifdef ENABLE_OTLP_GRPC_SSL_MTLS_PREVIEW +int test_client_cert_not_found() +{ + otlp::OtlpGrpcExporterOptions opts; + + set_common_opts(opts); + opts.ssl_credentials_cacert_path = opt_cert_dir + "/ca.pem"; + opts.ssl_client_cert_path = opt_cert_dir + "/no-such-file.pem"; + + instrumented_payload(opts); + + if (opt_mode == TestMode::kNone) + { + return expect_connection_failed(); + } + + if (!opt_secure && (opt_mode == TestMode::kHttp)) + { + return expect_success(); + } + + if (!opt_secure && (opt_mode == TestMode::kHttps)) + { + return expect_export_failed(); + } + + return expect_connection_failed(); +} + +int test_client_cert_invalid() +{ + otlp::OtlpGrpcExporterOptions opts; + + set_common_opts(opts); + opts.ssl_credentials_cacert_path = opt_cert_dir + "/ca.pem"; + opts.ssl_client_cert_path = opt_cert_dir + "/garbage.pem"; + + instrumented_payload(opts); + + if (opt_mode == TestMode::kNone) + { + return expect_connection_failed(); + } + + if (!opt_secure && (opt_mode == TestMode::kHttp)) + { + return expect_success(); + } + + if (!opt_secure && (opt_mode == TestMode::kHttps)) + { + return expect_export_failed(); + } + + return expect_connection_failed(); +} + +int test_client_cert_unreadable() +{ + otlp::OtlpGrpcExporterOptions opts; + + set_common_opts(opts); + opts.ssl_credentials_cacert_path = opt_cert_dir + "/ca.pem"; + opts.ssl_client_cert_path = opt_cert_dir + "/unreadable.pem"; + + instrumented_payload(opts); + + if (opt_mode == TestMode::kNone) + { + return expect_connection_failed(); + } + + if (!opt_secure && (opt_mode == TestMode::kHttp)) + { + return expect_success(); + } + + if (!opt_secure && (opt_mode == TestMode::kHttps)) + { + return expect_export_failed(); + } + + return expect_connection_failed(); +} + +int test_client_cert_no_key() +{ + otlp::OtlpGrpcExporterOptions opts; + + set_common_opts(opts); + opts.ssl_credentials_cacert_path = opt_cert_dir + "/ca.pem"; + opts.ssl_client_cert_path = opt_cert_dir + "/client_cert.pem"; + + instrumented_payload(opts); + + if (opt_mode == TestMode::kNone) + { + return expect_connection_failed(); + } + + if (!opt_secure && (opt_mode == TestMode::kHttp)) + { + return expect_success(); + } + + if (!opt_secure && (opt_mode == TestMode::kHttps)) + { + return expect_export_failed(); + } + + return expect_connection_failed(); +} + +int test_client_key_not_found() +{ + otlp::OtlpGrpcExporterOptions opts; + + set_common_opts(opts); + opts.ssl_credentials_cacert_path = opt_cert_dir + "/ca.pem"; + opts.ssl_client_cert_path = opt_cert_dir + "/client_cert.pem"; + opts.ssl_client_key_path = opt_cert_dir + "/no-such-file.pem"; + + instrumented_payload(opts); + + if (opt_mode == TestMode::kNone) + { + return expect_connection_failed(); + } + + if (!opt_secure && (opt_mode == TestMode::kHttp)) + { + return expect_success(); + } + + if (!opt_secure && (opt_mode == TestMode::kHttps)) + { + return expect_export_failed(); + } + + return expect_connection_failed(); +} + +int test_client_key_invalid() +{ + otlp::OtlpGrpcExporterOptions opts; + + set_common_opts(opts); + opts.ssl_credentials_cacert_path = opt_cert_dir + "/ca.pem"; + opts.ssl_client_cert_path = opt_cert_dir + "/client_cert.pem"; + opts.ssl_client_key_path = opt_cert_dir + "/garbage.pem"; + + instrumented_payload(opts); + + if (opt_mode == TestMode::kNone) + { + return expect_connection_failed(); + } + + if (!opt_secure && (opt_mode == TestMode::kHttp)) + { + return expect_success(); + } + + if (!opt_secure && (opt_mode == TestMode::kHttps)) + { + return expect_export_failed(); + } + + return expect_connection_failed(); +} + +int test_client_key_unreadable() +{ + otlp::OtlpGrpcExporterOptions opts; + + set_common_opts(opts); + opts.ssl_credentials_cacert_path = opt_cert_dir + "/ca.pem"; + opts.ssl_client_cert_path = opt_cert_dir + "/client_cert.pem"; + opts.ssl_client_key_path = opt_cert_dir + "/unreadable.pem"; + + instrumented_payload(opts); + + if (opt_mode == TestMode::kNone) + { + return expect_connection_failed(); + } + + if (!opt_secure && (opt_mode == TestMode::kHttp)) + { + return expect_success(); + } + + if (!opt_secure && (opt_mode == TestMode::kHttps)) + { + return expect_export_failed(); + } + + return expect_connection_failed(); +} + +int test_mtls_ok() +{ + otlp::OtlpGrpcExporterOptions opts; + + set_common_opts(opts); + opts.ssl_credentials_cacert_path = opt_cert_dir + "/ca.pem"; + opts.ssl_client_cert_path = opt_cert_dir + "/client_cert.pem"; + opts.ssl_client_key_path = opt_cert_dir + "/client_cert-key.pem"; + + instrumented_payload(opts); + + if (opt_mode == TestMode::kNone) + { + return expect_connection_failed(); + } + + if (!opt_secure && (opt_mode == TestMode::kHttp)) + { + return expect_connection_failed(); + } + + if (!opt_secure && (opt_mode == TestMode::kHttps)) + { + return expect_success(); + } + + if (opt_secure && (opt_mode == TestMode::kHttp)) + { + return expect_connection_failed(); + } + + return expect_success(); +} +#endif // ENABLE_OTLP_GRPC_SSL_MTLS_PREVIEW diff --git a/functional/otlp/otel-config-https-mtls.yaml b/functional/otlp/otel-config-https-mtls.yaml new file mode 100644 index 0000000000..4b32da4eac --- /dev/null +++ b/functional/otlp/otel-config-https-mtls.yaml @@ -0,0 +1,40 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +# +# To use directly: +# otelcol --config ... +# + +receivers: + otlp: + protocols: + grpc: + endpoint: 0.0.0.0:4317 + tls: + ca_file: ../cert/ca.pem + cert_file: ../cert/server_cert.pem + client_ca_file: ../cert/client_cert.pem + key_file: ../cert/server_cert-key.pem + min_version: "1.0" + max_version: "1.3" + +processors: + batch: + memory_limiter: + # 75% of maximum memory up to 4G + limit_mib: 1536 + # 25% of limit up to 2G + spike_limit_mib: 512 + check_interval: 5s + +exporters: + debug: + verbosity: detailed + +service: + pipelines: + traces: + receivers: [otlp] + processors: [memory_limiter, batch] + exporters: [debug] diff --git a/functional/otlp/otel-docker-config-https-mtls.yaml b/functional/otlp/otel-docker-config-https-mtls.yaml new file mode 100644 index 0000000000..f4109eb4dc --- /dev/null +++ b/functional/otlp/otel-docker-config-https-mtls.yaml @@ -0,0 +1,39 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +# +# To use inside a docker container +# + +receivers: + otlp: + protocols: + grpc: + endpoint: 0.0.0.0:4317 + tls: + ca_file: /otel-cpp/ca.pem + cert_file: /otel-cpp/server_cert.pem + client_ca_file: /otel-cpp/client_cert.pem + key_file: /otel-cpp/server_cert-key.pem + min_version: "1.0" + max_version: "1.3" + +processors: + batch: + memory_limiter: + # 75% of maximum memory up to 4G + limit_mib: 1536 + # 25% of limit up to 2G + spike_limit_mib: 512 + check_interval: 5s + +exporters: + debug: + verbosity: detailed + +service: + pipelines: + traces: + receivers: [otlp] + processors: [memory_limiter, batch] + exporters: [debug] diff --git a/functional/otlp/run_test.sh b/functional/otlp/run_test.sh index db51325987..da6598ce0e 100755 --- a/functional/otlp/run_test.sh +++ b/functional/otlp/run_test.sh @@ -9,11 +9,11 @@ set -e # - make sure docker is running # - set BUILD_DIR to the top level build directory, -[ -z "${BUILD_DIR}" ] && export BUILD_DIR=$HOME/build +[ -z "${BUILD_DIR}" ] && export BUILD_DIR="${HOME}/build" export CERT_DIR=../cert -export TEST_BIN_DIR=${BUILD_DIR}/functional/otlp/ +export TEST_BIN_DIR="${BUILD_DIR}/functional/otlp/" # SELINUX # https://docs.docker.com/storage/bind-mounts/#configure-the-selinux-label @@ -28,8 +28,6 @@ if [ -x "$(command -v getenforce)" ]; then fi; fi -${TEST_BIN_DIR}/func_otlp_http --list > test_list.txt - # # Prepare docker image # @@ -38,6 +36,13 @@ docker build -t otelcpp-func-test . echo "REPORT:" > report.log +# +# Exercising HTTP functional tests +# + +export TEST_EXECUTABLE="func_otlp_http" +export TEST_URL="localhost:4318/v1/traces" + # # MODE 'NONE' # @@ -109,6 +114,46 @@ echo "" docker stop otelcpp-test-https docker rm otelcpp-test-https +# +# Exercising gRPC functional tests +# +export TEST_EXECUTABLE="func_otlp_grpc" +export TEST_URL="localhost:4317" + +# +# MODE 'SSL' +# + +echo "" +echo "###############################################################" +echo "Starting otelcol --config otel-config-https-mtls.yaml" +echo "###############################################################" +echo "" + +docker run -d \ + -v `pwd`/otel-docker-config-https-mtls.yaml:/otel-cpp/otel-config.yaml${USE_MOUNT_OPTION} \ + -v `pwd`/../cert/ca.pem:/otel-cpp/ca.pem${USE_MOUNT_OPTION} \ + -v `pwd`/../cert/client_cert.pem:/otel-cpp/client_cert.pem${USE_MOUNT_OPTION} \ + -v `pwd`/../cert/server_cert.pem:/otel-cpp/server_cert.pem${USE_MOUNT_OPTION} \ + -v `pwd`/../cert/server_cert-key.pem:/otel-cpp/server_cert-key.pem${USE_MOUNT_OPTION} \ + -p 4317:4317 \ + --name otelcpp-test-grpc-mtls \ + otelcpp-func-test + +sleep 5; + +export SERVER_MODE="https" +./run_test_mode.sh + +echo "" +echo "###############################################################" +echo "Stopping otelcol (https / mTLS)" +echo "###############################################################" +echo "" + +docker stop otelcpp-test-grpc-mtls +docker rm otelcpp-test-grpc-mtls + echo "" echo "###############################################################" echo "TEST REPORT" @@ -126,7 +171,7 @@ echo "TEST VERDICT: ${PASSED_COUNT} PASSED, ${FAILED_COUNT} FAILED" echo "###############################################################" echo "" -if [ ${FAILED_COUNT} != "0" ]; then +if [ "${FAILED_COUNT}" != "0" ]; then # # CI FAILED # diff --git a/functional/otlp/run_test_mode.sh b/functional/otlp/run_test_mode.sh index 8bb87f6277..3f10ca8483 100755 --- a/functional/otlp/run_test_mode.sh +++ b/functional/otlp/run_test_mode.sh @@ -22,11 +22,17 @@ set -e [ -z "${SERVER_MODE}" ] && export SERVER_MODE="none" +[ -z "${TEST_EXECUTABLE}" ] && echo "Please specify TEST_EXECUTABLE name" && exit 1 + +[ -z "${TEST_URL}" ] && echo "Please specify TEST_URL endpoint (without scheme)" && exit 1 + export CERT_DIR=../cert -export TEST_BIN_DIR=${BUILD_DIR}/functional/otlp/ +export TEST_BIN_DIR="${BUILD_DIR}/functional/otlp/" + +[ ! -f "${TEST_BIN_DIR}/${TEST_EXECUTABLE}" ] && echo "::notice::Executable ${TEST_EXECUTABLE} not built in this configuration" && exit 0 -${TEST_BIN_DIR}/func_otlp_http --list > test_list.txt +"${TEST_BIN_DIR}/${TEST_EXECUTABLE}" --list > test_list.txt export TEST_FULL_NAME="" @@ -34,7 +40,7 @@ export TEST_FULL_NAME="" # Connect with no security # -export TEST_ENDPOINT="http://localhost:4318/v1/traces" +export TEST_ENDPOINT="http://${TEST_URL}" export TEST_RUN="insecure" for T in `cat test_list.txt` @@ -42,7 +48,7 @@ do echo "=====================================================================" echo "Running test ${T} on ${TEST_RUN} ${TEST_ENDPOINT} with server ${SERVER_MODE}" TEST_FULL_NAME="${T}-${TEST_RUN}-${SERVER_MODE}" - ${TEST_BIN_DIR}/func_otlp_http --debug --mode ${SERVER_MODE} --cert-dir ${CERT_DIR} --endpoint ${TEST_ENDPOINT} ${T} + "${TEST_BIN_DIR}/${TEST_EXECUTABLE}" --debug --mode ${SERVER_MODE} --cert-dir ${CERT_DIR} --endpoint ${TEST_ENDPOINT} ${T} RC=$? if [ ${RC} -eq 0 ]; then echo "TEST ${TEST_FULL_NAME}: PASSED" | tee -a report.log @@ -55,7 +61,7 @@ done # Connect with security # -export TEST_ENDPOINT="https://localhost:4318/v1/traces" +export TEST_ENDPOINT="https://${TEST_URL}" export TEST_RUN="secure" for T in `cat test_list.txt` @@ -63,7 +69,7 @@ do echo "=====================================================================" echo "Running test ${T} on ${TEST_RUN} ${TEST_ENDPOINT} with server ${SERVER_MODE}" TEST_FULL_NAME="${T}-${TEST_RUN}-${SERVER_MODE}" - ${TEST_BIN_DIR}/func_otlp_http --debug --mode ${SERVER_MODE} --cert-dir ${CERT_DIR} --endpoint ${TEST_ENDPOINT} ${T} + "${TEST_BIN_DIR}/${TEST_EXECUTABLE}" --debug --mode ${SERVER_MODE} --cert-dir ${CERT_DIR} --endpoint ${TEST_ENDPOINT} ${T} RC=$? if [ ${RC} -eq 0 ]; then echo "TEST ${TEST_FULL_NAME}: PASSED" | tee -a report.log