Move aws resource detectors from opentelemetry-java (#433)

This commit is contained in:
jack-berg 2022-09-12 22:02:35 -05:00 committed by GitHub
parent 95dc58daf3
commit 18be4f3d67
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 2001 additions and 1 deletions

View File

@ -10,6 +10,8 @@
# NOTE when adding/updating one of the component names, don't forget to update the associated
# `comp:*` labels
components:
aws-resources:
- willarmiros
aws-xray:
- willarmiros
consistent-sampling:

9
aws-resources/README.md Normal file
View File

@ -0,0 +1,9 @@
# OpenTelemetry AWS Resource Support
This module contains AWS resource detectors including Beanstalk, EC2, ECS, EKS, and Lambda.
## Component owners
- [William Armiros](https://github.com/willarmiros), AWS
Learn more about component owners in [component_owners.yml](../.github/component_owners.yml).

View File

@ -0,0 +1,28 @@
plugins {
id("otel.java-conventions")
// TODO: uncomment after 1.18.0 release
// id("otel.publish-conventions")
}
description = "OpenTelemetry AWS Resources Support"
dependencies {
api("io.opentelemetry:opentelemetry-api")
api("io.opentelemetry:opentelemetry-sdk")
implementation("io.opentelemetry:opentelemetry-semconv")
compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure")
implementation("com.fasterxml.jackson.core:jackson-core")
implementation("com.squareup.okhttp3:okhttp")
testImplementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure")
testImplementation("io.opentelemetry:opentelemetry-sdk-testing")
testImplementation("com.linecorp.armeria:armeria-junit5")
testRuntimeOnly("org.bouncycastle:bcpkix-jdk15on")
testImplementation("com.google.guava:guava")
testImplementation("org.skyscreamer:jsonassert")
}

View File

@ -0,0 +1,2 @@
# TODO: uncomment when ready to mark as stable
# otel.stable=true

View File

@ -0,0 +1,95 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.contrib.aws.resource;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* A factory for a {@link Resource} which provides information about the current EC2 instance if
* running on AWS Elastic Beanstalk.
*/
public final class BeanstalkResource {
private static final Logger logger = Logger.getLogger(BeanstalkResource.class.getName());
private static final String DEVELOPMENT_ID = "deployment_id";
private static final String VERSION_LABEL = "version_label";
private static final String ENVIRONMENT_NAME = "environment_name";
private static final String BEANSTALK_CONF_PATH = "/var/elasticbeanstalk/xray/environment.conf";
private static final JsonFactory JSON_FACTORY = new JsonFactory();
private static final Resource INSTANCE = buildResource();
/**
* Returns a factory for a {@link Resource} which provides information about the current EC2
* instance if running on AWS Elastic Beanstalk.
*/
public static Resource get() {
return INSTANCE;
}
private static Resource buildResource() {
return buildResource(BEANSTALK_CONF_PATH);
}
// Visible for testing
static Resource buildResource(String configPath) {
File configFile = new File(configPath);
if (!configFile.exists()) {
return Resource.empty();
}
AttributesBuilder attrBuilders = Attributes.builder();
try (JsonParser parser = JSON_FACTORY.createParser(configFile)) {
parser.nextToken();
if (!parser.isExpectedStartObjectToken()) {
logger.log(Level.WARNING, "Invalid Beanstalk config: ", configPath);
return Resource.create(attrBuilders.build(), ResourceAttributes.SCHEMA_URL);
}
while (parser.nextToken() != JsonToken.END_OBJECT) {
parser.nextValue();
String value = parser.getText();
switch (parser.getCurrentName()) {
case DEVELOPMENT_ID:
attrBuilders.put(ResourceAttributes.SERVICE_INSTANCE_ID, value);
break;
case VERSION_LABEL:
attrBuilders.put(ResourceAttributes.SERVICE_VERSION, value);
break;
case ENVIRONMENT_NAME:
attrBuilders.put(ResourceAttributes.SERVICE_NAMESPACE, value);
break;
default:
parser.skipChildren();
}
}
} catch (IOException e) {
logger.log(Level.WARNING, "Could not parse Beanstalk config.", e);
return Resource.empty();
}
attrBuilders.put(ResourceAttributes.CLOUD_PROVIDER, ResourceAttributes.CloudProviderValues.AWS);
attrBuilders.put(
ResourceAttributes.CLOUD_PLATFORM,
ResourceAttributes.CloudPlatformValues.AWS_ELASTIC_BEANSTALK);
return Resource.create(attrBuilders.build(), ResourceAttributes.SCHEMA_URL);
}
private BeanstalkResource() {}
}

View File

@ -0,0 +1,18 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.contrib.aws.resource;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider;
import io.opentelemetry.sdk.resources.Resource;
/** {@link ResourceProvider} for automatically configuring {@link BeanstalkResource}. */
public final class BeanstalkResourceProvider implements ResourceProvider {
@Override
public Resource createResource(ConfigProperties config) {
return BeanstalkResource.get();
}
}

View File

@ -0,0 +1,54 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.contrib.aws.resource;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
class DockerHelper {
private static final Logger logger = Logger.getLogger(DockerHelper.class.getName());
private static final int CONTAINER_ID_LENGTH = 64;
private static final String DEFAULT_CGROUP_PATH = "/proc/self/cgroup";
private final String cgroupPath;
DockerHelper() {
this(DEFAULT_CGROUP_PATH);
}
// Visible for testing
DockerHelper(String cgroupPath) {
this.cgroupPath = cgroupPath;
}
/**
* Get docker container id from local cgroup file.
*
* @return docker container ID. Empty string if it can`t be found.
*/
@SuppressWarnings("DefaultCharset")
public String getContainerId() {
try (BufferedReader br = new BufferedReader(new FileReader(cgroupPath))) {
String line;
while ((line = br.readLine()) != null) {
if (line.length() > CONTAINER_ID_LENGTH) {
return line.substring(line.length() - CONTAINER_ID_LENGTH);
}
}
} catch (FileNotFoundException e) {
logger.log(Level.WARNING, "Failed to read container id, cgroup file does not exist.");
} catch (IOException e) {
logger.log(Level.WARNING, "Unable to read container id: " + e.getMessage());
}
return "";
}
}

View File

@ -0,0 +1,155 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.contrib.aws.resource;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* A factory for a {@link Resource} which provides information about the current EC2 instance if
* running on AWS EC2.
*/
public final class Ec2Resource {
private static final Logger logger = Logger.getLogger(Ec2Resource.class.getName());
private static final JsonFactory JSON_FACTORY = new JsonFactory();
private static final String DEFAULT_IMDS_ENDPOINT = "169.254.169.254";
private static final Resource INSTANCE = buildResource();
/**
* Returns a @link Resource} which provides information about the current EC2 instance if running
* on AWS EC2.
*/
public static Resource get() {
return INSTANCE;
}
private static Resource buildResource() {
// This property 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.
return buildResource(
System.getProperty("otel.aws.imds.endpointOverride", DEFAULT_IMDS_ENDPOINT));
}
// Visible for testing
static Resource buildResource(String endpoint) {
String urlBase = "http://" + endpoint;
URL identityDocumentUrl;
URL hostnameUrl;
URL tokenUrl;
try {
identityDocumentUrl = new URL(urlBase + "/latest/dynamic/instance-identity/document");
hostnameUrl = new URL(urlBase + "/latest/meta-data/hostname");
tokenUrl = new URL(urlBase + "/latest/api/token");
} catch (MalformedURLException e) {
// Can only happen when overriding the endpoint in testing so just throw.
throw new IllegalArgumentException("Illegal endpoint: " + endpoint, e);
}
String token = fetchToken(tokenUrl);
// If token is empty, either IMDSv2 isn't enabled or an unexpected failure happened. We can
// still get data if IMDSv1 is enabled.
String identity = fetchIdentity(identityDocumentUrl, token);
if (identity.isEmpty()) {
// If no identity document, assume we are not actually running on EC2.
return Resource.empty();
}
String hostname = fetchHostname(hostnameUrl, token);
AttributesBuilder attrBuilders = Attributes.builder();
attrBuilders.put(ResourceAttributes.CLOUD_PROVIDER, ResourceAttributes.CloudProviderValues.AWS);
attrBuilders.put(
ResourceAttributes.CLOUD_PLATFORM, ResourceAttributes.CloudPlatformValues.AWS_EC2);
try (JsonParser parser = JSON_FACTORY.createParser(identity)) {
parser.nextToken();
if (!parser.isExpectedStartObjectToken()) {
throw new IOException("Invalid JSON:" + identity);
}
while (parser.nextToken() != JsonToken.END_OBJECT) {
String value = parser.nextTextValue();
switch (parser.getCurrentName()) {
case "instanceId":
attrBuilders.put(ResourceAttributes.HOST_ID, value);
break;
case "availabilityZone":
attrBuilders.put(ResourceAttributes.CLOUD_AVAILABILITY_ZONE, value);
break;
case "instanceType":
attrBuilders.put(ResourceAttributes.HOST_TYPE, value);
break;
case "imageId":
attrBuilders.put(ResourceAttributes.HOST_IMAGE_ID, value);
break;
case "accountId":
attrBuilders.put(ResourceAttributes.CLOUD_ACCOUNT_ID, value);
break;
case "region":
attrBuilders.put(ResourceAttributes.CLOUD_REGION, value);
break;
default:
parser.skipChildren();
}
}
} catch (IOException e) {
logger.log(Level.WARNING, "Could not parse identity document, resource not filled.", e);
return Resource.empty();
}
attrBuilders.put(ResourceAttributes.HOST_NAME, hostname);
return Resource.create(attrBuilders.build(), ResourceAttributes.SCHEMA_URL);
}
private static String fetchToken(URL tokenUrl) {
return fetchString("PUT", tokenUrl, "", /* includeTtl= */ true);
}
private static String fetchIdentity(URL identityDocumentUrl, String token) {
return fetchString("GET", identityDocumentUrl, token, /* includeTtl= */ false);
}
private static String fetchHostname(URL hostnameUrl, String token) {
return fetchString("GET", hostnameUrl, token, /* includeTtl= */ false);
}
// Generic HTTP fetch function for IMDS.
private static String fetchString(String httpMethod, URL url, String token, boolean includeTtl) {
SimpleHttpClient client = new SimpleHttpClient();
Map<String, String> headers = new HashMap<>();
if (includeTtl) {
headers.put("X-aws-ec2-metadata-token-ttl-seconds", "60");
}
if (!token.isEmpty()) {
headers.put("X-aws-ec2-metadata-token", token);
}
return client.fetchString(httpMethod, url.toString(), headers, null);
}
private Ec2Resource() {}
}

View File

@ -0,0 +1,18 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.contrib.aws.resource;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider;
import io.opentelemetry.sdk.resources.Resource;
/** {@link ResourceProvider} for automatically configuring {@link Ec2Resource}. */
public final class Ec2ResourceProvider implements ResourceProvider {
@Override
public Resource createResource(ConfigProperties config) {
return Ec2Resource.get();
}
}

View File

@ -0,0 +1,266 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.contrib.aws.resource;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
import java.io.IOException;
import java.util.Collections;
import java.util.Map;
import java.util.Optional;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.Nullable;
/**
* A factory for a {@link Resource} which provides information about the current ECS container if
* running on AWS ECS.
*/
public final class EcsResource {
private static final Logger logger = Logger.getLogger(EcsResource.class.getName());
private static final JsonFactory JSON_FACTORY = new JsonFactory();
private static final String ECS_METADATA_KEY_V4 = "ECS_CONTAINER_METADATA_URI_V4";
private static final String ECS_METADATA_KEY_V3 = "ECS_CONTAINER_METADATA_URI";
private static final Resource INSTANCE = buildResource();
/**
* Returns a factory for a {@link Resource} which provides information about the current ECS
* container if running on AWS ECS.
*/
public static Resource get() {
return INSTANCE;
}
private static Resource buildResource() {
return buildResource(System.getenv(), new SimpleHttpClient());
}
// Visible for testing
static Resource buildResource(Map<String, String> sysEnv, SimpleHttpClient httpClient) {
// Note: If V4 is set V3 is set as well, so check V4 first.
String ecsMetadataUrl =
sysEnv.getOrDefault(ECS_METADATA_KEY_V4, sysEnv.getOrDefault(ECS_METADATA_KEY_V3, ""));
if (!ecsMetadataUrl.isEmpty()) {
AttributesBuilder attrBuilders = Attributes.builder();
fetchMetadata(httpClient, ecsMetadataUrl, attrBuilders);
// For TaskARN, Family, Revision.
// May put the same attribute twice but that shouldn't matter.
fetchMetadata(httpClient, ecsMetadataUrl + "/task", attrBuilders);
return Resource.create(attrBuilders.build(), ResourceAttributes.SCHEMA_URL);
}
// Not running on ECS
return Resource.empty();
}
static void fetchMetadata(
SimpleHttpClient httpClient, String url, AttributesBuilder attrBuilders) {
String json = httpClient.fetchString("GET", url, Collections.emptyMap(), null);
if (json.isEmpty()) {
return;
}
attrBuilders.put(ResourceAttributes.CLOUD_PROVIDER, ResourceAttributes.CloudProviderValues.AWS);
attrBuilders.put(
ResourceAttributes.CLOUD_PLATFORM, ResourceAttributes.CloudPlatformValues.AWS_ECS);
try (JsonParser parser = JSON_FACTORY.createParser(json)) {
parser.nextToken();
LogArnBuilder logArnBuilder = new LogArnBuilder();
parseResponse(parser, attrBuilders, logArnBuilder);
logArnBuilder
.getLogGroupArn()
.ifPresent(
logGroupArn -> {
attrBuilders.put(
ResourceAttributes.AWS_LOG_GROUP_ARNS, Collections.singletonList(logGroupArn));
});
logArnBuilder
.getLogStreamArn()
.ifPresent(
logStreamArn -> {
attrBuilders.put(
ResourceAttributes.AWS_LOG_STREAM_ARNS,
Collections.singletonList(logStreamArn));
});
} catch (IOException e) {
logger.log(Level.WARNING, "Can't get ECS metadata", e);
}
}
static void parseResponse(
JsonParser parser, AttributesBuilder attrBuilders, LogArnBuilder logArnBuilder)
throws IOException {
if (!parser.isExpectedStartObjectToken()) {
logger.log(Level.WARNING, "Couldn't parse ECS metadata, invalid JSON");
return;
}
while (parser.nextToken() != JsonToken.END_OBJECT) {
String value = parser.nextTextValue();
switch (parser.getCurrentName()) {
case "DockerId":
attrBuilders.put(ResourceAttributes.CONTAINER_ID, value);
break;
case "DockerName":
attrBuilders.put(ResourceAttributes.CONTAINER_NAME, value);
break;
case "ContainerARN":
attrBuilders.put(ResourceAttributes.AWS_ECS_CONTAINER_ARN, value);
logArnBuilder.setContainerArn(value);
break;
case "Image":
DockerImage parsedImage = DockerImage.parse(value);
if (parsedImage != null) {
attrBuilders.put(ResourceAttributes.CONTAINER_IMAGE_NAME, parsedImage.getRepository());
attrBuilders.put(ResourceAttributes.CONTAINER_IMAGE_TAG, parsedImage.getTag());
}
break;
case "ImageID":
attrBuilders.put("aws.ecs.container.image.id", value);
break;
case "LogOptions":
// Recursively parse LogOptions
parseResponse(parser, attrBuilders, logArnBuilder);
break;
case "awslogs-group":
attrBuilders.put(ResourceAttributes.AWS_LOG_GROUP_NAMES, value);
logArnBuilder.setLogGroupName(value);
break;
case "awslogs-stream":
attrBuilders.put(ResourceAttributes.AWS_LOG_STREAM_NAMES, value);
logArnBuilder.setLogStreamName(value);
break;
case "awslogs-region":
logArnBuilder.setRegion(value);
break;
case "TaskARN":
attrBuilders.put(ResourceAttributes.AWS_ECS_TASK_ARN, value);
break;
case "LaunchType":
attrBuilders.put(ResourceAttributes.AWS_ECS_LAUNCHTYPE, value.toLowerCase());
break;
case "Family":
attrBuilders.put(ResourceAttributes.AWS_ECS_TASK_FAMILY, value);
break;
case "Revision":
attrBuilders.put(ResourceAttributes.AWS_ECS_TASK_REVISION, value);
break;
default:
parser.skipChildren();
break;
}
}
}
private EcsResource() {}
/**
* This builder can piece together the ARN of a log group or a log stream from region, account,
* group name and stream name as the ARN isn't part of the ECS metadata.
*
* <p>If we just set AWS_LOG_GROUP_NAMES then the CloudWatch X-Ray traces view displays "An error
* occurred fetching your data". That's why it's important we set the ARN.
*/
private static class LogArnBuilder {
@Nullable String region;
@Nullable String account;
@Nullable String logGroupName;
@Nullable String logStreamName;
void setRegion(@Nullable String region) {
this.region = region;
}
void setLogGroupName(@Nullable String logGroupName) {
this.logGroupName = logGroupName;
}
void setLogStreamName(@Nullable String logStreamName) {
this.logStreamName = logStreamName;
}
void setContainerArn(@Nullable String containerArn) {
if (containerArn != null) {
account = containerArn.split(":")[4];
}
}
Optional<String> getLogGroupArn() {
if (region == null || account == null || logGroupName == null) {
return Optional.empty();
}
return Optional.of("arn:aws:logs:" + region + ":" + account + ":log-group:" + logGroupName);
}
Optional<String> getLogStreamArn() {
if (region == null || account == null || logGroupName == null || logStreamName == null) {
return Optional.empty();
}
return Optional.of(
"arn:aws:logs:"
+ region
+ ":"
+ account
+ ":log-group:"
+ logGroupName
+ ":log-stream:"
+ logStreamName);
}
}
/** This can parse a Docker image name into its parts: repository, tag and sha256. */
private static class DockerImage {
private static final Pattern imagePattern =
Pattern.compile(
"^(?<repository>([^/\\s]+/)?([^:\\s]+))(:(?<tag>[^@\\s]+))?(@sha256:(?<sha256>\\d+))?$");
final String repository;
final String tag;
private DockerImage(String repository, String tag) {
this.repository = repository;
this.tag = tag;
}
String getRepository() {
return repository;
}
String getTag() {
return tag;
}
@Nullable
static DockerImage parse(@Nullable String image) {
if (image == null || image.isEmpty()) {
return null;
}
Matcher matcher = imagePattern.matcher(image);
if (!matcher.matches()) {
logger.log(Level.WARNING, "Couldn't parse image '" + image + "'");
return null;
}
String repository = matcher.group("repository");
String tag = matcher.group("tag");
if (tag == null || tag.isEmpty()) {
tag = "latest";
}
return new DockerImage(repository, tag);
}
}
}

View File

@ -0,0 +1,18 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.contrib.aws.resource;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider;
import io.opentelemetry.sdk.resources.Resource;
/** {@link ResourceProvider} for automatically configuring {@link EcsResource}. */
public final class EcsResourceProvider implements ResourceProvider {
@Override
public Resource createResource(ConfigProperties config) {
return EcsResource.get();
}
}

View File

@ -0,0 +1,159 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.contrib.aws.resource;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* A factory for a {@link Resource} which provides information about the current ECS container if
* running on AWS EKS.
*/
public final class EksResource {
private static final Logger logger = Logger.getLogger(EksResource.class.getName());
private static final JsonFactory JSON_FACTORY = new JsonFactory();
static final String K8S_SVC_URL = "https://kubernetes.default.svc";
static final String AUTH_CONFIGMAP_PATH = "/api/v1/namespaces/kube-system/configmaps/aws-auth";
static final String CW_CONFIGMAP_PATH =
"/api/v1/namespaces/amazon-cloudwatch/configmaps/cluster-info";
private static final String K8S_TOKEN_PATH =
"/var/run/secrets/kubernetes.io/serviceaccount/token";
private static final String K8S_CERT_PATH =
"/var/run/secrets/kubernetes.io/serviceaccount/ca.crt";
private static final Resource INSTANCE = buildResource();
/**
* Returns a factory for a {@link Resource} which provides information about the current ECS
* container if running on AWS EKS.
*/
public static Resource get() {
return INSTANCE;
}
private static Resource buildResource() {
return buildResource(new SimpleHttpClient(), new DockerHelper(), K8S_TOKEN_PATH, K8S_CERT_PATH);
}
// Visible for testing
static Resource buildResource(
SimpleHttpClient httpClient,
DockerHelper dockerHelper,
String k8sTokenPath,
String k8sKeystorePath) {
if (!isEks(k8sTokenPath, k8sKeystorePath, httpClient)) {
return Resource.empty();
}
AttributesBuilder attrBuilders = Attributes.builder();
attrBuilders.put(ResourceAttributes.CLOUD_PROVIDER, ResourceAttributes.CloudProviderValues.AWS);
attrBuilders.put(
ResourceAttributes.CLOUD_PLATFORM, ResourceAttributes.CloudPlatformValues.AWS_EKS);
String clusterName = getClusterName(httpClient);
if (clusterName != null && !clusterName.isEmpty()) {
attrBuilders.put(ResourceAttributes.K8S_CLUSTER_NAME, clusterName);
}
String containerId = dockerHelper.getContainerId();
if (containerId != null && !containerId.isEmpty()) {
attrBuilders.put(ResourceAttributes.CONTAINER_ID, containerId);
}
return Resource.create(attrBuilders.build(), ResourceAttributes.SCHEMA_URL);
}
private static boolean isEks(
String k8sTokenPath, String k8sKeystorePath, SimpleHttpClient httpClient) {
if (!isK8s(k8sTokenPath, k8sKeystorePath)) {
logger.log(Level.FINE, "Not running on k8s.");
return false;
}
Map<String, String> requestProperties = new HashMap<>();
requestProperties.put("Authorization", getK8sCredHeader());
String awsAuth =
httpClient.fetchString(
"GET", K8S_SVC_URL + AUTH_CONFIGMAP_PATH, requestProperties, K8S_CERT_PATH);
return awsAuth != null && !awsAuth.isEmpty();
}
private static boolean isK8s(String k8sTokenPath, String k8sKeystorePath) {
File k8sTokeyFile = new File(k8sTokenPath);
File k8sKeystoreFile = new File(k8sKeystorePath);
return k8sTokeyFile.exists() && k8sKeystoreFile.exists();
}
private static String getClusterName(SimpleHttpClient httpClient) {
Map<String, String> requestProperties = new HashMap<>();
requestProperties.put("Authorization", getK8sCredHeader());
String json =
httpClient.fetchString(
"GET", K8S_SVC_URL + CW_CONFIGMAP_PATH, requestProperties, K8S_CERT_PATH);
try (JsonParser parser = JSON_FACTORY.createParser(json)) {
parser.nextToken();
if (!parser.isExpectedStartObjectToken()) {
throw new IOException("Invalid JSON:" + json);
}
while (parser.nextToken() != JsonToken.END_OBJECT) {
parser.nextToken();
if (!parser.getCurrentName().equals("data")) {
parser.skipChildren();
continue;
}
if (!parser.isExpectedStartObjectToken()) {
throw new IOException("Invalid JSON:" + json);
}
while (parser.nextToken() != JsonToken.END_OBJECT) {
String value = parser.nextTextValue();
if (!parser.getCurrentName().equals("cluster.name")) {
parser.skipChildren();
continue;
}
return value;
}
}
} catch (IOException e) {
logger.log(Level.WARNING, "Can't get cluster name on EKS.", e);
}
return "";
}
private static String getK8sCredHeader() {
try {
String content =
new String(Files.readAllBytes(Paths.get(K8S_TOKEN_PATH)), StandardCharsets.UTF_8);
return "Bearer " + content;
} catch (IOException e) {
logger.log(Level.WARNING, "Unable to load K8s client token.", e);
}
return "";
}
private EksResource() {}
}

View File

@ -0,0 +1,18 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.contrib.aws.resource;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider;
import io.opentelemetry.sdk.resources.Resource;
/** {@link ResourceProvider} for automatically configuring {@link EksResource}. */
public final class EksResourceProvider implements ResourceProvider {
@Override
public Resource createResource(ConfigProperties config) {
return EksResource.get();
}
}

View File

@ -0,0 +1,66 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.contrib.aws.resource;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
import java.util.Map;
import java.util.stream.Stream;
/** A factory for a {@link Resource} which provides information about the AWS Lambda function. */
public final class LambdaResource {
private static final Resource INSTANCE = buildResource();
/**
* Returns a factory for a {@link Resource} which provides information about the AWS Lambda
* function.
*/
public static Resource get() {
return INSTANCE;
}
private static Resource buildResource() {
return buildResource(System.getenv());
}
// Visible for testing
static Resource buildResource(Map<String, String> environmentVariables) {
String region = environmentVariables.getOrDefault("AWS_REGION", "");
String functionName = environmentVariables.getOrDefault("AWS_LAMBDA_FUNCTION_NAME", "");
String functionVersion = environmentVariables.getOrDefault("AWS_LAMBDA_FUNCTION_VERSION", "");
if (!isLambda(functionName, functionVersion)) {
return Resource.empty();
}
AttributesBuilder builder =
Attributes.builder()
.put(ResourceAttributes.CLOUD_PROVIDER, ResourceAttributes.CloudProviderValues.AWS);
builder.put(
ResourceAttributes.CLOUD_PLATFORM, ResourceAttributes.CloudPlatformValues.AWS_LAMBDA);
if (!region.isEmpty()) {
builder.put(ResourceAttributes.CLOUD_REGION, region);
}
if (!functionName.isEmpty()) {
builder.put(ResourceAttributes.FAAS_NAME, functionName);
}
if (!functionVersion.isEmpty()) {
builder.put(ResourceAttributes.FAAS_VERSION, functionVersion);
}
return Resource.create(builder.build(), ResourceAttributes.SCHEMA_URL);
}
private static boolean isLambda(String... envVariables) {
return Stream.of(envVariables).anyMatch(v -> !v.isEmpty());
}
private LambdaResource() {}
}

View File

@ -0,0 +1,18 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.contrib.aws.resource;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider;
import io.opentelemetry.sdk.resources.Resource;
/** {@link ResourceProvider} for automatically configuring {@link LambdaResource}. */
public final class LambdaResourceProvider implements ResourceProvider {
@Override
public Resource createResource(ConfigProperties config) {
return LambdaResource.get();
}
}

View File

@ -0,0 +1,145 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.contrib.aws.resource;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.time.Duration;
import java.util.Collection;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
/** A simple HTTP client based on OkHttp. Not meant for high throughput. */
final class SimpleHttpClient {
private static final Logger logger = Logger.getLogger(SimpleHttpClient.class.getName());
private static final Duration TIMEOUT = Duration.ofSeconds(2);
private static final RequestBody EMPTY_BODY = RequestBody.create(new byte[0]);
/** Fetch a string from a remote server. */
public String fetchString(
String httpMethod, String urlStr, Map<String, String> headers, @Nullable String certPath) {
OkHttpClient.Builder clientBuilder =
new OkHttpClient.Builder()
.callTimeout(TIMEOUT)
.connectTimeout(TIMEOUT)
.readTimeout(TIMEOUT);
if (urlStr.startsWith("https") && certPath != null) {
KeyStore keyStore = getKeystoreForTrustedCert(certPath);
X509TrustManager trustManager = buildTrustManager(keyStore);
SSLSocketFactory socketFactory = buildSslSocketFactory(trustManager);
if (socketFactory != null) {
clientBuilder.sslSocketFactory(socketFactory, trustManager);
}
}
OkHttpClient client = clientBuilder.build();
// AWS incorrectly uses PUT despite having no request body, OkHttp will only allow us to send
// GET with null body or PUT with empty string body
RequestBody requestBody = null;
if (httpMethod.equals("PUT")) {
requestBody = EMPTY_BODY;
}
Request.Builder requestBuilder =
new Request.Builder().url(urlStr).method(httpMethod, requestBody);
headers.forEach(requestBuilder::addHeader);
try (Response response = client.newCall(requestBuilder.build()).execute()) {
int responseCode = response.code();
if (responseCode != 200) {
logger.log(
Level.FINE,
"Error response from "
+ urlStr
+ " code ("
+ responseCode
+ ") text "
+ response.message());
return "";
}
ResponseBody body = response.body();
return body != null ? body.string() : "";
} catch (IOException e) {
logger.log(Level.FINE, "SimpleHttpClient fetch string failed.", e);
}
return "";
}
@Nullable
private static X509TrustManager buildTrustManager(@Nullable KeyStore keyStore) {
if (keyStore == null) {
return null;
}
try {
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
return (X509TrustManager) tmf.getTrustManagers()[0];
} catch (Exception e) {
logger.log(Level.WARNING, "Build SslSocketFactory for K8s restful client exception.", e);
return null;
}
}
@Nullable
private static SSLSocketFactory buildSslSocketFactory(@Nullable TrustManager trustManager) {
if (trustManager == null) {
return null;
}
try {
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, new TrustManager[] {trustManager}, null);
return context.getSocketFactory();
} catch (Exception e) {
logger.log(Level.WARNING, "Build SslSocketFactory for K8s restful client exception.", e);
}
return null;
}
@Nullable
private static KeyStore getKeystoreForTrustedCert(String certPath) {
try (FileInputStream fis = new FileInputStream(certPath)) {
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null, null);
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
Collection<? extends Certificate> certificates = certificateFactory.generateCertificates(fis);
int i = 0;
for (Certificate certificate : certificates) {
trustStore.setCertificateEntry("cert_" + i, certificate);
i++;
}
return trustStore;
} catch (Exception e) {
logger.log(Level.WARNING, "Cannot load KeyStore from " + certPath);
return null;
}
}
}

