Skip to content

Commit

Permalink
feat: custom Dash0 injector
Browse files Browse the repository at this point in the history
  • Loading branch information
mmanciop committed Sep 24, 2024
1 parent 1fed614 commit cb7752c
Show file tree
Hide file tree
Showing 8 changed files with 211 additions and 1 deletion.
12 changes: 11 additions & 1 deletion images/instrumentation/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
# build injector
FROM ubuntu:24.04 AS build-injector

RUN apt-get update && apt-get install build-essential -y && apt-get autoremove -y && apt-get autoclean -y

COPY ./injector/src /dash0-init-container/injector
WORKDIR /dash0-init-container/injector
RUN gcc -shared -nostdlib -fPIC -Wl,--version-script=dash0_injector.exports.map dash0_injector.c -o dash0_injector.so

# build Node.js artifacts
FROM node:20.13.1-alpine3.19 AS build-node.js
RUN mkdir -p /dash0-init-container/instrumentation/node.js
RUN mkdir -p /instrumentation/node.js
WORKDIR /dash0-init-container/instrumentation/node.js
COPY node.js/package* .
COPY node.js/dash0hq-opentelemetry-*.tgz .
Expand All @@ -17,6 +26,7 @@ COPY copy-instrumentation.sh /

# copy node.js artifacts
RUN mkdir -p /dash0-init-container/instrumentation
COPY --from=build-injector /dash0-init-container/injector/dash0_injector.so /dash0-init-container/injector/dash0_injector.so
COPY --from=build-node.js /dash0-init-container/instrumentation/node.js /dash0-init-container/instrumentation/node.js

WORKDIR /
Expand Down
109 changes: 109 additions & 0 deletions images/instrumentation/injector/src/dash0_injector.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#include <unistd.h>
#include <stdint.h>

#define ALIGN (sizeof(size_t))
#define UCHAR_MAX 255
#define ONES ((size_t)-1/UCHAR_MAX)
#define HIGHS (ONES * (UCHAR_MAX/2+1))
#define HASZERO(x) ((x)-ONES & ~(x) & HIGHS)

#define NODE_OPTIONS_ENV_VAR_NAME "NODE_OPTIONS"
#define NODE_OPTIONS_DASH0_REQUIRE "--require /__dash0__/instrumentation/node.js/node_modules/@dash0hq/opentelemetry"

extern char **__environ;

size_t __strlen(const char *s)
{
const char *a = s;
const size_t *w;
for (; (uintptr_t)s % ALIGN; s++) if (!*s) return s-a;
for (w = (const void *)s; !HASZERO(*w); w++);
for (s = (const void *)w; *s; s++);
return s-a;
}

char *__strchrnul(const char *s, int c)
{
size_t *w, k;

c = (unsigned char)c;
if (!c) return (char *)s + __strlen(s);

for (; (uintptr_t)s % ALIGN; s++)
if (!*s || *(unsigned char *)s == c) return (char *)s;
k = ONES * c;
for (w = (void *)s; !HASZERO(*w) && !HASZERO(*w^k); w++);
for (s = (void *)w; *s && *(unsigned char *)s != c; s++);
return (char *)s;
}

char *__strcpy(char *restrict dest, const char *restrict src)
{
const unsigned char *s = src;
unsigned char *d = dest;
while ((*d++ = *s++));
return dest;
}

char *__strcat(char *restrict dest, const char *restrict src)
{
__strcpy(dest + __strlen(dest), src);
return dest;
}

int __strcmp(const char *l, const char *r)
{
for (; *l==*r && *l; l++, r++);
return *(unsigned char *)l - *(unsigned char *)r;
}

int __strncmp(const char *_l, const char *_r, size_t n)
{
const unsigned char *l=(void *)_l, *r=(void *)_r;
if (!n--) return 0;
for (; *l && *r && n && *l == *r ; l++, r++, n--);
return *l - *r;
}

char *__getenv(const char *name)
{
size_t l = __strchrnul(name, '=') - name;
if (l && !name[l] && __environ)
for (char **e = __environ; *e; e++)
if (!__strncmp(name, *e, l) && l[*e] == '=')
return *e + l+1;
return 0;
}

