Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,9 @@ subprojects {
junit5_runtime : "org.junit.jupiter:junit-jupiter-engine:${junit5Version}",
junit5_params : "org.junit.jupiter:junit-jupiter-params:${junit5Version}",
mockito : "org.mockito:mockito-inline:${mockitoVersion}",
mockito_jupiter: "org.mockito:mockito-junit-jupiter:${mockitoVersion}",
slf4j_simple: "org.slf4j:slf4j-simple:${slf4jVersion}",
spring_boot_starter_test: "org.springframework.boot:spring-boot-starter-test:${springVersion}",
opentelemetry_sdk_testing: "io.opentelemetry:opentelemetry-sdk-testing:${openTelemetryVersion}",
test_containers: "org.testcontainers:testcontainers:${testContainersVersion}",
wiremock : "com.github.tomakehurst:wiremock-jre8:${wiremockVersion}",
Expand Down
68 changes: 68 additions & 0 deletions javaagent-extensions/gcp-auth/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Google Cloud Authentication Extension for OpenTelemetry Java Agent

The Google Cloud Auth Extension allows the users to export telemetry from their applications auto-instrumented using the OpenTelemetry Java Agent to Google Cloud using the built-in OTLP exporters.
The extension takes care of the necessary configuration required to authenticate to GCP to successfully export telemetry.

## Prerequisites

### Ensure the presence of Google Cloud Credentials on your machine/environment

```shell
gcloud auth application-default login
```
Executing this command will save your application credentials to default path which will depend on the type of machine -
- Linux, macOS: `$HOME/.config/gcloud/application_default_credentials.json`
- Windows: `%APPDATA%\gcloud\application_default_credentials.json`

**NOTE: This method of authentication is not recommended for production environments.**

Next, export the credentials to `GOOGLE_APPLICATION_CREDENTIALS` environment variable -

For Linux & MacOS:
```shell
export GOOGLE_APPLICATION_CREDENTIALS=$HOME/.config/gcloud/application_default_credentials.json
```

These credentials are built-in running in a Google App Engine, Google Cloud Shell or Google Compute Engine environment.

### Configuring the extension

The extension can be configured either by environment variables or system properties.

Here is a list of configurable options for the extension:

- `GOOGLE_CLOUD_PROJECT`: Environment variable that represents the Google Cloud Project ID to which the telemetry needs to be exported.
- Can also be configured using `google.cloud.project` system property.
- If this option is not configured, the extension would infer GCP Project ID from the application default credentials. For more information on application default credentials, see [here](https://cloud.google.com/docs/authentication/application-default-credentials).

## Usage

The OpenTelemetry Java Agent Extension can be easily added to any Java application by modifying the startup command to the application.
For more information on Extensions, see the [documentation here](https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/examples/extension/README.md).

Below is a snippet showing how to add the extension to a Java application using the Gradle build system.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do users typically configure the agent through gradle?

Copy link
Owner Author

@psx95 psx95 Dec 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about the typical use case, however in this case, the command line args would look extremely similar.

The integration test showcases the agent configuration directly on command line.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use cases vary as broadly as the ecosystem does.

I think it's good to include this here.


```gradle
// Specify OpenTelemetry Autoinstrumentation Java Agent Path.
def otelAgentPath = <OpenTelemetry Java Agent location>
// Specify the path for Google Cloud Authentication Extension for the Java Agent.
def extensionPath = <Google Cloud Authentication Extension location>
def googleCloudProjectId = <Your Google Cloud Project ID>
def googleOtlpEndpoint = <Google Cloud OTLP endpoint>

application {
...
"-javaagent:${otelAgentPath}",
"-Dotel.javaagent.extensions=${extensionPath}",
// Configure the GCP Auth extension using system properties.
// This can also be configured using environment variables.
"-Dgoogle.cloud.project=${googleCloudProjectId}",
// Configure auto instrumentation.
"-Dotel.exporter.otlp.traces.endpoint=${googleOtlpEndpoint}",
'-Dotel.java.global-autoconfigure.enabled=true',
// Optionally enable the built-in GCP resource detector
'-Dotel.resource.providers.gcp.enabled=true'
'-Dotel.traces.exporter=otlp',
'-Dotel.metrics.exporter=logging',
}
```
124 changes: 124 additions & 0 deletions javaagent-extensions/gcp-auth/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
plugins {
id 'java'
id 'java-library'
id 'com.github.johnrengelman.shadow'
id 'org.springframework.boot' version '2.7.18'
}

description = 'OpenTelemetry Java Agent Extension that enables authentication support for OTLP exporters'

tasks {
assemble.dependsOn(shadowJar)
}

shadowJar {
archiveFileName.set("gcp-auth-extension.jar")
}

jar {
// Disable standard jar
enabled = false
}

// Custom configurations used to facilitate running the integration test
configurations {
agent
}

dependencies {
annotationProcessor(libraries.auto_service)
// We use `compileOnly` dependency because during runtime all necessary classes are provided by javaagent itself.
compileOnly(libraries.auto_service_annotations)
compileOnly(libraries.opentelemetry_api)
compileOnly(libraries.opentelemetry_otlp_exporter)
compileOnly(libraries.opentelemetry_sdk_autoconf)
compileOnly(libraries.opentelemetry_autoconfigure_spi)

// Only dependencies added to `implementation` configuration will be picked up by Shadow plugin
// and added to the resulting jar for our extension's distribution.
implementation(libraries.google_auth)

// test dependencies
testRuntimeOnly(testLibraries.junit5_runtime)
testImplementation(testLibraries.junit5)
testImplementation(libraries.opentelemetry_api)
testImplementation(libraries.opentelemetry_otlp_exporter)
testImplementation(testLibraries.opentelemetry_sdk_testing)
testImplementation(testLibraries.mockito)
testImplementation(testLibraries.mockito_jupiter)
testImplementation(libraries.opentelemetry_sdk_autoconf)
// for implementing smoke test application
testImplementation(libraries.spring_boot_starter_web)
testImplementation(testLibraries.spring_boot_starter_test)
testImplementation("org.mock-server:mockserver-netty:5.15.0")
testImplementation("org.awaitility:awaitility:4.2.2")
testImplementation("io.opentelemetry.proto:opentelemetry-proto:1.4.0-alpha")
testImplementation("io.opentelemetry.instrumentation:opentelemetry-instrumentation-annotations:2.8.0")
// OTel instrumentation used in the sample app to facilitate integration testing
agent agentLibraries.agent
}

// task to copy and rename the Java Auto-Instrumentation Agent into 'libs' folder
tasks.register('copyAgent', Copy) {
into layout.buildDirectory.dir("libs")
from configurations.agent {
rename "opentelemetry-javaagent(.*).jar", "otel-agent.jar"
}
}

def builtLibsDir = layout.buildDirectory.dir("libs").get().toString()
def javaAgentJarPath = builtLibsDir + "/otel-agent.jar"
def authExtensionJarPath = builtLibsDir + "/gcp-auth-extension.jar"

// this task is run as part of the integration test so it is necessary to
// configure this
tasks.named('bootJar').configure {
dependsOn('copyAgent')
}

build {
// disable bootJar in build since it only runs as part of test
tasks.named('bootJar').configure {
enabled = false
}
}

test {
dependsOn 'shadowJar'
dependsOn 'copyAgent'
useJUnitPlatform()

def fakeCredsFilePath = project.file("src/test/resources/fakecreds.json").getAbsolutePath()

environment("GOOGLE_CLOUD_QUOTA_PROJECT", "quota-project-id")
environment("GOOGLE_APPLICATION_CREDENTIALS", fakeCredsFilePath.toString())
jvmArgs = [
"-javaagent:${javaAgentJarPath}",
"-Dotel.javaagent.extensions=${authExtensionJarPath}",
"-Dgoogle.cloud.project=my-gcp-project",
"-Dotel.java.global-autoconfigure.enabled=true",
"-Dotel.exporter.otlp.endpoint=http://localhost:4318",
"-Dotel.resource.providers.gcp.enabled=true",
"-Dotel.traces.exporter=otlp",
"-Dotel.bsp.schedule.delay=2000",
"-Dotel.metrics.exporter=none",
"-Dotel.logs.exporter=none",
"-Dotel.exporter.otlp.protocol=http/protobuf",
"-Dmockserver.logLevel=off"
]
}
1 change: 1 addition & 0 deletions javaagent-extensions/gcp-auth/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
release.enabled=false
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
package com.google.cloud.opentelemetry.extension.auth;

import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException;
import java.util.Locale;
import java.util.function.Supplier;

/**
* An enum representing configurable options for a GCP Authentication Extension. Each option has a
* user-readable name and can be configured using environment variables or system properties.
*/
public enum ConfigurableOption {
/**
* Represents the Google Cloud Project ID option. Can be configured using the environment variable
* `GOOGLE_CLOUD_PROJECT` or the system property `google.cloud.project`.
*/
GOOGLE_CLOUD_PROJECT("Google Cloud Project ID");

private static final String OPTION_NOT_CONFIGURED_MSG =
"GCP Authentication Extension not configured properly: %s not configured. Configure it by exporting environment variable %s or system property %s";

private final String userReadableName;
private final String environmentVariableName;
private final String systemPropertyName;

ConfigurableOption(String userReadableName) {
this.userReadableName = userReadableName;
this.environmentVariableName = this.name();
this.systemPropertyName =
this.environmentVariableName.toLowerCase(Locale.ENGLISH).replace('_', '.');
}

/**
* Returns the environment variable name associated with this option.
*
* @return the environment variable name (e.g., GOOGLE_CLOUD_PROJECT)
*/
String getEnvironmentVariable() {
return this.environmentVariableName;
}

/**
* Returns the system property name associated with this option.
*
* @return the system property name (e.g., google.cloud.project)
*/
String getSystemProperty() {
return this.systemPropertyName;
}

/**
* Retrieves the configured value for this option. This method checks the environment variable
* first and then the system property.
*
* @return The configured value as a string, or throws an exception if not configured.
* @throws ConfigurationException if neither the environment variable nor the system property is
* set.
*/
String getConfiguredValue() throws ConfigurationException {
String envVar = System.getenv(this.getEnvironmentVariable());
String sysProp = System.getProperty(this.getSystemProperty());

if (envVar != null && !envVar.isEmpty()) {
return envVar;
} else if (sysProp != null && !sysProp.isEmpty()) {
return sysProp;
} else {
throw new ConfigurationException(
String.format(
OPTION_NOT_CONFIGURED_MSG,
this.userReadableName,
this.getEnvironmentVariable(),
this.getSystemProperty()));
}
}

/**
* Retrieves the value for this option, prioritizing environment variables and system properties.
* If neither an environment variable nor a system property is set for this option, the provided
* fallback function is used to determine the value.
*
* @param fallback A {@link Supplier} that provides the default value for the option when it is
* not explicitly configured via an environment variable or system property.
* @return The configured value for the option, obtained from the environment variable, system
* property, or the fallback function, in that order of precedence.
*/
String getConfiguredValueWithFallback(Supplier<String> fallback) {
try {
return this.getConfiguredValue();
} catch (ConfigurationException e) {
return fallback.get();
}
}
}
Loading
Loading