diff options
author | Yang Song <songy23@users.noreply.github.com> | 2018-04-26 09:53:32 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-04-26 09:53:32 -0700 |
commit | 86dadeeae96698c24489ce5817e6474224be8802 (patch) | |
tree | 37a7278247c7f1d6df6dd12e6377efc68d714a21 | |
parent | 610ff80ebec2b831a7ea6cc73ad4614152a36ad9 (diff) | |
download | opencensus-java-86dadeeae96698c24489ce5817e6474224be8802.tar.gz |
Move monitored resource detection utils to a separate artifact. (#1135)
Move monitored resource detection utils to a separate artifact, so that it can be reused by both Stackdriver Monitoring and Stackdriver Trace.
16 files changed, 624 insertions, 158 deletions
diff --git a/all/build.gradle b/all/build.gradle index e8440728..044dc13f 100644 --- a/all/build.gradle +++ b/all/build.gradle @@ -16,6 +16,7 @@ def subprojects = [ project(':opencensus-contrib-grpc-util'), project(':opencensus-contrib-grpc-metrics'), project(':opencensus-contrib-http-util'), + project(':opencensus-contrib-monitored-resource-util'), project(':opencensus-contrib-zpages'), project(':opencensus-exporter-trace-logging'), project(':opencensus-exporter-trace-stackdriver'), @@ -34,6 +35,7 @@ def subprojects_javadoc = [ project(':opencensus-contrib-grpc-util'), project(':opencensus-contrib-grpc-metrics'), project(':opencensus-contrib-http-util'), + project(':opencensus-contrib-monitored-resource-util'), project(':opencensus-contrib-zpages'), project(':opencensus-exporter-trace-logging'), project(':opencensus-exporter-trace-stackdriver'), diff --git a/build.gradle b/build.gradle index d36b571a..47b7502e 100644 --- a/build.gradle +++ b/build.gradle @@ -362,6 +362,7 @@ subprojects { 'opencensus-contrib-grpc-metrics', 'opencensus-contrib-grpc-util', 'opencensus-contrib-http-util', + 'opencensus-contrib-monitored-resource-util', 'opencensus-contrib-zpages', 'opencensus-exporter-stats-prometheus', 'opencensus-exporter-stats-signalfx', diff --git a/buildscripts/import-control.xml b/buildscripts/import-control.xml index 75ae826a..38cd747d 100644 --- a/buildscripts/import-control.xml +++ b/buildscripts/import-control.xml @@ -92,6 +92,9 @@ General guidelines on imports: <allow pkg="io.opencensus.tags"/> <allow pkg="io.opencensus.trace"/> </subpackage> + <subpackage name="monitoredresource.util"> + <allow pkg="io.opencensus.contrib.monitoredresource.util"/> + </subpackage> </subpackage> <subpackage name="exporter"> <allow pkg="com.google.common"/> @@ -113,6 +116,7 @@ General guidelines on imports: <allow pkg="com.google"/> <allow pkg="io.opencensus.exporter.stats.stackdriver"/> <allow pkg="io.opencensus.trace"/> + <allow pkg="io.opencensus.contrib.monitoredresource.util"/> </subpackage> </subpackage> <subpackage name="trace"> diff --git a/contrib/monitored_resource_util/README.md b/contrib/monitored_resource_util/README.md new file mode 100644 index 00000000..00b9feaf --- /dev/null +++ b/contrib/monitored_resource_util/README.md @@ -0,0 +1,34 @@ +# OpenCensus Monitored Resources Util +[![Build Status][travis-image]][travis-url] +[![Windows Build Status][appveyor-image]][appveyor-url] +[![Maven Central][maven-image]][maven-url] + +The *OpenCensus Monitored Resource Util for Java* is a collection of utilities for auto detecting +monitored resource when exporting stats, based on the environment where the application is running. + +## Quickstart + +### Add the dependencies to your project + +For Maven add to your `pom.xml`: +```xml +<dependencies> + <dependency> + <groupId>io.opencensus</groupId> + <artifactId>opencensus-contrib-monitored-resource-util</artifactId> + <version>0.13.0</version> + </dependency> +</dependencies> +``` + +For Gradle add to your dependencies: +```gradle +compile 'io.opencensus:opencensus-contrib-monitored-resource-util:0.13.0' +``` + +[travis-image]: https://travis-ci.org/census-instrumentation/opencensus-java.svg?branch=master +[travis-url]: https://travis-ci.org/census-instrumentation/opencensus-java +[appveyor-image]: https://ci.appveyor.com/api/projects/status/hxthmpkxar4jq4be/branch/master?svg=true +[appveyor-url]: https://ci.appveyor.com/project/opencensusjavateam/opencensus-java/branch/master +[maven-image]: https://maven-badges.herokuapp.com/maven-central/io.opencensus/opencensus-contrib-monitoredresource-util/badge.svg +[maven-url]: https://maven-badges.herokuapp.com/maven-central/io.opencensus/opencensus-contrib-monitoredresource-util diff --git a/contrib/monitored_resource_util/build.gradle b/contrib/monitored_resource_util/build.gradle new file mode 100644 index 00000000..141ff7fc --- /dev/null +++ b/contrib/monitored_resource_util/build.gradle @@ -0,0 +1,14 @@ +description = 'OpenCensus Monitored Resource Util' + +apply plugin: 'java' + +[compileJava, compileTestJava].each() { + it.sourceCompatibility = 1.6 + it.targetCompatibility = 1.6 +} + +dependencies { + compileOnly libraries.auto_value + + signature "org.codehaus.mojo.signature:java16:+@signature" +} diff --git a/exporters/stats/stackdriver/src/main/java/io/opencensus/exporter/stats/stackdriver/AwsIdentityDocUtils.java b/contrib/monitored_resource_util/src/main/java/io/opencensus/contrib/monitoredresource/util/AwsIdentityDocUtils.java index fbbdf80b..03b0bd4d 100644 --- a/exporters/stats/stackdriver/src/main/java/io/opencensus/exporter/stats/stackdriver/AwsIdentityDocUtils.java +++ b/contrib/monitored_resource_util/src/main/java/io/opencensus/contrib/monitoredresource/util/AwsIdentityDocUtils.java @@ -14,12 +14,8 @@ * limitations under the License. */ -package io.opencensus.exporter.stats.stackdriver; +package io.opencensus.contrib.monitoredresource.util; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Charsets; -import com.google.common.base.Splitter; -import com.google.common.collect.Maps; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -27,7 +23,8 @@ import java.io.Reader; import java.net.HttpURLConnection; import java.net.URI; import java.nio.CharBuffer; -import java.util.List; +import java.nio.charset.Charset; +import java.util.HashMap; import java.util.Map; import javax.annotation.concurrent.GuardedBy; @@ -36,11 +33,10 @@ final class AwsIdentityDocUtils { private static final Object monitor = new Object(); private static final int AWS_IDENTITY_DOC_BUF_SIZE = 0x800; // 2K chars (4K bytes) - private static final Splitter AWS_IDENTITY_DOC_LINE_BREAK_SPLITTER = Splitter.on('\n'); - private static final Splitter AWS_IDENTITY_DOC_COLON_SPLITTER = Splitter.on(':'); + private static final String AWS_IDENTITY_DOC_LINE_BREAK_SPLITTER = "\n"; + private static final String AWS_IDENTITY_DOC_COLON_SPLITTER = ":"; - @VisibleForTesting - static final URI AWS_INSTANCE_IDENTITY_DOCUMENT_URI = + private static final URI AWS_INSTANCE_IDENTITY_DOCUMENT_URI = URI.create("http://169.254.169.254/latest/dynamic/instance-identity/document"); @GuardedBy("monitor") @@ -67,7 +63,7 @@ final class AwsIdentityDocUtils { InputStream stream = null; try { stream = openStream(AWS_INSTANCE_IDENTITY_DOCUMENT_URI); - String awsIdentityDocument = slurp(new InputStreamReader(stream, Charsets.UTF_8)); + String awsIdentityDocument = slurp(new InputStreamReader(stream, Charset.forName("UTF-8"))); synchronized (monitor) { awsEnvVarMap = parseAwsIdentityDocument(awsIdentityDocument); } @@ -110,17 +106,18 @@ final class AwsIdentityDocUtils { // AWS Instance Identity Document is a JSON file. // See docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-identity-documents.html. - @VisibleForTesting static Map<String, String> parseAwsIdentityDocument(String awsIdentityDocument) { - Map<String, String> map = Maps.newHashMap(); - List<String> lines = AWS_IDENTITY_DOC_LINE_BREAK_SPLITTER.splitToList(awsIdentityDocument); + Map<String, String> map = new HashMap<String, String>(); + @SuppressWarnings("StringSplitter") + String[] lines = awsIdentityDocument.split(AWS_IDENTITY_DOC_LINE_BREAK_SPLITTER, -1); for (String line : lines) { - List<String> keyValuePair = AWS_IDENTITY_DOC_COLON_SPLITTER.splitToList(line); - if (keyValuePair.size() != 2) { + @SuppressWarnings("StringSplitter") + String[] keyValuePair = line.split(AWS_IDENTITY_DOC_COLON_SPLITTER, -1); + if (keyValuePair.length != 2) { continue; } - String key = keyValuePair.get(0).replaceAll("[\" ]", ""); - String value = keyValuePair.get(1).replaceAll("[\" ,]", ""); + String key = keyValuePair[0].replaceAll("[\" ]", ""); + String value = keyValuePair[1].replaceAll("[\" ,]", ""); map.put(key, value); } return map; diff --git a/contrib/monitored_resource_util/src/main/java/io/opencensus/contrib/monitoredresource/util/GcpMetadataConfig.java b/contrib/monitored_resource_util/src/main/java/io/opencensus/contrib/monitoredresource/util/GcpMetadataConfig.java new file mode 100644 index 00000000..c09d1c65 --- /dev/null +++ b/contrib/monitored_resource_util/src/main/java/io/opencensus/contrib/monitoredresource/util/GcpMetadataConfig.java @@ -0,0 +1,90 @@ +/* + * Copyright 2018, OpenCensus Authors + * + * 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 io.opencensus.contrib.monitoredresource.util; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.nio.charset.Charset; +import javax.annotation.Nullable; + +/** + * Retrieves Google Cloud project-id and a limited set of instance attributes from Metadata server. + * + * @see <a href="https://cloud.google.com/compute/docs/storing-retrieving-metadata"> + * https://cloud.google.com/compute/docs/storing-retrieving-metadata</a> + */ +final class GcpMetadataConfig { + + private static final String METADATA_URL = "http://metadata/computeMetadata/v1/"; + + private GcpMetadataConfig() {} + + @Nullable + static String getProjectId() { + return getAttribute("project/project-id"); + } + + @Nullable + static String getZone() { + String zoneId = getAttribute("instance/zone"); + if (zoneId == null) { + return null; + } + if (zoneId.contains("/")) { + return zoneId.substring(zoneId.lastIndexOf('/') + 1); + } + return zoneId; + } + + @Nullable + static String getInstanceId() { + return getAttribute("instance/id"); + } + + @Nullable + static String getClusterName() { + return getAttribute("instance/attributes/cluster-name"); + } + + @Nullable + private static String getAttribute(String attributeName) { + try { + URL url = new URL(METADATA_URL + attributeName); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestProperty("Metadata-Flavor", "Google"); + InputStream input = connection.getInputStream(); + if (connection.getResponseCode() == 200) { + BufferedReader reader = null; + try { + reader = new BufferedReader(new InputStreamReader(input, Charset.forName("UTF-8"))); + return reader.readLine(); + } finally { + if (reader != null) { + reader.close(); + } + } + } + } catch (IOException ignore) { + // ignore + } + return null; + } +} diff --git a/contrib/monitored_resource_util/src/main/java/io/opencensus/contrib/monitoredresource/util/MonitoredResource.java b/contrib/monitored_resource_util/src/main/java/io/opencensus/contrib/monitoredresource/util/MonitoredResource.java new file mode 100644 index 00000000..c7e91117 --- /dev/null +++ b/contrib/monitored_resource_util/src/main/java/io/opencensus/contrib/monitoredresource/util/MonitoredResource.java @@ -0,0 +1,252 @@ +/* + * Copyright 2018, OpenCensus Authors + * + * 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 io.opencensus.contrib.monitoredresource.util; + +import com.google.auto.value.AutoValue; +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; + +/** + * {@link MonitoredResource} represents an auto-detected monitored resource used by application for + * exporting stats. It has a {@code ResourceType} associated with a mapping from resource labels to + * values. + * + * @since 0.13 + */ +@Immutable +public abstract class MonitoredResource { + + MonitoredResource() {} + + /** + * Returns the {@link ResourceType} of this {@link MonitoredResource}. + * + * @return the {@code ResourceType}. + * @since 0.13 + */ + public abstract ResourceType getResourceType(); + + /* + * Returns the first of two given parameters that is not null, if either is, or otherwise + * throws a NullPointerException. + */ + private static <T> T firstNonNull(@Nullable T first, @Nullable T second) { + if (first != null) { + return first; + } + if (second != null) { + return second; + } + throw new NullPointerException("Both parameters are null"); + } + + // TODO(songya): consider using a tagged union match() approach (that will introduce + // dependency on opencensus-api). + + /** + * {@link MonitoredResource} for AWS EC2 instance. + * + * @since 0.13 + */ + @Immutable + @AutoValue + public abstract static class AwsEc2InstanceMonitoredResource extends MonitoredResource { + + private static final String AWS_ACCOUNT = + firstNonNull(AwsIdentityDocUtils.getValueFromAwsIdentityDocument("accountId"), ""); + private static final String AWS_INSTANCE_ID = + firstNonNull(AwsIdentityDocUtils.getValueFromAwsIdentityDocument("instanceId"), ""); + private static final String AWS_REGION = + firstNonNull(AwsIdentityDocUtils.getValueFromAwsIdentityDocument("region"), ""); + + @Override + public ResourceType getResourceType() { + return ResourceType.AWS_EC2_INSTANCE; + } + + /** + * Returns the AWS account ID. + * + * @return the AWS account ID. + * @since 0.13 + */ + public abstract String getAccount(); + + /** + * Returns the AWS EC2 instance ID. + * + * @return the AWS EC2 instance ID. + * @since 0.13 + */ + public abstract String getInstanceId(); + + /** + * Returns the AWS region. + * + * @return the AWS region. + * @since 0.13 + */ + public abstract String getRegion(); + + static AwsEc2InstanceMonitoredResource create() { + return new AutoValue_MonitoredResource_AwsEc2InstanceMonitoredResource( + AWS_ACCOUNT, AWS_INSTANCE_ID, AWS_REGION); + } + } + + /** + * {@link MonitoredResource} for GCP GCE instance. + * + * @since 0.13 + */ + @Immutable + @AutoValue + public abstract static class GcpGceInstanceMonitoredResource extends MonitoredResource { + + private static final String GCP_ACCOUNT_ID = firstNonNull(GcpMetadataConfig.getProjectId(), ""); + private static final String GCP_INSTANCE_ID = + firstNonNull(GcpMetadataConfig.getInstanceId(), ""); + private static final String GCP_ZONE = firstNonNull(GcpMetadataConfig.getZone(), ""); + + @Override + public ResourceType getResourceType() { + return ResourceType.GCP_GCE_INSTANCE; + } + + /** + * Returns the GCP account number for the instance. + * + * @return the GCP account number for the instance. + * @since 0.13 + */ + public abstract String getAccount(); + + /** + * Returns the GCP GCE instance ID. + * + * @return the GCP GCE instance ID. + * @since 0.13 + */ + public abstract String getInstanceId(); + + /** + * Returns the GCP zone. + * + * @return the GCP zone. + * @since 0.13 + */ + public abstract String getZone(); + + static GcpGceInstanceMonitoredResource create() { + return new AutoValue_MonitoredResource_GcpGceInstanceMonitoredResource( + GCP_ACCOUNT_ID, GCP_INSTANCE_ID, GCP_ZONE); + } + } + + /** + * {@link MonitoredResource} for GCP GKE container. + * + * @since 0.13 + */ + @Immutable + @AutoValue + public abstract static class GcpGkeContainerMonitoredResource extends MonitoredResource { + + private static final String GCP_ACCOUNT_ID = firstNonNull(GcpMetadataConfig.getProjectId(), ""); + private static final String GCP_CLUSTER_NAME = + firstNonNull(GcpMetadataConfig.getClusterName(), ""); + private static final String GCP_CONTAINER_NAME = + firstNonNull(System.getenv("CONTAINER_NAME"), ""); + private static final String GCP_NAMESPACE_ID = firstNonNull(System.getenv("NAMESPACE"), ""); + private static final String GCP_INSTANCE_ID = + firstNonNull(GcpMetadataConfig.getInstanceId(), ""); + private static final String GCP_POD_ID = firstNonNull(System.getenv("HOSTNAME"), ""); + private static final String GCP_ZONE = firstNonNull(GcpMetadataConfig.getZone(), ""); + + @Override + public ResourceType getResourceType() { + return ResourceType.GCP_GKE_CONTAINER; + } + + /** + * Returns the GCP account number for the instance. + * + * @return the GCP account number for the instance. + * @since 0.13 + */ + public abstract String getAccount(); + + /** + * Returns the GCP GKE cluster name. + * + * @return the GCP GKE cluster name. + * @since 0.13 + */ + public abstract String getClusterName(); + + /** + * Returns the GCP GKE container name. + * + * @return the GCP GKE container name. + * @since 0.13 + */ + public abstract String getContainerName(); + + /** + * Returns the GCP GKE namespace ID. + * + * @return the GCP GKE namespace ID. + * @since 0.13 + */ + public abstract String getNamespaceId(); + + /** + * Returns the GCP GKE instance ID. + * + * @return the GCP GKE instance ID. + * @since 0.13 + */ + public abstract String getInstanceId(); + + /** + * Returns the GCP GKE Pod ID. + * + * @return the GCP GKE Pod ID. + * @since 0.13 + */ + public abstract String getPodId(); + + /** + * Returns the GCP zone. + * + * @return the GCP zone. + * @since 0.13 + */ + public abstract String getZone(); + + static GcpGkeContainerMonitoredResource create() { + return new AutoValue_MonitoredResource_GcpGkeContainerMonitoredResource( + GCP_ACCOUNT_ID, + GCP_CLUSTER_NAME, + GCP_CONTAINER_NAME, + GCP_NAMESPACE_ID, + GCP_INSTANCE_ID, + GCP_POD_ID, + GCP_ZONE); + } + } +} diff --git a/contrib/monitored_resource_util/src/main/java/io/opencensus/contrib/monitoredresource/util/MonitoredResourceUtils.java b/contrib/monitored_resource_util/src/main/java/io/opencensus/contrib/monitoredresource/util/MonitoredResourceUtils.java new file mode 100644 index 00000000..8ff0ff98 --- /dev/null +++ b/contrib/monitored_resource_util/src/main/java/io/opencensus/contrib/monitoredresource/util/MonitoredResourceUtils.java @@ -0,0 +1,54 @@ +/* + * Copyright 2018, OpenCensus Authors + * + * 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 io.opencensus.contrib.monitoredresource.util; + +import io.opencensus.contrib.monitoredresource.util.MonitoredResource.AwsEc2InstanceMonitoredResource; +import io.opencensus.contrib.monitoredresource.util.MonitoredResource.GcpGceInstanceMonitoredResource; +import io.opencensus.contrib.monitoredresource.util.MonitoredResource.GcpGkeContainerMonitoredResource; +import javax.annotation.Nullable; + +/** + * Utilities for for auto detecting monitored resource based on the environment where the + * application is running. + * + * @since 0.13 + */ +public final class MonitoredResourceUtils { + + /** + * Returns a self-configured monitored resource, or {@code null} if the application is not running + * on a supported environment. + * + * @return a {@code MonitoredResource}. + * @since 0.13 + */ + @Nullable + public static MonitoredResource getDefaultResource() { + if (System.getenv("KUBERNETES_SERVICE_HOST") != null) { + return GcpGkeContainerMonitoredResource.create(); + } + if (GcpMetadataConfig.getInstanceId() != null) { + return GcpGceInstanceMonitoredResource.create(); + } + if (AwsIdentityDocUtils.isRunningOnAwsEc2()) { + return AwsEc2InstanceMonitoredResource.create(); + } + return null; + } + + private MonitoredResourceUtils() {} +} diff --git a/contrib/monitored_resource_util/src/main/java/io/opencensus/contrib/monitoredresource/util/ResourceType.java b/contrib/monitored_resource_util/src/main/java/io/opencensus/contrib/monitoredresource/util/ResourceType.java new file mode 100644 index 00000000..f2816676 --- /dev/null +++ b/contrib/monitored_resource_util/src/main/java/io/opencensus/contrib/monitoredresource/util/ResourceType.java @@ -0,0 +1,47 @@ +/* + * Copyright 2018, OpenCensus Authors + * + * 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 io.opencensus.contrib.monitoredresource.util; + +/** + * {@link ResourceType} represents the type of supported monitored resources that can be + * automatically detected by OpenCensus. + * + * @since 0.13 + */ +public enum ResourceType { + + /** + * Resource for GCP GKE container. + * + * @since 0.13 + */ + GCP_GKE_CONTAINER, + + /** + * Resource for GCP GCE instance. + * + * @since 0.13 + */ + GCP_GCE_INSTANCE, + + /** + * Resource for AWS EC2 instance. + * + * @since 0.13 + */ + AWS_EC2_INSTANCE +} diff --git a/exporters/stats/stackdriver/src/test/java/io/opencensus/exporter/stats/stackdriver/AwsIdentityDocUtilsTest.java b/contrib/monitored_resource_util/src/test/java/io/opencensus/contrib/monitoredresource/util/AwsIdentityDocUtilsTest.java index 9c15f087..77d98493 100644 --- a/exporters/stats/stackdriver/src/test/java/io/opencensus/exporter/stats/stackdriver/AwsIdentityDocUtilsTest.java +++ b/contrib/monitored_resource_util/src/test/java/io/opencensus/contrib/monitoredresource/util/AwsIdentityDocUtilsTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.opencensus.exporter.stats.stackdriver; +package io.opencensus.contrib.monitoredresource.util; import static com.google.common.truth.Truth.assertThat; diff --git a/contrib/monitored_resource_util/src/test/java/io/opencensus/contrib/monitoredresource/util/MonitoredResourceUtilsTest.java b/contrib/monitored_resource_util/src/test/java/io/opencensus/contrib/monitoredresource/util/MonitoredResourceUtilsTest.java new file mode 100644 index 00000000..01927a2d --- /dev/null +++ b/contrib/monitored_resource_util/src/test/java/io/opencensus/contrib/monitoredresource/util/MonitoredResourceUtilsTest.java @@ -0,0 +1,42 @@ +/* + * Copyright 2018, OpenCensus Authors + * + * 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 io.opencensus.contrib.monitoredresource.util; + +import static com.google.common.truth.Truth.assertThat; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Tests for {@link MonitoredResourceUtils}. */ +@RunWith(JUnit4.class) +public class MonitoredResourceUtilsTest { + + @Test + public void testGetDefaultResource() { + MonitoredResource resource = MonitoredResourceUtils.getDefaultResource(); + if (System.getenv("KUBERNETES_SERVICE_HOST") != null) { + assertThat(resource.getResourceType()).isEqualTo(ResourceType.GCP_GKE_CONTAINER); + } else if (GcpMetadataConfig.getInstanceId() != null) { + assertThat(resource.getResourceType()).isEqualTo(ResourceType.GCP_GCE_INSTANCE); + } else if (AwsIdentityDocUtils.isRunningOnAwsEc2()) { + assertThat(resource.getResourceType()).isEqualTo(ResourceType.AWS_EC2_INSTANCE); + } else { + assertThat(resource).isNull(); + } + } +} diff --git a/exporters/stats/stackdriver/build.gradle b/exporters/stats/stackdriver/build.gradle index c2ed07aa..ebf59b70 100644 --- a/exporters/stats/stackdriver/build.gradle +++ b/exporters/stats/stackdriver/build.gradle @@ -9,6 +9,7 @@ dependencies { compileOnly libraries.auto_value compile project(':opencensus-api'), + project(':opencensus-contrib-monitored-resource-util'), libraries.google_auth compile (libraries.google_cloud_monitoring) { diff --git a/exporters/stats/stackdriver/src/main/java/io/opencensus/exporter/stats/stackdriver/StackdriverExportUtils.java b/exporters/stats/stackdriver/src/main/java/io/opencensus/exporter/stats/stackdriver/StackdriverExportUtils.java index f97217b5..8dc5f76c 100644 --- a/exporters/stats/stackdriver/src/main/java/io/opencensus/exporter/stats/stackdriver/StackdriverExportUtils.java +++ b/exporters/stats/stackdriver/src/main/java/io/opencensus/exporter/stats/stackdriver/StackdriverExportUtils.java @@ -29,7 +29,6 @@ import com.google.api.MetricDescriptor.MetricKind; import com.google.api.MonitoredResource; import com.google.cloud.MetadataConfig; import com.google.common.annotations.VisibleForTesting; -import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.monitoring.v3.Point; @@ -39,6 +38,11 @@ import com.google.monitoring.v3.TypedValue; import com.google.protobuf.Timestamp; import io.opencensus.common.Function; import io.opencensus.common.Functions; +import io.opencensus.contrib.monitoredresource.util.MonitoredResource.AwsEc2InstanceMonitoredResource; +import io.opencensus.contrib.monitoredresource.util.MonitoredResource.GcpGceInstanceMonitoredResource; +import io.opencensus.contrib.monitoredresource.util.MonitoredResource.GcpGkeContainerMonitoredResource; +import io.opencensus.contrib.monitoredresource.util.MonitoredResourceUtils; +import io.opencensus.contrib.monitoredresource.util.ResourceType; import io.opencensus.stats.Aggregation; import io.opencensus.stats.AggregationData; import io.opencensus.stats.AggregationData.CountData; @@ -72,10 +76,10 @@ final class StackdriverExportUtils { @VisibleForTesting static final String LABEL_DESCRIPTION = "OpenCensus TagKey"; @VisibleForTesting static final String OPENCENSUS_TASK = "opencensus_task"; @VisibleForTesting static final String OPENCENSUS_TASK_DESCRIPTION = "Opencensus task identifier"; - @VisibleForTesting static final String GCP_GKE_CONTAINER = "gke_container"; - @VisibleForTesting static final String GCP_GCE_INSTANCE = "gce_instance"; - @VisibleForTesting static final String AWS_EC2_INSTANCE = "aws_ec2_instance"; - @VisibleForTesting static final String GLOBAL = "global"; + private static final String GCP_GKE_CONTAINER = "gke_container"; + private static final String GCP_GCE_INSTANCE = "gce_instance"; + private static final String AWS_EC2_INSTANCE = "aws_ec2_instance"; + private static final String GLOBAL = "global"; private static final Logger logger = Logger.getLogger(StackdriverExportUtils.class.getName()); private static final String CUSTOM_METRIC_DOMAIN = "custom.googleapis.com"; @@ -356,135 +360,71 @@ final class StackdriverExportUtils { .build(); } - private enum Label { - GcpClusterName("cluster_name"), - GcpContainerName("container_name"), - GcpNamespaceId("namespace_id"), - GcpInstanceId("instance_id"), - GcpInstanceName("instance_name"), - GcpGkePodId("pod_id"), - GcpZone("zone"), - AwsAccount("aws_account"), - AwsInstanceId("instance_id"), - AwsRegion("region"); - - private final String key; - - Label(String key) { - this.key = key; - } - - String getKey() { - return key; - } - } - - private enum Resource { - GkeContainer(GCP_GKE_CONTAINER), - GceInstance(GCP_GCE_INSTANCE), - AwsEc2Instance(AWS_EC2_INSTANCE), - Global(GLOBAL); - - private final String key; - - Resource(String key) { - this.key = key; - } - - String getKey() { - return key; - } - } - - private static final ImmutableMultimap<Resource, Label> RESOURCE_TYPE_WITH_LABELS = - ImmutableMultimap.<Resource, Label>builder() - .putAll( - Resource.GkeContainer, - Label.GcpClusterName, - Label.GcpContainerName, - Label.GcpNamespaceId, - Label.GcpInstanceId, - Label.GcpGkePodId, - Label.GcpZone) - .putAll(Resource.GceInstance, Label.GcpInstanceId, Label.GcpZone) - .putAll(Resource.AwsEc2Instance, Label.AwsAccount, Label.AwsInstanceId, Label.AwsRegion) - .build(); - - /* Return a self-configured monitored Resource. */ + /* Return a self-configured Stackdriver monitored resource. */ static MonitoredResource getDefaultResource() { - Resource detectedResourceType = getAutoDetectedResourceType(); - String resourceType = detectedResourceType.getKey(); - MonitoredResource.Builder builder = MonitoredResource.newBuilder().setType(resourceType); - if (MetadataConfig.getProjectId() != null) { - // For default resource, always use the project id from MetadataConfig. This allows stats from - // other projects (e.g from GCE running in another project) to be collected. - builder.putLabels(PROJECT_ID_LABEL_KEY, MetadataConfig.getProjectId()); - } - for (Label label : RESOURCE_TYPE_WITH_LABELS.get(detectedResourceType)) { - String value = getValue(label); - if (value == null) { - value = ""; + MonitoredResource.Builder builder = MonitoredResource.newBuilder(); + io.opencensus.contrib.monitoredresource.util.MonitoredResource autoDetectedResource = + MonitoredResourceUtils.getDefaultResource(); + if (autoDetectedResource == null) { + builder.setType(GLOBAL); + if (MetadataConfig.getProjectId() != null) { + // For default global resource, always use the project id from MetadataConfig. This allows + // stats from other projects (e.g from GAE running in another project) to be collected. + builder.putLabels(PROJECT_ID_LABEL_KEY, MetadataConfig.getProjectId()); } - // Label values can be null or empty, but each label key must have an associated value. - builder.putLabels(label.getKey(), value); + return builder.build(); } + builder.setType(mapToStackdriverResourceType(autoDetectedResource.getResourceType())); + setMonitoredResourceLabelsForBuilder(builder, autoDetectedResource); return builder.build(); } - @javax.annotation.Nullable - private static String getValue(Label label) { - String value; - switch (label) { - case GcpClusterName: - value = MetadataConfig.getClusterName(); - break; - case GcpInstanceId: - value = MetadataConfig.getInstanceId(); - break; - case GcpInstanceName: - value = System.getenv("GAE_INSTANCE"); - break; - case GcpGkePodId: - value = System.getenv("HOSTNAME"); - break; - case GcpZone: - value = MetadataConfig.getZone(); - break; - case GcpContainerName: - value = System.getenv("CONTAINER_NAME"); - break; - case GcpNamespaceId: - value = System.getenv("NAMESPACE"); - break; - case AwsAccount: - value = AwsIdentityDocUtils.getValueFromAwsIdentityDocument("accountId"); - break; - case AwsInstanceId: - value = AwsIdentityDocUtils.getValueFromAwsIdentityDocument("instanceId"); - break; - case AwsRegion: - value = "aws:" + AwsIdentityDocUtils.getValueFromAwsIdentityDocument("region"); - break; - default: - value = null; - break; + private static String mapToStackdriverResourceType(ResourceType resourceType) { + switch (resourceType) { + case GCP_GCE_INSTANCE: + return GCP_GCE_INSTANCE; + case GCP_GKE_CONTAINER: + return GCP_GKE_CONTAINER; + case AWS_EC2_INSTANCE: + return AWS_EC2_INSTANCE; } - return value; + throw new IllegalArgumentException("Unknown resource type."); } - /* Detects monitored Resource type using environment variables, else return global as default. */ - private static Resource getAutoDetectedResourceType() { - if (System.getenv("KUBERNETES_SERVICE_HOST") != null) { - return Resource.GkeContainer; - } - if (MetadataConfig.getInstanceId() != null) { - return Resource.GceInstance; - } - if (AwsIdentityDocUtils.isRunningOnAwsEc2()) { - return Resource.AwsEc2Instance; + private static void setMonitoredResourceLabelsForBuilder( + MonitoredResource.Builder builder, + io.opencensus.contrib.monitoredresource.util.MonitoredResource autoDetectedResource) { + switch (autoDetectedResource.getResourceType()) { + case GCP_GCE_INSTANCE: + @SuppressWarnings("unchecked") + GcpGceInstanceMonitoredResource gcpGceInstanceMonitoredResource = + (GcpGceInstanceMonitoredResource) autoDetectedResource; + builder.putLabels(PROJECT_ID_LABEL_KEY, gcpGceInstanceMonitoredResource.getAccount()); + builder.putLabels("instance_id", gcpGceInstanceMonitoredResource.getInstanceId()); + builder.putLabels("zone", gcpGceInstanceMonitoredResource.getZone()); + return; + case GCP_GKE_CONTAINER: + @SuppressWarnings("unchecked") + GcpGkeContainerMonitoredResource gcpGkeContainerMonitoredResource = + (GcpGkeContainerMonitoredResource) autoDetectedResource; + builder.putLabels(PROJECT_ID_LABEL_KEY, gcpGkeContainerMonitoredResource.getAccount()); + builder.putLabels("cluster_name", gcpGkeContainerMonitoredResource.getClusterName()); + builder.putLabels("container_name", gcpGkeContainerMonitoredResource.getContainerName()); + builder.putLabels("namespace_id", gcpGkeContainerMonitoredResource.getNamespaceId()); + builder.putLabels("instance_id", gcpGkeContainerMonitoredResource.getInstanceId()); + builder.putLabels("pod_id", gcpGkeContainerMonitoredResource.getPodId()); + builder.putLabels("zone", gcpGkeContainerMonitoredResource.getZone()); + return; + case AWS_EC2_INSTANCE: + @SuppressWarnings("unchecked") + AwsEc2InstanceMonitoredResource awsEc2InstanceMonitoredResource = + (AwsEc2InstanceMonitoredResource) autoDetectedResource; + builder.putLabels("aws_account", awsEc2InstanceMonitoredResource.getAccount()); + builder.putLabels("instance_id", awsEc2InstanceMonitoredResource.getInstanceId()); + builder.putLabels("region", "aws:" + awsEc2InstanceMonitoredResource.getRegion()); + return; } - // default Resource type - return Resource.Global; + throw new IllegalArgumentException("Unknown subclass of MonitoredResource."); } private StackdriverExportUtils() {} diff --git a/exporters/stats/stackdriver/src/test/java/io/opencensus/exporter/stats/stackdriver/StackdriverExportUtilsTest.java b/exporters/stats/stackdriver/src/test/java/io/opencensus/exporter/stats/stackdriver/StackdriverExportUtilsTest.java index 16e73b1f..a78a96ea 100644 --- a/exporters/stats/stackdriver/src/test/java/io/opencensus/exporter/stats/stackdriver/StackdriverExportUtilsTest.java +++ b/exporters/stats/stackdriver/src/test/java/io/opencensus/exporter/stats/stackdriver/StackdriverExportUtilsTest.java @@ -26,7 +26,6 @@ import com.google.api.Metric; import com.google.api.MetricDescriptor; import com.google.api.MetricDescriptor.MetricKind; import com.google.api.MonitoredResource; -import com.google.cloud.MetadataConfig; import com.google.common.collect.ImmutableMap; import com.google.monitoring.v3.Point; import com.google.monitoring.v3.TimeInterval; @@ -501,18 +500,4 @@ public class StackdriverExportUtilsTest { .addPoints(StackdriverExportUtils.createPoint(sumData, cumulativeData, SUM)) .build()); } - - @Test - public void testGetDefaultResource() { - MonitoredResource resource = StackdriverExportUtils.getDefaultResource(); - if (System.getenv("KUBERNETES_SERVICE_HOST") != null) { - assertThat(resource.getType()).isEqualTo(StackdriverExportUtils.GCP_GKE_CONTAINER); - } else if (MetadataConfig.getInstanceId() != null) { - assertThat(resource.getType()).isEqualTo(StackdriverExportUtils.GCP_GCE_INSTANCE); - } else if (AwsIdentityDocUtils.isRunningOnAwsEc2()) { - assertThat(resource.getType()).isEqualTo(StackdriverExportUtils.AWS_EC2_INSTANCE); - } else { - assertThat(resource.getType()).isEqualTo(StackdriverExportUtils.GLOBAL); - } - } } diff --git a/settings.gradle b/settings.gradle index 0d2617e2..3d9a0ee0 100644 --- a/settings.gradle +++ b/settings.gradle @@ -17,6 +17,7 @@ include ":opencensus-contrib-agent" include ":opencensus-contrib-grpc-metrics" include ":opencensus-contrib-grpc-util" include ":opencensus-contrib-http-util" +include ":opencensus-contrib-monitored-resource-util" project(':opencensus-api').projectDir = "$rootDir/api" as File project(':opencensus-impl-core').projectDir = "$rootDir/impl_core" as File @@ -27,6 +28,8 @@ project(':opencensus-contrib-agent').projectDir = "$rootDir/contrib/agent" as Fi project(':opencensus-contrib-grpc-metrics').projectDir = "$rootDir/contrib/grpc_metrics" as File project(':opencensus-contrib-grpc-util').projectDir = "$rootDir/contrib/grpc_util" as File project(':opencensus-contrib-http-util').projectDir = "$rootDir/contrib/http_util" as File +project(':opencensus-contrib-monitored-resource-util').projectDir = + "$rootDir/contrib/monitored_resource_util" as File project(':opencensus-exporter-trace-instana').projectDir = "$rootDir/exporters/trace/instana" as File project(':opencensus-exporter-trace-logging').projectDir = |