View File

@ -0,0 +1,13 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
/**
* {@link io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider} implementations for inferring
* resource information for AWS services.
*/
@ParametersAreNonnullByDefault
package io.opentelemetry.contrib.aws.resource;
import javax.annotation.ParametersAreNonnullByDefault;

View File

@ -0,0 +1,5 @@
io.opentelemetry.contrib.aws.resource.BeanstalkResourceProvider
io.opentelemetry.contrib.aws.resource.Ec2ResourceProvider
io.opentelemetry.contrib.aws.resource.EcsResourceProvider
io.opentelemetry.contrib.aws.resource.EksResourceProvider
io.opentelemetry.contrib.aws.resource.LambdaResourceProvider

View File

@ -0,0 +1,69 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.contrib.aws.resource;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
import com.google.common.base.Charsets;
import com.google.common.io.Files;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
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;
class BeanstalkResourceTest {
@Test
void testCreateAttributes(@TempDir File tempFolder) throws IOException {
File file = new File(tempFolder, "beanstalk.config");
String content =
"{\"noise\": \"noise\", \"deployment_id\":4,\""
+ "version_label\":\"2\",\"environment_name\":\"HttpSubscriber-env\"}";
Files.write(content.getBytes(Charsets.UTF_8), file);
Resource resource = BeanstalkResource.buildResource(file.getPath());
Attributes attributes = resource.getAttributes();
assertThat(attributes)
.containsOnly(
entry(ResourceAttributes.CLOUD_PROVIDER, "aws"),
entry(ResourceAttributes.CLOUD_PLATFORM, "aws_elastic_beanstalk"),
entry(ResourceAttributes.SERVICE_INSTANCE_ID, "4"),
entry(ResourceAttributes.SERVICE_VERSION, "2"),
entry(ResourceAttributes.SERVICE_NAMESPACE, "HttpSubscriber-env"));
assertThat(resource.getSchemaUrl()).isEqualTo(ResourceAttributes.SCHEMA_URL);
}
@Test
void testConfigFileMissing() {
Attributes attributes =
BeanstalkResource.buildResource("a_file_never_existing").getAttributes();
assertThat(attributes).isEmpty();
}
@Test
void testBadConfigFile(@TempDir File tempFolder) throws IOException {
File file = new File(tempFolder, "beanstalk.config");
String content =
"\"deployment_id\":4,\"version_label\":\"2\",\""
+ "environment_name\":\"HttpSubscriber-env\"}";
Files.write(content.getBytes(Charsets.UTF_8), file);
Attributes attributes = BeanstalkResource.buildResource(file.getPath()).getAttributes();
assertThat(attributes).isEmpty();
}
@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(BeanstalkResourceProvider.class::isInstance);
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.contrib.aws.resource;
import static org.assertj.core.api.Assertions.assertThat;
import com.google.common.base.Charsets;
import com.google.common.io.Files;
import java.io.File;
import java.io.IOException;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
class DockerHelperTest {
@Test
void testCgroupFileMissing() {
DockerHelper dockerHelper = new DockerHelper("a_file_never_existing");
assertThat(dockerHelper.getContainerId()).isEmpty();
}
@Test
void testContainerIdMissing(@TempDir File tempFolder) throws IOException {
File file = new File(tempFolder, "no_container_id");
String content = "13:pids:/\n" + "12:hugetlb:/\n" + "11:net_prio:/";
Files.write(content.getBytes(Charsets.UTF_8), file);
DockerHelper dockerHelper = new DockerHelper(file.getPath());
assertThat(dockerHelper.getContainerId()).isEmpty();
}
@Test
void testGetContainerId(@TempDir File tempFolder) throws IOException {
File file = new File(tempFolder, "cgroup");
String expected = "386a1920640799b5bf5a39bd94e489e5159a88677d96ca822ce7c433ff350163";
String content = "dummy\n11:devices:/ecs/bbc36dd0-5ee0-4007-ba96-c590e0b278d2/" + expected;
Files.write(content.getBytes(Charsets.UTF_8), file);
DockerHelper dockerHelper = new DockerHelper(file.getPath());
assertThat(dockerHelper.getContainerId()).isEqualTo(expected);
}
}

View File

@ -0,0 +1,139 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.contrib.aws.resource;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
import com.linecorp.armeria.common.AggregatedHttpRequest;
import com.linecorp.armeria.common.HttpResponse;
import com.linecorp.armeria.common.HttpStatus;
import com.linecorp.armeria.common.MediaType;
import com.linecorp.armeria.testing.junit5.server.mock.MockWebServerExtension;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
import java.util.ServiceLoader;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
class Ec2ResourceTest {
// From https://docs.amazonaws.cn/en_us/AWSEC2/latest/UserGuide/instance-identity-documents.html
private static final String IDENTITY_DOCUMENT =
"{\n"
+ " \"devpayProductCodes\" : null,\n"
+ " \"marketplaceProductCodes\" : [ \"1abc2defghijklm3nopqrs4tu\" ], \n"
+ " \"availabilityZone\" : \"us-west-2b\",\n"
+ " \"privateIp\" : \"10.158.112.84\",\n"
+ " \"version\" : \"2017-09-30\",\n"
+ " \"instanceId\" : \"i-1234567890abcdef0\",\n"
+ " \"billingProducts\" : null,\n"
+ " \"instanceType\" : \"t2.micro\",\n"
+ " \"accountId\" : \"123456789012\",\n"
+ " \"imageId\" : \"ami-5fb8c835\",\n"
+ " \"pendingTime\" : \"2016-11-19T16:32:11Z\",\n"
+ " \"architecture\" : \"x86_64\",\n"
+ " \"kernelId\" : null,\n"
+ " \"ramdiskId\" : null,\n"
+ " \"region\" : \"us-west-2\"\n"
+ "}";
@RegisterExtension public static MockWebServerExtension server = new MockWebServerExtension();
@Test
void imdsv2() {
server.enqueue(HttpResponse.of("token"));
server.enqueue(HttpResponse.of(MediaType.JSON_UTF_8, IDENTITY_DOCUMENT));
server.enqueue(HttpResponse.of("ec2-1-2-3-4"));
Resource resource = Ec2Resource.buildResource("localhost:" + server.httpPort());
assertThat(resource.getSchemaUrl()).isEqualTo(ResourceAttributes.SCHEMA_URL);
Attributes attributes = resource.getAttributes();
assertThat(attributes)
.containsOnly(
entry(ResourceAttributes.CLOUD_PROVIDER, "aws"),
entry(ResourceAttributes.CLOUD_PLATFORM, "aws_ec2"),
entry(ResourceAttributes.HOST_ID, "i-1234567890abcdef0"),
entry(ResourceAttributes.CLOUD_AVAILABILITY_ZONE, "us-west-2b"),
entry(ResourceAttributes.HOST_TYPE, "t2.micro"),
entry(ResourceAttributes.HOST_IMAGE_ID, "ami-5fb8c835"),
entry(ResourceAttributes.CLOUD_ACCOUNT_ID, "123456789012"),
entry(ResourceAttributes.CLOUD_REGION, "us-west-2"),
entry(ResourceAttributes.HOST_NAME, "ec2-1-2-3-4"));
AggregatedHttpRequest request1 = server.takeRequest().request();
assertThat(request1.path()).isEqualTo("/latest/api/token");
assertThat(request1.headers().get("X-aws-ec2-metadata-token-ttl-seconds")).isEqualTo("60");
AggregatedHttpRequest request2 = server.takeRequest().request();
assertThat(request2.path()).isEqualTo("/latest/dynamic/instance-identity/document");
assertThat(request2.headers().get("X-aws-ec2-metadata-token")).isEqualTo("token");
AggregatedHttpRequest request3 = server.takeRequest().request();
assertThat(request3.path()).isEqualTo("/latest/meta-data/hostname");
assertThat(request3.headers().get("X-aws-ec2-metadata-token")).isEqualTo("token");
}
@Test
void imdsv1() {
server.enqueue(HttpResponse.of(HttpStatus.NOT_FOUND));
server.enqueue(HttpResponse.of(MediaType.JSON_UTF_8, IDENTITY_DOCUMENT));
server.enqueue(HttpResponse.of("ec2-1-2-3-4"));
Resource resource = Ec2Resource.buildResource("localhost:" + server.httpPort());
assertThat(resource.getSchemaUrl()).isEqualTo(ResourceAttributes.SCHEMA_URL);
Attributes attributes = resource.getAttributes();
assertThat(attributes)
.containsOnly(
entry(ResourceAttributes.CLOUD_PROVIDER, "aws"),
entry(ResourceAttributes.CLOUD_PLATFORM, "aws_ec2"),
entry(ResourceAttributes.HOST_ID, "i-1234567890abcdef0"),
entry(ResourceAttributes.CLOUD_AVAILABILITY_ZONE, "us-west-2b"),
entry(ResourceAttributes.HOST_TYPE, "t2.micro"),
entry(ResourceAttributes.HOST_IMAGE_ID, "ami-5fb8c835"),
entry(ResourceAttributes.CLOUD_ACCOUNT_ID, "123456789012"),
entry(ResourceAttributes.CLOUD_REGION, "us-west-2"),
entry(ResourceAttributes.HOST_NAME, "ec2-1-2-3-4"));
AggregatedHttpRequest request1 = server.takeRequest().request();
assertThat(request1.path()).isEqualTo("/latest/api/token");
assertThat(request1.headers().get("X-aws-ec2-metadata-token-ttl-seconds")).isEqualTo("60");
AggregatedHttpRequest request2 = server.takeRequest().request();
assertThat(request2.path()).isEqualTo("/latest/dynamic/instance-identity/document");
assertThat(request2.headers().get("X-aws-ec2-metadata-token")).isNull();
}
@Test
void badJson() {
server.enqueue(HttpResponse.of(HttpStatus.NOT_FOUND));
server.enqueue(HttpResponse.of(MediaType.JSON_UTF_8, "I'm not JSON"));
Attributes attributes =
Ec2Resource.buildResource("localhost:" + server.httpPort()).getAttributes();
assertThat(attributes).isEmpty();
AggregatedHttpRequest request1 = server.takeRequest().request();
assertThat(request1.path()).isEqualTo("/latest/api/token");
assertThat(request1.headers().get("X-aws-ec2-metadata-token-ttl-seconds")).isEqualTo("60");
AggregatedHttpRequest request2 = server.takeRequest().request();
assertThat(request2.path()).isEqualTo("/latest/dynamic/instance-identity/document");
assertThat(request2.headers().get("X-aws-ec2-metadata-token")).isNull();
}
@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(Ec2ResourceProvider.class::isInstance);
}
}

