diff --git a/sdk_extensions/aws_v1_support/src/main/java/io/opentelemetry/sdk/extensions/trace/aws/resource/AwsResource.java b/sdk_extensions/aws_v1_support/src/main/java/io/opentelemetry/sdk/extensions/trace/aws/resource/AwsResource.java deleted file mode 100644 index d0684d3b5bd..00000000000 --- a/sdk_extensions/aws_v1_support/src/main/java/io/opentelemetry/sdk/extensions/trace/aws/resource/AwsResource.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2020, OpenTelemetry 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.opentelemetry.sdk.extensions.trace.aws.resource; - -import com.google.common.annotations.VisibleForTesting; -import io.opentelemetry.common.AttributeValue; -import io.opentelemetry.common.Attributes; -import io.opentelemetry.common.ReadableKeyValuePairs.KeyValueConsumer; -import io.opentelemetry.sdk.resources.Resource; - -/** Populates the attributes for creating a {@link Resource} that is running in AWS. */ -public abstract class AwsResource { - - /** - * Returns a {@link Resource} which is filled with attributes describing the current AWS - * environment, e.g., metadata for the instance if the app is running on EC2. - */ - public static Resource create() { - return create(new Ec2Resource(), new EcsResource(), new BeanstalkResource()); - } - - @VisibleForTesting - static Resource create(AwsResource... populators) { - final Attributes.Builder attrBuilder = Attributes.newBuilder(); - for (AwsResource populator : populators) { - Attributes attrs = populator.createAttributes(); - attrs.forEach( - new KeyValueConsumer() { - @Override - public void consume(String key, AttributeValue value) { - attrBuilder.setAttribute(key, value); - } - }); - } - - return Resource.create(attrBuilder.build()); - } - - /** Retrurns a {@link Attributes} for constructing a {@link Resource}. */ - abstract Attributes createAttributes(); -} diff --git a/sdk_extensions/aws_v1_support/src/main/java/io/opentelemetry/sdk/extensions/trace/aws/resource/BeanstalkResource.java b/sdk_extensions/aws_v1_support/src/main/java/io/opentelemetry/sdk/extensions/trace/aws/resource/BeanstalkResource.java index f24e58e8062..ec728754f6b 100644 --- a/sdk_extensions/aws_v1_support/src/main/java/io/opentelemetry/sdk/extensions/trace/aws/resource/BeanstalkResource.java +++ b/sdk_extensions/aws_v1_support/src/main/java/io/opentelemetry/sdk/extensions/trace/aws/resource/BeanstalkResource.java @@ -22,12 +22,17 @@ import com.google.common.annotations.VisibleForTesting; import io.opentelemetry.common.Attributes; import io.opentelemetry.sdk.resources.ResourceConstants; +import io.opentelemetry.sdk.resources.ResourceProvider; import java.io.File; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; -class BeanstalkResource extends AwsResource { +/** + * A {@link ResourceProvider} which provides information about the current EC2 instance if running + * on AWS Elastic Beanstalk. + */ +public class BeanstalkResource extends ResourceProvider { private static final Logger logger = Logger.getLogger(BeanstalkResource.class.getName()); @@ -39,7 +44,11 @@ class BeanstalkResource extends AwsResource { private final String configPath; - BeanstalkResource() { + /** + * Returns a {@link BeanstalkResource} which attempts to compute information about the Beanstalk + * environment if available. + */ + public BeanstalkResource() { this(BEANSTALK_CONF_PATH); } @@ -49,7 +58,7 @@ class BeanstalkResource extends AwsResource { } @Override - Attributes createAttributes() { + public Attributes getAttributes() { File configFile = new File(configPath); if (!configFile.exists()) { return Attributes.empty(); diff --git a/sdk_extensions/aws_v1_support/src/main/java/io/opentelemetry/sdk/extensions/trace/aws/resource/Ec2Resource.java b/sdk_extensions/aws_v1_support/src/main/java/io/opentelemetry/sdk/extensions/trace/aws/resource/Ec2Resource.java index b0a0d098958..8bf5ef88dd3 100644 --- a/sdk_extensions/aws_v1_support/src/main/java/io/opentelemetry/sdk/extensions/trace/aws/resource/Ec2Resource.java +++ b/sdk_extensions/aws_v1_support/src/main/java/io/opentelemetry/sdk/extensions/trace/aws/resource/Ec2Resource.java @@ -23,6 +23,7 @@ import com.google.common.io.ByteStreams; import io.opentelemetry.common.Attributes; import io.opentelemetry.sdk.resources.ResourceConstants; +import io.opentelemetry.sdk.resources.ResourceProvider; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; @@ -35,7 +36,11 @@ import java.util.logging.Level; import java.util.logging.Logger; -class Ec2Resource extends AwsResource { +/** + * A {@link ResourceProvider} which provides information about the current EC2 instance if running + * on AWS EC2. + */ +public class Ec2Resource extends ResourceProvider { private static final Logger logger = Logger.getLogger(Ec2Resource.class.getName()); @@ -49,7 +54,11 @@ class Ec2Resource extends AwsResource { private final URL hostnameUrl; private final URL tokenUrl; - Ec2Resource() { + /** + * Returns a {@link Ec2Resource} which attempts to compute information about this instance if + * available. + */ + public Ec2Resource() { // This is only for testing e.g., with a mock IMDS server and never in production so we just // read from a system property. This is similar to the AWS SDK. this(System.getProperty("otel.aws.imds.endpointOverride", DEFAULT_IMDS_ENDPOINT)); @@ -137,7 +146,7 @@ private static String readResponseString(HttpURLConnection connection) { } @Override - Attributes createAttributes() { + public Attributes getAttributes() { String token = fetchToken(); // If token is empty, either IMDSv2 isn't enabled or an unexpected failure happened. We can diff --git a/sdk_extensions/aws_v1_support/src/main/java/io/opentelemetry/sdk/extensions/trace/aws/resource/EcsResource.java b/sdk_extensions/aws_v1_support/src/main/java/io/opentelemetry/sdk/extensions/trace/aws/resource/EcsResource.java index 60cd5ffe475..4c49afdb74c 100644 --- a/sdk_extensions/aws_v1_support/src/main/java/io/opentelemetry/sdk/extensions/trace/aws/resource/EcsResource.java +++ b/sdk_extensions/aws_v1_support/src/main/java/io/opentelemetry/sdk/extensions/trace/aws/resource/EcsResource.java @@ -20,13 +20,18 @@ import com.google.common.base.Strings; import io.opentelemetry.common.Attributes; import io.opentelemetry.sdk.resources.ResourceConstants; +import io.opentelemetry.sdk.resources.ResourceProvider; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; -class EcsResource extends AwsResource { +/** + * A {@link ResourceProvider} which provides information about the current ECS container if running + * on AWS ECS. + */ +public class EcsResource extends ResourceProvider { private static final Logger logger = Logger.getLogger(EcsResource.class.getName()); @@ -36,7 +41,11 @@ class EcsResource extends AwsResource { private final Map sysEnv; private final DockerHelper dockerHelper; - EcsResource() { + /** + * Returns a {@link Ec2Resource} which attempts to compute information about this ECS container if + * available. + */ + public EcsResource() { this(System.getenv(), new DockerHelper()); } @@ -47,7 +56,7 @@ class EcsResource extends AwsResource { } @Override - Attributes createAttributes() { + public Attributes getAttributes() { if (!isOnEcs()) { return Attributes.empty(); } diff --git a/sdk_extensions/aws_v1_support/src/main/resources/META-INF/services/io.opentelemetry.sdk.resources.ResourceProvider b/sdk_extensions/aws_v1_support/src/main/resources/META-INF/services/io.opentelemetry.sdk.resources.ResourceProvider new file mode 100644 index 00000000000..8ef7413cb67 --- /dev/null +++ b/sdk_extensions/aws_v1_support/src/main/resources/META-INF/services/io.opentelemetry.sdk.resources.ResourceProvider @@ -0,0 +1,3 @@ +io.opentelemetry.sdk.extensions.trace.aws.resource.BeanstalkResource +io.opentelemetry.sdk.extensions.trace.aws.resource.Ec2Resource +io.opentelemetry.sdk.extensions.trace.aws.resource.EcsResource diff --git a/sdk_extensions/aws_v1_support/src/test/java/io/opentelemetry/sdk/extensions/trace/aws/resource/AwsResourceTest.java b/sdk_extensions/aws_v1_support/src/test/java/io/opentelemetry/sdk/extensions/trace/aws/resource/AwsResourceTest.java deleted file mode 100644 index caecd2512fb..00000000000 --- a/sdk_extensions/aws_v1_support/src/test/java/io/opentelemetry/sdk/extensions/trace/aws/resource/AwsResourceTest.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2020, OpenTelemetry 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.opentelemetry.sdk.extensions.trace.aws.resource; - -import static io.opentelemetry.common.AttributeValue.booleanAttributeValue; -import static io.opentelemetry.common.AttributeValue.stringAttributeValue; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.when; - -import io.opentelemetry.common.Attributes; -import io.opentelemetry.sdk.resources.Resource; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -@ExtendWith(MockitoExtension.class) -class AwsResourceTest { - - @Mock private AwsResource populator1; - - @Mock private AwsResource populator2; - - @Test - void createsResource() { - when(populator1.createAttributes()) - .thenReturn( - Attributes.of( - "key1", stringAttributeValue("value1"), "key2", booleanAttributeValue(true))); - when(populator2.createAttributes()) - .thenReturn( - Attributes.of( - "key3", stringAttributeValue("value3"), "key1", stringAttributeValue("value0"))); - - Resource resource = AwsResource.create(populator1, populator2); - assertThat(resource.getAttributes()) - .isEqualTo( - Attributes.of( - "key1", stringAttributeValue("value1"), - "key2", booleanAttributeValue(true), - "key3", stringAttributeValue("value3"))); - } -} diff --git a/sdk_extensions/aws_v1_support/src/test/java/io/opentelemetry/sdk/extensions/trace/aws/resource/BeanstalkResourceTest.java b/sdk_extensions/aws_v1_support/src/test/java/io/opentelemetry/sdk/extensions/trace/aws/resource/BeanstalkResourceTest.java index 5500c2d248c..6699fe2d367 100644 --- a/sdk_extensions/aws_v1_support/src/test/java/io/opentelemetry/sdk/extensions/trace/aws/resource/BeanstalkResourceTest.java +++ b/sdk_extensions/aws_v1_support/src/test/java/io/opentelemetry/sdk/extensions/trace/aws/resource/BeanstalkResourceTest.java @@ -23,8 +23,10 @@ import com.google.common.io.Files; import io.opentelemetry.common.Attributes; import io.opentelemetry.sdk.resources.ResourceConstants; +import io.opentelemetry.sdk.resources.ResourceProvider; import java.io.File; import java.io.IOException; +import java.util.ServiceLoader; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; @@ -38,7 +40,7 @@ void testCreateAttributes(@TempDir File tempFolder) throws IOException { + "version_label\":\"2\",\"environment_name\":\"HttpSubscriber-env\"}"; Files.write(content.getBytes(Charsets.UTF_8), file); BeanstalkResource populator = new BeanstalkResource(file.getPath()); - Attributes attributes = populator.createAttributes(); + Attributes attributes = populator.getAttributes(); assertThat(attributes) .isEqualTo( Attributes.of( @@ -50,7 +52,7 @@ ResourceConstants.SERVICE_VERSION, stringAttributeValue("2"), @Test void testConfigFileMissing() throws IOException { BeanstalkResource populator = new BeanstalkResource("a_file_never_existing"); - Attributes attributes = populator.createAttributes(); + Attributes attributes = populator.getAttributes(); assertThat(attributes.isEmpty()).isTrue(); } @@ -62,7 +64,15 @@ void testBadConfigFile(@TempDir File tempFolder) throws IOException { + "environment_name\":\"HttpSubscriber-env\"}"; Files.write(content.getBytes(Charsets.UTF_8), file); BeanstalkResource populator = new BeanstalkResource(file.getPath()); - Attributes attributes = populator.createAttributes(); + Attributes attributes = populator.getAttributes(); assertThat(attributes.isEmpty()).isTrue(); } + + @Test + void inServiceLoader() { + // No practical way to test the attributes themselves so at least check the service loader picks + // it up. + assertThat(ServiceLoader.load(ResourceProvider.class)) + .anyMatch(BeanstalkResource.class::isInstance); + } } diff --git a/sdk_extensions/aws_v1_support/src/test/java/io/opentelemetry/sdk/extensions/trace/aws/resource/Ec2ResourceTest.java b/sdk_extensions/aws_v1_support/src/test/java/io/opentelemetry/sdk/extensions/trace/aws/resource/Ec2ResourceTest.java index 3badadbde07..44658174c7c 100644 --- a/sdk_extensions/aws_v1_support/src/test/java/io/opentelemetry/sdk/extensions/trace/aws/resource/Ec2ResourceTest.java +++ b/sdk_extensions/aws_v1_support/src/test/java/io/opentelemetry/sdk/extensions/trace/aws/resource/Ec2ResourceTest.java @@ -33,6 +33,8 @@ import com.github.tomakehurst.wiremock.junit.WireMockClassRule; import io.opentelemetry.common.Attributes; import io.opentelemetry.sdk.resources.ResourceConstants; +import io.opentelemetry.sdk.resources.ResourceProvider; +import java.util.ServiceLoader; import org.junit.Before; import org.junit.ClassRule; import org.junit.Test; @@ -77,7 +79,7 @@ public void imdsv2() { .willReturn(okJson(IDENTITY_DOCUMENT))); stubFor(any(urlPathEqualTo("/latest/meta-data/hostname")).willReturn(ok("ec2-1-2-3-4"))); - Attributes attributes = populator.createAttributes(); + Attributes attributes = populator.getAttributes(); Attributes.Builder expectedAttrBuilders = Attributes.newBuilder(); expectedAttrBuilders.setAttribute(ResourceConstants.HOST_ID, "i-1234567890abcdef0"); expectedAttrBuilders.setAttribute(ResourceConstants.CLOUD_ZONE, "us-west-2b"); @@ -108,7 +110,7 @@ public void imdsv1() { .willReturn(okJson(IDENTITY_DOCUMENT))); stubFor(any(urlPathEqualTo("/latest/meta-data/hostname")).willReturn(ok("ec2-1-2-3-4"))); - Attributes attributes = populator.createAttributes(); + Attributes attributes = populator.getAttributes(); Attributes.Builder expectedAttrBuilders = Attributes.newBuilder(); expectedAttrBuilders.setAttribute(ResourceConstants.HOST_ID, "i-1234567890abcdef0"); expectedAttrBuilders.setAttribute(ResourceConstants.CLOUD_ZONE, "us-west-2b"); @@ -135,7 +137,7 @@ public void badJson() { any(urlPathEqualTo("/latest/dynamic/instance-identity/document")) .willReturn(okJson("I'm not JSON"))); - Attributes attributes = populator.createAttributes(); + Attributes attributes = populator.getAttributes(); assertThat(attributes.isEmpty()).isTrue(); verify( @@ -145,4 +147,11 @@ public void badJson() { getRequestedFor(urlEqualTo("/latest/dynamic/instance-identity/document")) .withoutHeader("X-aws-ec2-metadata-token")); } + + @Test + public void inServiceLoader() { + // No practical way to test the attributes themselves so at least check the service loader picks + // it up. + assertThat(ServiceLoader.load(ResourceProvider.class)).anyMatch(Ec2Resource.class::isInstance); + } } diff --git a/sdk_extensions/aws_v1_support/src/test/java/io/opentelemetry/sdk/extensions/trace/aws/resource/EcsResourceTest.java b/sdk_extensions/aws_v1_support/src/test/java/io/opentelemetry/sdk/extensions/trace/aws/resource/EcsResourceTest.java index 88d2436b59e..b933c1c3197 100644 --- a/sdk_extensions/aws_v1_support/src/test/java/io/opentelemetry/sdk/extensions/trace/aws/resource/EcsResourceTest.java +++ b/sdk_extensions/aws_v1_support/src/test/java/io/opentelemetry/sdk/extensions/trace/aws/resource/EcsResourceTest.java @@ -22,10 +22,12 @@ import io.opentelemetry.common.Attributes; import io.opentelemetry.sdk.resources.ResourceConstants; +import io.opentelemetry.sdk.resources.ResourceProvider; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.HashMap; import java.util.Map; +import java.util.ServiceLoader; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; @@ -44,7 +46,7 @@ void testCreateAttributes() throws UnknownHostException { Map mockSysEnv = new HashMap<>(); mockSysEnv.put(ECS_METADATA_KEY_V3, "ecs_metadata_v3_uri"); EcsResource populator = new EcsResource(mockSysEnv, mockDockerHelper); - Attributes attributes = populator.createAttributes(); + Attributes attributes = populator.getAttributes(); assertThat(attributes) .isEqualTo( Attributes.of( @@ -60,7 +62,7 @@ void testNotOnEcs() { mockSysEnv.put(ECS_METADATA_KEY_V3, ""); mockSysEnv.put(ECS_METADATA_KEY_V4, ""); EcsResource populator = new EcsResource(mockSysEnv, mockDockerHelper); - Attributes attributes = populator.createAttributes(); + Attributes attributes = populator.getAttributes(); assertThat(attributes.isEmpty()).isTrue(); } @@ -70,11 +72,18 @@ void testContainerIdMissing() throws UnknownHostException { Map mockSysEnv = new HashMap<>(); mockSysEnv.put(ECS_METADATA_KEY_V4, "ecs_metadata_v4_uri"); EcsResource populator = new EcsResource(mockSysEnv, mockDockerHelper); - Attributes attributes = populator.createAttributes(); + Attributes attributes = populator.getAttributes(); assertThat(attributes) .isEqualTo( Attributes.of( ResourceConstants.CONTAINER_NAME, stringAttributeValue(InetAddress.getLocalHost().getHostName()))); } + + @Test + void inServiceLoader() { + // No practical way to test the attributes themselves so at least check the service loader picks + // it up. + assertThat(ServiceLoader.load(ResourceProvider.class)).anyMatch(EcsResource.class::isInstance); + } }