Skip to content

Commit

Permalink
keep current injector around and use it by default
Browse files Browse the repository at this point in the history
Pods can opt-in to new injector by setting the DASH0_EXPERIMENTAL_INJECTOR env var
  • Loading branch information
mmanciop committed Jan 29, 2025
1 parent 6853d0e commit dd9b98b
Show file tree
Hide file tree
Showing 7 changed files with 308 additions and 5 deletions.
18 changes: 17 additions & 1 deletion images/instrumentation/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,22 @@
# injector_build_start - do not remove this line (see images/instrumentation/injector/test/scripts/test-all.sh)

# build injector
FROM alpine:3.21 AS build-injector
FROM ubuntu:24.04 AS build-injector
RUN apt-get update && \
apt-get install --no-install-recommends build-essential -y

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

# build experimental, zig-based injector
FROM alpine:3.21 AS build-injector-experimental

## Zig is available as a community package on Alpine
RUN apk add zig --repository=https://dl-cdn.alpinelinux.org/alpine/edge/community
Expand Down Expand Up @@ -41,6 +56,7 @@ COPY copy-instrumentation.sh /
# copy artifacts (distros, injector binary) from the build stages to the final image
RUN mkdir -p /dash0-init-container/instrumentation
COPY --from=build-injector /dash0-init-container/bin /dash0-init-container
COPY --from=build-injector-experimental /dash0-init-container/bin/dash0_injector.so /dash0-init-container/dash0_injector_zig.so
COPY --from=build-jvm /dash0-init-container/instrumentation/jvm/build /dash0-init-container/instrumentation/jvm
COPY --from=build-node.js /dash0-init-container/instrumentation/node.js /dash0-init-container/instrumentation/node.js

Expand Down
6 changes: 5 additions & 1 deletion images/instrumentation/copy-instrumentation.sh
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,11 @@ fi
cp -R "${DASH0_INSTRUMENTATION_FOLDER_SOURCE}"/ "${DASH0_INSTRUMENTATION_FOLDER_DESTINATION}"

# Copy the injector from the init container to the monitored container.
cp "${DASH0_INJECTOR_FOLDER_SOURCE}"/*.so "${DASH0_INSTRUMENTATION_FOLDER_DESTINATION}"/
if [ -n "${DASH0_EXPERIMENTAL_INJECTOR:-}" ]; then
cp "${DASH0_INJECTOR_FOLDER_SOURCE}"/dash0_injector_zig.so "${DASH0_INSTRUMENTATION_FOLDER_DESTINATION}"/dash0_injector.so
else
cp "${DASH0_INJECTOR_FOLDER_SOURCE}"/dash0_injector.so "${DASH0_INSTRUMENTATION_FOLDER_DESTINATION}"/dash0_injector.so
fi

if [ -n "${DASH0_DEBUG:-}" ]; then
find "${DASH0_INSTRUMENTATION_FOLDER_DESTINATION}"
Expand Down
30 changes: 29 additions & 1 deletion images/instrumentation/injector/bin/NOTICE
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,32 @@ Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
limitations under the License.




This codebase contains code from musl libc (https://musl.libc.org)
subject to the following license terms:

----------------------------------------------------------------------
Copyright © 2005-2014 Rich Felker, et al.

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
245 changes: 245 additions & 0 deletions images/instrumentation/injector/src/dash0_injector.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
#include <stdint.h>
#include <unistd.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 JAVA_TOOL_OPTIONS_ENV_VAR_NAME "JAVA_TOOL_OPTIONS"
#define JAVA_TOOL_OPTIONS_DASH0_REQUIRE \
"-javaagent:" \
"/__dash0__/instrumentation/jvm/opentelemetry-javaagent.jar"
#define NODE_OPTIONS_ENV_VAR_NAME "NODE_OPTIONS"
#define NODE_OPTIONS_DASH0_REQUIRE \
"--require " \
"/__dash0__/instrumentation/node.js/node_modules/@dash0hq/opentelemetry"
#define OTEL_RESOURCE_ATTRIBUTES_ENV_VAR_NAME "OTEL_RESOURCE_ATTRIBUTES"
#define DASH0_NAMESPACE_NAME_ENV_VAR_NAME "DASH0_NAMESPACE_NAME"
#define DASH0_POD_UID_ENV_VAR_NAME "DASH0_POD_UID"
#define DASH0_POD_NAME_ENV_VAR_NAME "DASH0_POD_NAME"
#define DASH0_POD_CONTAINER_NAME_VAR_NAME "DASH0_CONTAINER_NAME"

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 cachedModifiedOtelResourceAttributesValue[1012];
char cachedModifiedRuntimeOptionsValue[1012];

char *__appendResourceAttributes(const char *buffer, const char *origValue) {
char *namespaceName = __getenv(DASH0_NAMESPACE_NAME_ENV_VAR_NAME);
char *podUid = __getenv(DASH0_POD_UID_ENV_VAR_NAME);
char *podName = __getenv(DASH0_POD_NAME_ENV_VAR_NAME);
char *containerName = __getenv(DASH0_POD_CONTAINER_NAME_VAR_NAME);

int attributeCount = 0;

/*
* We do not perform octect escaping in the resource attributes as
* specified in
* https://opentelemetry.io/docs/specs/otel/resource/sdk/#specifying-resource-information-via-an-environment-variable
* because the values that are passed down to the injector comes from
* fields that Kubernetes already enforces to either conform to RFC 1035
* or RFC RFC 1123
* (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#dns-label-names),
* and in either case, none of the characters allowed require escaping
* based on https://www.w3.org/TR/baggage/#header-content
*/

