-
Notifications
You must be signed in to change notification settings - Fork 131
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add GCP authentication extension (#1631)
- Loading branch information
Showing
16 changed files
with
1,046 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
# Google Cloud Authentication Extension | ||
|
||
The Google Cloud Auth Extension allows the users to export telemetry from their applications 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 | ||
|
||
### With OpenTelemetry Java agent | ||
|
||
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. | ||
|
||
```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> | ||
val autoconf_config = listOf( | ||
"-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' | ||
) | ||
application { | ||
... | ||
applicationDefaultJvmArgs = autoconf_config | ||
... | ||
} | ||
``` | ||
|
||
### Without OpenTelemetry Java agent | ||
|
||
This extension can be used without the OpenTelemetry Java agent by leveraging the [OpenTelemetry SDK Autoconfigure](https://github.com/open-telemetry/opentelemetry-java/blob/main/sdk-extensions/autoconfigure/README.md) module.\ | ||
When using the autoconfigured SDK, simply adding this extension as a dependency automatically configures authentication headers and resource attributes for spans, enabling export to Google Cloud. | ||
|
||
Below is a snippet showing how to use this extension as a dependency when the application is not instrumented using the OpenTelemetry Java agent. | ||
|
||
```gradle | ||
dependencies { | ||
implementation("io.opentelemetry:opentelemetry-api") | ||
implementation("io.opentelemetry:opentelemetry-sdk") | ||
implementation("io.opentelemetry:opentelemetry-exporter-otlp") | ||
implementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure") | ||
// include the auth extension dependency | ||
implementation("io.opentelemetry.contrib:opentelemetry-gcp-auth-extension") | ||
// other dependencies | ||
... | ||
} | ||
val autoconf_config = listOf( | ||
'-Dgoogle.cloud.project=your-gcp-project-id', | ||
'-Dotel.exporter.otlp.endpoint=https://your.otlp.endpoint:1234', | ||
'-Dotel.traces.exporter=otlp', | ||
'-Dotel.java.global-autoconfigure.enabled=true' | ||
// any additional args | ||
... | ||
) | ||
application { | ||
applicationDefaultJvmArgs = autoconf_config | ||
// additional configuration | ||
... | ||
} | ||
``` | ||
|
||
## Component Owners | ||
|
||
- [Josh Suereth](https://github.com/jsuereth), Google | ||
- [Pranav Sharma](https://github.com/psx95), Google | ||
|
||
Learn more about component owners in [component_owners.yml](../.github/component_owners.yml). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
plugins { | ||
id("otel.java-conventions") | ||
id("otel.publish-conventions") | ||
id("com.github.johnrengelman.shadow") | ||
id("org.springframework.boot") version "2.7.18" | ||
} | ||
|
||
description = "OpenTelemetry extension that provides GCP authentication support for OTLP exporters" | ||
otelJava.moduleName.set("io.opentelemetry.contrib.gcp.auth") | ||
|
||
val agent: Configuration by configurations.creating { | ||
isCanBeResolved = true | ||
isCanBeConsumed = false | ||
} | ||
|
||
dependencies { | ||
annotationProcessor("com.google.auto.service:auto-service") | ||
// We use `compileOnly` dependency because during runtime all necessary classes are provided by | ||
// javaagent itself. | ||
compileOnly("com.google.auto.service:auto-service-annotations") | ||
compileOnly("io.opentelemetry:opentelemetry-api") | ||
compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure") | ||
compileOnly("io.opentelemetry:opentelemetry-exporter-otlp") | ||
|
||
// Only dependencies added to `implementation` configuration will be picked up by Shadow plugin | ||
implementation("com.google.auth:google-auth-library-oauth2-http:1.30.1") | ||
|
||
// Test dependencies | ||
testCompileOnly("com.google.auto.service:auto-service-annotations") | ||
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") | ||
testImplementation("org.junit.jupiter:junit-jupiter-api") | ||
|
||
testImplementation("io.opentelemetry:opentelemetry-api") | ||
testImplementation("io.opentelemetry:opentelemetry-exporter-otlp") | ||
testImplementation("io.opentelemetry:opentelemetry-sdk-testing") | ||
testImplementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure") | ||
testImplementation("io.opentelemetry.instrumentation:opentelemetry-instrumentation-annotations") | ||
|
||
testImplementation("org.awaitility:awaitility") | ||
testImplementation("org.mockito:mockito-inline") | ||
testImplementation("org.mockito:mockito-junit-jupiter") | ||
testImplementation("org.mock-server:mockserver-netty:5.15.0") | ||
testImplementation("io.opentelemetry.proto:opentelemetry-proto:1.4.0-alpha") | ||
testImplementation("org.springframework.boot:spring-boot-starter-web:2.7.18") | ||
testImplementation("org.springframework.boot:spring-boot-starter:2.7.18") | ||
testImplementation("org.springframework.boot:spring-boot-starter-test:2.7.18") | ||
|
||
agent("io.opentelemetry.javaagent:opentelemetry-javaagent") | ||
} | ||
|
||
tasks { | ||
test { | ||
useJUnitPlatform() | ||
// exclude integration test | ||
exclude("io/opentelemetry/contrib/gcp/auth/GcpAuthExtensionEndToEndTest.class") | ||
} | ||
|
||
shadowJar { | ||
archiveClassifier.set("") | ||
} | ||
|
||
jar { | ||
// Disable standard jar | ||
enabled = false | ||
} | ||
|
||
assemble { | ||
dependsOn(shadowJar) | ||
} | ||
|
||
bootJar { | ||
// disable bootJar in build since it only runs as part of test | ||
enabled = false | ||
} | ||
} | ||
|
||
val builtLibsDir = layout.buildDirectory.dir("libs").get().asFile.absolutePath | ||
val javaAgentJarPath = "$builtLibsDir/otel-agent.jar" | ||
val authExtensionJarPath = "${tasks.shadowJar.get().archiveFile.get()}" | ||
|
||
tasks.register<Copy>("copyAgent") { | ||
into(layout.buildDirectory.dir("libs")) | ||
from(configurations.named("agent") { | ||
rename("opentelemetry-javaagent(.*).jar", "otel-agent.jar") | ||
}) | ||
} | ||
|
||
tasks.register<Test>("IntegrationTest") { | ||
dependsOn(tasks.shadowJar) | ||
dependsOn(tasks.named("copyAgent")) | ||
|
||
useJUnitPlatform() | ||
// include only the integration test file | ||
include("io/opentelemetry/contrib/gcp/auth/GcpAuthExtensionEndToEndTest.class") | ||
|
||
val fakeCredsFilePath = project.file("src/test/resources/fakecreds.json").absolutePath | ||
|
||
environment("GOOGLE_CLOUD_QUOTA_PROJECT", "quota-project-id") | ||
environment("GOOGLE_APPLICATION_CREDENTIALS", fakeCredsFilePath) | ||
jvmArgs = listOf( | ||
"-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" | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
# TODO: uncomment when ready to mark as stable | ||
# otel.stable=true |
93 changes: 93 additions & 0 deletions
93
gcp-auth-extension/src/main/java/io/opentelemetry/contrib/gcp/auth/ConfigurableOption.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.contrib.gcp.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 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() { | ||
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( | ||
"GCP Authentication Extension not configured properly: %s not configured. Configure it by exporting environment variable %s or system property %s", | ||
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(); | ||
} | ||
} | ||
} |
Oops, something went wrong.