View File

@ -0,0 +1,143 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.contrib.aws.resource;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
import static org.mockito.Mockito.when;
import com.google.common.io.Resources;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
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;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
class EcsResourceTest {
private static final String ECS_METADATA_KEY_V4 = "ECS_CONTAINER_METADATA_URI_V4";
private static final String ECS_METADATA_KEY_V3 = "ECS_CONTAINER_METADATA_URI";
@Mock private SimpleHttpClient mockHttpClient;
@Test
void testCreateAttributesV3() throws IOException {
Map<String, String> mockSysEnv = new HashMap<>();
mockSysEnv.put(ECS_METADATA_KEY_V3, "ecs_metadata_v3_uri");
when(mockHttpClient.fetchString("GET", "ecs_metadata_v3_uri", Collections.emptyMap(), null))
.thenReturn(readResourceJson("ecs-container-metadata-v3.json"));
when(mockHttpClient.fetchString(
"GET", "ecs_metadata_v3_uri/task", Collections.emptyMap(), null))
.thenReturn(readResourceJson("ecs-task-metadata-v3.json"));
Resource resource = EcsResource.buildResource(mockSysEnv, mockHttpClient);
Attributes attributes = resource.getAttributes();
assertThat(resource.getSchemaUrl()).isEqualTo(ResourceAttributes.SCHEMA_URL);
assertThat(attributes)
.containsOnly(
entry(ResourceAttributes.CLOUD_PROVIDER, "aws"),
entry(ResourceAttributes.CLOUD_PLATFORM, "aws_ecs"),
entry(ResourceAttributes.CONTAINER_NAME, "ecs-nginx-5-nginx-curl-ccccb9f49db0dfe0d901"),
entry(
ResourceAttributes.CONTAINER_ID,
"43481a6ce4842eec8fe72fc28500c6b52edcc0917f105b83379f88cac1ff3946"),
entry(ResourceAttributes.CONTAINER_IMAGE_NAME, "nrdlngr/nginx-curl"),
entry(ResourceAttributes.CONTAINER_IMAGE_TAG, "latest"),
entry(
AttributeKey.stringKey("aws.ecs.container.image.id"),
"sha256:2e00ae64383cfc865ba0a2ba37f61b50a120d2d9378559dcd458dc0de47bc165"),
entry(
ResourceAttributes.AWS_ECS_TASK_ARN,
"arn:aws:ecs:us-east-2:012345678910:task/9781c248-0edd-4cdb-9a93-f63cb662a5d3"),
entry(ResourceAttributes.AWS_ECS_TASK_FAMILY, "nginx"),
entry(ResourceAttributes.AWS_ECS_TASK_REVISION, "5"));
}
@Test
void testCreateAttributesV4() throws IOException {
Map<String, String> mockSysEnv = new HashMap<>();
mockSysEnv.put(ECS_METADATA_KEY_V4, "ecs_metadata_v4_uri");
when(mockHttpClient.fetchString("GET", "ecs_metadata_v4_uri", Collections.emptyMap(), null))
.thenReturn(readResourceJson("ecs-container-metadata-v4.json"));
when(mockHttpClient.fetchString(
"GET", "ecs_metadata_v4_uri/task", Collections.emptyMap(), null))
.thenReturn(readResourceJson("ecs-task-metadata-v4.json"));
Resource resource = EcsResource.buildResource(mockSysEnv, mockHttpClient);
Attributes attributes = resource.getAttributes();
assertThat(resource.getSchemaUrl()).isEqualTo(ResourceAttributes.SCHEMA_URL);
assertThat(attributes)
.containsOnly(
entry(ResourceAttributes.CLOUD_PROVIDER, "aws"),
entry(ResourceAttributes.CLOUD_PLATFORM, "aws_ecs"),
entry(ResourceAttributes.CONTAINER_NAME, "ecs-curltest-26-curl-cca48e8dcadd97805600"),
entry(
ResourceAttributes.CONTAINER_ID,
"ea32192c8553fbff06c9340478a2ff089b2bb5646fb718b4ee206641c9086d66"),
entry(
ResourceAttributes.CONTAINER_IMAGE_NAME,
"111122223333.dkr.ecr.us-west-2.amazonaws.com/curltest"),
entry(ResourceAttributes.CONTAINER_IMAGE_TAG, "latest"),
entry(
AttributeKey.stringKey("aws.ecs.container.image.id"),
"sha256:d691691e9652791a60114e67b365688d20d19940dde7c4736ea30e660d8d3553"),
entry(
ResourceAttributes.AWS_ECS_CONTAINER_ARN,
"arn:aws:ecs:us-west-2:111122223333:container/0206b271-b33f-47ab-86c6-a0ba208a70a9"),
entry(
ResourceAttributes.AWS_LOG_GROUP_NAMES, Collections.singletonList("/ecs/metadata")),
entry(
ResourceAttributes.AWS_LOG_GROUP_ARNS,
Collections.singletonList(
"arn:aws:logs:us-west-2:111122223333:log-group:/ecs/metadata")),
entry(
ResourceAttributes.AWS_LOG_STREAM_NAMES,
Collections.singletonList("ecs/curl/8f03e41243824aea923aca126495f665")),
entry(
ResourceAttributes.AWS_LOG_STREAM_ARNS,
Collections.singletonList(
"arn:aws:logs:us-west-2:111122223333:log-group:/ecs/metadata:log-stream:ecs/curl/8f03e41243824aea923aca126495f665")),
entry(
ResourceAttributes.AWS_ECS_TASK_ARN,
"arn:aws:ecs:us-west-2:111122223333:task/default/158d1c8083dd49d6b527399fd6414f5c"),
entry(ResourceAttributes.AWS_ECS_LAUNCHTYPE, "ec2"),
entry(ResourceAttributes.AWS_ECS_TASK_FAMILY, "curltest"),
entry(ResourceAttributes.AWS_ECS_TASK_REVISION, "26"));
}
@Test
void testNotOnEcs() {
Map<String, String> mockSysEnv = new HashMap<>();
mockSysEnv.put(ECS_METADATA_KEY_V3, "");
mockSysEnv.put(ECS_METADATA_KEY_V4, "");
Attributes attributes = EcsResource.buildResource(mockSysEnv, mockHttpClient).getAttributes();
assertThat(attributes).isEmpty();
}
@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(EcsResourceProvider.class::isInstance);
}
String readResourceJson(String resourceName) throws IOException {
return Resources.toString(Resources.getResource(resourceName), StandardCharsets.UTF_8);
}
}

