diff --git a/api/bazel/repositories.bzl b/api/bazel/repositories.bzl index f7783702969ca..490e72491756b 100644 --- a/api/bazel/repositories.bzl +++ b/api/bazel/repositories.bzl @@ -27,9 +27,8 @@ def api_dependencies(): build_file_content = PROMETHEUSMETRICS_BUILD_CONTENT, ) envoy_http_archive( - name = "io_opencensus_trace", + name = "opencensus_proto", locations = REPOSITORY_LOCATIONS, - build_file_content = OPENCENSUSTRACE_BUILD_CONTENT, ) envoy_http_archive( name = "kafka_source", @@ -38,8 +37,9 @@ def api_dependencies(): ) GOOGLEAPIS_BUILD_CONTENT = """ -load("@com_google_protobuf//:protobuf.bzl", "cc_proto_library", "py_proto_library") +load("@com_google_protobuf//:protobuf.bzl", "py_proto_library") load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") +load("@com_github_grpc_grpc//bazel:cc_grpc_library.bzl", "cc_grpc_library") filegroup( name = "api_httpbody_protos_src", @@ -58,12 +58,7 @@ proto_library( cc_proto_library( name = "api_httpbody_protos", - srcs = [ - "google/api/httpbody.proto", - ], - default_runtime = "@com_google_protobuf//:protobuf", - protoc = "@com_google_protobuf//:protoc", - deps = ["@com_google_protobuf//:cc_wkt_protos"], + deps = [":api_httpbody_protos_proto"], visibility = ["//visibility:public"], ) @@ -114,14 +109,7 @@ proto_library( cc_proto_library( name = "http_api_protos", - srcs = [ - "google/api/annotations.proto", - "google/api/http.proto", - ], - default_runtime = "@com_google_protobuf//:protobuf", - protoc = "@com_google_protobuf//:protoc", - linkstatic = 1, - deps = ["@com_google_protobuf//:cc_wkt_protos"], + deps = [":http_api_protos_proto"], visibility = ["//visibility:public"], ) @@ -164,14 +152,9 @@ proto_library( ) cc_proto_library( - name = "rpc_status_protos", - srcs = ["google/rpc/status.proto"], - default_runtime = "@com_google_protobuf//:protobuf", - protoc = "@com_google_protobuf//:protoc", - deps = [ - "@com_google_protobuf//:cc_wkt_protos" - ], - visibility = ["//visibility:public"], + name = "rpc_status_protos", + deps = [":rpc_status_protos_lib"], + visibility = ["//visibility:public"], ) go_proto_library( @@ -195,6 +178,35 @@ py_proto_library( visibility = ["//visibility:public"], deps = ["@com_google_protobuf//:protobuf_python"], ) + +proto_library( + name = "tracing_proto_proto", + srcs = [ + "google/devtools/cloudtrace/v2/trace.proto", + "google/devtools/cloudtrace/v2/tracing.proto", + ], + deps = [ + ":http_api_protos_proto", + ":rpc_status_protos_lib", + "@com_google_protobuf//:timestamp_proto", + "@com_google_protobuf//:wrappers_proto", + "@com_google_protobuf//:empty_proto", + ], +) + +cc_proto_library( + name = "tracing_proto_cc", + deps = [":tracing_proto_proto"], +) + +cc_grpc_library( + name = "tracing_proto", + srcs = [":tracing_proto_proto"], + deps = [":tracing_proto_cc"], + grpc_only = True, + visibility = ["@io_opencensus_cpp//opencensus:__subpackages__"], +) + """ GOGOPROTO_BUILD_CONTENT = """ diff --git a/api/bazel/repository_locations.bzl b/api/bazel/repository_locations.bzl index b31c59aebd8cc..541f6c4a5d767 100644 --- a/api/bazel/repository_locations.bzl +++ b/api/bazel/repository_locations.bzl @@ -4,8 +4,8 @@ BAZEL_SKYLIB_SHA256 = "2ef429f5d7ce7111263289644d233707dba35e39696377ebab8b0bc70 GOGOPROTO_RELEASE = "1.2.1" GOGOPROTO_SHA256 = "99e423905ba8921e86817607a5294ffeedb66fdd4a85efce5eb2848f715fdb3a" -OPENCENSUS_RELEASE = "0.1.0" -OPENCENSUS_SHA256 = "4fd21cc6de63d7cb979fd749d8101ff425905aa0826fed26019d1c311fcf19a7" +OPENCENSUS_PROTO_GIT_SHA = "d5d80953a8c2ff4633087af6933cd152678434bb" # Mar 18, 2019 +OPENCENSUS_PROTO_SHA256 = "a4e87a1da21d1b3a16674332c3ee6e2689d52f3532e2ff8cb4a626c8bcdabcfc" PGV_GIT_SHA = "26db5cb7c01a67c6a2e21a832106406185530b2f" PGV_SHA256 = "6510cbcf69d99059c652ae2376f6240bc761d0b019cd962225f4f609be361e26" @@ -44,10 +44,10 @@ REPOSITORY_LOCATIONS = dict( strip_prefix = "client_model-" + PROMETHEUS_GIT_SHA, urls = ["https://github.com/prometheus/client_model/archive/" + PROMETHEUS_GIT_SHA + ".tar.gz"], ), - io_opencensus_trace = dict( - sha256 = OPENCENSUS_SHA256, - strip_prefix = "opencensus-proto-" + OPENCENSUS_RELEASE + "/src/opencensus/proto/trace/v1", - urls = ["https://github.com/census-instrumentation/opencensus-proto/archive/v" + OPENCENSUS_RELEASE + ".tar.gz"], + opencensus_proto = dict( + sha256 = OPENCENSUS_PROTO_SHA256, + strip_prefix = "opencensus-proto-" + OPENCENSUS_PROTO_GIT_SHA + "/src", + urls = ["https://github.com/census-instrumentation/opencensus-proto/archive/" + OPENCENSUS_PROTO_GIT_SHA + ".tar.gz"], ), kafka_source = dict( sha256 = KAFKA_SOURCE_SHA, diff --git a/api/envoy/config/trace/v2/BUILD b/api/envoy/config/trace/v2/BUILD index 2afbb6f263252..b00f63dafb454 100644 --- a/api/envoy/config/trace/v2/BUILD +++ b/api/envoy/config/trace/v2/BUILD @@ -10,6 +10,7 @@ api_proto_library_internal( ], deps = [ "//envoy/api/v2/core:grpc_service", + "@opencensus_proto//opencensus/proto/trace/v1:trace_config_proto", ], ) @@ -18,5 +19,6 @@ api_go_proto_library( proto = ":trace", deps = [ "//envoy/api/v2/core:grpc_service_go_proto", + "@opencensus_proto//opencensus/proto/trace/v1:trace_and_config_proto_go", ], ) diff --git a/api/envoy/config/trace/v2/trace.proto b/api/envoy/config/trace/v2/trace.proto index e21766b9a2a02..21fb8d8ddf217 100644 --- a/api/envoy/config/trace/v2/trace.proto +++ b/api/envoy/config/trace/v2/trace.proto @@ -11,6 +11,7 @@ option java_package = "io.envoyproxy.envoy.config.trace.v2"; option go_package = "v2"; import "envoy/api/v2/core/grpc_service.proto"; +import "opencensus/proto/trace/v1/trace_config.proto"; import "google/protobuf/any.proto"; import "google/protobuf/struct.proto"; @@ -33,6 +34,7 @@ message Tracing { // - *envoy.zipkin* // - *envoy.dynamic.ot* // - *envoy.tracers.datadog* + // - *envoy.tracers.opencensus* string name = 1 [(validate.rules).string.min_bytes = 1]; // Trace driver specific configuration which depends on the driver being instantiated. @@ -42,6 +44,7 @@ message Tracing { // - :ref:`ZipkinConfig ` // - :ref:`DynamicOtConfig ` // - :ref:`DatadogConfig ` + // - :ref:`OpenCensusConfig ` oneof config_type { google.protobuf.Struct config = 2; @@ -103,6 +106,52 @@ message DatadogConfig { string service_name = 2 [(validate.rules).string.min_bytes = 1]; } +// Configuration for the OpenCensus tracer. +// [#proto-status: experimental] +message OpenCensusConfig { + // Configures tracing, e.g. the sampler, max number of annotations, etc. + opencensus.proto.trace.v1.TraceConfig trace_config = 1; + + // Enables the stdout exporter if set to true. This is intended for debugging + // purposes. + bool stdout_exporter_enabled = 2; + + // Enables the Stackdriver exporter if set to true. The project_id must also + // be set. + bool stackdriver_exporter_enabled = 3; + + // The Cloud project_id to use for Stackdriver tracing. + string stackdriver_project_id = 4; + + // Enables the Zipkin exporter if set to true. The url and service name must + // also be set. + bool zipkin_exporter_enabled = 5; + + // The URL to Zipkin, e.g. "http://127.0.0.1:9411/api/v2/spans" + string zipkin_url = 6; + + // The Zipkin service name. + string zipkin_service_name = 7; + + enum TraceContext { + // W3C Trace-Context format "traceparent:" header. + trace_context = 0; + + // Binary "grpc-trace-bin:" header. + grpc_trace_bin = 1; + + // "X-Cloud-Trace-Context:" header. + cloud_trace_context = 2; + } + + // List of incoming trace context headers we will accept. First one found + // wins. + repeated TraceContext incoming_trace_context = 8; + + // List of outgoing trace context headers we will produce. + repeated TraceContext outgoing_trace_context = 9; +} + // Configuration structure. message TraceServiceConfig { // The upstream gRPC cluster that hosts the metrics service. diff --git a/api/envoy/service/trace/v2/BUILD b/api/envoy/service/trace/v2/BUILD index 245dc85af6a8d..2b3367f0af456 100644 --- a/api/envoy/service/trace/v2/BUILD +++ b/api/envoy/service/trace/v2/BUILD @@ -9,7 +9,7 @@ api_proto_library_internal( require_py = 0, deps = [ "//envoy/api/v2/core:base", - "@io_opencensus_trace//:trace_model", + "@opencensus_proto//opencensus/proto/trace/v1:trace_proto", ], ) @@ -18,6 +18,6 @@ api_go_grpc_library( proto = ":trace_service", deps = [ "//envoy/api/v2/core:base_go_proto", - "@io_opencensus_trace//:trace_model_go_proto", + "@opencensus_proto//opencensus/proto/trace/v1:trace_proto_go", ], ) diff --git a/api/envoy/service/trace/v2/trace_service.proto b/api/envoy/service/trace/v2/trace_service.proto index a6385bb1f7430..ec87b35606516 100644 --- a/api/envoy/service/trace/v2/trace_service.proto +++ b/api/envoy/service/trace/v2/trace_service.proto @@ -11,7 +11,7 @@ option go_package = "v2"; option java_generic_services = true; import "envoy/api/v2/core/base.proto"; -import "trace.proto"; +import "opencensus/proto/trace/v1/trace.proto"; import "google/api/annotations.proto"; diff --git a/bazel/external/rapidjson.BUILD b/bazel/external/rapidjson.BUILD index 0a8a617510d57..97948eee70722 100644 --- a/bazel/external/rapidjson.BUILD +++ b/bazel/external/rapidjson.BUILD @@ -3,6 +3,7 @@ licenses(["notice"]) # Apache 2 cc_library( name = "rapidjson", hdrs = glob(["include/rapidjson/**/*.h"]), + defines = ["RAPIDJSON_HAS_STDSTRING=1"], includes = ["include"], visibility = ["//visibility:public"], ) diff --git a/bazel/foreign_cc/BUILD b/bazel/foreign_cc/BUILD index 54117a841379f..69b5703c17b6a 100644 --- a/bazel/foreign_cc/BUILD +++ b/bazel/foreign_cc/BUILD @@ -66,6 +66,26 @@ envoy_cmake_external( }), ) +envoy_cmake_external( + name = "curl", + cache_entries = { + "BUILD_CURL_EXE": "off", + "BUILD_SHARED_LIBS": "off", + "CMAKE_BUILD_TYPE": "RelWithDebInfo", + "HTTP_ONLY": "on", + "BUILD_TESTING": "off", + "CMAKE_USE_OPENSSL": "off", + "CURL_CA_PATH": "none", + "CURL_HIDDEN_SYMBOLS": "off", + "CMAKE_USE_LIBSSH2": "off", + }, + lib_source = "@com_github_curl//:all", + static_libraries = select({ + "//bazel:windows_x86_64": ["curl.lib"], + "//conditions:default": ["libcurl.a"], + }), +) + envoy_cmake_external( name = "event", cache_entries = { diff --git a/bazel/foreign_cc/com_lightstep_tracer_cpp.patch b/bazel/foreign_cc/com_lightstep_tracer_cpp.patch new file mode 100644 index 0000000000000..c0a4055ae4832 --- /dev/null +++ b/bazel/foreign_cc/com_lightstep_tracer_cpp.patch @@ -0,0 +1,11 @@ +--- lightstep-tracer-common/BUILD 2019-05-29 13:04:55.651083767 -0700 ++++ lightstep-tracer-common/BUILD 2019-05-29 13:04:59.711080560 -0700 +@@ -2,7 +2,7 @@ + name = "collector_proto", + srcs = ["collector.proto"], + deps = [ +- "@lightstep_vendored_googleapis//:googleapis_proto", ++ "@googleapis//:http_api_protos_proto", + "@com_google_protobuf//:timestamp_proto", + ], + visibility = ["//visibility:public"], diff --git a/bazel/foreign_cc/io_opencensus_cpp.patch b/bazel/foreign_cc/io_opencensus_cpp.patch new file mode 100644 index 0000000000000..f10ea86a9a149 --- /dev/null +++ b/bazel/foreign_cc/io_opencensus_cpp.patch @@ -0,0 +1,52 @@ +--- copts.bzl 2019-06-03 16:42:49.752903373 -0700 ++++ opencensus/copts.bzl 2019-06-03 16:43:58.136847278 -0700 +@@ -22,25 +22,25 @@ + """ + + load( +- "@com_google_absl//absl:copts/GENERATED_copts.bzl", +- "ABSL_GCC_FLAGS", +- "ABSL_GCC_TEST_FLAGS", +- "ABSL_LLVM_FLAGS", +- "ABSL_LLVM_TEST_FLAGS", +- "ABSL_MSVC_FLAGS", +- "ABSL_MSVC_TEST_FLAGS", ++ "@com_google_absl//absl:copts.bzl", ++ "GCC_FLAGS", ++ "GCC_TEST_FLAGS", ++ "LLVM_FLAGS", ++ "LLVM_TEST_FLAGS", ++ "MSVC_FLAGS", ++ "MSVC_TEST_FLAGS", + ) + + WERROR = ["-Werror=return-type", "-Werror=switch"] + + DEFAULT_COPTS = select({ +- "//opencensus:llvm_compiler": ABSL_LLVM_FLAGS + WERROR, +- "//opencensus:windows": ABSL_MSVC_FLAGS, +- "//conditions:default": ABSL_GCC_FLAGS + WERROR, ++ "//opencensus:llvm_compiler": LLVM_FLAGS + WERROR, ++ "//opencensus:windows": MSVC_FLAGS, ++ "//conditions:default": GCC_FLAGS + WERROR, + }) + + TEST_COPTS = DEFAULT_COPTS + select({ +- "//opencensus:llvm_compiler": ABSL_LLVM_TEST_FLAGS + WERROR, +- "//opencensus:windows": ABSL_MSVC_TEST_FLAGS, +- "//conditions:default": ABSL_GCC_TEST_FLAGS + WERROR, ++ "//opencensus:llvm_compiler": LLVM_TEST_FLAGS + WERROR, ++ "//opencensus:windows": MSVC_TEST_FLAGS, ++ "//conditions:default": GCC_TEST_FLAGS + WERROR, + }) +--- BUILD 2019-06-03 18:30:58.511651926 -0700 ++++ opencensus/exporters/trace/stackdriver/BUILD 2019-06-03 18:32:38.107571186 -0700 +@@ -28,7 +28,7 @@ + copts = DEFAULT_COPTS, + visibility = ["//visibility:public"], + deps = [ +- "//google/devtools/cloudtrace/v2:tracing_proto", ++ "@googleapis//:tracing_proto", + "//opencensus/common:version", + "//opencensus/common/internal/grpc:status", + "//opencensus/common/internal/grpc:with_user_agent", diff --git a/bazel/repositories.bzl b/bazel/repositories.bzl index 6cc771b3ea04a..72c5c6e9d87c2 100644 --- a/bazel/repositories.bzl +++ b/bazel/repositories.bzl @@ -148,6 +148,9 @@ def envoy_dependencies(skip_targets = []): _com_google_absl() _com_google_googletest() _com_google_protobuf() + _io_opencensus_cpp() + _com_github_curl() + _com_github_envoyproxy_sqlparser() _com_googlesource_quiche() _com_lightstep_tracer_cpp() _io_opentracing_cpp() @@ -339,10 +342,13 @@ def _io_opentracing_cpp(): ) def _com_lightstep_tracer_cpp(): + location = REPOSITORY_LOCATIONS["com_lightstep_tracer_cpp"] _repository_impl("com_lightstep_tracer_cpp") - _repository_impl( - name = "lightstep_vendored_googleapis", - build_file = "@com_lightstep_tracer_cpp//:lightstep-tracer-common/third_party/googleapis/BUILD", + http_archive( + name = "com_lightstep_tracer_cpp", + patch_args = ["-p0"], + patches = ["@envoy//bazel/foreign_cc:com_lightstep_tracer_cpp.patch"], + **location ) native.bind( name = "lightstep", @@ -511,6 +517,58 @@ def _com_google_protobuf(): actual = "@com_google_protobuf//util/python:python_headers", ) +def _io_opencensus_cpp(): + location = REPOSITORY_LOCATIONS["io_opencensus_cpp"] + http_archive( + name = "io_opencensus_cpp", + patch_args = ["-p0"], + patches = ["@envoy//bazel/foreign_cc:io_opencensus_cpp.patch"], + **location + ) + native.bind( + name = "opencensus_trace", + actual = "@io_opencensus_cpp//opencensus/trace", + ) + native.bind( + name = "opencensus_trace_cloud_trace_context", + actual = "@io_opencensus_cpp//opencensus/trace:cloud_trace_context", + ) + native.bind( + name = "opencensus_trace_grpc_trace_bin", + actual = "@io_opencensus_cpp//opencensus/trace:grpc_trace_bin", + ) + native.bind( + name = "opencensus_trace_trace_context", + actual = "@io_opencensus_cpp//opencensus/trace:trace_context", + ) + native.bind( + name = "opencensus_exporter_stdout", + actual = "@io_opencensus_cpp//opencensus/exporters/trace/stdout:stdout_exporter", + ) + native.bind( + name = "opencensus_exporter_stackdriver", + actual = "@io_opencensus_cpp//opencensus/exporters/trace/stackdriver:stackdriver_exporter", + ) + native.bind( + name = "opencensus_exporter_zipkin", + actual = "@io_opencensus_cpp//opencensus/exporters/trace/zipkin:zipkin_exporter", + ) + +def _com_github_curl(): + # Used by OpenCensus Zipkin exporter. + location = REPOSITORY_LOCATIONS["com_github_curl"] + http_archive( + name = "com_github_curl", + build_file_content = BUILD_ALL_CONTENT + """ +cc_library(name = "curl", visibility = ["//visibility:public"], deps = ["@envoy//bazel/foreign_cc:curl"]) +""", + **location + ) + native.bind( + name = "curl", + actual = "@envoy//bazel/foreign_cc:curl", + ) + def _com_googlesource_quiche(): location = REPOSITORY_LOCATIONS["com_googlesource_quiche"] genrule_repository( diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index aa55df22d7338..e6b5f3fb354b2 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -112,12 +112,6 @@ REPOSITORY_LOCATIONS = dict( strip_prefix = "lightstep-tracer-cpp-0.8.0", urls = ["https://github.com/lightstep/lightstep-tracer-cpp/archive/v0.8.0.tar.gz"], ), - lightstep_vendored_googleapis = dict( - sha256 = "d1ef4f790eeaa805e7b364de05b91f9eed66bd6ae46f1483bbf49c33d86998e5", - strip_prefix = "googleapis-d6f78d948c53f3b400bb46996eb3084359914f9b", - # From: https://github.com/lightstep/lightstep-tracer-cpp/blob/v0.8.0/lightstep-tracer-common/third_party/googleapis/README.lightstep-tracer-common#L6 - urls = ["https://github.com/googleapis/googleapis/archive/d6f78d948c53f3b400bb46996eb3084359914f9b.tar.gz"], - ), com_github_datadog_dd_opentracing_cpp = dict( sha256 = "a3d1c03e7af570fa64c01df259e6e9bb78637a6bd9c65c6bf7e8703e466dc22f", strip_prefix = "dd-opentracing-cpp-0.4.2", @@ -237,6 +231,16 @@ REPOSITORY_LOCATIONS = dict( strip_prefix = "subpar-2.0.0", urls = ["https://github.com/google/subpar/archive/2.0.0.tar.gz"], ), + io_opencensus_cpp = dict( + sha256 = "d6d68704c419a9e892bd1f942e09509ebc5a318499a1abcf2c09734e5dc56e19", + strip_prefix = "opencensus-cpp-1145dd77ffb7a2845c71c8e6ca188ef55e4ff607", + urls = ["https://github.com/census-instrumentation/opencensus-cpp/archive/1145dd77ffb7a2845c71c8e6ca188ef55e4ff607.tar.gz"], + ), + com_github_curl = dict( + sha256 = "d483b89062832e211c887d7cf1b65c902d591b48c11fe7d174af781681580b41", + strip_prefix = "curl-7.63.0", + urls = ["https://github.com/curl/curl/releases/download/curl-7_63_0/curl-7.63.0.tar.gz"], + ), com_googlesource_quiche = dict( # Static snapshot of https://quiche.googlesource.com/quiche/+archive/7bf7c3c358eb954e463bde14ea27444f4bd8ea05.tar.gz sha256 = "36fe180d532a9ccb18cd32328af5231636c7408104523f9ed5eebbad75f1e039", diff --git a/source/common/common/base64.cc b/source/common/common/base64.cc index 9a56871464b94..a63b9faf21d2c 100644 --- a/source/common/common/base64.cc +++ b/source/common/common/base64.cc @@ -141,22 +141,35 @@ inline void encodeLast(uint64_t pos, uint8_t last_char, std::string& ret, } // namespace std::string Base64::decode(const std::string& input) { - if (input.length() % 4 || input.empty()) { + if (input.length() % 4) { + return EMPTY_STRING; + } + return decodeWithoutPadding(input); +} + +std::string Base64::decodeWithoutPadding(absl::string_view input) { + if (input.empty()) { return EMPTY_STRING; } - // Last position before "valid" padding character. - uint64_t last = input.length() - 1; - size_t max_length = input.length() / 4 * 3; // At most last two chars can be '='. - if (input[input.length() - 1] == '=') { - max_length--; - last = input.length() - 2; - if (input[input.length() - 2] == '=') { - max_length--; - last = input.length() - 3; + size_t n = input.length(); + if (input[n - 1] == '=') { + n--; + if (n > 0 && input[n - 1] == '=') { + n--; } } + // Last position before "valid" padding character. + uint64_t last = n - 1; + // Determine output length. + size_t max_length = (n + 3) / 4 * 3; + if (n % 4 == 3) { + max_length -= 1; + } + if (n % 4 == 2) { + max_length -= 2; + } std::string ret; ret.reserve(max_length); @@ -170,6 +183,7 @@ std::string Base64::decode(const std::string& input) { return EMPTY_STRING; } + ASSERT(ret.size() == max_length); return ret; } diff --git a/source/common/common/base64.h b/source/common/common/base64.h index ea614eadedd56..13beff40b64ab 100644 --- a/source/common/common/base64.h +++ b/source/common/common/base64.h @@ -5,6 +5,8 @@ #include "envoy/buffer/buffer.h" +#include "absl/strings/string_view.h" + namespace Envoy { /** @@ -43,6 +45,15 @@ class Base64 { * bytes. */ static std::string decode(const std::string& input); + + /** + * Base64 decode an input string. Padding is not required. + * @param input supplies the input to decode. + * + * Note, decoded string may contain '\0' at any position, it should be treated as a sequence of + * bytes. + */ + static std::string decodeWithoutPadding(absl::string_view input); }; /** diff --git a/source/extensions/extensions_build_config.bzl b/source/extensions/extensions_build_config.bzl index 7f948dbe3e4d8..93fd4108aa8bc 100644 --- a/source/extensions/extensions_build_config.bzl +++ b/source/extensions/extensions_build_config.bzl @@ -120,6 +120,7 @@ EXTENSIONS = { "envoy.tracers.lightstep": "//source/extensions/tracers/lightstep:config", "envoy.tracers.datadog": "//source/extensions/tracers/datadog:config", "envoy.tracers.zipkin": "//source/extensions/tracers/zipkin:config", + "envoy.tracers.opencensus": "//source/extensions/tracers/opencensus:config", # # Transport sockets diff --git a/source/extensions/tracers/opencensus/BUILD b/source/extensions/tracers/opencensus/BUILD new file mode 100644 index 0000000000000..90bc4f9fc7ed9 --- /dev/null +++ b/source/extensions/tracers/opencensus/BUILD @@ -0,0 +1,41 @@ +licenses(["notice"]) # Apache 2 + +# Trace driver for OpenCensus: https://opencensus.io/ + +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_library", + "envoy_package", +) + +envoy_package() + +envoy_cc_library( + name = "config", + srcs = ["config.cc"], + hdrs = ["config.h"], + deps = [ + ":opencensus_tracer_impl", + "//source/extensions/tracers:well_known_names", + "//source/extensions/tracers/common:factory_base_lib", + ], +) + +envoy_cc_library( + name = "opencensus_tracer_impl", + srcs = ["opencensus_tracer_impl.cc"], + hdrs = ["opencensus_tracer_impl.h"], + external_deps = [ + "opencensus_trace", + "opencensus_trace_cloud_trace_context", + "opencensus_trace_grpc_trace_bin", + "opencensus_trace_trace_context", + "opencensus_exporter_stdout", + "opencensus_exporter_stackdriver", + "opencensus_exporter_zipkin", + ], + deps = [ + "//source/common/config:utility_lib", + "//source/common/tracing:http_tracer_lib", + ], +) diff --git a/source/extensions/tracers/opencensus/config.cc b/source/extensions/tracers/opencensus/config.cc new file mode 100644 index 0000000000000..038c329aff185 --- /dev/null +++ b/source/extensions/tracers/opencensus/config.cc @@ -0,0 +1,31 @@ +#include "extensions/tracers/opencensus/config.h" + +#include "envoy/registry/registry.h" + +#include "common/tracing/http_tracer_impl.h" + +#include "extensions/tracers/opencensus/opencensus_tracer_impl.h" +#include "extensions/tracers/well_known_names.h" + +namespace Envoy { +namespace Extensions { +namespace Tracers { +namespace OpenCensus { + +OpenCensusTracerFactory::OpenCensusTracerFactory() : FactoryBase(TracerNames::get().OpenCensus) {} + +Tracing::HttpTracerPtr OpenCensusTracerFactory::createHttpTracerTyped( + const envoy::config::trace::v2::OpenCensusConfig& proto_config, Server::Instance& server) { + Tracing::DriverPtr driver = std::make_unique(proto_config); + return std::make_unique(std::move(driver), server.localInfo()); +} + +/** + * Static registration for the OpenCensus tracer. @see RegisterFactory. + */ +REGISTER_FACTORY(OpenCensusTracerFactory, Server::Configuration::TracerFactory); + +} // namespace OpenCensus +} // namespace Tracers +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/tracers/opencensus/config.h b/source/extensions/tracers/opencensus/config.h new file mode 100644 index 0000000000000..20c2beea3f62d --- /dev/null +++ b/source/extensions/tracers/opencensus/config.h @@ -0,0 +1,32 @@ +#pragma once + +#include + +#include "envoy/config/trace/v2/trace.pb.validate.h" + +#include "extensions/tracers/common/factory_base.h" + +namespace Envoy { +namespace Extensions { +namespace Tracers { +namespace OpenCensus { + +/** + * Config registration for the OpenCensus tracer. @see TracerFactory. + */ +class OpenCensusTracerFactory + : public Common::FactoryBase { +public: + OpenCensusTracerFactory(); + +private: + // FactoryBase + Tracing::HttpTracerPtr + createHttpTracerTyped(const envoy::config::trace::v2::OpenCensusConfig& proto_config, + Server::Instance& server) override; +}; + +} // namespace OpenCensus +} // namespace Tracers +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/tracers/opencensus/opencensus_tracer_impl.cc b/source/extensions/tracers/opencensus/opencensus_tracer_impl.cc new file mode 100644 index 0000000000000..67e291a8cae34 --- /dev/null +++ b/source/extensions/tracers/opencensus/opencensus_tracer_impl.cc @@ -0,0 +1,258 @@ +#include "extensions/tracers/opencensus/opencensus_tracer_impl.h" + +#include "envoy/http/header_map.h" + +#include "common/common/base64.h" + +#include "absl/strings/str_cat.h" +#include "opencensus/exporters/trace/stackdriver/stackdriver_exporter.h" +#include "opencensus/exporters/trace/stdout/stdout_exporter.h" +#include "opencensus/exporters/trace/zipkin/zipkin_exporter.h" +#include "opencensus/trace/propagation/cloud_trace_context.h" +#include "opencensus/trace/propagation/grpc_trace_bin.h" +#include "opencensus/trace/propagation/trace_context.h" +#include "opencensus/trace/sampler.h" +#include "opencensus/trace/span.h" +#include "opencensus/trace/span_context.h" +#include "opencensus/trace/trace_config.h" +#include "opencensus/trace/trace_params.h" + +namespace Envoy { +namespace Extensions { +namespace Tracers { +namespace OpenCensus { + +namespace { + +class ConstantValues { +public: + const Http::LowerCaseString TRACEPARENT{"traceparent"}; + const Http::LowerCaseString GRPC_TRACE_BIN{"grpc-trace-bin"}; + const Http::LowerCaseString X_CLOUD_TRACE_CONTEXT{"x-cloud-trace-context"}; +}; + +typedef ConstSingleton Constants; + +/** + * OpenCensus tracing implementation of the Envoy Span object. + */ +class Span : public Tracing::Span { +public: + Span(const Tracing::Config& config, const envoy::config::trace::v2::OpenCensusConfig& oc_config, + Http::HeaderMap& request_headers, const std::string& operation_name, SystemTime start_time, + const Tracing::Decision tracing_decision); + + // Used by spawnChild(). + Span(const envoy::config::trace::v2::OpenCensusConfig& oc_config, + ::opencensus::trace::Span&& span); + + void setOperation(absl::string_view operation) override; + void setTag(absl::string_view name, absl::string_view value) override; + void log(SystemTime timestamp, const std::string& event) override; + void finishSpan() override; + void injectContext(Http::HeaderMap& request_headers) override; + Tracing::SpanPtr spawnChild(const Tracing::Config& config, const std::string& name, + SystemTime start_time) override; + void setSampled(bool sampled) override; + +private: + ::opencensus::trace::Span span_; + const envoy::config::trace::v2::OpenCensusConfig& oc_config_; +}; + +::opencensus::trace::Span +startSpanHelper(const std::string& name, bool traced, const Http::HeaderMap& request_headers, + const envoy::config::trace::v2::OpenCensusConfig& oc_config) { + // Determine if there is a parent context. + using OpenCensusConfig = envoy::config::trace::v2::OpenCensusConfig; + ::opencensus::trace::SpanContext parent_ctx; + for (const auto& incoming : oc_config.incoming_trace_context()) { + bool found = false; + switch (incoming) { + case OpenCensusConfig::trace_context: { + const Http::HeaderEntry* header = request_headers.get(Constants::get().TRACEPARENT); + if (header != nullptr) { + found = true; + parent_ctx = ::opencensus::trace::propagation::FromTraceParentHeader( + header->value().getStringView()); + } + break; + } + + case OpenCensusConfig::grpc_trace_bin: { + const Http::HeaderEntry* header = request_headers.get(Constants::get().GRPC_TRACE_BIN); + if (header != nullptr) { + found = true; + parent_ctx = ::opencensus::trace::propagation::FromGrpcTraceBinHeader( + Base64::decodeWithoutPadding(header->value().getStringView())); + } + break; + } + + case OpenCensusConfig::cloud_trace_context: { + const Http::HeaderEntry* header = request_headers.get(Constants::get().X_CLOUD_TRACE_CONTEXT); + if (header != nullptr) { + found = true; + parent_ctx = ::opencensus::trace::propagation::FromCloudTraceContextHeader( + header->value().getStringView()); + } + break; + } + } + // First header found wins. + if (found) { + break; + } + } + + // Honor Envoy's tracing decision. + ::opencensus::trace::AlwaysSampler always_sampler; + ::opencensus::trace::NeverSampler never_sampler; + // This is safe because opts are not used after StartSpan. + ::opencensus::trace::StartSpanOptions opts{&never_sampler}; + if (traced) { + opts.sampler = &always_sampler; + } + + if (parent_ctx.IsValid()) { + return ::opencensus::trace::Span::StartSpanWithRemoteParent(name, parent_ctx, opts); + } + return ::opencensus::trace::Span::StartSpan(name, /*parent=*/nullptr, opts); +} + +Span::Span(const Tracing::Config& config, + const envoy::config::trace::v2::OpenCensusConfig& oc_config, + Http::HeaderMap& request_headers, const std::string& operation_name, + SystemTime /*start_time*/, const Tracing::Decision tracing_decision) + : span_(startSpanHelper(operation_name, tracing_decision.traced, request_headers, oc_config)), + oc_config_(oc_config) { + span_.AddAttribute("OperationName", config.operationName() == Tracing::OperationName::Ingress + ? "Ingress" + : "Egress"); +} + +Span::Span(const envoy::config::trace::v2::OpenCensusConfig& oc_config, + ::opencensus::trace::Span&& span) + : span_(std::move(span)), oc_config_(oc_config) {} + +void Span::setOperation(absl::string_view operation) { + span_.AddAnnotation("setOperation", {{"operation", operation}}); +} + +void Span::setTag(absl::string_view name, absl::string_view value) { + span_.AddAttribute(name, value); +} + +void Span::log(SystemTime /*timestamp*/, const std::string& event) { + // timestamp is ignored. + span_.AddAnnotation(event); +} + +void Span::finishSpan() { span_.End(); } + +void Span::injectContext(Http::HeaderMap& request_headers) { + using OpenCensusConfig = envoy::config::trace::v2::OpenCensusConfig; + for (const auto& outgoing : oc_config_.outgoing_trace_context()) { + switch (outgoing) { + case OpenCensusConfig::trace_context: + request_headers.setReferenceKey( + Constants::get().TRACEPARENT, + ::opencensus::trace::propagation::ToTraceParentHeader(span_.context())); + break; + + case OpenCensusConfig::grpc_trace_bin: { + std::string val = ::opencensus::trace::propagation::ToGrpcTraceBinHeader(span_.context()); + val = Base64::encode(val.data(), val.size(), /*add_padding=*/false); + request_headers.setReferenceKey(Constants::get().GRPC_TRACE_BIN, val); + break; + } + + case OpenCensusConfig::cloud_trace_context: + request_headers.setReferenceKey( + Constants::get().X_CLOUD_TRACE_CONTEXT, + ::opencensus::trace::propagation::ToCloudTraceContextHeader(span_.context())); + break; + } + } +} + +Tracing::SpanPtr Span::spawnChild(const Tracing::Config& /*config*/, const std::string& name, + SystemTime /*start_time*/) { + span_.AddAnnotation("spawnChild"); + return std::make_unique(oc_config_, + ::opencensus::trace::Span::StartSpan(name, /*parent=*/&span_)); +} + +void Span::setSampled(bool sampled) { span_.AddAnnotation("setSampled", {{"sampled", sampled}}); } + +} // namespace + +Driver::Driver(const envoy::config::trace::v2::OpenCensusConfig& oc_config) + : oc_config_(oc_config) { + if (oc_config.has_trace_config()) { + applyTraceConfig(oc_config.trace_config()); + } + if (oc_config.stdout_exporter_enabled()) { + ::opencensus::exporters::trace::StdoutExporter::Register(); + } + if (oc_config.stackdriver_exporter_enabled()) { + ::opencensus::exporters::trace::StackdriverOptions opts; + opts.project_id = oc_config.stackdriver_project_id(); + ::opencensus::exporters::trace::StackdriverExporter::Register(opts); + } + if (oc_config.zipkin_exporter_enabled()) { + ::opencensus::exporters::trace::ZipkinExporterOptions opts(oc_config.zipkin_url()); + opts.service_name = oc_config.zipkin_service_name(); + ::opencensus::exporters::trace::ZipkinExporter::Register(opts); + } +} + +void Driver::applyTraceConfig(const opencensus::proto::trace::v1::TraceConfig& config) { + using SamplerCase = opencensus::proto::trace::v1::TraceConfig::SamplerCase; + using opencensus::proto::trace::v1::ConstantSampler; + constexpr double kDefaultSamplingProbability = 1e-4; + double probability = kDefaultSamplingProbability; + + switch (config.sampler_case()) { + case SamplerCase::kProbabilitySampler: + probability = config.probability_sampler().samplingprobability(); + break; + case SamplerCase::kConstantSampler: + switch (config.constant_sampler().decision()) { + case ConstantSampler::ALWAYS_OFF: + probability = 0.; + break; + case ConstantSampler::ALWAYS_ON: + case ConstantSampler::ALWAYS_PARENT: + probability = 1.; + break; + default: + break; /* Keep default probability. */ + } + break; + case SamplerCase::kRateLimitingSampler: + ENVOY_LOG(error, "RateLimitingSampler is not supported."); + break; + case SamplerCase::SAMPLER_NOT_SET: + break; // Keep default. + default: + ENVOY_LOG(error, "Unknown sampler type in TraceConfig."); + } + + ::opencensus::trace::TraceConfig::SetCurrentTraceParams(::opencensus::trace::TraceParams{ + uint32_t(config.max_number_of_attributes()), uint32_t(config.max_number_of_annotations()), + uint32_t(config.max_number_of_message_events()), uint32_t(config.max_number_of_links()), + ::opencensus::trace::ProbabilitySampler(probability)}); +} + +Tracing::SpanPtr Driver::startSpan(const Tracing::Config& config, Http::HeaderMap& request_headers, + const std::string& operation_name, SystemTime start_time, + const Tracing::Decision tracing_decision) { + return std::make_unique(config, oc_config_, request_headers, operation_name, start_time, + tracing_decision); +} + +} // namespace OpenCensus +} // namespace Tracers +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/tracers/opencensus/opencensus_tracer_impl.h b/source/extensions/tracers/opencensus/opencensus_tracer_impl.h new file mode 100644 index 0000000000000..cb5990bd763a4 --- /dev/null +++ b/source/extensions/tracers/opencensus/opencensus_tracer_impl.h @@ -0,0 +1,36 @@ +#pragma once + +#include "envoy/config/trace/v2/trace.pb.validate.h" +#include "envoy/tracing/http_tracer.h" + +#include "common/common/logger.h" + +namespace Envoy { +namespace Extensions { +namespace Tracers { +namespace OpenCensus { + +/** + * OpenCensus tracing driver. + */ +class Driver : public Tracing::Driver, Logger::Loggable { +public: + Driver(const envoy::config::trace::v2::OpenCensusConfig& oc_config); + + /** + * Implements the abstract Driver's startSpan operation. + */ + Tracing::SpanPtr startSpan(const Tracing::Config& config, Http::HeaderMap& request_headers, + const std::string& operation_name, SystemTime start_time, + const Tracing::Decision tracing_decision) override; + +private: + void applyTraceConfig(const opencensus::proto::trace::v1::TraceConfig& config); + + const envoy::config::trace::v2::OpenCensusConfig oc_config_; +}; + +} // namespace OpenCensus +} // namespace Tracers +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/tracers/well_known_names.h b/source/extensions/tracers/well_known_names.h index 2b141f61eda4a..e3e1b3f556ff7 100644 --- a/source/extensions/tracers/well_known_names.h +++ b/source/extensions/tracers/well_known_names.h @@ -21,6 +21,8 @@ class TracerNameValues { const std::string DynamicOt = "envoy.dynamic.ot"; // Datadog tracer const std::string Datadog = "envoy.tracers.datadog"; + // OpenCensus tracer + const std::string OpenCensus = "envoy.tracers.opencensus"; }; typedef ConstSingleton TracerNames; diff --git a/test/common/common/base64_test.cc b/test/common/common/base64_test.cc index 0affe2f93a135..04ad1dfb24b9e 100644 --- a/test/common/common/base64_test.cc +++ b/test/common/common/base64_test.cc @@ -87,9 +87,29 @@ TEST(Base64Test, DecodeFailure) { EXPECT_EQ("", Base64::decode("Zg..")); EXPECT_EQ("", Base64::decode("..Zg")); EXPECT_EQ("", Base64::decode("A===")); + EXPECT_EQ("", Base64::decode("====")); EXPECT_EQ("", Base64::decode("123")); } +TEST(Base64Test, DecodeWithoutPadding) { + EXPECT_EQ("foo", Base64::decodeWithoutPadding("Zm9v")); + EXPECT_EQ("fo", Base64::decodeWithoutPadding("Zm8")); + EXPECT_EQ("f", Base64::decodeWithoutPadding("Zg")); + EXPECT_EQ("foobar", Base64::decodeWithoutPadding("Zm9vYmFy")); + EXPECT_EQ("fooba", Base64::decodeWithoutPadding("Zm9vYmE")); + EXPECT_EQ("foob", Base64::decodeWithoutPadding("Zm9vYg")); + + EXPECT_EQ("", Base64::decodeWithoutPadding("")); + EXPECT_EQ("", Base64::decodeWithoutPadding("=")); + EXPECT_EQ("", Base64::decodeWithoutPadding("==")); + EXPECT_EQ("", Base64::decodeWithoutPadding("===")); + EXPECT_EQ("", Base64::decodeWithoutPadding("====")); + + EXPECT_EQ("f", Base64::decodeWithoutPadding("Zg")); + EXPECT_EQ("f", Base64::decodeWithoutPadding("Zg=")); + EXPECT_EQ("f", Base64::decodeWithoutPadding("Zg==")); +} + TEST(Base64Test, MultiSlicesBufferEncode) { Buffer::OwnedImpl buffer; buffer.add("foob", 4); diff --git a/test/extensions/tracers/opencensus/BUILD b/test/extensions/tracers/opencensus/BUILD new file mode 100644 index 0000000000000..d4d7d4cfb0791 --- /dev/null +++ b/test/extensions/tracers/opencensus/BUILD @@ -0,0 +1,35 @@ +licenses(["notice"]) # Apache 2 + +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_test_library", + "envoy_package", +) +load( + "//test/extensions:extensions_build_system.bzl", + "envoy_extension_cc_test", +) + +envoy_package() + +envoy_extension_cc_test( + name = "tracer_test", + srcs = ["tracer_test.cc"], + extension_name = "envoy.tracers.opencensus", + deps = [ + "//source/extensions/tracers/opencensus:opencensus_tracer_impl", + "//test/mocks/http:http_mocks", + "//test/mocks/tracing:tracing_mocks", + ], +) + +envoy_extension_cc_test( + name = "config_test", + srcs = ["config_test.cc"], + extension_name = "envoy.tracers.opencensus", + deps = [ + "//source/extensions/tracers/opencensus:config", + "//test/mocks/server:server_mocks", + "//test/test_common:utility_lib", + ], +) diff --git a/test/extensions/tracers/opencensus/config_test.cc b/test/extensions/tracers/opencensus/config_test.cc new file mode 100644 index 0000000000000..31442eee70079 --- /dev/null +++ b/test/extensions/tracers/opencensus/config_test.cc @@ -0,0 +1,83 @@ +#include "envoy/registry/registry.h" + +#include "extensions/tracers/opencensus/config.h" + +#include "test/mocks/server/mocks.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "opencensus/trace/sampler.h" +#include "opencensus/trace/trace_config.h" + +namespace Envoy { +namespace Extensions { +namespace Tracers { +namespace OpenCensus { + +TEST(OpenCensusTracerConfigTest, OpenCensusHttpTracer) { + NiceMock server; + const std::string yaml_string = R"EOF( + http: + name: envoy.tracers.opencensus + )EOF"; + + envoy::config::trace::v2::Tracing configuration; + TestUtility::loadFromYaml(yaml_string, configuration); + + OpenCensusTracerFactory factory; + auto message = Config::Utility::translateToFactoryConfig( + configuration.http(), ProtobufMessage::getStrictValidationVisitor(), factory); + Tracing::HttpTracerPtr tracer = factory.createHttpTracer(*message, server); + EXPECT_NE(nullptr, tracer); +} + +TEST(OpenCensusTracerConfigTest, OpenCensusHttpTracerWithTypedConfig) { + NiceMock server; + const std::string yaml_string = R"EOF( + http: + name: envoy.tracers.opencensus + typed_config: + "@type": type.googleapis.com/envoy.config.trace.v2.OpenCensusConfig + trace_config: + rate_limiting_sampler: + qps: 123 + max_number_of_attributes: 12 + max_number_of_annotations: 34 + max_number_of_message_events: 56 + max_number_of_links: 78 + stdout_exporter_enabled: true + stackdriver_exporter_enabled: true + stackdriver_project_id: test_project_id + zipkin_exporter_enabled: true + zipkin_url: http://127.0.0.1:9411/api/v2/spans + zipkin_service_name: test_service + incoming_trace_context: trace_context + incoming_trace_context: grpc_trace_bin + incoming_trace_context: cloud_trace_context + outgoing_trace_context: trace_context + )EOF"; + + envoy::config::trace::v2::Tracing configuration; + TestUtility::loadFromYaml(yaml_string, configuration); + + OpenCensusTracerFactory factory; + auto message = Config::Utility::translateToFactoryConfig( + configuration.http(), ProtobufMessage::getStrictValidationVisitor(), factory); + Tracing::HttpTracerPtr tracer = factory.createHttpTracer(*message, server); + EXPECT_NE(nullptr, tracer); + + // Reset TraceParams back to default. + ::opencensus::trace::TraceConfig::SetCurrentTraceParams( + {32, 32, 128, 32, ::opencensus::trace::ProbabilitySampler(1e-4)}); +} + +TEST(OpenCensusTracerConfigTest, DoubleRegistrationTest) { + EXPECT_THROW_WITH_MESSAGE( + (Registry::RegisterFactory()), + EnvoyException, "Double registration for name: 'envoy.tracers.opencensus'"); +} + +} // namespace OpenCensus +} // namespace Tracers +} // namespace Extensions +} // namespace Envoy diff --git a/test/extensions/tracers/opencensus/tracer_test.cc b/test/extensions/tracers/opencensus/tracer_test.cc new file mode 100644 index 0000000000000..b6b69c938ebfb --- /dev/null +++ b/test/extensions/tracers/opencensus/tracer_test.cc @@ -0,0 +1,288 @@ +// Usage: +// bazel run //test/extensions/tracers/opencensus:tracer_test -- -l debug + +#include +#include +#include + +#include "envoy/config/trace/v2/trace.pb.validate.h" + +#include "common/common/base64.h" + +#include "extensions/tracers/opencensus/opencensus_tracer_impl.h" + +#include "test/mocks/http/mocks.h" +#include "test/mocks/tracing/mocks.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "opencensus/trace/exporter/span_data.h" +#include "opencensus/trace/exporter/span_exporter.h" +#include "opencensus/trace/propagation/cloud_trace_context.h" +#include "opencensus/trace/propagation/grpc_trace_bin.h" +#include "opencensus/trace/propagation/trace_context.h" +#include "opencensus/trace/span.h" +#include "opencensus/trace/span_id.h" + +using testing::NiceMock; +using testing::Return; + +namespace opencensus { +namespace trace { +namespace exporter { + +class SpanExporterTestPeer { +public: + static constexpr auto& exportForTesting = SpanExporter::ExportForTesting; +}; + +} // namespace exporter +} // namespace trace +} // namespace opencensus + +namespace Envoy { +namespace Extensions { +namespace Tracers { +namespace OpenCensus { + +using envoy::config::trace::v2::OpenCensusConfig; +using ::opencensus::trace::exporter::SpanData; +using ::opencensus::trace::exporter::SpanExporter; + +namespace { + +// Custom export handler. We register this as an OpenCensus trace exporter, and +// use it to catch the spans we produce. +class SpanCatcher : public SpanExporter::Handler { +public: + void Export(const std::vector& spans) override { + absl::MutexLock lock(&mu_); + for (const auto& span : spans) { + spans_.emplace_back(span); + } + } + + // Returns generated SpanData, and clears the catcher. + std::vector catchSpans() { + // OpenCensus's trace exporter is running in a background thread, waiting + // for a periodic export. Force it to flush right now. + opencensus::trace::exporter::SpanExporterTestPeer::exportForTesting(); + absl::MutexLock lock(&mu_); + std::vector ret = std::move(spans_); + spans_.clear(); + return ret; + } + +private: + mutable absl::Mutex mu_; + std::vector spans_ GUARDED_BY(mu_); +}; + +// Use a Singleton SpanCatcher. +SpanCatcher* getSpanCatcher() { + static auto g_span_catcher = new SpanCatcher(); + return g_span_catcher; +} + +// Call this before generating spans to register the exporter that catches them. +void registerSpanCatcher() { + static bool done_once = false; + if (done_once) { + return; + } + SpanExporter::RegisterHandler(absl::WrapUnique(getSpanCatcher())); + done_once = true; +} + +} // namespace + +// Create a Span via the driver, test all of the Tracing::Span API, and verify +// the produced SpanData. +TEST(OpenCensusTracerTest, Span) { + registerSpanCatcher(); + OpenCensusConfig oc_config; + std::unique_ptr driver(new OpenCensus::Driver(oc_config)); + + NiceMock config; + Http::TestHeaderMapImpl request_headers{ + {":path", "/"}, {":method", "GET"}, {"x-request-id", "foo"}}; + const std::string operation_name{"my_operation_1"}; + SystemTime start_time; + + { + Tracing::SpanPtr span = driver->startSpan(config, request_headers, operation_name, start_time, + {Tracing::Reason::Sampling, true}); + span->setOperation("different_name"); + span->setTag("my_key", "my_value"); + span->log(start_time, "my annotation"); + // injectContext is tested in another unit test. + Tracing::SpanPtr child = span->spawnChild(config, "child_span", start_time); + child->finishSpan(); + span->setSampled(false); // Abandon tracer. + span->finishSpan(); + } + + // Retrieve SpanData from the OpenCensus trace exporter. + std::vector spans = getSpanCatcher()->catchSpans(); + ASSERT_EQ(2, spans.size()); + ::opencensus::trace::SpanId parent_span_id; + + // Check contents of parent span. + { + const auto& sd = (spans[0].name() == operation_name) ? spans[0] : spans[1]; + ENVOY_LOG_MISC(debug, "{}", sd.DebugString()); + + EXPECT_EQ("my_operation_1", sd.name()); + EXPECT_TRUE(sd.context().IsValid()); + EXPECT_TRUE(sd.context().trace_options().IsSampled()); + ::opencensus::trace::SpanId zeros; + EXPECT_EQ(zeros, sd.parent_span_id()); + parent_span_id = sd.context().span_id(); + + ASSERT_EQ(4, sd.annotations().events().size()); + EXPECT_EQ("setOperation", sd.annotations().events()[0].event().description()); + EXPECT_EQ("my annotation", sd.annotations().events()[1].event().description()); + EXPECT_EQ("spawnChild", sd.annotations().events()[2].event().description()); + EXPECT_EQ("setSampled", sd.annotations().events()[3].event().description()); + EXPECT_TRUE(sd.has_ended()); + } + + // And child span. + { + const auto& sd = (spans[0].name() == "child_span") ? spans[0] : spans[1]; + ENVOY_LOG_MISC(debug, "{}", sd.DebugString()); + + EXPECT_EQ("child_span", sd.name()); + EXPECT_TRUE(sd.context().IsValid()); + EXPECT_TRUE(sd.context().trace_options().IsSampled()); + EXPECT_EQ(parent_span_id, sd.parent_span_id()); + EXPECT_TRUE(sd.has_ended()); + } +} + +// Test that trace context propagation works. +TEST(OpenCensusTracerTest, PropagateTraceContext) { + registerSpanCatcher(); + // The test calls the helper with each kind of incoming context in turn. + auto helper = [](const std::string& header, const std::string& value) { + OpenCensusConfig oc_config; + oc_config.add_incoming_trace_context(OpenCensusConfig::trace_context); + oc_config.add_incoming_trace_context(OpenCensusConfig::grpc_trace_bin); + oc_config.add_incoming_trace_context(OpenCensusConfig::cloud_trace_context); + oc_config.add_outgoing_trace_context(OpenCensusConfig::trace_context); + oc_config.add_outgoing_trace_context(OpenCensusConfig::grpc_trace_bin); + oc_config.add_outgoing_trace_context(OpenCensusConfig::cloud_trace_context); + std::unique_ptr driver(new OpenCensus::Driver(oc_config)); + NiceMock config; + Http::TestHeaderMapImpl request_headers{ + {":path", "/"}, + {":method", "GET"}, + {"x-request-id", "foo"}, + {header, value}, + }; + const std::string operation_name{"my_operation_2"}; + SystemTime start_time; + Http::TestHeaderMapImpl injected_headers; + { + Tracing::SpanPtr span = driver->startSpan(config, request_headers, operation_name, start_time, + {Tracing::Reason::Sampling, false}); + span->injectContext(injected_headers); + span->finishSpan(); + } + + // Retrieve SpanData from the OpenCensus trace exporter. + std::vector spans = getSpanCatcher()->catchSpans(); + ASSERT_EQ(1, spans.size()); + const auto& sd = spans[0]; + ENVOY_LOG_MISC(debug, "{}", sd.DebugString()); + + // Check contents. + EXPECT_TRUE(sd.has_remote_parent()); + EXPECT_EQ("6162636465666768", sd.parent_span_id().ToHex()); + EXPECT_EQ("404142434445464748494a4b4c4d4e4f", sd.context().trace_id().ToHex()); + EXPECT_TRUE(sd.context().trace_options().IsSampled()) + << "parent was sampled, child should be also"; + + // Check injectContext. + using Envoy::Http::LowerCaseString; + { + auto val = injected_headers.get(LowerCaseString("traceparent")); + ASSERT_NE(nullptr, val); + EXPECT_EQ(::opencensus::trace::propagation::ToTraceParentHeader(sd.context()), + val->value().getStringView()); + } + { + auto val = injected_headers.get(LowerCaseString("grpc-trace-bin")); + ASSERT_NE(nullptr, val); + std::string expected = ::opencensus::trace::propagation::ToGrpcTraceBinHeader(sd.context()); + expected = Base64::encode(expected.data(), expected.size(), /*add_padding=*/false); + EXPECT_EQ(expected, val->value().getStringView()); + } + { + auto val = injected_headers.get(LowerCaseString("x-cloud-trace-context")); + ASSERT_NE(nullptr, val); + EXPECT_EQ(::opencensus::trace::propagation::ToCloudTraceContextHeader(sd.context()), + val->value().getStringView()); + } + }; + + helper("traceparent", "00-404142434445464748494a4b4c4d4e4f-6162636465666768-01"); + helper("grpc-trace-bin", "AABAQUJDREVGR0hJSktMTU5PAWFiY2RlZmdoAgE"); + helper("x-cloud-trace-context", "404142434445464748494a4b4c4d4e4f/7017280452245743464;o=1"); +} + +namespace { + +// Create a Span using the given config and return how many spans made it to +// the exporter (either zero or one). +int SamplerTestHelper(const OpenCensusConfig& oc_config) { + registerSpanCatcher(); + std::unique_ptr driver(new OpenCensus::Driver(oc_config)); + auto span = ::opencensus::trace::Span::StartSpan("test_span"); + span.End(); + // Retrieve SpanData from the OpenCensus trace exporter. + std::vector spans = getSpanCatcher()->catchSpans(); + EXPECT_GE(spans.size(), 0); + EXPECT_LE(spans.size(), 1); + if (!spans.empty()) { + EXPECT_TRUE(spans[0].context().trace_options().IsSampled()); + } + return spans.size(); +} + +} // namespace + +// Test constant_sampler that's always on. +TEST(OpenCensusTracerTest, ConstantSamplerAlwaysOn) { + OpenCensusConfig oc_config; + oc_config.mutable_trace_config()->mutable_constant_sampler()->set_decision( + ::opencensus::proto::trace::v1::ConstantSampler::ALWAYS_ON); + EXPECT_EQ(1, SamplerTestHelper(oc_config)); +} + +// Test constant_sampler that's always off. +TEST(OpenCensusTracerTest, ConstantSamplerAlwaysOff) { + OpenCensusConfig oc_config; + oc_config.mutable_trace_config()->mutable_constant_sampler()->set_decision( + ::opencensus::proto::trace::v1::ConstantSampler::ALWAYS_OFF); + EXPECT_EQ(0, SamplerTestHelper(oc_config)); +} + +// Test probability_sampler that's always on. +TEST(OpenCensusTracerTest, ProbabilitySamplerAlwaysOn) { + OpenCensusConfig oc_config; + oc_config.mutable_trace_config()->mutable_probability_sampler()->set_samplingprobability(1.0); + EXPECT_EQ(1, SamplerTestHelper(oc_config)); +} + +// Test probability_sampler that's always off. +TEST(OpenCensusTracerTest, ProbabilitySamplerAlwaysOff) { + OpenCensusConfig oc_config; + oc_config.mutable_trace_config()->mutable_probability_sampler()->set_samplingprobability(0.0); + EXPECT_EQ(0, SamplerTestHelper(oc_config)); +} + +} // namespace OpenCensus +} // namespace Tracers +} // namespace Extensions +} // namespace Envoy diff --git a/tools/check_repositories.sh b/tools/check_repositories.sh index d333ccc332eb4..cb73d67b497cc 100755 --- a/tools/check_repositories.sh +++ b/tools/check_repositories.sh @@ -12,8 +12,8 @@ fi # Check whether number of defined `url =` or `urls =` and `sha256 =` kwargs in # repository definitions is equal. -urls_count=$(git grep -E "url(s)? =" -- '*.bzl' | wc -l) -sha256sums_count=$(git grep -E "sha256 =" -- '*.bzl' | wc -l) +urls_count=$(git grep -E "\