/*
* Buffers of statically-allocated memory that we can use to safely return to
* the program manipulated values of env vars without dynamic allocations.
*/
char val1[1012];
char val2[1012];

char *getenv(const char *name)
{
char *origValue = __getenv(name);
int l = __strlen(name);

char *nodeOptionsVarName = NODE_OPTIONS_ENV_VAR_NAME;
if (__strcmp(name, nodeOptionsVarName) == 0)
{
if (__strlen(val1) == 0)
{
if (origValue != NULL)
{
__strcat(val1, origValue);
__strcat(val1, " ");
}

char *nodeOptionsDash0Require = NODE_OPTIONS_DASH0_REQUIRE;
__strcat(val1, nodeOptionsDash0Require);
}

return val1;
}

return origValue;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
global:
getenv;
local:
*;
};
13 changes: 13 additions & 0 deletions images/instrumentation/injector/test/node/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
ARG base_image
ARG instr_image

FROM ${instr_image} AS instr

FROM ${base_image}

# TODO Normalize node.js path

COPY ./test-cases /test-cases

COPY --from=instr /dash0-init-container /__dash0__
ENV LD_PRELOAD=/__dash0__/injector/dash0_injector.so
10 changes: 10 additions & 0 deletions images/instrumentation/injector/test/node/base-images
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# One base image per line
node:22-alpine
node:22-bookworm
node:22-bullseye
node:20-alpine
node:20-bookworm
node:20-bullseye
node:18-alpine
node:18-bookworm
node:18-bullseye
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"name": "@dash0/injector_tests_node-options-includes-dash0-require"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const nodeOptions = process.env["NODE_OPTIONS"] || "";

if (!nodeOptions.includes("--require /__dash0__/instrumentation/node.js/node_modules/@dash0hq/opentelemetry")) {
console.error(`No Dash0 distro require found in NODE_OPTIONS value: '${nodeOptions}'`)
process.exit(1);
}
53 changes: 53 additions & 0 deletions images/instrumentation/injector/test/test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#/bin/env bash

readonly script_dir=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )

function run_tests () {
local runtime="${1}"
local docker_image="${2}"

for t in ${script_dir}/${runtime}/test-cases/*/ ; do
test=$(basename $(realpath "${t}"))
printf '%s' "Test '${test}' " | sed 's/^/ /' # `echo -n` does not work on Mac OS
tempfile=$(mktemp)
docker run ${docker_image} "npm" "start" "--prefix" "/test-cases/${test}" > ${tempfile} 2>&1
if [ $? -eq 0 ]; then
echo OK
else
echo FAIL
echo 'Output (stdout + stderr):'
cat ${tempfile} | sed 's/^/ /'
fi
rm ${tempfile}
done
}

# Build instr image
instr_image="${1}"

if [ -z "${instr_image}" ]; then
instr_image="dash0-instrumentation:test-latest"
echo "Building instrumentation image '${instr_image}' (no instrumentation image tag provided in input)"

tempfile=$(mktemp)
if ! docker build ../.. -t "${instr_image}" > ${tempfile} 2>&1; then
echo "Failed to build the instrumentation image:"
cat ${tempfile} | sed 's/^/ /'
fi
rm ${tempfile}
fi

# Run tests
for r in ${script_dir}/*/ ; do
runtime=$(basename $(realpath "${r}"))
echo "Runtime '${runtime}'"
grep '^[^#;]' "${script_dir}/${runtime}/base-images" | while read -r base_image ; do
echo "Base image '${base_image}'" | sed 's/^/ /'
build_output=$(docker build "${script_dir}/${runtime}" --build-arg "instr_image=${instr_image}" --build-arg "base_image=${base_image}" -t "test-${runtime}:latest" 2>&1 | sed 's/^/ /')
if [ $? -ne 0 ]; then
echo "${build_output}"
exit 1
fi
run_tests "${runtime}" "test-${runtime}:latest" | sed 's/^/ /'
done
done

0 comments on commit cb7752c

Please sign in to comment.