if (namespaceName != NULL && __strlen(namespaceName) > 0) {
__strcat(buffer, "k8s.namespace.name=");
__strcat(buffer, namespaceName);
attributeCount += 1;
}

if (podName != NULL && __strlen(podName) > 0) {
if (attributeCount > 0) {
__strcat(buffer, ",");
}

__strcat(buffer, "k8s.pod.name=");
__strcat(buffer, podName);
attributeCount += 1;
}

if (podUid != NULL && __strlen(podUid) > 0) {
if (attributeCount > 0) {
__strcat(buffer, ",");
}

__strcat(buffer, "k8s.pod.uid=");
__strcat(buffer, podUid);
attributeCount += 1;
}

if (containerName != NULL && __strlen(containerName) > 0) {
if (attributeCount > 0) {
__strcat(buffer, ",");
}

__strcat(buffer, "k8s.container.name=");
__strcat(buffer, containerName);
attributeCount += 1;
}

if (origValue != NULL && __strlen(origValue) > 0) {
if (attributeCount > 0) {
__strcat(buffer, ",");
}

__strcat(buffer, origValue);
}
}

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

char *otelResourceAttributesVarName = OTEL_RESOURCE_ATTRIBUTES_ENV_VAR_NAME;
char *javaToolOptionsVarName = JAVA_TOOL_OPTIONS_ENV_VAR_NAME;
char *nodeOptionsVarName = NODE_OPTIONS_ENV_VAR_NAME;
if (__strcmp(name, otelResourceAttributesVarName) == 0) {
if (__strlen(cachedModifiedOtelResourceAttributesValue) == 0) {
// This environment variable (OTEL_RESOURCE_ATTRIBUTES) has not been
// requested before, calculate the modified value and cache it.
__appendResourceAttributes(cachedModifiedOtelResourceAttributesValue,
origValue);
}

return cachedModifiedOtelResourceAttributesValue;
} else if (__strcmp(name, javaToolOptionsVarName) == 0) {
if (__strlen(cachedModifiedRuntimeOptionsValue) == 0) {
// No runtime environment variable has been requested before,
// calculate the modified value and cache it.

// Prepend our --require as the first item to the JAVA_TOOL_OPTIONS
// string.
char *javaToolOptionsDash0Require = JAVA_TOOL_OPTIONS_DASH0_REQUIRE;
__strcat(cachedModifiedRuntimeOptionsValue, javaToolOptionsDash0Require);

// The Java runtime does not look up the OTEL_RESOURCE_ATTRIBUTES env var
// using getenv(), but rather by parsing the environment block
// (/proc/env/<pid>) directly, which we cannot affect with the getenv
// hook. So, instead, we append the resource attributes as the
// -Dotel.resource.attributes Java system property.
// If the -Dotel.resource.attributes system property is already set,
// the user-defined property will take precedence:
//
// % JAVA_TOOL_OPTIONS="-Dprop=B" jshell -R -Dprop=A
// Picked up JAVA_TOOL_OPTIONS: -Dprop=B
// | Welcome to JShell -- Version 17.0.12
// | For an introduction type: /help intro
//
// jshell> System.getProperty("prop")
// $1 ==> "A"

char *otelResourceAttributesViaEnv =
__getenv(OTEL_RESOURCE_ATTRIBUTES_ENV_VAR_NAME);
__strcat(cachedModifiedRuntimeOptionsValue,
" -Dotel.resource.attributes=");
__appendResourceAttributes(cachedModifiedRuntimeOptionsValue,
otelResourceAttributesViaEnv);

if (origValue != NULL && __strlen(origValue) > 0) {
// If JAVA_TOOL_OPTIONS were present, append the existing
// JAVA_TOOL_OPTIONS after our --javaagent.
__strcat(cachedModifiedRuntimeOptionsValue, " ");
__strcat(cachedModifiedRuntimeOptionsValue, origValue);
}
}

return cachedModifiedRuntimeOptionsValue;
} else if (__strcmp(name, nodeOptionsVarName) == 0) {
if (__strlen(cachedModifiedRuntimeOptionsValue) == 0) {
// No runtime environment variable has been requested before,
// calculate the modified value and cache it.

// Prepend our --require as the first item to the NODE_OPTIONS string.
char *nodeOptionsDash0Require = NODE_OPTIONS_DASH0_REQUIRE;
__strcat(cachedModifiedRuntimeOptionsValue, nodeOptionsDash0Require);

if (origValue != NULL && __strlen(origValue) > 0) {
// If NODE_OPTIONS were present, append the existing NODE_OPTIONS after
// our --require.
__strcat(cachedModifiedRuntimeOptionsValue, " ");
__strcat(cachedModifiedRuntimeOptionsValue, origValue);
}
}

return cachedModifiedRuntimeOptionsValue;
}