View File

@ -0,0 +1,85 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.contrib.aws.resource;
import static io.opentelemetry.contrib.aws.resource.EksResource.AUTH_CONFIGMAP_PATH;
import static io.opentelemetry.contrib.aws.resource.EksResource.CW_CONFIGMAP_PATH;
import static io.opentelemetry.contrib.aws.resource.EksResource.K8S_SVC_URL;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import com.google.common.base.Charsets;
import com.google.common.io.Files;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
import java.io.File;
import java.io.IOException;
import java.util.ServiceLoader;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.io.TempDir;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
public class EksResourceTest {
@Mock private DockerHelper mockDockerHelper;
@Mock private SimpleHttpClient httpClient;
@Test
void testEks(@TempDir File tempFolder) throws IOException {
File mockK8sTokenFile = new File(tempFolder, "k8sToken");
String token = "token123";
Files.write(token.getBytes(Charsets.UTF_8), mockK8sTokenFile);
File mockK8sKeystoreFile = new File(tempFolder, "k8sCert");
String truststore = "truststore123";
Files.write(truststore.getBytes(Charsets.UTF_8), mockK8sKeystoreFile);
when(httpClient.fetchString(any(), Mockito.eq(K8S_SVC_URL + AUTH_CONFIGMAP_PATH), any(), any()))
.thenReturn("not empty");
when(httpClient.fetchString(any(), Mockito.eq(K8S_SVC_URL + CW_CONFIGMAP_PATH), any(), any()))
.thenReturn("{\"data\":{\"cluster.name\":\"my-cluster\"}}");
when(mockDockerHelper.getContainerId()).thenReturn("0123456789A");
Resource eksResource =
EksResource.buildResource(
httpClient,
mockDockerHelper,
mockK8sTokenFile.getPath(),
mockK8sKeystoreFile.getPath());
Attributes attributes = eksResource.getAttributes();
assertThat(eksResource.getSchemaUrl()).isEqualTo(ResourceAttributes.SCHEMA_URL);
assertThat(attributes)
.containsOnly(
entry(ResourceAttributes.CLOUD_PROVIDER, "aws"),
entry(ResourceAttributes.CLOUD_PLATFORM, "aws_eks"),
entry(ResourceAttributes.K8S_CLUSTER_NAME, "my-cluster"),
entry(ResourceAttributes.CONTAINER_ID, "0123456789A"));
}
@Test
void testNotEks() {
Resource eksResource = EksResource.buildResource(httpClient, mockDockerHelper, "", "");
Attributes attributes = eksResource.getAttributes();
assertThat(attributes).isEmpty();
}
@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(EksResourceProvider.class::isInstance);
}
}

