From 42803c47836e7a7b6c414bb4ccfde955197677bd Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Thu, 14 Mar 2024 12:28:44 +0100 Subject: [PATCH 01/13] add Azure resource provider --- azure-resources/build.gradle.kts | 32 ++++ .../AzureAppServiceResourceProvider.java | 102 +++++++++++ .../resource/AzureVmResourceProvider.java | 161 ++++++++++++++++++ .../azure/resource/CloudResourceProvider.java | 19 +++ ...try.sdk.autoconfigure.spi.ResourceProvider | 2 + .../AzureAppServiceResourceProviderTest.java | 88 ++++++++++ .../resource/AzureVmResourceProviderTest.java | 57 +++++++ .../src/test/resources/response.json | 159 +++++++++++++++++ settings.gradle.kts | 1 + 9 files changed, 621 insertions(+) create mode 100644 azure-resources/build.gradle.kts create mode 100644 azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureAppServiceResourceProvider.java create mode 100644 azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureVmResourceProvider.java create mode 100644 azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/CloudResourceProvider.java create mode 100644 azure-resources/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider create mode 100644 azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureAppServiceResourceProviderTest.java create mode 100644 azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureVmResourceProviderTest.java create mode 100644 azure-resources/src/test/resources/response.json diff --git a/azure-resources/build.gradle.kts b/azure-resources/build.gradle.kts new file mode 100644 index 000000000..232f0bb34 --- /dev/null +++ b/azure-resources/build.gradle.kts @@ -0,0 +1,32 @@ +plugins { + id("otel.java-conventions") + + id("otel.publish-conventions") +} + +description = "OpenTelemetry GCP Resources Support" +otelJava.moduleName.set("io.opentelemetry.contrib.gcp.resource") + +dependencies { + api("io.opentelemetry:opentelemetry-api") + api("io.opentelemetry:opentelemetry-sdk") + + // Provides GCP resource detection support +// implementation("com.google.cloud.opentelemetry:detector-resources-support:0.27.0") + + implementation("io.opentelemetry.semconv: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("org.mockito:mockito-core") + testImplementation("com.google.guava:guava") + + testImplementation("org.junit.jupiter:junit-jupiter-api") + testImplementation("org.assertj:assertj-core") +} diff --git a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureAppServiceResourceProvider.java b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureAppServiceResourceProvider.java new file mode 100644 index 000000000..792f31d9f --- /dev/null +++ b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureAppServiceResourceProvider.java @@ -0,0 +1,102 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.contrib.azure.resource; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.api.internal.StringUtils; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.semconv.ResourceAttributes; +import java.util.HashMap; +import java.util.Map; +import javax.annotation.Nullable; + +public class AzureAppServiceResourceProvider extends CloudResourceProvider { + + static final AttributeKey AZURE_APP_SERVICE_STAMP_RESOURCE_ATTRIBUTE = + AttributeKey.stringKey("azure.app.service.stamp"); + private static final String REGION_NAME = "REGION_NAME"; + private static final String WEBSITE_HOME_STAMPNAME = "WEBSITE_HOME_STAMPNAME"; + private static final String WEBSITE_HOSTNAME = "WEBSITE_HOSTNAME"; + private static final String WEBSITE_INSTANCE_ID = "WEBSITE_INSTANCE_ID"; + private static final String WEBSITE_OWNER_NAME = "WEBSITE_OWNER_NAME"; + private static final String WEBSITE_RESOURCE_GROUP = "WEBSITE_RESOURCE_GROUP"; + private static final String WEBSITE_SITE_NAME = "WEBSITE_SITE_NAME"; + private static final String WEBSITE_SLOT_NAME = "WEBSITE_SLOT_NAME"; + + private static final Map, String> APP_SERVICE_ATTRIBUTE_ENV_VARS = + new HashMap<>(); + + static { + APP_SERVICE_ATTRIBUTE_ENV_VARS.put(ResourceAttributes.CLOUD_REGION, REGION_NAME); + APP_SERVICE_ATTRIBUTE_ENV_VARS.put( + ResourceAttributes.DEPLOYMENT_ENVIRONMENT, WEBSITE_SLOT_NAME); + APP_SERVICE_ATTRIBUTE_ENV_VARS.put(ResourceAttributes.HOST_ID, WEBSITE_HOSTNAME); + APP_SERVICE_ATTRIBUTE_ENV_VARS.put(ResourceAttributes.SERVICE_INSTANCE_ID, WEBSITE_INSTANCE_ID); + APP_SERVICE_ATTRIBUTE_ENV_VARS.put( + AZURE_APP_SERVICE_STAMP_RESOURCE_ATTRIBUTE, WEBSITE_HOME_STAMPNAME); + } + + private final Map env; + + // SPI + public AzureAppServiceResourceProvider() { + this(System.getenv()); + } + + // Visible for testing + AzureAppServiceResourceProvider(Map env) { + this.env = env; + } + + @Override + public Resource createResource(ConfigProperties config) { + String name = env.get(WEBSITE_SITE_NAME); + if (name == null) { + return Resource.empty(); + } + AttributesBuilder builder = AzureVmResourceProvider.azureAttributeBuilder(); + builder.put(ResourceAttributes.CLOUD_PLATFORM, "azure_app_service"); + builder.put(ResourceAttributes.SERVICE_NAME, name); + + String resourceUri = resourceUri(name); + if (resourceUri != null) { + builder.put(ResourceAttributes.CLOUD_RESOURCE_ID, resourceUri); + APP_SERVICE_ATTRIBUTE_ENV_VARS.forEach( + (key, value) -> { + String envValue = env.get(value); + if (envValue != null) { + builder.put(key, envValue); + } + }); + } + + return Resource.create(builder.build()); + } + + @Nullable + private String resourceUri(String websiteName) { + String websiteResourceGroup = env.get(WEBSITE_RESOURCE_GROUP); + String websiteOwnerName = env.get(WEBSITE_OWNER_NAME); + + String subscriptionId; + if (websiteOwnerName != null && websiteOwnerName.contains("+")) { + subscriptionId = websiteOwnerName.substring(0, websiteOwnerName.indexOf("+")); + } else { + subscriptionId = websiteOwnerName; + } + + if (StringUtils.isNullOrEmpty(websiteResourceGroup) + || StringUtils.isNullOrEmpty(subscriptionId)) { + return null; + } + + return String.format( + "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Web/sites/%s", + subscriptionId, websiteResourceGroup, websiteName); + } +} diff --git a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureVmResourceProvider.java b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureVmResourceProvider.java new file mode 100644 index 000000000..653fe7ced --- /dev/null +++ b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureVmResourceProvider.java @@ -0,0 +1,161 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.contrib.azure.resource; + +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.semconv.ResourceAttributes; +import java.io.IOException; +import java.time.Duration; +import java.util.Optional; +import java.util.function.Supplier; +import java.util.logging.Level; +import java.util.logging.Logger; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.ResponseBody; +import org.jetbrains.annotations.NotNull; + +public class AzureVmResourceProvider extends CloudResourceProvider { + + private static final JsonFactory JSON_FACTORY = new JsonFactory(); + + private static final Duration TIMEOUT = Duration.ofMillis(500); + + private static final Logger logger = Logger.getLogger(AzureVmResourceProvider.class.getName()); + private static final String URL = + "http://169.254.169.254/metadata/instance?api-version=2021-02-01"; + private final Supplier> client; + + // SPI + public AzureVmResourceProvider() { + this(AzureVmResourceProvider::fetchMetadata); + } + + // visible for testing + public AzureVmResourceProvider(Supplier> client) { + this.client = client; + } + + @Override + public int order() { + // run after the fast cloud resource providers that only check environment variables + return 100; + } + + @Override + public Resource createResource(ConfigProperties config) { + return client.get().map(AzureVmResourceProvider::parseMetadata).orElse(Resource.empty()); + } + + private static Resource parseMetadata(String body) { + AttributesBuilder builder = azureAttributeBuilder(); + builder.put(ResourceAttributes.CLOUD_PLATFORM, ResourceAttributes.CloudPlatformValues.AZURE_VM); + try (JsonParser parser = JSON_FACTORY.createParser(body)) { + parser.nextToken(); + parseResponse(parser, builder); + } catch (IOException e) { + logger.log(Level.FINE, "Can't get Azure VM metadata", e); + } + return Resource.create(builder.build()); + } + + @NotNull + static AttributesBuilder azureAttributeBuilder() { + AttributesBuilder builder = Attributes.builder(); + builder.put(ResourceAttributes.CLOUD_PROVIDER, ResourceAttributes.CloudProviderValues.AZURE); + return builder; + } + + static void parseResponse(JsonParser parser, AttributesBuilder builder) throws IOException { + if (!parser.isExpectedStartObjectToken()) { + logger.log(Level.FINE, "Couldn't parse ECS metadata, invalid JSON"); + return; + } + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String value = parser.nextTextValue(); + switch (parser.currentName()) { + case "compute": + // go inside + break; + case "location": + builder.put(ResourceAttributes.CLOUD_REGION, value); + break; + case "resourceId": + builder.put(ResourceAttributes.CLOUD_RESOURCE_ID, value); + break; + case "vmId": + builder.put(ResourceAttributes.HOST_ID, value); + break; + case "name": + builder.put(ResourceAttributes.HOST_NAME, value); + break; + case "vmSize": + builder.put(ResourceAttributes.HOST_TYPE, value); + break; + case "osType": + builder.put(ResourceAttributes.OS_TYPE, value); + break; + case "version": + builder.put(ResourceAttributes.OS_VERSION, value); + break; + case "vmScaleSetName": + builder.put(AttributeKey.stringKey("azure.vm.scaleset.name"), value); + break; + case "sku": + builder.put(AttributeKey.stringKey("azure.vm.sku"), value); + break; + default: + parser.skipChildren(); + break; + } + } + } + + private static Optional fetchMetadata() { + OkHttpClient client = + new OkHttpClient.Builder() + .callTimeout(TIMEOUT) + .connectTimeout(TIMEOUT) + .readTimeout(TIMEOUT) + .build(); + + Request request = new Request.Builder().url(URL).get().addHeader("Metadata", "true").build(); + + try (Response response = client.newCall(request).execute()) { + int responseCode = response.code(); + if (responseCode != 200) { + logger.log( + Level.FINE, + "Error response from " + + URL + + " code (" + + responseCode + + ") text " + + response.message()); + return Optional.empty(); + } + ResponseBody body = response.body(); + if (body == null) { + logger.fine("Failed to fetch Azure VM metadata: empty"); + return Optional.empty(); + } + + return Optional.of(body.string()); + } catch (IOException e) { + logger.log(Level.FINE, "Failed to fetch Azure VM metadata", e); + return Optional.empty(); + } + } +} diff --git a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/CloudResourceProvider.java b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/CloudResourceProvider.java new file mode 100644 index 000000000..10758bd48 --- /dev/null +++ b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/CloudResourceProvider.java @@ -0,0 +1,19 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.contrib.azure.resource; + +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.internal.ConditionalResourceProvider; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.semconv.ResourceAttributes; + +public abstract class CloudResourceProvider implements ConditionalResourceProvider { + + @Override + public final boolean shouldApply(ConfigProperties config, Resource existing) { + return existing.getAttribute(ResourceAttributes.CLOUD_PROVIDER) == null; + } +} diff --git a/azure-resources/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider b/azure-resources/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider new file mode 100644 index 000000000..bb9b46000 --- /dev/null +++ b/azure-resources/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider @@ -0,0 +1,2 @@ +io.opentelemetry.contrib.azure.resource.AzureAppServiceResourceProvider +io.opentelemetry.contrib.azure.resource.AzureVmResourceProvider diff --git a/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureAppServiceResourceProviderTest.java b/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureAppServiceResourceProviderTest.java new file mode 100644 index 000000000..aa9912923 --- /dev/null +++ b/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureAppServiceResourceProviderTest.java @@ -0,0 +1,88 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.contrib.azure.resource; + +import com.google.common.collect.ImmutableMap; +import io.opentelemetry.sdk.testing.assertj.AttributesAssert; +import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; +import io.opentelemetry.semconv.ResourceAttributes; +import java.util.HashMap; +import java.util.Map; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Test; + +class AzureAppServiceResourceProviderTest { + + private static final String TEST_WEBSITE_SITE_NAME = "TEST_WEBSITE_SITE_NAME"; + private static final String TEST_REGION_NAME = "TEST_REGION_NAME"; + private static final String TEST_WEBSITE_SLOT_NAME = "TEST_WEBSITE_SLOT_NAME"; + private static final String TEST_WEBSITE_HOSTNAME = "TEST_WEBSITE_HOSTNAME"; + private static final String TEST_WEBSITE_INSTANCE_ID = "TEST_WEBSITE_INSTANCE_ID"; + private static final String TEST_WEBSITE_HOME_STAMPNAME = "TEST_WEBSITE_HOME_STAMPNAME"; + private static final String TEST_WEBSITE_RESOURCE_GROUP = "TEST_WEBSITE_RESOURCE_GROUP"; + private static final String TEST_WEBSITE_OWNER_NAME = "TEST_WEBSITE_OWNER_NAME"; + private static final ImmutableMap DEFAULT_ENV_VARS = + ImmutableMap.of( + "WEBSITE_SITE_NAME", TEST_WEBSITE_SITE_NAME, + "REGION_NAME", TEST_REGION_NAME, + "WEBSITE_SLOT_NAME", TEST_WEBSITE_SLOT_NAME, + "WEBSITE_HOSTNAME", TEST_WEBSITE_HOSTNAME, + "WEBSITE_INSTANCE_ID", TEST_WEBSITE_INSTANCE_ID, + "WEBSITE_HOME_STAMPNAME", TEST_WEBSITE_HOME_STAMPNAME, + "WEBSITE_RESOURCE_GROUP", TEST_WEBSITE_RESOURCE_GROUP, + "WEBSITE_OWNER_NAME", TEST_WEBSITE_OWNER_NAME); + + @Test + void defaultValues() { + createResource(DEFAULT_ENV_VARS) + .containsEntry(ResourceAttributes.SERVICE_NAME, TEST_WEBSITE_SITE_NAME) + .containsEntry(ResourceAttributes.CLOUD_PROVIDER, "azure") + .containsEntry(ResourceAttributes.CLOUD_PLATFORM, "azure_app_service") + .containsEntry( + ResourceAttributes.CLOUD_RESOURCE_ID, + "/subscriptions/TEST_WEBSITE_OWNER_NAME/resourceGroups/TEST_WEBSITE_RESOURCE_GROUP/providers/Microsoft.Web/sites/TEST_WEBSITE_SITE_NAME") + .containsEntry(ResourceAttributes.CLOUD_REGION, TEST_REGION_NAME) + .containsEntry(ResourceAttributes.DEPLOYMENT_ENVIRONMENT, TEST_WEBSITE_SLOT_NAME) + .containsEntry(ResourceAttributes.HOST_ID, TEST_WEBSITE_HOSTNAME) + .containsEntry(ResourceAttributes.SERVICE_INSTANCE_ID, TEST_WEBSITE_INSTANCE_ID) + .containsEntry( + AzureAppServiceResourceProvider.AZURE_APP_SERVICE_STAMP_RESOURCE_ATTRIBUTE, + TEST_WEBSITE_HOME_STAMPNAME); + } + + @Test + void subscriptionFromOwner() { + HashMap map = new HashMap<>(DEFAULT_ENV_VARS); + map.put("WEBSITE_OWNER_NAME", "foo+bar"); + + createResource(map) + .containsEntry( + ResourceAttributes.CLOUD_RESOURCE_ID, + "/subscriptions/foo/resourceGroups/TEST_WEBSITE_RESOURCE_GROUP/providers/Microsoft.Web/sites/TEST_WEBSITE_SITE_NAME"); + } + + @Test + void noResourceId() { + HashMap map = new HashMap<>(DEFAULT_ENV_VARS); + map.remove("WEBSITE_RESOURCE_GROUP"); + + createResource(map).doesNotContainKey(ResourceAttributes.CLOUD_RESOURCE_ID); + } + + @Test + void noWebsite() { + HashMap map = new HashMap<>(DEFAULT_ENV_VARS); + map.remove("WEBSITE_SITE_NAME"); + + createResource(map).isEmpty(); + } + + @NotNull + private static AttributesAssert createResource(Map map) { + return OpenTelemetryAssertions.assertThat( + new AzureAppServiceResourceProvider(map).createResource(null).getAttributes()); + } +} diff --git a/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureVmResourceProviderTest.java b/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureVmResourceProviderTest.java new file mode 100644 index 000000000..e45b6ae34 --- /dev/null +++ b/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureVmResourceProviderTest.java @@ -0,0 +1,57 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.contrib.azure.resource; + +import com.google.common.base.Charsets; +import com.google.common.io.CharStreams; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.sdk.testing.assertj.AttributesAssert; +import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; +import io.opentelemetry.semconv.ResourceAttributes; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.Objects; +import java.util.Optional; +import org.junit.jupiter.api.Test; + +class AzureVmResourceProviderTest { + @Test + void success() { + Resource resource = new AzureVmResourceProvider(() -> Optional.of(read())).createResource(null); + AttributesAssert attributesAssert = + OpenTelemetryAssertions.assertThat(resource.getAttributes()); + + attributesAssert + .containsEntry(ResourceAttributes.CLOUD_PROVIDER, "azure") + .containsEntry( + ResourceAttributes.CLOUD_PLATFORM, ResourceAttributes.CloudPlatformValues.AZURE_VM) + .containsEntry(ResourceAttributes.CLOUD_REGION, "westus") + .containsEntry( + ResourceAttributes.CLOUD_RESOURCE_ID, + "/subscriptions/xxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/resourceGroups/macikgo-test-may-23/providers/Microsoft.Compute/virtualMachines/examplevmname") + .containsEntry(ResourceAttributes.HOST_ID, "02aab8a4-74ef-476e-8182-f6d2ba4166a6") + .containsEntry(ResourceAttributes.HOST_NAME, "examplevmname") + .containsEntry(ResourceAttributes.HOST_TYPE, "Standard_A3") + .containsEntry(ResourceAttributes.OS_TYPE, "Linux") + .containsEntry(ResourceAttributes.OS_VERSION, "15.05.22") + .containsEntry("azure.vm.scaleset.name", "crpteste9vflji9") + .containsEntry("azure.vm.sku", "18.04-LTS"); + } + + private static String read() { + try { + return CharStreams.toString( + new InputStreamReader( + Objects.requireNonNull( + AzureVmResourceProviderTest.class + .getClassLoader() + .getResourceAsStream("response.json")), + Charsets.UTF_8)); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } +} diff --git a/azure-resources/src/test/resources/response.json b/azure-resources/src/test/resources/response.json new file mode 100644 index 000000000..255b41fba --- /dev/null +++ b/azure-resources/src/test/resources/response.json @@ -0,0 +1,159 @@ +{ + "compute": { + "azEnvironment": "AZUREPUBLICCLOUD", + "additionalCapabilities": { + "hibernationEnabled": "true" + }, + "hostGroup": { + "id": "testHostGroupId" + }, + "extendedLocation": { + "type": "edgeZone", + "name": "microsoftlosangeles" + }, + "evictionPolicy": "", + "isHostCompatibilityLayerVm": "true", + "licenseType": "", + "location": "westus", + "name": "examplevmname", + "offer": "UbuntuServer", + "osProfile": { + "adminUsername": "admin", + "computerName": "examplevmname", + "disablePasswordAuthentication": "true" + }, + "osType": "Linux", + "placementGroupId": "f67c14ab-e92c-408c-ae2d-da15866ec79a", + "plan": { + "name": "planName", + "product": "planProduct", + "publisher": "planPublisher" + }, + "platformFaultDomain": "36", + "platformSubFaultDomain": "", + "platformUpdateDomain": "42", + "priority": "Regular", + "publicKeys": [{ + "keyData": "ssh-rsa 0", + "path": "/home/user/.ssh/authorized_keys0" + }, + { + "keyData": "ssh-rsa 1", + "path": "/home/user/.ssh/authorized_keys1" + } + ], + "publisher": "Canonical", + "resourceGroupName": "macikgo-test-may-23", + "resourceId": "/subscriptions/xxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/resourceGroups/macikgo-test-may-23/providers/Microsoft.Compute/virtualMachines/examplevmname", + "securityProfile": { + "secureBootEnabled": "true", + "virtualTpmEnabled": "false", + "encryptionAtHost": "true", + "securityType": "TrustedLaunch" + }, + "sku": "18.04-LTS", + "storageProfile": { + "dataDisks": [{ + "bytesPerSecondThrottle": "979202048", + "caching": "None", + "createOption": "Empty", + "diskCapacityBytes": "274877906944", + "diskSizeGB": "1024", + "image": { + "uri": "" + }, + "isSharedDisk": "false", + "isUltraDisk": "true", + "lun": "0", + "managedDisk": { + "id": "/subscriptions/xxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/resourceGroups/macikgo-test-may-23/providers/Microsoft.Compute/disks/exampledatadiskname", + "storageAccountType": "StandardSSD_LRS" + }, + "name": "exampledatadiskname", + "opsPerSecondThrottle": "65280", + "vhd": { + "uri": "" + }, + "writeAcceleratorEnabled": "false" + }], + "imageReference": { + "id": "", + "offer": "UbuntuServer", + "publisher": "Canonical", + "sku": "16.04.0-LTS", + "version": "latest", + "communityGalleryImageId": "/CommunityGalleries/testgallery/Images/1804Gen2/Versions/latest", + "sharedGalleryImageId": "/SharedGalleries/1P/Images/gen2/Versions/latest", + "exactVersion": "1.1686127202.30113" + }, + "osDisk": { + "caching": "ReadWrite", + "createOption": "FromImage", + "diskSizeGB": "30", + "diffDiskSettings": { + "option": "Local" + }, + "encryptionSettings": { + "enabled": "false", + "diskEncryptionKey": { + "sourceVault": { + "id": "/subscriptions/test-source-guid/resourceGroups/testrg/providers/Microsoft.KeyVault/vaults/test-kv" + }, + "secretUrl": "https://test-disk.vault.azure.net/secrets/xxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/xxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx" + }, + "keyEncryptionKey": { + "sourceVault": { + "id": "/subscriptions/test-key-guid/resourceGroups/testrg/providers/Microsoft.KeyVault/vaults/test-kv" + }, + "keyUrl": "https://test-key.vault.azure.net/secrets/xxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/xxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx" + } + }, + "image": { + "uri": "" + }, + "managedDisk": { + "id": "/subscriptions/xxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/resourceGroups/macikgo-test-may-23/providers/Microsoft.Compute/disks/exampleosdiskname", + "storageAccountType": "StandardSSD_LRS" + }, + "name": "exampleosdiskname", + "osType": "Linux", + "vhd": { + "uri": "" + }, + "writeAcceleratorEnabled": "false" + }, + "resourceDisk": { + "size": "4096" + } + }, + "subscriptionId": "xxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx", + "tags": "baz:bash;foo:bar", + "version": "15.05.22", + "virtualMachineScaleSet": { + "id": "/subscriptions/xxxxxxxx-xxxxx-xxx-xxx-xxxx/resourceGroups/resource-group-name/providers/Microsoft.Compute/virtualMachineScaleSets/virtual-machine-scale-set-name" + }, + "vmId": "02aab8a4-74ef-476e-8182-f6d2ba4166a6", + "vmScaleSetName": "crpteste9vflji9", + "vmSize": "Standard_A3", + "zone": "" + }, + "network": { + "interface": [{ + "ipv4": { + "ipAddress": [{ + "privateIpAddress": "10.144.133.132", + "publicIpAddress": "" + }], + "subnet": [{ + "address": "10.144.133.128", + "prefix": "26" + }] + }, + "ipv6": { + "ipAddress": [ + ] + }, + "macAddress": "0011AAFFBB22" + }] + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 882cd2cb9..d0fabf6db 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -61,6 +61,7 @@ include(":all") include(":aws-resources") include(":aws-xray") include(":aws-xray-propagator") +include(":azure-resources") include(":baggage-processor") include(":compressors:compressor-zstd") include(":consistent-sampling") From 05c5833bcd3037acd866069b179e0e75d283da9f Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Thu, 14 Mar 2024 13:43:03 +0100 Subject: [PATCH 02/13] add Azure VM resource provider --- azure-resources/build.gradle.kts | 1 + .../resource/AzureVmResourceProvider.java | 34 +++++---- .../resource/AzureVmResourceProviderTest.java | 72 +++++++++++++++++-- 3 files changed, 88 insertions(+), 19 deletions(-) diff --git a/azure-resources/build.gradle.kts b/azure-resources/build.gradle.kts index 232f0bb34..290327e4e 100644 --- a/azure-resources/build.gradle.kts +++ b/azure-resources/build.gradle.kts @@ -29,4 +29,5 @@ dependencies { testImplementation("org.junit.jupiter:junit-jupiter-api") testImplementation("org.assertj:assertj-core") + testImplementation("com.linecorp.armeria:armeria-junit5") } diff --git a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureVmResourceProvider.java b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureVmResourceProvider.java index 653fe7ced..29ade9762 100644 --- a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureVmResourceProvider.java +++ b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureVmResourceProvider.java @@ -15,7 +15,10 @@ import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.semconv.ResourceAttributes; import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; import java.time.Duration; +import java.util.Objects; import java.util.Optional; import java.util.function.Supplier; import java.util.logging.Level; @@ -23,23 +26,30 @@ import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; -import okhttp3.ResponseBody; import org.jetbrains.annotations.NotNull; public class AzureVmResourceProvider extends CloudResourceProvider { private static final JsonFactory JSON_FACTORY = new JsonFactory(); - private static final Duration TIMEOUT = Duration.ofMillis(500); + private static final Duration TIMEOUT = Duration.ofSeconds(1); private static final Logger logger = Logger.getLogger(AzureVmResourceProvider.class.getName()); - private static final String URL = - "http://169.254.169.254/metadata/instance?api-version=2021-02-01"; + private static final URL METADATA_URL; + + static { + try { + METADATA_URL = new URL("http://169.254.169.254/metadata/instance?api-version=2021-02-01"); + } catch (MalformedURLException e) { + throw new IllegalStateException(e); + } + } + private final Supplier> client; // SPI public AzureVmResourceProvider() { - this(AzureVmResourceProvider::fetchMetadata); + this(() -> fetchMetadata(METADATA_URL)); } // visible for testing @@ -123,7 +133,8 @@ static void parseResponse(JsonParser parser, AttributesBuilder builder) throws I } } - private static Optional fetchMetadata() { + // visible for testing + static Optional fetchMetadata(URL url) { OkHttpClient client = new OkHttpClient.Builder() .callTimeout(TIMEOUT) @@ -131,7 +142,7 @@ private static Optional fetchMetadata() { .readTimeout(TIMEOUT) .build(); - Request request = new Request.Builder().url(URL).get().addHeader("Metadata", "true").build(); + Request request = new Request.Builder().url(url).get().addHeader("Metadata", "true").build(); try (Response response = client.newCall(request).execute()) { int responseCode = response.code(); @@ -139,20 +150,15 @@ private static Optional fetchMetadata() { logger.log( Level.FINE, "Error response from " - + URL + + url + " code (" + responseCode + ") text " + response.message()); return Optional.empty(); } - ResponseBody body = response.body(); - if (body == null) { - logger.fine("Failed to fetch Azure VM metadata: empty"); - return Optional.empty(); - } - return Optional.of(body.string()); + return Optional.of(Objects.requireNonNull(response.body()).string()); } catch (IOException e) { logger.log(Level.FINE, "Failed to fetch Azure VM metadata", e); return Optional.empty(); diff --git a/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureVmResourceProviderTest.java b/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureVmResourceProviderTest.java index e45b6ae34..88a0ceacb 100644 --- a/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureVmResourceProviderTest.java +++ b/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureVmResourceProviderTest.java @@ -7,23 +7,85 @@ import com.google.common.base.Charsets; import com.google.common.io.CharStreams; +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.sdk.resources.Resource; import io.opentelemetry.sdk.testing.assertj.AttributesAssert; import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; import io.opentelemetry.semconv.ResourceAttributes; import java.io.IOException; import java.io.InputStreamReader; +import java.net.MalformedURLException; import java.util.Objects; import java.util.Optional; +import java.util.function.Supplier; +import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; class AzureVmResourceProviderTest { + + @RegisterExtension + public static final MockWebServerExtension server = new MockWebServerExtension(); + + @Test + void successFromFile() { + assertDefaultAttributes(createResource(() -> Optional.of(okResponse()))); + } + + @Test + void successFromMockServer() { + server.enqueue(HttpResponse.of(MediaType.JSON, okResponse())); + assertDefaultAttributes(mockServerResponse()); + } + + @Test + void responseNotFound() { + server.enqueue(HttpResponse.of(HttpStatus.NOT_FOUND)); + mockServerResponse().isEmpty(); + } + + @Test + void responseEmpty() { + server.enqueue(HttpResponse.of("")); + assertOnlyProvider(mockServerResponse()); + } + @Test - void success() { - Resource resource = new AzureVmResourceProvider(() -> Optional.of(read())).createResource(null); - AttributesAssert attributesAssert = - OpenTelemetryAssertions.assertThat(resource.getAttributes()); + void responseEmptyJson() { + server.enqueue(HttpResponse.of("{}")); + assertOnlyProvider(mockServerResponse()); + } + + @NotNull + private static AttributesAssert mockServerResponse() { + return createResource( + () -> { + try { + return AzureVmResourceProvider.fetchMetadata(server.httpUri().toURL()); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + }); + } + + @NotNull + private static AttributesAssert createResource(Supplier> client) { + Resource resource = new AzureVmResourceProvider(client).createResource(null); + return OpenTelemetryAssertions.assertThat(resource.getAttributes()); + } + + private static void assertOnlyProvider(AttributesAssert attributesAssert) { + attributesAssert + .hasSize(2) + .containsEntry(ResourceAttributes.CLOUD_PROVIDER, "azure") + .containsEntry( + ResourceAttributes.CLOUD_PLATFORM, ResourceAttributes.CloudPlatformValues.AZURE_VM); + } + private static void assertDefaultAttributes(AttributesAssert attributesAssert) { attributesAssert .containsEntry(ResourceAttributes.CLOUD_PROVIDER, "azure") .containsEntry( @@ -41,7 +103,7 @@ void success() { .containsEntry("azure.vm.sku", "18.04-LTS"); } - private static String read() { + private static String okResponse() { try { return CharStreams.toString( new InputStreamReader( From d3f158276bebb0f3cd329e46c38e20ceaff90159 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Thu, 14 Mar 2024 15:20:04 +0100 Subject: [PATCH 03/13] add Azure functions resource provider --- .../AzureAppServiceResourceProvider.java | 39 +++++------ .../azure/resource/AzureEnvVarPlatform.java | 40 ++++++++++++ .../AzureFunctionsResourceProvider.java | 65 +++++++++++++++++++ .../AzureAppServiceResourceProviderTest.java | 8 +++ .../AzureFunctionsResourceProviderTest.java | 55 ++++++++++++++++ 5 files changed, 185 insertions(+), 22 deletions(-) create mode 100644 azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureEnvVarPlatform.java create mode 100644 azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureFunctionsResourceProvider.java create mode 100644 azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureFunctionsResourceProviderTest.java diff --git a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureAppServiceResourceProvider.java b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureAppServiceResourceProvider.java index 792f31d9f..780c9a023 100644 --- a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureAppServiceResourceProvider.java +++ b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureAppServiceResourceProvider.java @@ -13,32 +13,30 @@ import io.opentelemetry.semconv.ResourceAttributes; import java.util.HashMap; import java.util.Map; +import java.util.Objects; import javax.annotation.Nullable; public class AzureAppServiceResourceProvider extends CloudResourceProvider { static final AttributeKey AZURE_APP_SERVICE_STAMP_RESOURCE_ATTRIBUTE = AttributeKey.stringKey("azure.app.service.stamp"); - private static final String REGION_NAME = "REGION_NAME"; + static final String REGION_NAME = "REGION_NAME"; private static final String WEBSITE_HOME_STAMPNAME = "WEBSITE_HOME_STAMPNAME"; private static final String WEBSITE_HOSTNAME = "WEBSITE_HOSTNAME"; - private static final String WEBSITE_INSTANCE_ID = "WEBSITE_INSTANCE_ID"; + static final String WEBSITE_INSTANCE_ID = "WEBSITE_INSTANCE_ID"; private static final String WEBSITE_OWNER_NAME = "WEBSITE_OWNER_NAME"; private static final String WEBSITE_RESOURCE_GROUP = "WEBSITE_RESOURCE_GROUP"; - private static final String WEBSITE_SITE_NAME = "WEBSITE_SITE_NAME"; + static final String WEBSITE_SITE_NAME = "WEBSITE_SITE_NAME"; private static final String WEBSITE_SLOT_NAME = "WEBSITE_SLOT_NAME"; - private static final Map, String> APP_SERVICE_ATTRIBUTE_ENV_VARS = - new HashMap<>(); + private static final Map, String> ENV_VAR_MAPPING = new HashMap<>(); static { - APP_SERVICE_ATTRIBUTE_ENV_VARS.put(ResourceAttributes.CLOUD_REGION, REGION_NAME); - APP_SERVICE_ATTRIBUTE_ENV_VARS.put( - ResourceAttributes.DEPLOYMENT_ENVIRONMENT, WEBSITE_SLOT_NAME); - APP_SERVICE_ATTRIBUTE_ENV_VARS.put(ResourceAttributes.HOST_ID, WEBSITE_HOSTNAME); - APP_SERVICE_ATTRIBUTE_ENV_VARS.put(ResourceAttributes.SERVICE_INSTANCE_ID, WEBSITE_INSTANCE_ID); - APP_SERVICE_ATTRIBUTE_ENV_VARS.put( - AZURE_APP_SERVICE_STAMP_RESOURCE_ATTRIBUTE, WEBSITE_HOME_STAMPNAME); + ENV_VAR_MAPPING.put(ResourceAttributes.CLOUD_REGION, REGION_NAME); + ENV_VAR_MAPPING.put(ResourceAttributes.DEPLOYMENT_ENVIRONMENT, WEBSITE_SLOT_NAME); + ENV_VAR_MAPPING.put(ResourceAttributes.HOST_ID, WEBSITE_HOSTNAME); + ENV_VAR_MAPPING.put(ResourceAttributes.SERVICE_INSTANCE_ID, WEBSITE_INSTANCE_ID); + ENV_VAR_MAPPING.put(AZURE_APP_SERVICE_STAMP_RESOURCE_ATTRIBUTE, WEBSITE_HOME_STAMPNAME); } private final Map env; @@ -55,24 +53,21 @@ public AzureAppServiceResourceProvider() { @Override public Resource createResource(ConfigProperties config) { - String name = env.get(WEBSITE_SITE_NAME); - if (name == null) { + AzureEnvVarPlatform detect = AzureEnvVarPlatform.detect(env); + if (detect != AzureEnvVarPlatform.APP_SERVICE) { return Resource.empty(); } + String name = Objects.requireNonNull(env.get(WEBSITE_SITE_NAME)); AttributesBuilder builder = AzureVmResourceProvider.azureAttributeBuilder(); - builder.put(ResourceAttributes.CLOUD_PLATFORM, "azure_app_service"); + builder.put( + ResourceAttributes.CLOUD_PLATFORM, + ResourceAttributes.CloudPlatformValues.AZURE_APP_SERVICE); builder.put(ResourceAttributes.SERVICE_NAME, name); String resourceUri = resourceUri(name); if (resourceUri != null) { builder.put(ResourceAttributes.CLOUD_RESOURCE_ID, resourceUri); - APP_SERVICE_ATTRIBUTE_ENV_VARS.forEach( - (key, value) -> { - String envValue = env.get(value); - if (envValue != null) { - builder.put(key, envValue); - } - }); + AzureEnvVarPlatform.addAttributesFromEnv(ENV_VAR_MAPPING, env, builder); } return Resource.create(builder.build()); diff --git a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureEnvVarPlatform.java b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureEnvVarPlatform.java new file mode 100644 index 000000000..7abcda033 --- /dev/null +++ b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureEnvVarPlatform.java @@ -0,0 +1,40 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.contrib.azure.resource; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.AttributesBuilder; +import java.util.Map; + +public enum AzureEnvVarPlatform { + APP_SERVICE, + FUNCTIONS, + NONE; + + static AzureEnvVarPlatform detect(Map env) { + String name = env.get(AzureAppServiceResourceProvider.WEBSITE_SITE_NAME); + if (name == null) { + return NONE; + } + if (env.get(AzureFunctionsResourceProvider.FUNCTIONS_VERSION) != null) { + return FUNCTIONS; + } + return APP_SERVICE; + } + + static void addAttributesFromEnv( + Map, String> mapping, + Map env, + AttributesBuilder builder) { + mapping.forEach( + (key, value) -> { + String envValue = env.get(value); + if (envValue != null) { + builder.put(key, envValue); + } + }); + } +} diff --git a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureFunctionsResourceProvider.java b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureFunctionsResourceProvider.java new file mode 100644 index 000000000..116ca299a --- /dev/null +++ b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureFunctionsResourceProvider.java @@ -0,0 +1,65 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.contrib.azure.resource; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.semconv.ResourceAttributes; +import java.util.HashMap; +import java.util.Map; + +public class AzureFunctionsResourceProvider extends CloudResourceProvider { + + static final String FUNCTIONS_VERSION = "FUNCTIONS_EXTENSION_VERSION"; + private static final String FUNCTIONS_MEM_LIMIT = "WEBSITE_MEMORY_LIMIT_MB"; + + private static final Map, String> ENV_VAR_MAPPING = new HashMap<>(); + + static { + ENV_VAR_MAPPING.put( + ResourceAttributes.CLOUD_REGION, AzureAppServiceResourceProvider.REGION_NAME); + ENV_VAR_MAPPING.put( + ResourceAttributes.FAAS_NAME, AzureAppServiceResourceProvider.WEBSITE_SITE_NAME); + ENV_VAR_MAPPING.put(ResourceAttributes.FAAS_VERSION, FUNCTIONS_VERSION); + ENV_VAR_MAPPING.put( + ResourceAttributes.FAAS_INSTANCE, AzureAppServiceResourceProvider.WEBSITE_INSTANCE_ID); + } + + private final Map env; + + // SPI + public AzureFunctionsResourceProvider() { + this(System.getenv()); + } + + // Visible for testing + AzureFunctionsResourceProvider(Map env) { + this.env = env; + } + + @Override + public Resource createResource(ConfigProperties config) { + AzureEnvVarPlatform detect = AzureEnvVarPlatform.detect(env); + if (detect != AzureEnvVarPlatform.FUNCTIONS) { + return Resource.empty(); + } + + AttributesBuilder builder = AzureVmResourceProvider.azureAttributeBuilder(); + builder.put( + ResourceAttributes.CLOUD_PLATFORM, ResourceAttributes.CloudPlatformValues.AZURE_FUNCTIONS); + + String limit = env.get(FUNCTIONS_MEM_LIMIT); + if (limit != null) { + builder.put(ResourceAttributes.FAAS_MAX_MEMORY, Long.parseLong(limit)); + } + + AzureEnvVarPlatform.addAttributesFromEnv(ENV_VAR_MAPPING, env, builder); + + return Resource.create(builder.build()); + } +} diff --git a/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureAppServiceResourceProviderTest.java b/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureAppServiceResourceProviderTest.java index aa9912923..1cc3e996f 100644 --- a/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureAppServiceResourceProviderTest.java +++ b/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureAppServiceResourceProviderTest.java @@ -80,6 +80,14 @@ void noWebsite() { createResource(map).isEmpty(); } + @Test + void isFunction() { + HashMap map = new HashMap<>(DEFAULT_ENV_VARS); + map.put("FUNCTIONS_EXTENSION_VERSION", "3.0"); + + createResource(map).isEmpty(); + } + @NotNull private static AttributesAssert createResource(Map map) { return OpenTelemetryAssertions.assertThat( diff --git a/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureFunctionsResourceProviderTest.java b/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureFunctionsResourceProviderTest.java new file mode 100644 index 000000000..0146ee518 --- /dev/null +++ b/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureFunctionsResourceProviderTest.java @@ -0,0 +1,55 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.contrib.azure.resource; + +import com.google.common.collect.ImmutableMap; +import io.opentelemetry.sdk.testing.assertj.AttributesAssert; +import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; +import io.opentelemetry.semconv.ResourceAttributes; +import java.util.HashMap; +import java.util.Map; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Test; + +class AzureFunctionsResourceProviderTest { + private static final String TEST_WEBSITE_SITE_NAME = "TEST_WEBSITE_SITE_NAME"; + private static final String TEST_REGION_NAME = "TEST_REGION_NAME"; + private static final String TEST_FUNCTION_VERSION = "TEST_VERSION"; + private static final String TEST_WEBSITE_INSTANCE_ID = "TEST_WEBSITE_INSTANCE_ID"; + private static final String TEST_MEM_LIMIT = "1024"; + private static final ImmutableMap DEFAULT_ENV_VARS = + ImmutableMap.of( + "WEBSITE_SITE_NAME", TEST_WEBSITE_SITE_NAME, + "REGION_NAME", TEST_REGION_NAME, + "WEBSITE_MEMORY_LIMIT_MB", TEST_MEM_LIMIT, + "FUNCTIONS_EXTENSION_VERSION", TEST_FUNCTION_VERSION, + "WEBSITE_INSTANCE_ID", TEST_WEBSITE_INSTANCE_ID); + + @Test + void defaultValues() { + createResource(DEFAULT_ENV_VARS) + .containsEntry(ResourceAttributes.CLOUD_PROVIDER, "azure") + .containsEntry(ResourceAttributes.CLOUD_PLATFORM, "azure_functions") + .containsEntry(ResourceAttributes.FAAS_NAME, TEST_WEBSITE_SITE_NAME) + .containsEntry(ResourceAttributes.FAAS_VERSION, TEST_FUNCTION_VERSION) + .containsEntry(ResourceAttributes.FAAS_INSTANCE, TEST_WEBSITE_INSTANCE_ID) + .containsEntry(ResourceAttributes.FAAS_MAX_MEMORY, Long.parseLong(TEST_MEM_LIMIT)); + } + + @Test + void isNotFunction() { + HashMap map = new HashMap<>(DEFAULT_ENV_VARS); + map.remove("FUNCTIONS_EXTENSION_VERSION"); + + createResource(map).isEmpty(); + } + + @NotNull + private static AttributesAssert createResource(Map map) { + return OpenTelemetryAssertions.assertThat( + new AzureFunctionsResourceProvider(map).createResource(null).getAttributes()); + } +} From a922cafc2fddc055259053e91de4795f2410b345 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Thu, 14 Mar 2024 15:44:16 +0100 Subject: [PATCH 04/13] add Azure container resource provider --- .../AzureAppServiceResourceProvider.java | 7 +-- .../AzureContainersResourceProvider.java | 57 +++++++++++++++++++ .../azure/resource/AzureEnvVarPlatform.java | 5 ++ .../AzureFunctionsResourceProvider.java | 6 +- .../resource/AzureVmResourceProvider.java | 7 ++- .../AzureContainersResourceProviderTest.java | 51 +++++++++++++++++ 6 files changed, 123 insertions(+), 10 deletions(-) create mode 100644 azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureContainersResourceProvider.java create mode 100644 azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureContainersResourceProviderTest.java diff --git a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureAppServiceResourceProvider.java b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureAppServiceResourceProvider.java index 780c9a023..1f2e42f77 100644 --- a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureAppServiceResourceProvider.java +++ b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureAppServiceResourceProvider.java @@ -58,10 +58,9 @@ public Resource createResource(ConfigProperties config) { return Resource.empty(); } String name = Objects.requireNonNull(env.get(WEBSITE_SITE_NAME)); - AttributesBuilder builder = AzureVmResourceProvider.azureAttributeBuilder(); - builder.put( - ResourceAttributes.CLOUD_PLATFORM, - ResourceAttributes.CloudPlatformValues.AZURE_APP_SERVICE); + AttributesBuilder builder = + AzureVmResourceProvider.azureAttributeBuilder( + ResourceAttributes.CloudPlatformValues.AZURE_APP_SERVICE); builder.put(ResourceAttributes.SERVICE_NAME, name); String resourceUri = resourceUri(name); diff --git a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureContainersResourceProvider.java b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureContainersResourceProvider.java new file mode 100644 index 000000000..b700add6f --- /dev/null +++ b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureContainersResourceProvider.java @@ -0,0 +1,57 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.contrib.azure.resource; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.semconv.ResourceAttributes; +import java.util.HashMap; +import java.util.Map; + +public class AzureContainersResourceProvider extends CloudResourceProvider { + + static final String CONTAINER_APP_NAME = "CONTAINER_APP_NAME"; + + private static final String CONTAINER_APP_REPLICA_NAME = "CONTAINER_APP_REPLICA_NAME"; + private static final String CONTAINER_APP_REVISION = "CONTAINER_APP_REVISION"; + + private static final Map, String> ENV_VAR_MAPPING = new HashMap<>(); + + static { + ENV_VAR_MAPPING.put(ResourceAttributes.SERVICE_NAME, CONTAINER_APP_NAME); + ENV_VAR_MAPPING.put(ResourceAttributes.SERVICE_INSTANCE_ID, CONTAINER_APP_REPLICA_NAME); + ENV_VAR_MAPPING.put(ResourceAttributes.SERVICE_VERSION, CONTAINER_APP_REVISION); + } + + private final Map env; + + // SPI + public AzureContainersResourceProvider() { + this(System.getenv()); + } + + // Visible for testing + AzureContainersResourceProvider(Map env) { + this.env = env; + } + + @Override + public Resource createResource(ConfigProperties config) { + AzureEnvVarPlatform detect = AzureEnvVarPlatform.detect(env); + if (detect != AzureEnvVarPlatform.CONTAINERS) { + return Resource.empty(); + } + + AttributesBuilder builder = + AzureVmResourceProvider.azureAttributeBuilder("azure_container_apps"); + + AzureEnvVarPlatform.addAttributesFromEnv(ENV_VAR_MAPPING, env, builder); + + return Resource.create(builder.build()); + } +} diff --git a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureEnvVarPlatform.java b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureEnvVarPlatform.java index 7abcda033..d95b680b7 100644 --- a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureEnvVarPlatform.java +++ b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureEnvVarPlatform.java @@ -12,9 +12,14 @@ public enum AzureEnvVarPlatform { APP_SERVICE, FUNCTIONS, + CONTAINERS, NONE; static AzureEnvVarPlatform detect(Map env) { + String appName = env.get(AzureContainersResourceProvider.CONTAINER_APP_NAME); + if (appName != null) { + return CONTAINERS; + } String name = env.get(AzureAppServiceResourceProvider.WEBSITE_SITE_NAME); if (name == null) { return NONE; diff --git a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureFunctionsResourceProvider.java b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureFunctionsResourceProvider.java index 116ca299a..e48f80c91 100644 --- a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureFunctionsResourceProvider.java +++ b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureFunctionsResourceProvider.java @@ -49,9 +49,9 @@ public Resource createResource(ConfigProperties config) { return Resource.empty(); } - AttributesBuilder builder = AzureVmResourceProvider.azureAttributeBuilder(); - builder.put( - ResourceAttributes.CLOUD_PLATFORM, ResourceAttributes.CloudPlatformValues.AZURE_FUNCTIONS); + AttributesBuilder builder = + AzureVmResourceProvider.azureAttributeBuilder( + ResourceAttributes.CloudPlatformValues.AZURE_FUNCTIONS); String limit = env.get(FUNCTIONS_MEM_LIMIT); if (limit != null) { diff --git a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureVmResourceProvider.java b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureVmResourceProvider.java index 29ade9762..c506f4fc8 100644 --- a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureVmResourceProvider.java +++ b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureVmResourceProvider.java @@ -69,8 +69,8 @@ public Resource createResource(ConfigProperties config) { } private static Resource parseMetadata(String body) { - AttributesBuilder builder = azureAttributeBuilder(); - builder.put(ResourceAttributes.CLOUD_PLATFORM, ResourceAttributes.CloudPlatformValues.AZURE_VM); + AttributesBuilder builder = + azureAttributeBuilder(ResourceAttributes.CloudPlatformValues.AZURE_VM); try (JsonParser parser = JSON_FACTORY.createParser(body)) { parser.nextToken(); parseResponse(parser, builder); @@ -81,9 +81,10 @@ private static Resource parseMetadata(String body) { } @NotNull - static AttributesBuilder azureAttributeBuilder() { + static AttributesBuilder azureAttributeBuilder(String platform) { AttributesBuilder builder = Attributes.builder(); builder.put(ResourceAttributes.CLOUD_PROVIDER, ResourceAttributes.CloudProviderValues.AZURE); + builder.put(ResourceAttributes.CLOUD_PLATFORM, platform); return builder; } diff --git a/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureContainersResourceProviderTest.java b/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureContainersResourceProviderTest.java new file mode 100644 index 000000000..64ff2842d --- /dev/null +++ b/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureContainersResourceProviderTest.java @@ -0,0 +1,51 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.contrib.azure.resource; + +import com.google.common.collect.ImmutableMap; +import io.opentelemetry.sdk.testing.assertj.AttributesAssert; +import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; +import io.opentelemetry.semconv.ResourceAttributes; +import java.util.HashMap; +import java.util.Map; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Test; + +class AzureContainersResourceProviderTest { + private static final String TEST_APP_NAME = "TEST_APP_NAME"; + private static final String TEST_REPLICA_NAME = "TEST_REPLICA_NAME"; + private static final String TEST_REVISION = "TEST_REVISION"; + + private static final ImmutableMap DEFAULT_ENV_VARS = + ImmutableMap.of( + "CONTAINER_APP_NAME", TEST_APP_NAME, + "CONTAINER_APP_REPLICA_NAME", TEST_REPLICA_NAME, + "CONTAINER_APP_REVISION", TEST_REVISION); + + @Test + void defaultValues() { + createResource(DEFAULT_ENV_VARS) + .containsEntry(ResourceAttributes.CLOUD_PROVIDER, "azure") + .containsEntry(ResourceAttributes.CLOUD_PLATFORM, "azure_container_apps") + .containsEntry(ResourceAttributes.SERVICE_NAME, TEST_APP_NAME) + .containsEntry(ResourceAttributes.SERVICE_INSTANCE_ID, TEST_REPLICA_NAME) + .containsEntry(ResourceAttributes.SERVICE_VERSION, TEST_REVISION); + } + + @Test + void isNotContainer() { + HashMap map = new HashMap<>(DEFAULT_ENV_VARS); + map.remove("CONTAINER_APP_NAME"); + + createResource(map).isEmpty(); + } + + @NotNull + private static AttributesAssert createResource(Map map) { + return OpenTelemetryAssertions.assertThat( + new AzureContainersResourceProvider(map).createResource(null).getAttributes()); + } +} From 3a40285465d42868d0a19a569769525169f7c26d Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Thu, 14 Mar 2024 17:35:28 +0100 Subject: [PATCH 05/13] add Azure vm resource provider --- .../resource/AzureVmResourceProvider.java | 89 +++++++++++-------- 1 file changed, 53 insertions(+), 36 deletions(-) diff --git a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureVmResourceProvider.java b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureVmResourceProvider.java index c506f4fc8..5c0098311 100644 --- a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureVmResourceProvider.java +++ b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureVmResourceProvider.java @@ -18,8 +18,11 @@ import java.net.MalformedURLException; import java.net.URL; import java.time.Duration; +import java.util.HashMap; +import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.function.BiConsumer; import java.util.function.Supplier; import java.util.logging.Level; import java.util.logging.Logger; @@ -30,6 +33,20 @@ public class AzureVmResourceProvider extends CloudResourceProvider { + private static final Map> COMPUTE_MAPPING = new HashMap<>(); + + static { + COMPUTE_MAPPING.put("location", ResourceAttributes.CLOUD_REGION); + COMPUTE_MAPPING.put("resourceId", ResourceAttributes.CLOUD_RESOURCE_ID); + COMPUTE_MAPPING.put("vmId", ResourceAttributes.HOST_ID); + COMPUTE_MAPPING.put("name", ResourceAttributes.HOST_NAME); + COMPUTE_MAPPING.put("vmSize", ResourceAttributes.HOST_TYPE); + COMPUTE_MAPPING.put("osType", ResourceAttributes.OS_TYPE); + COMPUTE_MAPPING.put("version", ResourceAttributes.OS_VERSION); + COMPUTE_MAPPING.put("vmScaleSetName", AttributeKey.stringKey("azure.vm.scaleset.name")); + COMPUTE_MAPPING.put("sku", AttributeKey.stringKey("azure.vm.sku")); + } + private static final JsonFactory JSON_FACTORY = new JsonFactory(); private static final Duration TIMEOUT = Duration.ofSeconds(1); @@ -94,43 +111,43 @@ static void parseResponse(JsonParser parser, AttributesBuilder builder) throws I return; } + consumeJson( + parser, + (name, value) -> { + try { + if (name.equals("compute")) { + consumeCompute(parser, builder); + } else { + parser.skipChildren(); + } + } catch (IOException e) { + throw new IllegalStateException(e); + } + }); + } + + private static void consumeCompute(JsonParser parser, AttributesBuilder builder) + throws IOException { + consumeJson( + parser, + (computeName, computeValue) -> { + AttributeKey key = COMPUTE_MAPPING.get(computeName); + if (key != null) { + builder.put(key, computeValue); + } else { + try { + parser.skipChildren(); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + }); + } + + private static void consumeJson(JsonParser parser, BiConsumer consumer) + throws IOException { while (parser.nextToken() != JsonToken.END_OBJECT) { - String value = parser.nextTextValue(); - switch (parser.currentName()) { - case "compute": - // go inside - break; - case "location": - builder.put(ResourceAttributes.CLOUD_REGION, value); - break; - case "resourceId": - builder.put(ResourceAttributes.CLOUD_RESOURCE_ID, value); - break; - case "vmId": - builder.put(ResourceAttributes.HOST_ID, value); - break; - case "name": - builder.put(ResourceAttributes.HOST_NAME, value); - break; - case "vmSize": - builder.put(ResourceAttributes.HOST_TYPE, value); - break; - case "osType": - builder.put(ResourceAttributes.OS_TYPE, value); - break; - case "version": - builder.put(ResourceAttributes.OS_VERSION, value); - break; - case "vmScaleSetName": - builder.put(AttributeKey.stringKey("azure.vm.scaleset.name"), value); - break; - case "sku": - builder.put(AttributeKey.stringKey("azure.vm.sku"), value); - break; - default: - parser.skipChildren(); - break; - } + consumer.accept(parser.currentName(), parser.nextTextValue()); } } From fafaf49a8b5cd04cd0404136aff1175c12e7ac1a Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Thu, 14 Mar 2024 18:42:28 +0100 Subject: [PATCH 06/13] docs --- azure-resources/README.md | 34 +++++++++++++++++++ .../AzureAppServiceResourceProvider.java | 3 +- 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 azure-resources/README.md diff --git a/azure-resources/README.md b/azure-resources/README.md new file mode 100644 index 000000000..2428b1394 --- /dev/null +++ b/azure-resources/README.md @@ -0,0 +1,34 @@ +# Azure Resource Detectors for OpenTelemetry + +This module provides Azure resource detectors for OpenTelemetry. + +The following OpenTelemetry semantic conventions will be detected: + +| Resource attribute | VM | Functions | App Service | Containers | +|-------------------------|----------|-----------------|-------------------|----------------------| +| cloud.platform | azure_vm | azure_functions | azure_app_service | azure_container_apps | +| cloud.provider | azure | azure | azure | azure | +| cloud.resource.id | auto | | auto | | +| cloud.region | auto | auto | auto | | +| deployment.environment | | | auto | | +| host.id | auto | | auto | | +| host.name | auto | | | | +| host.type | auto | | | | +| os.type | auto | | | | +| os.version | auto | | | | +| azure.vm.scaleset.name | auto | | | | +| azure.vm.sku | auto | | | | +| service.name | | | auto | auto | +| service.version | | | | auto | +| service.instance.id | | | auto | auto | +| azure.app.service.stamp | | | auto | | +| faas.name | | auto | | | +| faas.version | | auto | | | +| faas.instance | | auto | | | +| faas.faas.max_memory | | auto | | | + +## Component Owners + +TODO + +Learn more about component owners in [component_owners.yml](../.github/component_owners.yml). diff --git a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureAppServiceResourceProvider.java b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureAppServiceResourceProvider.java index 1f2e42f77..c54f15062 100644 --- a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureAppServiceResourceProvider.java +++ b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureAppServiceResourceProvider.java @@ -66,9 +66,10 @@ public Resource createResource(ConfigProperties config) { String resourceUri = resourceUri(name); if (resourceUri != null) { builder.put(ResourceAttributes.CLOUD_RESOURCE_ID, resourceUri); - AzureEnvVarPlatform.addAttributesFromEnv(ENV_VAR_MAPPING, env, builder); } + AzureEnvVarPlatform.addAttributesFromEnv(ENV_VAR_MAPPING, env, builder); + return Resource.create(builder.build()); } From 85de56d5e616f5259a224786b3d8e7c0c66e14ec Mon Sep 17 00:00:00 2001 From: heyams Date: Thu, 18 Apr 2024 09:27:28 -0700 Subject: [PATCH 07/13] Update --- azure-resources/build.gradle.kts | 6 ++++++ .../azure/resource/AzureAppServiceResourceProvider.java | 9 +++++++-- .../azure/resource/AzureContainersResourceProvider.java | 9 +++++++-- .../azure/resource/AzureFunctionsResourceProvider.java | 9 +++++++-- ....opentelemetry.sdk.autoconfigure.spi.ResourceProvider | 1 + 5 files changed, 28 insertions(+), 6 deletions(-) diff --git a/azure-resources/build.gradle.kts b/azure-resources/build.gradle.kts index 290327e4e..5e4da0244 100644 --- a/azure-resources/build.gradle.kts +++ b/azure-resources/build.gradle.kts @@ -2,11 +2,17 @@ plugins { id("otel.java-conventions") id("otel.publish-conventions") + id("maven-publish") } description = "OpenTelemetry GCP Resources Support" otelJava.moduleName.set("io.opentelemetry.contrib.gcp.resource") +// enable publishing to maven local +java { + withSourcesJar() +} + dependencies { api("io.opentelemetry:opentelemetry-api") api("io.opentelemetry:opentelemetry-sdk") diff --git a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureAppServiceResourceProvider.java b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureAppServiceResourceProvider.java index c54f15062..b5ede6744 100644 --- a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureAppServiceResourceProvider.java +++ b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureAppServiceResourceProvider.java @@ -6,6 +6,7 @@ package io.opentelemetry.contrib.azure.resource; import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.api.internal.StringUtils; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; @@ -53,9 +54,13 @@ public AzureAppServiceResourceProvider() { @Override public Resource createResource(ConfigProperties config) { + return Resource.create(getAttributes()); + } + + public Attributes getAttributes() { AzureEnvVarPlatform detect = AzureEnvVarPlatform.detect(env); if (detect != AzureEnvVarPlatform.APP_SERVICE) { - return Resource.empty(); + return Attributes.empty(); } String name = Objects.requireNonNull(env.get(WEBSITE_SITE_NAME)); AttributesBuilder builder = @@ -70,7 +75,7 @@ public Resource createResource(ConfigProperties config) { AzureEnvVarPlatform.addAttributesFromEnv(ENV_VAR_MAPPING, env, builder); - return Resource.create(builder.build()); + return builder.build(); } @Nullable diff --git a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureContainersResourceProvider.java b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureContainersResourceProvider.java index b700add6f..67717f2f9 100644 --- a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureContainersResourceProvider.java +++ b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureContainersResourceProvider.java @@ -6,6 +6,7 @@ package io.opentelemetry.contrib.azure.resource; import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.resources.Resource; @@ -42,9 +43,13 @@ public AzureContainersResourceProvider() { @Override public Resource createResource(ConfigProperties config) { + return Resource.create(getAttributes()); + } + + private Attributes getAttributes() { AzureEnvVarPlatform detect = AzureEnvVarPlatform.detect(env); if (detect != AzureEnvVarPlatform.CONTAINERS) { - return Resource.empty(); + return Attributes.empty(); } AttributesBuilder builder = @@ -52,6 +57,6 @@ public Resource createResource(ConfigProperties config) { AzureEnvVarPlatform.addAttributesFromEnv(ENV_VAR_MAPPING, env, builder); - return Resource.create(builder.build()); + return builder.build(); } } diff --git a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureFunctionsResourceProvider.java b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureFunctionsResourceProvider.java index e48f80c91..feddd14d7 100644 --- a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureFunctionsResourceProvider.java +++ b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureFunctionsResourceProvider.java @@ -6,6 +6,7 @@ package io.opentelemetry.contrib.azure.resource; import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.resources.Resource; @@ -44,9 +45,13 @@ public AzureFunctionsResourceProvider() { @Override public Resource createResource(ConfigProperties config) { + return Resource.create(getAttributes()); + } + + public Attributes getAttributes() { AzureEnvVarPlatform detect = AzureEnvVarPlatform.detect(env); if (detect != AzureEnvVarPlatform.FUNCTIONS) { - return Resource.empty(); + return Attributes.empty(); } AttributesBuilder builder = @@ -60,6 +65,6 @@ public Resource createResource(ConfigProperties config) { AzureEnvVarPlatform.addAttributesFromEnv(ENV_VAR_MAPPING, env, builder); - return Resource.create(builder.build()); + return builder.build(); } } diff --git a/azure-resources/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider b/azure-resources/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider index bb9b46000..19104d8c4 100644 --- a/azure-resources/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider +++ b/azure-resources/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider @@ -1,2 +1,3 @@ io.opentelemetry.contrib.azure.resource.AzureAppServiceResourceProvider +io.opentelemetry.contrib.azure.resource.AzureFunctionsResourceProvider io.opentelemetry.contrib.azure.resource.AzureVmResourceProvider From 0c63c43f13cef9799bafe49e5503371b1a317ee0 Mon Sep 17 00:00:00 2001 From: heyams Date: Thu, 18 Apr 2024 14:54:02 -0700 Subject: [PATCH 08/13] Update --- .../contrib/azure/resource/AzureContainersResourceProvider.java | 2 +- .../contrib/azure/resource/AzureEnvVarPlatform.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureContainersResourceProvider.java b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureContainersResourceProvider.java index 67717f2f9..3d1367a59 100644 --- a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureContainersResourceProvider.java +++ b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureContainersResourceProvider.java @@ -46,7 +46,7 @@ public Resource createResource(ConfigProperties config) { return Resource.create(getAttributes()); } - private Attributes getAttributes() { + public Attributes getAttributes() { AzureEnvVarPlatform detect = AzureEnvVarPlatform.detect(env); if (detect != AzureEnvVarPlatform.CONTAINERS) { return Attributes.empty(); diff --git a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureEnvVarPlatform.java b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureEnvVarPlatform.java index d95b680b7..2d9c528d5 100644 --- a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureEnvVarPlatform.java +++ b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureEnvVarPlatform.java @@ -15,7 +15,7 @@ public enum AzureEnvVarPlatform { CONTAINERS, NONE; - static AzureEnvVarPlatform detect(Map env) { + public static AzureEnvVarPlatform detect(Map env) { String appName = env.get(AzureContainersResourceProvider.CONTAINER_APP_NAME); if (appName != null) { return CONTAINERS; From 03ee7282bbd095534804821ed626b2824d782be0 Mon Sep 17 00:00:00 2001 From: heyams Date: Thu, 18 Apr 2024 14:58:59 -0700 Subject: [PATCH 09/13] Rename enum --- .../azure/resource/AzureContainersResourceProvider.java | 2 +- .../contrib/azure/resource/AzureEnvVarPlatform.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureContainersResourceProvider.java b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureContainersResourceProvider.java index 3d1367a59..e4e57e96d 100644 --- a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureContainersResourceProvider.java +++ b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureContainersResourceProvider.java @@ -48,7 +48,7 @@ public Resource createResource(ConfigProperties config) { public Attributes getAttributes() { AzureEnvVarPlatform detect = AzureEnvVarPlatform.detect(env); - if (detect != AzureEnvVarPlatform.CONTAINERS) { + if (detect != AzureEnvVarPlatform.CONTAINER_APP) { return Attributes.empty(); } diff --git a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureEnvVarPlatform.java b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureEnvVarPlatform.java index 2d9c528d5..257f05ba8 100644 --- a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureEnvVarPlatform.java +++ b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureEnvVarPlatform.java @@ -12,13 +12,13 @@ public enum AzureEnvVarPlatform { APP_SERVICE, FUNCTIONS, - CONTAINERS, + CONTAINER_APP, NONE; public static AzureEnvVarPlatform detect(Map env) { String appName = env.get(AzureContainersResourceProvider.CONTAINER_APP_NAME); if (appName != null) { - return CONTAINERS; + return CONTAINER_APP; } String name = env.get(AzureAppServiceResourceProvider.WEBSITE_SITE_NAME); if (name == null) { From 68dbb23bc8d6dfa2d001bf4ec21a1294e3018194 Mon Sep 17 00:00:00 2001 From: heyams Date: Wed, 5 Jun 2024 12:09:29 -0700 Subject: [PATCH 10/13] Fix deprecated ResourceAttributes --- azure-resources/build.gradle.kts | 5 +-- .../AzureAppServiceResourceProvider.java | 23 ++++++++----- .../AzureContainersResourceProvider.java | 11 ++++--- .../AzureFunctionsResourceProvider.java | 21 ++++++------ .../resource/AzureVmResourceProvider.java | 32 ++++++++++++------- .../azure/resource/CloudResourceProvider.java | 5 +-- 6 files changed, 59 insertions(+), 38 deletions(-) diff --git a/azure-resources/build.gradle.kts b/azure-resources/build.gradle.kts index 5e4da0244..9cfff9133 100644 --- a/azure-resources/build.gradle.kts +++ b/azure-resources/build.gradle.kts @@ -17,10 +17,7 @@ dependencies { api("io.opentelemetry:opentelemetry-api") api("io.opentelemetry:opentelemetry-sdk") - // Provides GCP resource detection support -// implementation("com.google.cloud.opentelemetry:detector-resources-support:0.27.0") - - implementation("io.opentelemetry.semconv:opentelemetry-semconv") + implementation("io.opentelemetry.semconv:opentelemetry-semconv-incubating") compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure") diff --git a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureAppServiceResourceProvider.java b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureAppServiceResourceProvider.java index b5ede6744..cc77f3df0 100644 --- a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureAppServiceResourceProvider.java +++ b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureAppServiceResourceProvider.java @@ -5,13 +5,20 @@ package io.opentelemetry.contrib.azure.resource; +import static io.opentelemetry.semconv.ServiceAttributes.SERVICE_NAME; +import static io.opentelemetry.semconv.incubating.CloudIncubatingAttributes.CLOUD_REGION; +import static io.opentelemetry.semconv.incubating.CloudIncubatingAttributes.CLOUD_RESOURCE_ID; +import static io.opentelemetry.semconv.incubating.DeploymentIncubatingAttributes.DEPLOYMENT_ENVIRONMENT; +import static io.opentelemetry.semconv.incubating.HostIncubatingAttributes.HOST_ID; +import static io.opentelemetry.semconv.incubating.ServiceIncubatingAttributes.SERVICE_INSTANCE_ID; + import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.api.internal.StringUtils; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.resources.Resource; -import io.opentelemetry.semconv.ResourceAttributes; +import io.opentelemetry.semconv.incubating.CloudIncubatingAttributes; import java.util.HashMap; import java.util.Map; import java.util.Objects; @@ -33,10 +40,10 @@ public class AzureAppServiceResourceProvider extends CloudResourceProvider { private static final Map, String> ENV_VAR_MAPPING = new HashMap<>(); static { - ENV_VAR_MAPPING.put(ResourceAttributes.CLOUD_REGION, REGION_NAME); - ENV_VAR_MAPPING.put(ResourceAttributes.DEPLOYMENT_ENVIRONMENT, WEBSITE_SLOT_NAME); - ENV_VAR_MAPPING.put(ResourceAttributes.HOST_ID, WEBSITE_HOSTNAME); - ENV_VAR_MAPPING.put(ResourceAttributes.SERVICE_INSTANCE_ID, WEBSITE_INSTANCE_ID); + ENV_VAR_MAPPING.put(CLOUD_REGION, REGION_NAME); + ENV_VAR_MAPPING.put(DEPLOYMENT_ENVIRONMENT, WEBSITE_SLOT_NAME); + ENV_VAR_MAPPING.put(HOST_ID, WEBSITE_HOSTNAME); + ENV_VAR_MAPPING.put(SERVICE_INSTANCE_ID, WEBSITE_INSTANCE_ID); ENV_VAR_MAPPING.put(AZURE_APP_SERVICE_STAMP_RESOURCE_ATTRIBUTE, WEBSITE_HOME_STAMPNAME); } @@ -65,12 +72,12 @@ public Attributes getAttributes() { String name = Objects.requireNonNull(env.get(WEBSITE_SITE_NAME)); AttributesBuilder builder = AzureVmResourceProvider.azureAttributeBuilder( - ResourceAttributes.CloudPlatformValues.AZURE_APP_SERVICE); - builder.put(ResourceAttributes.SERVICE_NAME, name); + CloudIncubatingAttributes.CloudPlatformValues.AZURE_APP_SERVICE); + builder.put(SERVICE_NAME, name); String resourceUri = resourceUri(name); if (resourceUri != null) { - builder.put(ResourceAttributes.CLOUD_RESOURCE_ID, resourceUri); + builder.put(CLOUD_RESOURCE_ID, resourceUri); } AzureEnvVarPlatform.addAttributesFromEnv(ENV_VAR_MAPPING, env, builder); diff --git a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureContainersResourceProvider.java b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureContainersResourceProvider.java index e4e57e96d..be588e0ff 100644 --- a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureContainersResourceProvider.java +++ b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureContainersResourceProvider.java @@ -5,12 +5,15 @@ package io.opentelemetry.contrib.azure.resource; +import static io.opentelemetry.semconv.ServiceAttributes.SERVICE_NAME; +import static io.opentelemetry.semconv.ServiceAttributes.SERVICE_VERSION; +import static io.opentelemetry.semconv.incubating.ServiceIncubatingAttributes.SERVICE_INSTANCE_ID; + import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.resources.Resource; -import io.opentelemetry.semconv.ResourceAttributes; import java.util.HashMap; import java.util.Map; @@ -24,9 +27,9 @@ public class AzureContainersResourceProvider extends CloudResourceProvider { private static final Map, String> ENV_VAR_MAPPING = new HashMap<>(); static { - ENV_VAR_MAPPING.put(ResourceAttributes.SERVICE_NAME, CONTAINER_APP_NAME); - ENV_VAR_MAPPING.put(ResourceAttributes.SERVICE_INSTANCE_ID, CONTAINER_APP_REPLICA_NAME); - ENV_VAR_MAPPING.put(ResourceAttributes.SERVICE_VERSION, CONTAINER_APP_REVISION); + ENV_VAR_MAPPING.put(SERVICE_NAME, CONTAINER_APP_NAME); + ENV_VAR_MAPPING.put(SERVICE_INSTANCE_ID, CONTAINER_APP_REPLICA_NAME); + ENV_VAR_MAPPING.put(SERVICE_VERSION, CONTAINER_APP_REVISION); } private final Map env; diff --git a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureFunctionsResourceProvider.java b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureFunctionsResourceProvider.java index feddd14d7..0d6504325 100644 --- a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureFunctionsResourceProvider.java +++ b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureFunctionsResourceProvider.java @@ -5,12 +5,17 @@ package io.opentelemetry.contrib.azure.resource; +import static io.opentelemetry.semconv.incubating.FaasIncubatingAttributes.FAAS_INSTANCE; +import static io.opentelemetry.semconv.incubating.FaasIncubatingAttributes.FAAS_MAX_MEMORY; +import static io.opentelemetry.semconv.incubating.FaasIncubatingAttributes.FAAS_NAME; +import static io.opentelemetry.semconv.incubating.FaasIncubatingAttributes.FAAS_VERSION; + import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.resources.Resource; -import io.opentelemetry.semconv.ResourceAttributes; +import io.opentelemetry.semconv.incubating.CloudIncubatingAttributes; import java.util.HashMap; import java.util.Map; @@ -23,12 +28,10 @@ public class AzureFunctionsResourceProvider extends CloudResourceProvider { static { ENV_VAR_MAPPING.put( - ResourceAttributes.CLOUD_REGION, AzureAppServiceResourceProvider.REGION_NAME); - ENV_VAR_MAPPING.put( - ResourceAttributes.FAAS_NAME, AzureAppServiceResourceProvider.WEBSITE_SITE_NAME); - ENV_VAR_MAPPING.put(ResourceAttributes.FAAS_VERSION, FUNCTIONS_VERSION); - ENV_VAR_MAPPING.put( - ResourceAttributes.FAAS_INSTANCE, AzureAppServiceResourceProvider.WEBSITE_INSTANCE_ID); + CloudIncubatingAttributes.CLOUD_REGION, AzureAppServiceResourceProvider.REGION_NAME); + ENV_VAR_MAPPING.put(FAAS_NAME, AzureAppServiceResourceProvider.WEBSITE_SITE_NAME); + ENV_VAR_MAPPING.put(FAAS_VERSION, FUNCTIONS_VERSION); + ENV_VAR_MAPPING.put(FAAS_INSTANCE, AzureAppServiceResourceProvider.WEBSITE_INSTANCE_ID); } private final Map env; @@ -56,11 +59,11 @@ public Attributes getAttributes() { AttributesBuilder builder = AzureVmResourceProvider.azureAttributeBuilder( - ResourceAttributes.CloudPlatformValues.AZURE_FUNCTIONS); + CloudIncubatingAttributes.CloudPlatformValues.AZURE_FUNCTIONS); String limit = env.get(FUNCTIONS_MEM_LIMIT); if (limit != null) { - builder.put(ResourceAttributes.FAAS_MAX_MEMORY, Long.parseLong(limit)); + builder.put(FAAS_MAX_MEMORY, Long.parseLong(limit)); } AzureEnvVarPlatform.addAttributesFromEnv(ENV_VAR_MAPPING, env, builder); diff --git a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureVmResourceProvider.java b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureVmResourceProvider.java index 5c0098311..5e0218589 100644 --- a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureVmResourceProvider.java +++ b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureVmResourceProvider.java @@ -5,6 +5,16 @@ package io.opentelemetry.contrib.azure.resource; +import static io.opentelemetry.semconv.incubating.CloudIncubatingAttributes.CLOUD_PLATFORM; +import static io.opentelemetry.semconv.incubating.CloudIncubatingAttributes.CLOUD_PROVIDER; +import static io.opentelemetry.semconv.incubating.CloudIncubatingAttributes.CLOUD_REGION; +import static io.opentelemetry.semconv.incubating.CloudIncubatingAttributes.CLOUD_RESOURCE_ID; +import static io.opentelemetry.semconv.incubating.HostIncubatingAttributes.HOST_ID; +import static io.opentelemetry.semconv.incubating.HostIncubatingAttributes.HOST_NAME; +import static io.opentelemetry.semconv.incubating.HostIncubatingAttributes.HOST_TYPE; +import static io.opentelemetry.semconv.incubating.OsIncubatingAttributes.OS_TYPE; +import static io.opentelemetry.semconv.incubating.OsIncubatingAttributes.OS_VERSION; + import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; @@ -13,7 +23,7 @@ import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.resources.Resource; -import io.opentelemetry.semconv.ResourceAttributes; +import io.opentelemetry.semconv.incubating.CloudIncubatingAttributes; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; @@ -36,13 +46,13 @@ public class AzureVmResourceProvider extends CloudResourceProvider { private static final Map> COMPUTE_MAPPING = new HashMap<>(); static { - COMPUTE_MAPPING.put("location", ResourceAttributes.CLOUD_REGION); - COMPUTE_MAPPING.put("resourceId", ResourceAttributes.CLOUD_RESOURCE_ID); - COMPUTE_MAPPING.put("vmId", ResourceAttributes.HOST_ID); - COMPUTE_MAPPING.put("name", ResourceAttributes.HOST_NAME); - COMPUTE_MAPPING.put("vmSize", ResourceAttributes.HOST_TYPE); - COMPUTE_MAPPING.put("osType", ResourceAttributes.OS_TYPE); - COMPUTE_MAPPING.put("version", ResourceAttributes.OS_VERSION); + COMPUTE_MAPPING.put("location", CLOUD_REGION); + COMPUTE_MAPPING.put("resourceId", CLOUD_RESOURCE_ID); + COMPUTE_MAPPING.put("vmId", HOST_ID); + COMPUTE_MAPPING.put("name", HOST_NAME); + COMPUTE_MAPPING.put("vmSize", HOST_TYPE); + COMPUTE_MAPPING.put("osType", OS_TYPE); + COMPUTE_MAPPING.put("version", OS_VERSION); COMPUTE_MAPPING.put("vmScaleSetName", AttributeKey.stringKey("azure.vm.scaleset.name")); COMPUTE_MAPPING.put("sku", AttributeKey.stringKey("azure.vm.sku")); } @@ -87,7 +97,7 @@ public Resource createResource(ConfigProperties config) { private static Resource parseMetadata(String body) { AttributesBuilder builder = - azureAttributeBuilder(ResourceAttributes.CloudPlatformValues.AZURE_VM); + azureAttributeBuilder(CloudIncubatingAttributes.CloudPlatformValues.AZURE_VM); try (JsonParser parser = JSON_FACTORY.createParser(body)) { parser.nextToken(); parseResponse(parser, builder); @@ -100,8 +110,8 @@ private static Resource parseMetadata(String body) { @NotNull static AttributesBuilder azureAttributeBuilder(String platform) { AttributesBuilder builder = Attributes.builder(); - builder.put(ResourceAttributes.CLOUD_PROVIDER, ResourceAttributes.CloudProviderValues.AZURE); - builder.put(ResourceAttributes.CLOUD_PLATFORM, platform); + builder.put(CLOUD_PROVIDER, CloudIncubatingAttributes.CloudProviderValues.AZURE); + builder.put(CLOUD_PLATFORM, platform); return builder; } diff --git a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/CloudResourceProvider.java b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/CloudResourceProvider.java index 10758bd48..96fdeef27 100644 --- a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/CloudResourceProvider.java +++ b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/CloudResourceProvider.java @@ -5,15 +5,16 @@ package io.opentelemetry.contrib.azure.resource; +import static io.opentelemetry.semconv.incubating.CloudIncubatingAttributes.CLOUD_PROVIDER; + import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.internal.ConditionalResourceProvider; import io.opentelemetry.sdk.resources.Resource; -import io.opentelemetry.semconv.ResourceAttributes; public abstract class CloudResourceProvider implements ConditionalResourceProvider { @Override public final boolean shouldApply(ConfigProperties config, Resource existing) { - return existing.getAttribute(ResourceAttributes.CLOUD_PROVIDER) == null; + return existing.getAttribute(CLOUD_PROVIDER) == null; } } From 9cd5ec5be9a4c46894cb5203f07ba1f105ff0d44 Mon Sep 17 00:00:00 2001 From: heyams Date: Wed, 5 Jun 2024 12:15:05 -0700 Subject: [PATCH 11/13] Fix tests --- .../AzureAppServiceResourceProviderTest.java | 30 ++++++++++------ .../AzureContainersResourceProviderTest.java | 17 +++++---- .../AzureFunctionsResourceProviderTest.java | 20 +++++++---- .../resource/AzureVmResourceProviderTest.java | 36 +++++++++++-------- 4 files changed, 65 insertions(+), 38 deletions(-) diff --git a/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureAppServiceResourceProviderTest.java b/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureAppServiceResourceProviderTest.java index 1cc3e996f..936c6b7c7 100644 --- a/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureAppServiceResourceProviderTest.java +++ b/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureAppServiceResourceProviderTest.java @@ -5,10 +5,18 @@ package io.opentelemetry.contrib.azure.resource; +import static io.opentelemetry.semconv.ServiceAttributes.SERVICE_NAME; +import static io.opentelemetry.semconv.incubating.CloudIncubatingAttributes.CLOUD_PLATFORM; +import static io.opentelemetry.semconv.incubating.CloudIncubatingAttributes.CLOUD_PROVIDER; +import static io.opentelemetry.semconv.incubating.CloudIncubatingAttributes.CLOUD_REGION; +import static io.opentelemetry.semconv.incubating.CloudIncubatingAttributes.CLOUD_RESOURCE_ID; +import static io.opentelemetry.semconv.incubating.DeploymentIncubatingAttributes.DEPLOYMENT_ENVIRONMENT; +import static io.opentelemetry.semconv.incubating.HostIncubatingAttributes.HOST_ID; +import static io.opentelemetry.semconv.incubating.ServiceIncubatingAttributes.SERVICE_INSTANCE_ID; + import com.google.common.collect.ImmutableMap; import io.opentelemetry.sdk.testing.assertj.AttributesAssert; import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; -import io.opentelemetry.semconv.ResourceAttributes; import java.util.HashMap; import java.util.Map; import org.jetbrains.annotations.NotNull; @@ -38,16 +46,16 @@ class AzureAppServiceResourceProviderTest { @Test void defaultValues() { createResource(DEFAULT_ENV_VARS) - .containsEntry(ResourceAttributes.SERVICE_NAME, TEST_WEBSITE_SITE_NAME) - .containsEntry(ResourceAttributes.CLOUD_PROVIDER, "azure") - .containsEntry(ResourceAttributes.CLOUD_PLATFORM, "azure_app_service") + .containsEntry(SERVICE_NAME, TEST_WEBSITE_SITE_NAME) + .containsEntry(CLOUD_PROVIDER, "azure") + .containsEntry(CLOUD_PLATFORM, "azure_app_service") .containsEntry( - ResourceAttributes.CLOUD_RESOURCE_ID, + CLOUD_RESOURCE_ID, "/subscriptions/TEST_WEBSITE_OWNER_NAME/resourceGroups/TEST_WEBSITE_RESOURCE_GROUP/providers/Microsoft.Web/sites/TEST_WEBSITE_SITE_NAME") - .containsEntry(ResourceAttributes.CLOUD_REGION, TEST_REGION_NAME) - .containsEntry(ResourceAttributes.DEPLOYMENT_ENVIRONMENT, TEST_WEBSITE_SLOT_NAME) - .containsEntry(ResourceAttributes.HOST_ID, TEST_WEBSITE_HOSTNAME) - .containsEntry(ResourceAttributes.SERVICE_INSTANCE_ID, TEST_WEBSITE_INSTANCE_ID) + .containsEntry(CLOUD_REGION, TEST_REGION_NAME) + .containsEntry(DEPLOYMENT_ENVIRONMENT, TEST_WEBSITE_SLOT_NAME) + .containsEntry(HOST_ID, TEST_WEBSITE_HOSTNAME) + .containsEntry(SERVICE_INSTANCE_ID, TEST_WEBSITE_INSTANCE_ID) .containsEntry( AzureAppServiceResourceProvider.AZURE_APP_SERVICE_STAMP_RESOURCE_ATTRIBUTE, TEST_WEBSITE_HOME_STAMPNAME); @@ -60,7 +68,7 @@ void subscriptionFromOwner() { createResource(map) .containsEntry( - ResourceAttributes.CLOUD_RESOURCE_ID, + CLOUD_RESOURCE_ID, "/subscriptions/foo/resourceGroups/TEST_WEBSITE_RESOURCE_GROUP/providers/Microsoft.Web/sites/TEST_WEBSITE_SITE_NAME"); } @@ -69,7 +77,7 @@ void noResourceId() { HashMap map = new HashMap<>(DEFAULT_ENV_VARS); map.remove("WEBSITE_RESOURCE_GROUP"); - createResource(map).doesNotContainKey(ResourceAttributes.CLOUD_RESOURCE_ID); + createResource(map).doesNotContainKey(CLOUD_RESOURCE_ID); } @Test diff --git a/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureContainersResourceProviderTest.java b/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureContainersResourceProviderTest.java index 64ff2842d..5ac1a4be7 100644 --- a/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureContainersResourceProviderTest.java +++ b/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureContainersResourceProviderTest.java @@ -5,10 +5,15 @@ package io.opentelemetry.contrib.azure.resource; +import static io.opentelemetry.semconv.ServiceAttributes.SERVICE_NAME; +import static io.opentelemetry.semconv.ServiceAttributes.SERVICE_VERSION; +import static io.opentelemetry.semconv.incubating.CloudIncubatingAttributes.CLOUD_PLATFORM; +import static io.opentelemetry.semconv.incubating.CloudIncubatingAttributes.CLOUD_PROVIDER; +import static io.opentelemetry.semconv.incubating.ServiceIncubatingAttributes.SERVICE_INSTANCE_ID; + import com.google.common.collect.ImmutableMap; import io.opentelemetry.sdk.testing.assertj.AttributesAssert; import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; -import io.opentelemetry.semconv.ResourceAttributes; import java.util.HashMap; import java.util.Map; import org.jetbrains.annotations.NotNull; @@ -28,11 +33,11 @@ class AzureContainersResourceProviderTest { @Test void defaultValues() { createResource(DEFAULT_ENV_VARS) - .containsEntry(ResourceAttributes.CLOUD_PROVIDER, "azure") - .containsEntry(ResourceAttributes.CLOUD_PLATFORM, "azure_container_apps") - .containsEntry(ResourceAttributes.SERVICE_NAME, TEST_APP_NAME) - .containsEntry(ResourceAttributes.SERVICE_INSTANCE_ID, TEST_REPLICA_NAME) - .containsEntry(ResourceAttributes.SERVICE_VERSION, TEST_REVISION); + .containsEntry(CLOUD_PROVIDER, "azure") + .containsEntry(CLOUD_PLATFORM, "azure_container_apps") + .containsEntry(SERVICE_NAME, TEST_APP_NAME) + .containsEntry(SERVICE_INSTANCE_ID, TEST_REPLICA_NAME) + .containsEntry(SERVICE_VERSION, TEST_REVISION); } @Test diff --git a/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureFunctionsResourceProviderTest.java b/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureFunctionsResourceProviderTest.java index 0146ee518..520e44543 100644 --- a/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureFunctionsResourceProviderTest.java +++ b/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureFunctionsResourceProviderTest.java @@ -5,10 +5,16 @@ package io.opentelemetry.contrib.azure.resource; +import static io.opentelemetry.semconv.incubating.CloudIncubatingAttributes.CLOUD_PLATFORM; +import static io.opentelemetry.semconv.incubating.CloudIncubatingAttributes.CLOUD_PROVIDER; +import static io.opentelemetry.semconv.incubating.FaasIncubatingAttributes.FAAS_INSTANCE; +import static io.opentelemetry.semconv.incubating.FaasIncubatingAttributes.FAAS_MAX_MEMORY; +import static io.opentelemetry.semconv.incubating.FaasIncubatingAttributes.FAAS_NAME; +import static io.opentelemetry.semconv.incubating.FaasIncubatingAttributes.FAAS_VERSION; + import com.google.common.collect.ImmutableMap; import io.opentelemetry.sdk.testing.assertj.AttributesAssert; import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; -import io.opentelemetry.semconv.ResourceAttributes; import java.util.HashMap; import java.util.Map; import org.jetbrains.annotations.NotNull; @@ -31,12 +37,12 @@ class AzureFunctionsResourceProviderTest { @Test void defaultValues() { createResource(DEFAULT_ENV_VARS) - .containsEntry(ResourceAttributes.CLOUD_PROVIDER, "azure") - .containsEntry(ResourceAttributes.CLOUD_PLATFORM, "azure_functions") - .containsEntry(ResourceAttributes.FAAS_NAME, TEST_WEBSITE_SITE_NAME) - .containsEntry(ResourceAttributes.FAAS_VERSION, TEST_FUNCTION_VERSION) - .containsEntry(ResourceAttributes.FAAS_INSTANCE, TEST_WEBSITE_INSTANCE_ID) - .containsEntry(ResourceAttributes.FAAS_MAX_MEMORY, Long.parseLong(TEST_MEM_LIMIT)); + .containsEntry(CLOUD_PROVIDER, "azure") + .containsEntry(CLOUD_PLATFORM, "azure_functions") + .containsEntry(FAAS_NAME, TEST_WEBSITE_SITE_NAME) + .containsEntry(FAAS_VERSION, TEST_FUNCTION_VERSION) + .containsEntry(FAAS_INSTANCE, TEST_WEBSITE_INSTANCE_ID) + .containsEntry(FAAS_MAX_MEMORY, Long.parseLong(TEST_MEM_LIMIT)); } @Test diff --git a/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureVmResourceProviderTest.java b/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureVmResourceProviderTest.java index 88a0ceacb..46f103b45 100644 --- a/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureVmResourceProviderTest.java +++ b/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureVmResourceProviderTest.java @@ -5,6 +5,16 @@ package io.opentelemetry.contrib.azure.resource; +import static io.opentelemetry.semconv.incubating.CloudIncubatingAttributes.CLOUD_PLATFORM; +import static io.opentelemetry.semconv.incubating.CloudIncubatingAttributes.CLOUD_PROVIDER; +import static io.opentelemetry.semconv.incubating.CloudIncubatingAttributes.CLOUD_REGION; +import static io.opentelemetry.semconv.incubating.CloudIncubatingAttributes.CLOUD_RESOURCE_ID; +import static io.opentelemetry.semconv.incubating.HostIncubatingAttributes.HOST_ID; +import static io.opentelemetry.semconv.incubating.HostIncubatingAttributes.HOST_NAME; +import static io.opentelemetry.semconv.incubating.HostIncubatingAttributes.HOST_TYPE; +import static io.opentelemetry.semconv.incubating.OsIncubatingAttributes.OS_TYPE; +import static org.mockito.internal.util.Platform.OS_VERSION; + import com.google.common.base.Charsets; import com.google.common.io.CharStreams; import com.linecorp.armeria.common.HttpResponse; @@ -14,7 +24,7 @@ import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.testing.assertj.AttributesAssert; import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; -import io.opentelemetry.semconv.ResourceAttributes; +import io.opentelemetry.semconv.incubating.CloudIncubatingAttributes; import java.io.IOException; import java.io.InputStreamReader; import java.net.MalformedURLException; @@ -80,25 +90,23 @@ private static AttributesAssert createResource(Supplier> client private static void assertOnlyProvider(AttributesAssert attributesAssert) { attributesAssert .hasSize(2) - .containsEntry(ResourceAttributes.CLOUD_PROVIDER, "azure") - .containsEntry( - ResourceAttributes.CLOUD_PLATFORM, ResourceAttributes.CloudPlatformValues.AZURE_VM); + .containsEntry(CLOUD_PROVIDER, "azure") + .containsEntry(CLOUD_PLATFORM, CloudIncubatingAttributes.CloudPlatformValues.AZURE_VM); } private static void assertDefaultAttributes(AttributesAssert attributesAssert) { attributesAssert - .containsEntry(ResourceAttributes.CLOUD_PROVIDER, "azure") - .containsEntry( - ResourceAttributes.CLOUD_PLATFORM, ResourceAttributes.CloudPlatformValues.AZURE_VM) - .containsEntry(ResourceAttributes.CLOUD_REGION, "westus") + .containsEntry(CLOUD_PROVIDER, "azure") + .containsEntry(CLOUD_PLATFORM, CloudIncubatingAttributes.CloudPlatformValues.AZURE_VM) + .containsEntry(CLOUD_REGION, "westus") .containsEntry( - ResourceAttributes.CLOUD_RESOURCE_ID, + CLOUD_RESOURCE_ID, "/subscriptions/xxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/resourceGroups/macikgo-test-may-23/providers/Microsoft.Compute/virtualMachines/examplevmname") - .containsEntry(ResourceAttributes.HOST_ID, "02aab8a4-74ef-476e-8182-f6d2ba4166a6") - .containsEntry(ResourceAttributes.HOST_NAME, "examplevmname") - .containsEntry(ResourceAttributes.HOST_TYPE, "Standard_A3") - .containsEntry(ResourceAttributes.OS_TYPE, "Linux") - .containsEntry(ResourceAttributes.OS_VERSION, "15.05.22") + .containsEntry(HOST_ID, "02aab8a4-74ef-476e-8182-f6d2ba4166a6") + .containsEntry(HOST_NAME, "examplevmname") + .containsEntry(HOST_TYPE, "Standard_A3") + .containsEntry(OS_TYPE, "Linux") + .containsEntry(OS_VERSION, "15.05.22") .containsEntry("azure.vm.scaleset.name", "crpteste9vflji9") .containsEntry("azure.vm.sku", "18.04-LTS"); } From 9ea505d2c62ebc08f0eae02da68c18f23316c0f3 Mon Sep 17 00:00:00 2001 From: heyams Date: Wed, 5 Jun 2024 13:04:47 -0700 Subject: [PATCH 12/13] Fix import --- .../contrib/azure/resource/AzureVmResourceProviderTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureVmResourceProviderTest.java b/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureVmResourceProviderTest.java index 46f103b45..ee7ed3c14 100644 --- a/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureVmResourceProviderTest.java +++ b/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureVmResourceProviderTest.java @@ -13,7 +13,7 @@ import static io.opentelemetry.semconv.incubating.HostIncubatingAttributes.HOST_NAME; import static io.opentelemetry.semconv.incubating.HostIncubatingAttributes.HOST_TYPE; import static io.opentelemetry.semconv.incubating.OsIncubatingAttributes.OS_TYPE; -import static org.mockito.internal.util.Platform.OS_VERSION; +import static io.opentelemetry.semconv.incubating.OsIncubatingAttributes.OS_VERSION; import com.google.common.base.Charsets; import com.google.common.io.CharStreams; From ecb2125b7421de9fc8560faf73e49eec4a0fdafc Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Fri, 9 Aug 2024 14:43:51 +0200 Subject: [PATCH 13/13] add aks resource provider --- .../resource/AzureAksResourceProvider.java | 76 +++++++++++ .../azure/resource/AzureMetadataService.java | 75 +++++++++++ .../resource/AzureVmResourceProvider.java | 122 +++++++----------- .../AzureAksResourceProviderTest.java | 62 +++++++++ .../resource/AzureVmResourceProviderTest.java | 93 ++----------- .../MetadataBasedResourceProviderTest.java | 109 ++++++++++++++++ 6 files changed, 379 insertions(+), 158 deletions(-) create mode 100644 azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureAksResourceProvider.java create mode 100644 azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureMetadataService.java create mode 100644 azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureAksResourceProviderTest.java create mode 100644 azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/MetadataBasedResourceProviderTest.java diff --git a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureAksResourceProvider.java b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureAksResourceProvider.java new file mode 100644 index 000000000..2b09174a8 --- /dev/null +++ b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureAksResourceProvider.java @@ -0,0 +1,76 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.contrib.azure.resource; + +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.semconv.incubating.CloudIncubatingAttributes; +import io.opentelemetry.semconv.incubating.K8sIncubatingAttributes; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.function.Supplier; + +public class AzureAksResourceProvider extends CloudResourceProvider { + + private static final Map COMPUTE_MAPPING = new HashMap<>(); + + static { + COMPUTE_MAPPING.put( + "resourceGroupName", + new AzureVmResourceProvider.Entry( + K8sIncubatingAttributes.K8S_CLUSTER_NAME, AzureAksResourceProvider::parseClusterName)); + } + + // visible for testing + static String parseClusterName(String resourceGroup) { + // Code inspired by + // https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/exporter/datadogexporter/internal/hostmetadata/internal/azure/provider.go#L36 + String[] splitAll = resourceGroup.split("_"); + if (splitAll.length == 4 && splitAll[0].equalsIgnoreCase("mc")) { + return splitAll[splitAll.length - 2]; + } + return resourceGroup; + } + + // Environment variable that is set when running on Kubernetes + static final String KUBERNETES_SERVICE_HOST = "KUBERNETES_SERVICE_HOST"; + private final Supplier> client; + private final Map environment; + + // SPI + public AzureAksResourceProvider() { + this(AzureMetadataService.defaultClient(), System.getenv()); + } + + // visible for testing + public AzureAksResourceProvider( + Supplier> client, Map environment) { + this.client = client; + this.environment = environment; + } + + @Override + public int order() { + // run after the fast cloud resource providers that only check environment variables + // and before the AKS provider + return 100; + } + + @Override + public Resource createResource(ConfigProperties configProperties) { + if (environment.get(KUBERNETES_SERVICE_HOST) == null) { + return Resource.empty(); + } + return client + .get() + .map( + body -> + AzureVmResourceProvider.parseMetadata( + body, COMPUTE_MAPPING, CloudIncubatingAttributes.CloudPlatformValues.AZURE_AKS)) + .orElse(Resource.empty()); + } +} diff --git a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureMetadataService.java b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureMetadataService.java new file mode 100644 index 000000000..07f67e240 --- /dev/null +++ b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureMetadataService.java @@ -0,0 +1,75 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.contrib.azure.resource; + +import com.fasterxml.jackson.core.JsonFactory; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.time.Duration; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Supplier; +import java.util.logging.Level; +import java.util.logging.Logger; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; + +public class AzureMetadataService { + static final JsonFactory JSON_FACTORY = new JsonFactory(); + private static final URL METADATA_URL; + + static { + try { + METADATA_URL = new URL("http://169.254.169.254/metadata/instance?api-version=2021-02-01"); + } catch (MalformedURLException e) { + throw new IllegalStateException(e); + } + } + + private AzureMetadataService() {} + + private static final Duration TIMEOUT = Duration.ofSeconds(1); + + private static final Logger logger = Logger.getLogger(AzureMetadataService.class.getName()); + + static Supplier> defaultClient() { + return () -> fetchMetadata(METADATA_URL); + } + + // visible for testing + static Optional fetchMetadata(URL url) { + OkHttpClient client = + new OkHttpClient.Builder() + .callTimeout(TIMEOUT) + .connectTimeout(TIMEOUT) + .readTimeout(TIMEOUT) + .build(); + + Request request = new Request.Builder().url(url).get().addHeader("Metadata", "true").build(); + + try (Response response = client.newCall(request).execute()) { + int responseCode = response.code(); + if (responseCode != 200) { + logger.log( + Level.FINE, + "Error response from " + + url + + " code (" + + responseCode + + ") text " + + response.message()); + return Optional.empty(); + } + + return Optional.of(Objects.requireNonNull(response.body()).string()); + } catch (IOException e) { + logger.log(Level.FINE, "Failed to fetch Azure VM metadata", e); + return Optional.empty(); + } + } +} diff --git a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureVmResourceProvider.java b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureVmResourceProvider.java index 5e0218589..059ff22f8 100644 --- a/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureVmResourceProvider.java +++ b/azure-resources/src/main/java/io/opentelemetry/contrib/azure/resource/AzureVmResourceProvider.java @@ -15,7 +15,6 @@ import static io.opentelemetry.semconv.incubating.OsIncubatingAttributes.OS_TYPE; import static io.opentelemetry.semconv.incubating.OsIncubatingAttributes.OS_VERSION; -import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; import io.opentelemetry.api.common.AttributeKey; @@ -25,58 +24,54 @@ import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.semconv.incubating.CloudIncubatingAttributes; import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; -import java.time.Duration; import java.util.HashMap; import java.util.Map; -import java.util.Objects; import java.util.Optional; import java.util.function.BiConsumer; +import java.util.function.Function; import java.util.function.Supplier; import java.util.logging.Level; import java.util.logging.Logger; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; import org.jetbrains.annotations.NotNull; public class AzureVmResourceProvider extends CloudResourceProvider { - private static final Map> COMPUTE_MAPPING = new HashMap<>(); + static class Entry { + final AttributeKey key; + final Function transform; - static { - COMPUTE_MAPPING.put("location", CLOUD_REGION); - COMPUTE_MAPPING.put("resourceId", CLOUD_RESOURCE_ID); - COMPUTE_MAPPING.put("vmId", HOST_ID); - COMPUTE_MAPPING.put("name", HOST_NAME); - COMPUTE_MAPPING.put("vmSize", HOST_TYPE); - COMPUTE_MAPPING.put("osType", OS_TYPE); - COMPUTE_MAPPING.put("version", OS_VERSION); - COMPUTE_MAPPING.put("vmScaleSetName", AttributeKey.stringKey("azure.vm.scaleset.name")); - COMPUTE_MAPPING.put("sku", AttributeKey.stringKey("azure.vm.sku")); - } - - private static final JsonFactory JSON_FACTORY = new JsonFactory(); + Entry(AttributeKey key) { + this(key, Function.identity()); + } - private static final Duration TIMEOUT = Duration.ofSeconds(1); + Entry(AttributeKey key, Function transform) { + this.key = key; + this.transform = transform; + } + } - private static final Logger logger = Logger.getLogger(AzureVmResourceProvider.class.getName()); - private static final URL METADATA_URL; + private static final Map COMPUTE_MAPPING = new HashMap<>(); static { - try { - METADATA_URL = new URL("http://169.254.169.254/metadata/instance?api-version=2021-02-01"); - } catch (MalformedURLException e) { - throw new IllegalStateException(e); - } + COMPUTE_MAPPING.put("location", new Entry(CLOUD_REGION)); + COMPUTE_MAPPING.put("resourceId", new Entry(CLOUD_RESOURCE_ID)); + COMPUTE_MAPPING.put("vmId", new Entry(HOST_ID)); + COMPUTE_MAPPING.put("name", new Entry(HOST_NAME)); + COMPUTE_MAPPING.put("vmSize", new Entry(HOST_TYPE)); + COMPUTE_MAPPING.put("osType", new Entry(OS_TYPE)); + COMPUTE_MAPPING.put("version", new Entry(OS_VERSION)); + COMPUTE_MAPPING.put( + "vmScaleSetName", new Entry(AttributeKey.stringKey("azure.vm.scaleset.name"))); + COMPUTE_MAPPING.put("sku", new Entry(AttributeKey.stringKey("azure.vm.sku"))); } + private static final Logger logger = Logger.getLogger(AzureVmResourceProvider.class.getName()); + private final Supplier> client; // SPI public AzureVmResourceProvider() { - this(() -> fetchMetadata(METADATA_URL)); + this(AzureMetadataService.defaultClient()); } // visible for testing @@ -87,20 +82,26 @@ public AzureVmResourceProvider(Supplier> client) { @Override public int order() { // run after the fast cloud resource providers that only check environment variables + // and after the AKS provider return 100; } @Override public Resource createResource(ConfigProperties config) { - return client.get().map(AzureVmResourceProvider::parseMetadata).orElse(Resource.empty()); + return client + .get() + .map( + body -> + parseMetadata( + body, COMPUTE_MAPPING, CloudIncubatingAttributes.CloudPlatformValues.AZURE_VM)) + .orElse(Resource.empty()); } - private static Resource parseMetadata(String body) { - AttributesBuilder builder = - azureAttributeBuilder(CloudIncubatingAttributes.CloudPlatformValues.AZURE_VM); - try (JsonParser parser = JSON_FACTORY.createParser(body)) { + static Resource parseMetadata(String body, Map computeMapping, String platform) { + AttributesBuilder builder = azureAttributeBuilder(platform); + try (JsonParser parser = AzureMetadataService.JSON_FACTORY.createParser(body)) { parser.nextToken(); - parseResponse(parser, builder); + parseResponse(parser, builder, computeMapping); } catch (IOException e) { logger.log(Level.FINE, "Can't get Azure VM metadata", e); } @@ -115,7 +116,9 @@ static AttributesBuilder azureAttributeBuilder(String platform) { return builder; } - static void parseResponse(JsonParser parser, AttributesBuilder builder) throws IOException { + static void parseResponse( + JsonParser parser, AttributesBuilder builder, Map computeMapping) + throws IOException { if (!parser.isExpectedStartObjectToken()) { logger.log(Level.FINE, "Couldn't parse ECS metadata, invalid JSON"); return; @@ -126,7 +129,7 @@ static void parseResponse(JsonParser parser, AttributesBuilder builder) throws I (name, value) -> { try { if (name.equals("compute")) { - consumeCompute(parser, builder); + consumeCompute(parser, builder, computeMapping); } else { parser.skipChildren(); } @@ -136,14 +139,15 @@ static void parseResponse(JsonParser parser, AttributesBuilder builder) throws I }); } - private static void consumeCompute(JsonParser parser, AttributesBuilder builder) + private static void consumeCompute( + JsonParser parser, AttributesBuilder builder, Map computeMapping) throws IOException { consumeJson( parser, (computeName, computeValue) -> { - AttributeKey key = COMPUTE_MAPPING.get(computeName); - if (key != null) { - builder.put(key, computeValue); + Entry entry = computeMapping.get(computeName); + if (entry != null) { + builder.put(entry.key, entry.transform.apply(computeValue)); } else { try { parser.skipChildren(); @@ -160,36 +164,4 @@ private static void consumeJson(JsonParser parser, BiConsumer co consumer.accept(parser.currentName(), parser.nextTextValue()); } } - - // visible for testing - static Optional fetchMetadata(URL url) { - OkHttpClient client = - new OkHttpClient.Builder() - .callTimeout(TIMEOUT) - .connectTimeout(TIMEOUT) - .readTimeout(TIMEOUT) - .build(); - - Request request = new Request.Builder().url(url).get().addHeader("Metadata", "true").build(); - - try (Response response = client.newCall(request).execute()) { - int responseCode = response.code(); - if (responseCode != 200) { - logger.log( - Level.FINE, - "Error response from " - + url - + " code (" - + responseCode - + ") text " - + response.message()); - return Optional.empty(); - } - - return Optional.of(Objects.requireNonNull(response.body()).string()); - } catch (IOException e) { - logger.log(Level.FINE, "Failed to fetch Azure VM metadata", e); - return Optional.empty(); - } - } } diff --git a/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureAksResourceProviderTest.java b/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureAksResourceProviderTest.java new file mode 100644 index 000000000..81787ebc1 --- /dev/null +++ b/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureAksResourceProviderTest.java @@ -0,0 +1,62 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.contrib.azure.resource; + +import static io.opentelemetry.semconv.incubating.CloudIncubatingAttributes.CLOUD_PLATFORM; +import static io.opentelemetry.semconv.incubating.CloudIncubatingAttributes.CLOUD_PROVIDER; +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider; +import io.opentelemetry.sdk.testing.assertj.AttributesAssert; +import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; +import io.opentelemetry.semconv.incubating.CloudIncubatingAttributes; +import io.opentelemetry.semconv.incubating.K8sIncubatingAttributes; +import java.util.Collections; +import java.util.Optional; +import java.util.function.Supplier; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Test; + +class AzureAksResourceProviderTest extends MetadataBasedResourceProviderTest { + + @NotNull + @Override + protected ResourceProvider getResourceProvider(Supplier> client) { + return new AzureAksResourceProvider( + client, + Collections.singletonMap(AzureAksResourceProvider.KUBERNETES_SERVICE_HOST, "localhost")); + } + + @Override + protected String getPlatform() { + return CloudIncubatingAttributes.CloudPlatformValues.AZURE_AKS; + } + + @Override + protected void assertDefaultAttributes(AttributesAssert attributesAssert) { + attributesAssert + .containsEntry(CLOUD_PROVIDER, "azure") + .containsEntry(CLOUD_PLATFORM, CloudIncubatingAttributes.CloudPlatformValues.AZURE_AKS) + .containsEntry(K8sIncubatingAttributes.K8S_CLUSTER_NAME, "macikgo-test-may-23"); + } + + @Test + void notOnK8s() { + AzureAksResourceProvider provider = + new AzureAksResourceProvider(() -> Optional.of(okResponse()), Collections.emptyMap()); + Attributes attributes = provider.createResource(null).getAttributes(); + OpenTelemetryAssertions.assertThat(attributes).isEmpty(); + } + + @Test + void parseClusterName() { + String clusterName = + AzureAksResourceProvider.parseClusterName( + "mc_macikgo-test-may-23_macikgo-test-may-23_eastus"); + assertThat(clusterName).isEqualTo("macikgo-test-may-23"); + } +} diff --git a/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureVmResourceProviderTest.java b/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureVmResourceProviderTest.java index ee7ed3c14..281416ce5 100644 --- a/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureVmResourceProviderTest.java +++ b/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/AzureVmResourceProviderTest.java @@ -15,86 +15,27 @@ import static io.opentelemetry.semconv.incubating.OsIncubatingAttributes.OS_TYPE; import static io.opentelemetry.semconv.incubating.OsIncubatingAttributes.OS_VERSION; -import com.google.common.base.Charsets; -import com.google.common.io.CharStreams; -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.sdk.resources.Resource; +import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider; import io.opentelemetry.sdk.testing.assertj.AttributesAssert; -import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; import io.opentelemetry.semconv.incubating.CloudIncubatingAttributes; -import java.io.IOException; -import java.io.InputStreamReader; -import java.net.MalformedURLException; -import java.util.Objects; import java.util.Optional; import java.util.function.Supplier; import org.jetbrains.annotations.NotNull; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; - -class AzureVmResourceProviderTest { - - @RegisterExtension - public static final MockWebServerExtension server = new MockWebServerExtension(); - - @Test - void successFromFile() { - assertDefaultAttributes(createResource(() -> Optional.of(okResponse()))); - } - - @Test - void successFromMockServer() { - server.enqueue(HttpResponse.of(MediaType.JSON, okResponse())); - assertDefaultAttributes(mockServerResponse()); - } - - @Test - void responseNotFound() { - server.enqueue(HttpResponse.of(HttpStatus.NOT_FOUND)); - mockServerResponse().isEmpty(); - } - - @Test - void responseEmpty() { - server.enqueue(HttpResponse.of("")); - assertOnlyProvider(mockServerResponse()); - } - - @Test - void responseEmptyJson() { - server.enqueue(HttpResponse.of("{}")); - assertOnlyProvider(mockServerResponse()); - } +class AzureVmResourceProviderTest extends MetadataBasedResourceProviderTest { @NotNull - private static AttributesAssert mockServerResponse() { - return createResource( - () -> { - try { - return AzureVmResourceProvider.fetchMetadata(server.httpUri().toURL()); - } catch (MalformedURLException e) { - throw new RuntimeException(e); - } - }); + @Override + protected ResourceProvider getResourceProvider(Supplier> client) { + return new AzureVmResourceProvider(client); } - @NotNull - private static AttributesAssert createResource(Supplier> client) { - Resource resource = new AzureVmResourceProvider(client).createResource(null); - return OpenTelemetryAssertions.assertThat(resource.getAttributes()); + @Override + protected String getPlatform() { + return CloudIncubatingAttributes.CloudPlatformValues.AZURE_VM; } - private static void assertOnlyProvider(AttributesAssert attributesAssert) { - attributesAssert - .hasSize(2) - .containsEntry(CLOUD_PROVIDER, "azure") - .containsEntry(CLOUD_PLATFORM, CloudIncubatingAttributes.CloudPlatformValues.AZURE_VM); - } - - private static void assertDefaultAttributes(AttributesAssert attributesAssert) { + @Override + protected void assertDefaultAttributes(AttributesAssert attributesAssert) { attributesAssert .containsEntry(CLOUD_PROVIDER, "azure") .containsEntry(CLOUD_PLATFORM, CloudIncubatingAttributes.CloudPlatformValues.AZURE_VM) @@ -110,18 +51,4 @@ private static void assertDefaultAttributes(AttributesAssert attributesAssert) { .containsEntry("azure.vm.scaleset.name", "crpteste9vflji9") .containsEntry("azure.vm.sku", "18.04-LTS"); } - - private static String okResponse() { - try { - return CharStreams.toString( - new InputStreamReader( - Objects.requireNonNull( - AzureVmResourceProviderTest.class - .getClassLoader() - .getResourceAsStream("response.json")), - Charsets.UTF_8)); - } catch (IOException e) { - throw new IllegalStateException(e); - } - } } diff --git a/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/MetadataBasedResourceProviderTest.java b/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/MetadataBasedResourceProviderTest.java new file mode 100644 index 000000000..2168af3a8 --- /dev/null +++ b/azure-resources/src/test/java/io/opentelemetry/contrib/azure/resource/MetadataBasedResourceProviderTest.java @@ -0,0 +1,109 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.contrib.azure.resource; + +import static io.opentelemetry.semconv.incubating.CloudIncubatingAttributes.CLOUD_PLATFORM; +import static io.opentelemetry.semconv.incubating.CloudIncubatingAttributes.CLOUD_PROVIDER; + +import com.google.common.base.Charsets; +import com.google.common.io.CharStreams; +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.sdk.autoconfigure.spi.ResourceProvider; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.sdk.testing.assertj.AttributesAssert; +import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.MalformedURLException; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Supplier; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +public abstract class MetadataBasedResourceProviderTest { + @RegisterExtension + public static final MockWebServerExtension server = new MockWebServerExtension(); + + @NotNull + private AttributesAssert mockServerResponse() { + return createResource( + () -> { + try { + return AzureMetadataService.fetchMetadata(server.httpUri().toURL()); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + }); + } + + @NotNull + private AttributesAssert createResource(Supplier> client) { + Resource resource = getResourceProvider(client).createResource(null); + return OpenTelemetryAssertions.assertThat(resource.getAttributes()); + } + + @NotNull + protected abstract ResourceProvider getResourceProvider(Supplier> client); + + private void assertOnlyProvider(AttributesAssert attributesAssert) { + attributesAssert + .hasSize(2) + .containsEntry(CLOUD_PROVIDER, "azure") + .containsEntry(CLOUD_PLATFORM, getPlatform()); + } + + protected abstract String getPlatform(); + + protected abstract void assertDefaultAttributes(AttributesAssert attributesAssert); + + protected static String okResponse() { + try { + return CharStreams.toString( + new InputStreamReader( + Objects.requireNonNull( + AzureVmResourceProviderTest.class + .getClassLoader() + .getResourceAsStream("response.json")), + Charsets.UTF_8)); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + @Test + public void successFromFile() { + assertDefaultAttributes(createResource(() -> Optional.of(okResponse()))); + } + + @Test + public void successFromMockServer() { + server.enqueue(HttpResponse.of(MediaType.JSON, okResponse())); + assertDefaultAttributes(mockServerResponse()); + } + + @Test + public void responseNotFound() { + server.enqueue(HttpResponse.of(HttpStatus.NOT_FOUND)); + mockServerResponse().isEmpty(); + } + + @Test + public void responseEmpty() { + server.enqueue(HttpResponse.of("")); + assertOnlyProvider(mockServerResponse()); + } + + @Test + public void responseEmptyJson() { + server.enqueue(HttpResponse.of("{}")); + assertOnlyProvider(mockServerResponse()); + } +}