return origValue;
}
3 changes: 2 additions & 1 deletion images/instrumentation/test/jvm/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ ARG test_image=openjdk:24-jdk-bookworm
FROM ${instrumentation_image} AS init

FROM ${test_image}
ARG injector_path=/__dash0__/dash0_injector.so

COPY test-cases /test-cases
COPY .m2 /.m2
Expand All @@ -17,4 +18,4 @@ RUN for test in /test-cases/*; do (cd "/${test}" && JAVA_HOME=$(which java | xar
# setting the LD_PRELOAD environment variable via the operator.
COPY --from=init /dash0-init-container/*.so /__dash0__/
COPY --from=init /dash0-init-container/instrumentation /__dash0__/instrumentation
ENV LD_PRELOAD=/__dash0__/dash0_injector.so
ENV LD_PRELOAD=${injector_path}
3 changes: 2 additions & 1 deletion images/instrumentation/test/node/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ ARG base_image=node:20.18-bookworm
FROM ${instrumentation_image} AS init

FROM ${base_image}
ARG injector_path=/__dash0__/dash0_injector.so

COPY test-cases /test-cases

# The following lines emulate the behavior of running the instrumentation image as a Kubernetes init container and
# setting the LD_PRELOAD environment variable via the operator.
COPY --from=init /dash0-init-container/*.so /__dash0__/
COPY --from=init /dash0-init-container/instrumentation /__dash0__/instrumentation
ENV LD_PRELOAD=/__dash0__/dash0_injector.so
ENV LD_PRELOAD=${injector_path}

8 changes: 8 additions & 0 deletions images/instrumentation/test/test-all.sh
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,12 @@ run_tests_for_architecture() {
exit 1
fi

if [ "${2:-}" = "experimental" ]; then
injector_path="/__dash0__/dash0_injector_zig.so"
else
injector_path="/__dash0__/dash0_injector.so"
fi

echo ========================================
echo "running tests for architecture $arch"
echo ========================================
Expand Down Expand Up @@ -240,6 +246,7 @@ run_tests_for_architecture() {
--platform "$docker_platform" \
--build-arg "instrumentation_image=${instrumentation_image}" \
--build-arg "base_image=${base_image}" \
--build-arg "injector_path=${injector_path}" \
"${script_dir}/${runtime}" \
-t "$image_name_test" \
2>&1
Expand Down Expand Up @@ -284,6 +291,7 @@ for arch in "${all_architectures[@]}"; do
fi
fi
run_tests_for_architecture "$arch"
run_tests_for_architecture "$arch" "experimental"
done

if [[ $exit_code -ne 0 ]]; then
Expand Down

0 comments on commit dd9b98b

Please sign in to comment.