View File

@ -0,0 +1,70 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.contrib.aws.resource;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonMap;
import static org.assertj.core.api.Assertions.entry;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
import java.util.HashMap;
import java.util.Map;
import java.util.ServiceLoader;
import org.junit.jupiter.api.Test;
class LambdaResourceTest {
@Test
void shouldNotCreateResourceForNotLambda() {
Attributes attributes = LambdaResource.buildResource(emptyMap()).getAttributes();
assertThat(attributes).isEmpty();
}
@Test
void shouldAddNonEmptyAttributes() {
Resource resource =
LambdaResource.buildResource(singletonMap("AWS_LAMBDA_FUNCTION_NAME", "my-function"));
Attributes attributes = resource.getAttributes();
assertThat(resource.getSchemaUrl()).isEqualTo(ResourceAttributes.SCHEMA_URL);
assertThat(attributes)
.containsOnly(
entry(ResourceAttributes.CLOUD_PROVIDER, "aws"),
entry(ResourceAttributes.CLOUD_PLATFORM, "aws_lambda"),
entry(ResourceAttributes.FAAS_NAME, "my-function"));
}
@Test
void shouldAddAllAttributes() {
Map<String, String> envVars = new HashMap<>();
envVars.put("AWS_REGION", "us-east-1");
envVars.put("AWS_LAMBDA_FUNCTION_NAME", "my-function");
envVars.put("AWS_LAMBDA_FUNCTION_VERSION", "1.2.3");
Resource resource = LambdaResource.buildResource(envVars);
Attributes attributes = resource.getAttributes();
assertThat(resource.getSchemaUrl()).isEqualTo(ResourceAttributes.SCHEMA_URL);
assertThat(attributes)
.containsOnly(
entry(ResourceAttributes.CLOUD_PROVIDER, "aws"),
entry(ResourceAttributes.CLOUD_PLATFORM, "aws_lambda"),
entry(ResourceAttributes.CLOUD_REGION, "us-east-1"),
entry(ResourceAttributes.FAAS_NAME, "my-function"),
entry(ResourceAttributes.FAAS_VERSION, "1.2.3"));
}
@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(LambdaResourceProvider.class::isInstance);
}
}

