diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 5763fe069f9b..65e5fcbfa3b7 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -1266,6 +1266,7 @@ in nginx-modsecurity = runTest ./nginx-modsecurity.nix; nginx-moreheaders = runTest ./nginx-moreheaders.nix; nginx-njs = runTest ./nginx-njs.nix; + nginx-otel = runTest ./nginx-otel.nix; nginx-proxyprotocol = runTest ./nginx-proxyprotocol/default.nix; nginx-pubhtml = runTest ./nginx-pubhtml.nix; nginx-redirectcode = runTest ./nginx-redirectcode.nix; diff --git a/nixos/tests/nginx-otel.nix b/nixos/tests/nginx-otel.nix new file mode 100644 index 000000000000..7aa37d532cca --- /dev/null +++ b/nixos/tests/nginx-otel.nix @@ -0,0 +1,56 @@ +{ + name = "nginx-otel"; + + nodes.machine = + { pkgs, ... }: + { + services.opentelemetry-collector = { + enable = true; + settings = { + receivers.otlp.protocols.grpc.endpoint = "127.0.0.1:4317"; + exporters.debug.verbosity = "detailed"; + service.pipelines.traces = { + receivers = [ "otlp" ]; + exporters = [ "debug" ]; + }; + }; + }; + services.nginx = { + enable = true; + additionalModules = [ pkgs.nginxModules.otel ]; + commonHttpConfig = '' + otel_exporter { + endpoint localhost:4317; + } + otel_service_name "nginx-test"; + otel_trace on; + ''; + virtualHosts."localhost".locations."/".extraConfig = '' + otel_trace_context propagate; + otel_span_name "handle"; + return 200 "otel-ok"; + ''; + }; + }; + + testScript = + { nodes, ... }: + let + cfg = nodes.machine.services.nginx; + in + '' + machine.wait_for_unit("opentelemetry-collector") + machine.wait_for_open_port(4317) + machine.wait_for_unit("nginx") + machine.wait_for_open_port(80) + + machine.succeed("test -e ${cfg.package}/modules/ngx_otel_module.so") + machine.succeed("grep -F ngx_otel_module.so ${cfg.package}/etc/nginx/dynamic-modules.conf") + + response = machine.wait_until_succeeds("curl -fsS http://127.0.0.1/") + t.assertIn("otel-ok", response) + + machine.wait_until_succeeds("journalctl -u opentelemetry-collector --grep 'service.name: Str\\(nginx-test\\)'") + machine.wait_until_succeeds("journalctl -u opentelemetry-collector --grep 'Name +: handle'") + ''; +} diff --git a/pkgs/by-name/op/opentelemetry-cpp/package.nix b/pkgs/by-name/op/opentelemetry-cpp/package.nix index 619927712215..9731d1834b20 100644 --- a/pkgs/by-name/op/opentelemetry-cpp/package.nix +++ b/pkgs/by-name/op/opentelemetry-cpp/package.nix @@ -101,6 +101,9 @@ stdenv.mkDerivation (finalAttrs: { passthru.updateScript = nix-update-script { }; + # For dependents that compile the protos themselves. + passthru.opentelemetry-proto = opentelemetry-proto; + passthru.tests = { # Unfortunately there is no such thing as finalAttrs.finalPackage.override, # so we have to resort to this. diff --git a/pkgs/servers/http/nginx/generic.nix b/pkgs/servers/http/nginx/generic.nix index b0503a45e9c0..0f0b6a032c62 100644 --- a/pkgs/servers/http/nginx/generic.nix +++ b/pkgs/servers/http/nginx/generic.nix @@ -73,12 +73,19 @@ let throw "Module ${mod.name} does not support nginx version ${nginxVersion}!" ); + # Modules can adjust the nginx derivation through passthru.forNginx. + mkDerivation = + args: + lib.foldl (drv: mod: drv.overrideAttrs mod.forNginx) (stdenv.mkDerivation args) ( + lib.filter (mod: mod ? forNginx) modules + ); + in assert lib.assertMsg (lib.unique moduleNames == moduleNames) "nginx: duplicate modules: ${lib.concatStringsSep ", " moduleNames}. A common cause for this is that services.nginx.additionalModules adds a module which the nixos module itself already adds."; -stdenv.mkDerivation { +mkDerivation { inherit pname version nginxVersion; outputs = [ @@ -98,7 +105,8 @@ stdenv.mkDerivation { nativeBuildInputs = [ installShellFiles ] - ++ nativeBuildInputs; + ++ nativeBuildInputs + ++ mapModules "nativeBuildInputs"; buildInputs = [ openssl diff --git a/pkgs/servers/http/nginx/modules/otel/package.nix b/pkgs/servers/http/nginx/modules/otel/package.nix new file mode 100644 index 000000000000..436074995cce --- /dev/null +++ b/pkgs/servers/http/nginx/modules/otel/package.nix @@ -0,0 +1,71 @@ +{ + abseil-cpp, + c-ares, + cmake, + fetchFromGitHub, + grpc, + lib, + mkNginxPlugin, + nixosTests, + nlohmann_json, + openssl, + opentelemetry-cpp, + pkg-config, + protobuf, + re2, + zlib, +}: + +mkNginxPlugin (finalAttrs: { + pname = "otel"; + version = "0.1.2"; + + src = fetchFromGitHub { + owner = "nginxinc"; + repo = "nginx-otel"; + tag = "v${finalAttrs.version}"; + hash = "sha256-pGe+1nPH8zUQhcyVxH5/nxwNFMsoOYCUecuDVs1rS4o="; + }; + + # nginx-otel can only be loaded dynamically. + dynamic = true; + + nativeBuildInputs = [ + cmake + pkg-config + protobuf + grpc + ]; + + buildInputs = [ + grpc + protobuf + opentelemetry-cpp + c-ares + re2 + abseil-cpp + nlohmann_json + openssl + zlib + ]; + + # Read by the module's config script. nginx's install step does not know + # about the cmake-built .so, so cmake writes it into the modules dir itself. + preConfigure = '' + export NGX_OTEL_CMAKE_OPTS="-DNGX_OTEL_GRPC=package -DNGX_OTEL_SDK=package -DNGX_OTEL_PROTO_DIR=${opentelemetry-cpp.opentelemetry-proto} -DCMAKE_LIBRARY_OUTPUT_DIRECTORY=$out/modules" + ''; + + passthru = { + # cmake's setup hook would replace nginx's configurePhase. + forNginx.dontUseCmakeConfigure = true; + + tests = { inherit (nixosTests) nginx-otel; }; + }; + + meta = { + description = "OpenTelemetry support for nginx"; + homepage = "https://github.com/nginxinc/nginx-otel"; + license = lib.licenses.asl20; + maintainers = with lib.maintainers; [ kranzes ]; + }; +})