View File

@ -0,0 +1,113 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.contrib.aws.resource;
import static org.assertj.core.api.Assertions.assertThat;
import com.google.common.collect.ImmutableMap;
import com.linecorp.armeria.common.AggregatedHttpRequest;
import com.linecorp.armeria.common.HttpResponse;
import com.linecorp.armeria.server.ServerBuilder;
import com.linecorp.armeria.testing.junit5.server.SelfSignedCertificateExtension;
import com.linecorp.armeria.testing.junit5.server.ServerExtension;
import com.linecorp.armeria.testing.junit5.server.mock.MockWebServerExtension;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.api.io.TempDir;
class SimpleHttpClientTest {
@RegisterExtension public static MockWebServerExtension server = new MockWebServerExtension();
@Test
void testFetchString() {
server.enqueue(HttpResponse.of("expected result"));
ImmutableMap<String, String> requestPropertyMap =
ImmutableMap.of("key1", "value1", "key2", "value2");
String urlStr = String.format("http://localhost:%s%s", server.httpPort(), "/path");
SimpleHttpClient httpClient = new SimpleHttpClient();
String result = httpClient.fetchString("GET", urlStr, requestPropertyMap, null);
assertThat(result).isEqualTo("expected result");
AggregatedHttpRequest request1 = server.takeRequest().request();
assertThat(request1.path()).isEqualTo("/path");
assertThat(request1.headers().get("key1")).isEqualTo("value1");
assertThat(request1.headers().get("key2")).isEqualTo("value2");
}
@Test
void testFailedFetchString() {
ImmutableMap<String, String> requestPropertyMap =
ImmutableMap.of("key1", "value1", "key2", "value2");
String urlStr = String.format("http://localhost:%s%s", server.httpPort(), "/path");
SimpleHttpClient httpClient = new SimpleHttpClient();
String result = httpClient.fetchString("GET", urlStr, requestPropertyMap, null);
assertThat(result).isEmpty();
}
static class HttpsServerTest {
@RegisterExtension
@Order(1)
public static SelfSignedCertificateExtension certificate = new SelfSignedCertificateExtension();
@RegisterExtension
@Order(2)
public static ServerExtension server =
new ServerExtension() {
@Override
protected void configure(ServerBuilder sb) {
sb.tls(certificate.certificateFile(), certificate.privateKeyFile());
sb.service("/", (ctx, req) -> HttpResponse.of("Thanks for trusting me"));
}
};
@Test
void goodCert() {
SimpleHttpClient httpClient = new SimpleHttpClient();
String result =
httpClient.fetchString(
"GET",
"https://localhost:" + server.httpsPort() + "/",
Collections.emptyMap(),
certificate.certificateFile().getAbsolutePath());
assertThat(result).isEqualTo("Thanks for trusting me");
}
@Test
void missingCert() {
SimpleHttpClient httpClient = new SimpleHttpClient();
String result =
httpClient.fetchString(
"GET",
"https://localhost:" + server.httpsPort() + "/",
Collections.emptyMap(),
"/foo/bar/bad");
assertThat(result).isEmpty();
}
@Test
void badCert(@TempDir Path tempDir) throws Exception {
Path certFile = tempDir.resolve("test.crt");
Files.write(certFile, "bad cert".getBytes(StandardCharsets.UTF_8));
SimpleHttpClient httpClient = new SimpleHttpClient();
String result =
httpClient.fetchString(
"GET",
"https://localhost:" + server.httpsPort() + "/",
Collections.emptyMap(),
certFile.toString());
assertThat(result).isEmpty();
}
}
}

View File

@ -0,0 +1,31 @@
{
"DockerId": "43481a6ce4842eec8fe72fc28500c6b52edcc0917f105b83379f88cac1ff3946",
"Name": "nginx-curl",
"DockerName": "ecs-nginx-5-nginx-curl-ccccb9f49db0dfe0d901",
"Image": "nrdlngr/nginx-curl",
"ImageID": "sha256:2e00ae64383cfc865ba0a2ba37f61b50a120d2d9378559dcd458dc0de47bc165",
"Labels": {
"com.amazonaws.ecs.cluster": "default",
"com.amazonaws.ecs.container-name": "nginx-curl",
"com.amazonaws.ecs.task-arn": "arn:aws:ecs:us-east-2:012345678910:task/9781c248-0edd-4cdb-9a93-f63cb662a5d3",
"com.amazonaws.ecs.task-definition-family": "nginx",
"com.amazonaws.ecs.task-definition-version": "5"
},
"DesiredStatus": "RUNNING",
"KnownStatus": "RUNNING",
"Limits": {
"CPU": 512,
"Memory": 512
},
"CreatedAt": "2018-02-01T20:55:10.554941919Z",
"StartedAt": "2018-02-01T20:55:11.064236631Z",
"Type": "NORMAL",
"Networks": [
{
"NetworkMode": "awsvpc",
"IPv4Addresses": [
"10.0.2.106"
]
}
]
}

View File

@ -0,0 +1,44 @@
{
"DockerId": "ea32192c8553fbff06c9340478a2ff089b2bb5646fb718b4ee206641c9086d66",
"Name": "curl",
"DockerName": "ecs-curltest-26-curl-cca48e8dcadd97805600",
"Image": "111122223333.dkr.ecr.us-west-2.amazonaws.com/curltest:latest",
"ImageID": "sha256:d691691e9652791a60114e67b365688d20d19940dde7c4736ea30e660d8d3553",
"Labels": {
"com.amazonaws.ecs.cluster": "default",
"com.amazonaws.ecs.container-name": "curl",
"com.amazonaws.ecs.task-arn": "arn:aws:ecs:us-west-2:111122223333:task/default/8f03e41243824aea923aca126495f665",
"com.amazonaws.ecs.task-definition-family": "curltest",
"com.amazonaws.ecs.task-definition-version": "26"
},
"DesiredStatus": "RUNNING",
"KnownStatus": "RUNNING",
"Limits": {
"CPU": 10,
"Memory": 128
},
"CreatedAt": "2020-10-02T00:15:07.620912337Z",
"StartedAt": "2020-10-02T00:15:08.062559351Z",
"Type": "NORMAL",
"LogDriver": "awslogs",
"LogOptions": {
"awslogs-create-group": "true",
"awslogs-group": "/ecs/metadata",
"awslogs-region": "us-west-2",
"awslogs-stream": "ecs/curl/8f03e41243824aea923aca126495f665"
},
"ContainerARN": "arn:aws:ecs:us-west-2:111122223333:container/0206b271-b33f-47ab-86c6-a0ba208a70a9",
"Networks": [
{
"NetworkMode": "awsvpc",
"IPv4Addresses": [
"10.0.2.100"
],
"AttachmentIndex": 0,
"MACAddress": "0e:9e:32:c7:48:85",
"IPv4SubnetCIDRBlock": "10.0.2.0/24",
"PrivateDNSName": "ip-10-0-2-100.us-west-2.compute.internal",
"SubnetGatewayIpv4Address": "10.0.2.1/24"
}
]
}

View File

@ -0,0 +1,75 @@
{
"Cluster": "default",
"TaskARN": "arn:aws:ecs:us-east-2:012345678910:task/9781c248-0edd-4cdb-9a93-f63cb662a5d3",
"Family": "nginx",
"Revision": "5",
"DesiredStatus": "RUNNING",
"KnownStatus": "RUNNING",
"Containers": [
{
"DockerId": "731a0d6a3b4210e2448339bc7015aaa79bfe4fa256384f4102db86ef94cbbc4c",
"Name": "~internal~ecs~pause",
"DockerName": "ecs-nginx-5-internalecspause-acc699c0cbf2d6d11700",
"Image": "amazon/amazon-ecs-pause:0.1.0",
"ImageID": "",
"Labels": {
"com.amazonaws.ecs.cluster": "default",
"com.amazonaws.ecs.container-name": "~internal~ecs~pause",
"com.amazonaws.ecs.task-arn": "arn:aws:ecs:us-east-2:012345678910:task/9781c248-0edd-4cdb-9a93-f63cb662a5d3",
"com.amazonaws.ecs.task-definition-family": "nginx",
"com.amazonaws.ecs.task-definition-version": "5"
},
"DesiredStatus": "RESOURCES_PROVISIONED",
"KnownStatus": "RESOURCES_PROVISIONED",
"Limits": {
"CPU": 0,
"Memory": 0
},
"CreatedAt": "2018-02-01T20:55:08.366329616Z",
"StartedAt": "2018-02-01T20:55:09.058354915Z",
"Type": "CNI_PAUSE",
"Networks": [
{
"NetworkMode": "awsvpc",
"IPv4Addresses": [
"10.0.2.106"
]
}
]
},
{
"DockerId": "43481a6ce4842eec8fe72fc28500c6b52edcc0917f105b83379f88cac1ff3946",
"Name": "nginx-curl",
"DockerName": "ecs-nginx-5-nginx-curl-ccccb9f49db0dfe0d901",
"Image": "nrdlngr/nginx-curl",
"ImageID": "sha256:2e00ae64383cfc865ba0a2ba37f61b50a120d2d9378559dcd458dc0de47bc165",
"Labels": {
"com.amazonaws.ecs.cluster": "default",
"com.amazonaws.ecs.container-name": "nginx-curl",
"com.amazonaws.ecs.task-arn": "arn:aws:ecs:us-east-2:012345678910:task/9781c248-0edd-4cdb-9a93-f63cb662a5d3",
"com.amazonaws.ecs.task-definition-family": "nginx",
"com.amazonaws.ecs.task-definition-version": "5"
},
"DesiredStatus": "RUNNING",
"KnownStatus": "RUNNING",
"Limits": {
"CPU": 512,
"Memory": 512
},
"CreatedAt": "2018-02-01T20:55:10.554941919Z",
"StartedAt": "2018-02-01T20:55:11.064236631Z",
"Type": "NORMAL",
"Networks": [
{
"NetworkMode": "awsvpc",
"IPv4Addresses": [
"10.0.2.106"
]
}
]
}
],
"PullStartedAt": "2018-02-01T20:55:09.372495529Z",
"PullStoppedAt": "2018-02-01T20:55:10.552018345Z",
"AvailabilityZone": "us-east-2b"
}

View File

@ -0,0 +1,94 @@
{
"Cluster": "default",
"TaskARN": "arn:aws:ecs:us-west-2:111122223333:task/default/158d1c8083dd49d6b527399fd6414f5c",
"Family": "curltest",
"Revision": "26",
"DesiredStatus": "RUNNING",
"KnownStatus": "RUNNING",
"PullStartedAt": "2020-10-02T00:43:06.202617438Z",
"PullStoppedAt": "2020-10-02T00:43:06.31288465Z",
"AvailabilityZone": "us-west-2d",
"LaunchType": "EC2",
"Containers": [
{
"DockerId": "598cba581fe3f939459eaba1e071d5c93bb2c49b7d1ba7db6bb19deeb70d8e38",
"Name": "~internal~ecs~pause",
"DockerName": "ecs-curltest-26-internalecspause-e292d586b6f9dade4a00",
"Image": "amazon/amazon-ecs-pause:0.1.0",
"ImageID": "",
"Labels": {
"com.amazonaws.ecs.cluster": "default",
"com.amazonaws.ecs.container-name": "~internal~ecs~pause",
"com.amazonaws.ecs.task-arn": "arn:aws:ecs:us-west-2:111122223333:task/default/158d1c8083dd49d6b527399fd6414f5c",
"com.amazonaws.ecs.task-definition-family": "curltest",
"com.amazonaws.ecs.task-definition-version": "26"
},
"DesiredStatus": "RESOURCES_PROVISIONED",
"KnownStatus": "RESOURCES_PROVISIONED",
"Limits": {
"CPU": 0,
"Memory": 0
},
"CreatedAt": "2020-10-02T00:43:05.602352471Z",
"StartedAt": "2020-10-02T00:43:06.076707576Z",
"Type": "CNI_PAUSE",
"Networks": [
{
"NetworkMode": "awsvpc",
"IPv4Addresses": [
"10.0.2.61"
],
"AttachmentIndex": 0,
"MACAddress": "0e:10:e2:01:bd:91",
"IPv4SubnetCIDRBlock": "10.0.2.0/24",
"PrivateDNSName": "ip-10-0-2-61.us-west-2.compute.internal",
"SubnetGatewayIpv4Address": "10.0.2.1/24"
}
]
},
{
"DockerId": "ee08638adaaf009d78c248913f629e38299471d45fe7dc944d1039077e3424ca",
"Name": "curl",
"DockerName": "ecs-curltest-26-curl-a0e7dba5aca6d8cb2e00",
"Image": "111122223333.dkr.ecr.us-west-2.amazonaws.com/curltest:latest",
"ImageID": "sha256:d691691e9652791a60114e67b365688d20d19940dde7c4736ea30e660d8d3553",
"Labels": {
"com.amazonaws.ecs.cluster": "default",
"com.amazonaws.ecs.container-name": "curl",
"com.amazonaws.ecs.task-arn": "arn:aws:ecs:us-west-2:111122223333:task/default/158d1c8083dd49d6b527399fd6414f5c",
"com.amazonaws.ecs.task-definition-family": "curltest",
"com.amazonaws.ecs.task-definition-version": "26"
},
"DesiredStatus": "RUNNING",
"KnownStatus": "RUNNING",
"Limits": {
"CPU": 10,
"Memory": 128
},
"CreatedAt": "2020-10-02T00:43:06.326590752Z",
"StartedAt": "2020-10-02T00:43:06.767535449Z",
"Type": "NORMAL",
"LogDriver": "awslogs",
"LogOptions": {
"awslogs-create-group": "true",
"awslogs-group": "/ecs/metadata",
"awslogs-region": "us-west-2",
"awslogs-stream": "ecs/curl/158d1c8083dd49d6b527399fd6414f5c"
},
"ContainerARN": "arn:aws:ecs:us-west-2:111122223333:container/abb51bdd-11b4-467f-8f6c-adcfe1fe059d",
"Networks": [
{
"NetworkMode": "awsvpc",
"IPv4Addresses": [
"10.0.2.61"
],
"AttachmentIndex": 0,
"MACAddress": "0e:10:e2:01:bd:91",
"IPv4SubnetCIDRBlock": "10.0.2.0/24",
"PrivateDNSName": "ip-10-0-2-61.us-west-2.compute.internal",
"SubnetGatewayIpv4Address": "10.0.2.1/24"
}
]
}
]
}

View File

@ -0,0 +1 @@
mock-maker-inline

View File

@ -50,7 +50,7 @@ val DEPENDENCY_SETS = listOf(
),
DependencySet(
"org.mockito",
"4.3.1",
"4.7.0",
listOf("mockito-core", "mockito-junit-jupiter")
),
DependencySet(
@ -68,6 +68,7 @@ val DEPENDENCIES = listOf(
"com.uber.nullaway:nullaway:0.9.5",
"org.assertj:assertj-core:3.22.0",
"org.awaitility:awaitility:4.1.1",
"org.bouncycastle:bcpkix-jdk15on:1.70",
"org.junit-pioneer:junit-pioneer:1.7.0",
"org.skyscreamer:jsonassert:1.5.0"
)

View File

@ -38,6 +38,7 @@ gradleEnterprise {
rootProject.name = "opentelemetry-java-contrib"
include(":all")
include(":aws-resources")
include(":aws-xray")
include(":consistent-sampling")
include(":dependencyManagement")