From ff86c4c6927bb8dc94dd019d3dbc6aace71cb400 Mon Sep 17 00:00:00 2001 From: "jason.lo" Date: Wed, 16 Jun 2021 14:38:19 -0700 Subject: [PATCH 1/6] Support adding container.id to resource metadta --- .../opentelemetry-sdk-extension-resources.txt | 12 ++- .../resources/ContainerResource.java | 86 +++++++++++++++++++ .../resources/ContainerResourceProvider.java | 18 ++++ ...try.sdk.autoconfigure.spi.ResourceProvider | 1 + .../resources/ContainerResourceTest.java | 76 ++++++++++++++++ 5 files changed, 192 insertions(+), 1 deletion(-) create mode 100644 sdk-extensions/resources/src/main/java/io/opentelemetry/sdk/extension/resources/ContainerResource.java create mode 100644 sdk-extensions/resources/src/main/java/io/opentelemetry/sdk/extension/resources/ContainerResourceProvider.java create mode 100644 sdk-extensions/resources/src/test/java/io/opentelemetry/sdk/extension/resources/ContainerResourceTest.java diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-extension-resources.txt b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-extension-resources.txt index df26146497b..f697ff47c37 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-extension-resources.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-extension-resources.txt @@ -1,2 +1,12 @@ Comparing source compatibility of against -No changes. \ No newline at end of file ++++ NEW CLASS: PUBLIC(+) FINAL(+) io.opentelemetry.sdk.extension.resources.ContainerResource (not serializable) + +++ CLASS FILE FORMAT VERSION: 52.0 <- n.a. + +++ NEW SUPERCLASS: java.lang.Object + +++ NEW METHOD: PUBLIC(+) java.lang.String extractContainerId() + +++ NEW ANNOTATION: javax.annotation.Nullable + +++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.resources.Resource get() ++++ NEW CLASS: PUBLIC(+) io.opentelemetry.sdk.extension.resources.ContainerResourceProvider (not serializable) + +++ CLASS FILE FORMAT VERSION: 52.0 <- n.a. + +++ NEW SUPERCLASS: java.lang.Object + +++ NEW CONSTRUCTOR: PUBLIC(+) ContainerResourceProvider() + +++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.resources.Resource createResource(io.opentelemetry.sdk.autoconfigure.ConfigProperties) diff --git a/sdk-extensions/resources/src/main/java/io/opentelemetry/sdk/extension/resources/ContainerResource.java b/sdk-extensions/resources/src/main/java/io/opentelemetry/sdk/extension/resources/ContainerResource.java new file mode 100644 index 00000000000..4fd4b4c3984 --- /dev/null +++ b/sdk-extensions/resources/src/main/java/io/opentelemetry/sdk/extension/resources/ContainerResource.java @@ -0,0 +1,86 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.sdk.extension.resources; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.annotation.Nullable; + +/** Factory for {@link Resource} retrieving Container ID information. */ +public final class ContainerResource { + + private static final String UNIQUE_HOST_NAME_FILE_NAME = "/proc/self/cgroup"; + private static final Pattern HEX_EXTRACTOR = + Pattern.compile("^([\\w]*?-)?([a-fA-F0-9]+)(\\.[\\w]*?)?$"); + + private final String cgroupFilePath; + + // package private for testing purposes + ContainerResource(String cgroupFilePath) { + this.cgroupFilePath = cgroupFilePath; + } + + /** Returns resource with container information. */ + public static Resource get() { + ContainerResource factory = new ContainerResource(UNIQUE_HOST_NAME_FILE_NAME); + String containerId = factory.extractContainerId(); + + if (containerId == null) { + return Resource.empty(); + } + return Resource.create( + Attributes.builder().put(ResourceAttributes.CONTAINER_ID, containerId).build()); + } + + /** + * Each line of cgroup file looks like "14:name=systemd:/docker/.../... A hex string is expected + * inside the last section separated by '/' Each segment of the '/' can contain metadata separated + * by either '.' or '-' + * + *

We see this with CRI-O "crio-abcdef1234567890ABCDEF.freetext", then use {@linkplain + * ContainerResource#HEX_EXTRACTOR} to extract the container hex id + * + * @return containerId + */ + @Nullable + @SuppressWarnings("DefaultCharset") + public String extractContainerId() { + File nameFile = new File(cgroupFilePath); + if (nameFile.exists() && nameFile.canRead()) { + try (FileReader fr = new FileReader(nameFile); + BufferedReader br = new BufferedReader(fr); ) { + String line; + while ((line = br.readLine()) != null) { + if (!line.isEmpty()) { + // This cgroup output line should have the container id in it + String[] sections = line.split(File.separator); + if (sections.length > 1) { + String lastSection = sections[sections.length - 1]; + Matcher matcher = HEX_EXTRACTOR.matcher(lastSection); + if (matcher.matches() && matcher.group(2) != null && !matcher.group(2).isEmpty()) { + return matcher.group(2); + } + } + } + return null; + } + } catch (FileNotFoundException e) { + return null; + } catch (IOException e) { + return null; + } + } + return null; + } +} diff --git a/sdk-extensions/resources/src/main/java/io/opentelemetry/sdk/extension/resources/ContainerResourceProvider.java b/sdk-extensions/resources/src/main/java/io/opentelemetry/sdk/extension/resources/ContainerResourceProvider.java new file mode 100644 index 00000000000..bb9adb27698 --- /dev/null +++ b/sdk-extensions/resources/src/main/java/io/opentelemetry/sdk/extension/resources/ContainerResourceProvider.java @@ -0,0 +1,18 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.sdk.extension.resources; + +import io.opentelemetry.sdk.autoconfigure.ConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider; +import io.opentelemetry.sdk.resources.Resource; + +/** {@link ResourceProvider} for automatically configuring {@link ResourceProvider}. */ +public class ContainerResourceProvider implements ResourceProvider { + @Override + public Resource createResource(ConfigProperties config) { + return ContainerResource.get(); + } +} diff --git a/sdk-extensions/resources/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider b/sdk-extensions/resources/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider index b7120017c45..4f1b3ae5e08 100644 --- a/sdk-extensions/resources/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider +++ b/sdk-extensions/resources/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider @@ -2,3 +2,4 @@ io.opentelemetry.sdk.extension.resources.HostResourceProvider io.opentelemetry.sdk.extension.resources.OsResourceProvider io.opentelemetry.sdk.extension.resources.ProcessResourceProvider io.opentelemetry.sdk.extension.resources.ProcessRuntimeResourceProvider +io.opentelemetry.sdk.extension.resources.ContainerResourceProvider \ No newline at end of file diff --git a/sdk-extensions/resources/src/test/java/io/opentelemetry/sdk/extension/resources/ContainerResourceTest.java b/sdk-extensions/resources/src/test/java/io/opentelemetry/sdk/extension/resources/ContainerResourceTest.java new file mode 100644 index 00000000000..48448e5fa09 --- /dev/null +++ b/sdk-extensions/resources/src/test/java/io/opentelemetry/sdk/extension/resources/ContainerResourceTest.java @@ -0,0 +1,76 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.sdk.extension.resources; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +public class ContainerResourceTest { + + // Invalid because ID is not a hex string + private static final String INVALID_CGROUP_LINE_1 = + "13:name=systemd:/podruntime/docker/kubepods/ac679f8a8319c8cf7d38e1adf263bc08d23zzzz"; + + // with suffix + private static final String CGROUP_LINE_1 = + "13:name=systemd:/podruntime/docker/kubepods/ac679f8a8319c8cf7d38e1adf263bc08d23.aaaa"; + private static final String EXPECTED_CGROUP_1 = "ac679f8a8319c8cf7d38e1adf263bc08d23"; + + // with prefix and suffix + private static final String CGROUP_LINE_2 = + "13:name=systemd:/podruntime/docker/kubepods/crio-dc679f8a8319c8cf7d38e1adf263bc08d23.stuff"; + private static final String EXPECTED_CGROUP_2 = "dc679f8a8319c8cf7d38e1adf263bc08d23"; + + // just container id + private static final String CGROUP_LINE_3 = + "13:name=systemd:/pod/d86d75589bf6cc254f3e2cc29debdf85dde404998aa128997a819ff991827356"; + private static final String EXPECTED_CGROUP_3 = + "d86d75589bf6cc254f3e2cc29debdf85dde404998aa128997a819ff991827356"; + + // with prefix + private static final String CGROUP_LINE_4 = + "13:name=systemd:/podruntime/docker/kubepods/docker-dc579f8a8319c8cf7d38e1adf263bc08d23"; + private static final String EXPECTED_CGROUP_4 = "dc579f8a8319c8cf7d38e1adf263bc08d23"; + + @SuppressWarnings("DefaultCharset") + public File createCGroup(File file, String line) throws IOException { + file.createNewFile(); + try (FileWriter os = new FileWriter(file); + BufferedWriter bw = new BufferedWriter(os)) { + bw.write(""); + bw.write(line); + bw.flush(); + } + return file; + } + + @Test + public void testInvalidContainer(@TempDir File tempFolder) throws IOException { + File cgroup = createCGroup(new File(tempFolder, "cgroup1"), INVALID_CGROUP_LINE_1); + assertEquals(null, new ContainerResource(cgroup.getPath()).extractContainerId()); + } + + @Test + public void testContainer(@TempDir File tempFolder) throws IOException { + File cgroup = createCGroup(new File(tempFolder, "cgroup1"), CGROUP_LINE_1); + assertEquals(EXPECTED_CGROUP_1, new ContainerResource(cgroup.getPath()).extractContainerId()); + + File cgroup2 = createCGroup(new File(tempFolder, "cgroup2"), CGROUP_LINE_2); + assertEquals(EXPECTED_CGROUP_2, new ContainerResource(cgroup2.getPath()).extractContainerId()); + + File cgroup3 = createCGroup(new File(tempFolder, "cgroup3"), CGROUP_LINE_3); + assertEquals(EXPECTED_CGROUP_3, new ContainerResource(cgroup3.getPath()).extractContainerId()); + + File cgroup4 = createCGroup(new File(tempFolder, "cgroup4"), CGROUP_LINE_4); + assertEquals(EXPECTED_CGROUP_4, new ContainerResource(cgroup4.getPath()).extractContainerId()); + } +} From b9b3d71e2b47c5463ef05d89490db955f5b03504 Mon Sep 17 00:00:00 2001 From: "jason.lo" Date: Wed, 16 Jun 2021 17:30:02 -0700 Subject: [PATCH 2/6] Address review comments --- .../resources/ContainerResource.java | 46 +++++++++---------- 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/sdk-extensions/resources/src/main/java/io/opentelemetry/sdk/extension/resources/ContainerResource.java b/sdk-extensions/resources/src/main/java/io/opentelemetry/sdk/extension/resources/ContainerResource.java index 4fd4b4c3984..f90e49b3e8d 100644 --- a/sdk-extensions/resources/src/main/java/io/opentelemetry/sdk/extension/resources/ContainerResource.java +++ b/sdk-extensions/resources/src/main/java/io/opentelemetry/sdk/extension/resources/ContainerResource.java @@ -10,9 +10,10 @@ import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; import java.io.BufferedReader; import java.io.File; -import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.annotation.Nullable; @@ -20,6 +21,7 @@ /** Factory for {@link Resource} retrieving Container ID information. */ public final class ContainerResource { + private static final Logger logger = Logger.getLogger(ContainerResource.class.getName()); private static final String UNIQUE_HOST_NAME_FILE_NAME = "/proc/self/cgroup"; private static final Pattern HEX_EXTRACTOR = Pattern.compile("^([\\w]*?-)?([a-fA-F0-9]+)(\\.[\\w]*?)?$"); @@ -39,8 +41,7 @@ public static Resource get() { if (containerId == null) { return Resource.empty(); } - return Resource.create( - Attributes.builder().put(ResourceAttributes.CONTAINER_ID, containerId).build()); + return Resource.create(Attributes.of(ResourceAttributes.CONTAINER_ID, containerId)); } /** @@ -56,30 +57,25 @@ public static Resource get() { @Nullable @SuppressWarnings("DefaultCharset") public String extractContainerId() { - File nameFile = new File(cgroupFilePath); - if (nameFile.exists() && nameFile.canRead()) { - try (FileReader fr = new FileReader(nameFile); - BufferedReader br = new BufferedReader(fr); ) { - String line; - while ((line = br.readLine()) != null) { - if (!line.isEmpty()) { - // This cgroup output line should have the container id in it - String[] sections = line.split(File.separator); - if (sections.length > 1) { - String lastSection = sections[sections.length - 1]; - Matcher matcher = HEX_EXTRACTOR.matcher(lastSection); - if (matcher.matches() && matcher.group(2) != null && !matcher.group(2).isEmpty()) { - return matcher.group(2); - } - } - } - return null; + try (BufferedReader reader = new BufferedReader(new FileReader(cgroupFilePath))) { + String line; + while ((line = reader.readLine()) != null) { + if (line.isEmpty()) { + continue; + } + // This cgroup output line should have the container id in it + String[] sections = line.split(File.separator); + if (sections.length <= 1) { + continue; + } + String lastSection = sections[sections.length - 1]; + Matcher matcher = HEX_EXTRACTOR.matcher(lastSection); + if (matcher.matches() && matcher.group(2) != null && !matcher.group(2).isEmpty()) { + return matcher.group(2); } - } catch (FileNotFoundException e) { - return null; - } catch (IOException e) { - return null; } + } catch (IOException e) { + logger.log(Level.WARNING, "Unable to read file: " + e.getMessage()); } return null; } From 68ad3f055c9a7c43609db120f78564ddda6f9b4c Mon Sep 17 00:00:00 2001 From: "jason.lo" Date: Thu, 17 Jun 2021 09:51:55 -0700 Subject: [PATCH 3/6] Address review feedback --- .../opentelemetry-sdk-extension-resources.txt | 2 -- .../extension/resources/ContainerResource.java | 16 ++++++++++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-extension-resources.txt b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-extension-resources.txt index f697ff47c37..18cfbddfbe9 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-extension-resources.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-extension-resources.txt @@ -2,8 +2,6 @@ Comparing source compatibility of against +++ NEW CLASS: PUBLIC(+) FINAL(+) io.opentelemetry.sdk.extension.resources.ContainerResource (not serializable) +++ CLASS FILE FORMAT VERSION: 52.0 <- n.a. +++ NEW SUPERCLASS: java.lang.Object - +++ NEW METHOD: PUBLIC(+) java.lang.String extractContainerId() - +++ NEW ANNOTATION: javax.annotation.Nullable +++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.resources.Resource get() +++ NEW CLASS: PUBLIC(+) io.opentelemetry.sdk.extension.resources.ContainerResourceProvider (not serializable) +++ CLASS FILE FORMAT VERSION: 52.0 <- n.a. diff --git a/sdk-extensions/resources/src/main/java/io/opentelemetry/sdk/extension/resources/ContainerResource.java b/sdk-extensions/resources/src/main/java/io/opentelemetry/sdk/extension/resources/ContainerResource.java index f90e49b3e8d..39716f11e1a 100644 --- a/sdk-extensions/resources/src/main/java/io/opentelemetry/sdk/extension/resources/ContainerResource.java +++ b/sdk-extensions/resources/src/main/java/io/opentelemetry/sdk/extension/resources/ContainerResource.java @@ -33,10 +33,12 @@ public final class ContainerResource { this.cgroupFilePath = cgroupFilePath; } + private static final ContainerResource INSTANCE = + new ContainerResource(UNIQUE_HOST_NAME_FILE_NAME); + /** Returns resource with container information. */ public static Resource get() { - ContainerResource factory = new ContainerResource(UNIQUE_HOST_NAME_FILE_NAME); - String containerId = factory.extractContainerId(); + String containerId = INSTANCE.extractContainerId(); if (containerId == null) { return Resource.empty(); @@ -52,12 +54,18 @@ public static Resource get() { *

We see this with CRI-O "crio-abcdef1234567890ABCDEF.freetext", then use {@linkplain * ContainerResource#HEX_EXTRACTOR} to extract the container hex id * + *

package private for testing purposes + * * @return containerId */ @Nullable @SuppressWarnings("DefaultCharset") - public String extractContainerId() { - try (BufferedReader reader = new BufferedReader(new FileReader(cgroupFilePath))) { + String extractContainerId() { + File nameFile = new File(cgroupFilePath); + if (!nameFile.exists() || !nameFile.canRead()) { + return null; + } + try (BufferedReader reader = new BufferedReader(new FileReader(nameFile))) { String line; while ((line = reader.readLine()) != null) { if (line.isEmpty()) { From 7119d97760c8f2bfb55ae6794561b218f09d330f Mon Sep 17 00:00:00 2001 From: "jason.lo" Date: Thu, 17 Jun 2021 10:25:49 -0700 Subject: [PATCH 4/6] Change containerResource to cached Resource factory --- .../resources/ContainerResource.java | 26 +++++++++---------- .../resources/ContainerResourceTest.java | 11 ++++---- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/sdk-extensions/resources/src/main/java/io/opentelemetry/sdk/extension/resources/ContainerResource.java b/sdk-extensions/resources/src/main/java/io/opentelemetry/sdk/extension/resources/ContainerResource.java index 39716f11e1a..ed545f1dcce 100644 --- a/sdk-extensions/resources/src/main/java/io/opentelemetry/sdk/extension/resources/ContainerResource.java +++ b/sdk-extensions/resources/src/main/java/io/opentelemetry/sdk/extension/resources/ContainerResource.java @@ -25,25 +25,21 @@ public final class ContainerResource { private static final String UNIQUE_HOST_NAME_FILE_NAME = "/proc/self/cgroup"; private static final Pattern HEX_EXTRACTOR = Pattern.compile("^([\\w]*?-)?([a-fA-F0-9]+)(\\.[\\w]*?)?$"); + private static final Resource INSTANCE = buildResource(); - private final String cgroupFilePath; + static Resource buildResource() { + String containerId = extractContainerId(UNIQUE_HOST_NAME_FILE_NAME); - // package private for testing purposes - ContainerResource(String cgroupFilePath) { - this.cgroupFilePath = cgroupFilePath; + if (containerId == null) { + return Resource.empty(); + } else { + return Resource.create(Attributes.of(ResourceAttributes.CONTAINER_ID, containerId)); + } } - private static final ContainerResource INSTANCE = - new ContainerResource(UNIQUE_HOST_NAME_FILE_NAME); - /** Returns resource with container information. */ public static Resource get() { - String containerId = INSTANCE.extractContainerId(); - - if (containerId == null) { - return Resource.empty(); - } - return Resource.create(Attributes.of(ResourceAttributes.CONTAINER_ID, containerId)); + return INSTANCE; } /** @@ -60,7 +56,7 @@ public static Resource get() { */ @Nullable @SuppressWarnings("DefaultCharset") - String extractContainerId() { + static String extractContainerId(String cgroupFilePath) { File nameFile = new File(cgroupFilePath); if (!nameFile.exists() || !nameFile.canRead()) { return null; @@ -87,4 +83,6 @@ String extractContainerId() { } return null; } + + private ContainerResource() {} } diff --git a/sdk-extensions/resources/src/test/java/io/opentelemetry/sdk/extension/resources/ContainerResourceTest.java b/sdk-extensions/resources/src/test/java/io/opentelemetry/sdk/extension/resources/ContainerResourceTest.java index 48448e5fa09..94543b729f8 100644 --- a/sdk-extensions/resources/src/test/java/io/opentelemetry/sdk/extension/resources/ContainerResourceTest.java +++ b/sdk-extensions/resources/src/test/java/io/opentelemetry/sdk/extension/resources/ContainerResourceTest.java @@ -5,6 +5,7 @@ package io.opentelemetry.sdk.extension.resources; +import static io.opentelemetry.sdk.extension.resources.ContainerResource.extractContainerId; import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.BufferedWriter; @@ -56,21 +57,21 @@ public File createCGroup(File file, String line) throws IOException { @Test public void testInvalidContainer(@TempDir File tempFolder) throws IOException { File cgroup = createCGroup(new File(tempFolder, "cgroup1"), INVALID_CGROUP_LINE_1); - assertEquals(null, new ContainerResource(cgroup.getPath()).extractContainerId()); + assertEquals(null, extractContainerId(cgroup.getPath())); } @Test public void testContainer(@TempDir File tempFolder) throws IOException { File cgroup = createCGroup(new File(tempFolder, "cgroup1"), CGROUP_LINE_1); - assertEquals(EXPECTED_CGROUP_1, new ContainerResource(cgroup.getPath()).extractContainerId()); + assertEquals(EXPECTED_CGROUP_1, extractContainerId(cgroup.getPath())); File cgroup2 = createCGroup(new File(tempFolder, "cgroup2"), CGROUP_LINE_2); - assertEquals(EXPECTED_CGROUP_2, new ContainerResource(cgroup2.getPath()).extractContainerId()); + assertEquals(EXPECTED_CGROUP_2, extractContainerId(cgroup2.getPath())); File cgroup3 = createCGroup(new File(tempFolder, "cgroup3"), CGROUP_LINE_3); - assertEquals(EXPECTED_CGROUP_3, new ContainerResource(cgroup3.getPath()).extractContainerId()); + assertEquals(EXPECTED_CGROUP_3, extractContainerId(cgroup3.getPath())); File cgroup4 = createCGroup(new File(tempFolder, "cgroup4"), CGROUP_LINE_4); - assertEquals(EXPECTED_CGROUP_4, new ContainerResource(cgroup4.getPath()).extractContainerId()); + assertEquals(EXPECTED_CGROUP_4, extractContainerId(cgroup4.getPath())); } } From eff0d3de2c75988f57fe60ae493ba151a1c89ceb Mon Sep 17 00:00:00 2001 From: "jason.lo" Date: Thu, 17 Jun 2021 10:54:22 -0700 Subject: [PATCH 5/6] Make method private --- .../sdk/extension/resources/ContainerResource.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk-extensions/resources/src/main/java/io/opentelemetry/sdk/extension/resources/ContainerResource.java b/sdk-extensions/resources/src/main/java/io/opentelemetry/sdk/extension/resources/ContainerResource.java index ed545f1dcce..bab8ca3dd00 100644 --- a/sdk-extensions/resources/src/main/java/io/opentelemetry/sdk/extension/resources/ContainerResource.java +++ b/sdk-extensions/resources/src/main/java/io/opentelemetry/sdk/extension/resources/ContainerResource.java @@ -27,7 +27,7 @@ public final class ContainerResource { Pattern.compile("^([\\w]*?-)?([a-fA-F0-9]+)(\\.[\\w]*?)?$"); private static final Resource INSTANCE = buildResource(); - static Resource buildResource() { + private static Resource buildResource() { String containerId = extractContainerId(UNIQUE_HOST_NAME_FILE_NAME); if (containerId == null) { From 2a0e5992b31df655d828d026f2304a82c35d7d08 Mon Sep 17 00:00:00 2001 From: "jason.lo" Date: Tue, 29 Jun 2021 19:02:34 -0700 Subject: [PATCH 6/6] Change to Paths and Files --- .../resources/ContainerResource.java | 81 +++++++++++-------- ...try.sdk.autoconfigure.spi.ResourceProvider | 2 +- .../resources/ContainerResourceTest.java | 67 ++++++++------- 3 files changed, 87 insertions(+), 63 deletions(-) diff --git a/sdk-extensions/resources/src/main/java/io/opentelemetry/sdk/extension/resources/ContainerResource.java b/sdk-extensions/resources/src/main/java/io/opentelemetry/sdk/extension/resources/ContainerResource.java index bab8ca3dd00..cf1feb6878c 100644 --- a/sdk-extensions/resources/src/main/java/io/opentelemetry/sdk/extension/resources/ContainerResource.java +++ b/sdk-extensions/resources/src/main/java/io/opentelemetry/sdk/extension/resources/ContainerResource.java @@ -8,32 +8,37 @@ import io.opentelemetry.api.common.Attributes; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; -import java.io.BufferedReader; import java.io.File; -import java.io.FileReader; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Objects; +import java.util.Optional; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Stream; import javax.annotation.Nullable; +import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement; /** Factory for {@link Resource} retrieving Container ID information. */ public final class ContainerResource { private static final Logger logger = Logger.getLogger(ContainerResource.class.getName()); - private static final String UNIQUE_HOST_NAME_FILE_NAME = "/proc/self/cgroup"; - private static final Pattern HEX_EXTRACTOR = - Pattern.compile("^([\\w]*?-)?([a-fA-F0-9]+)(\\.[\\w]*?)?$"); - private static final Resource INSTANCE = buildResource(); + private static final Path UNIQUE_HOST_NAME_FILE_NAME = Paths.get("/proc/self/cgroup"); + private static final Pattern HEX_EXTRACTOR = Pattern.compile("^([a-fA-F0-9]+)$"); + private static final Resource INSTANCE = buildResource(UNIQUE_HOST_NAME_FILE_NAME); - private static Resource buildResource() { - String containerId = extractContainerId(UNIQUE_HOST_NAME_FILE_NAME); + // package private for testing + static Resource buildResource(Path path) { + Optional containerId = extractContainerId(path); - if (containerId == null) { - return Resource.empty(); + if (containerId.isPresent()) { + return Resource.create(Attributes.of(ResourceAttributes.CONTAINER_ID, containerId.get())); } else { - return Resource.create(Attributes.of(ResourceAttributes.CONTAINER_ID, containerId)); + return Resource.empty(); } } @@ -54,33 +59,45 @@ public static Resource get() { * * @return containerId */ + @IgnoreJRERequirement @Nullable @SuppressWarnings("DefaultCharset") - static String extractContainerId(String cgroupFilePath) { - File nameFile = new File(cgroupFilePath); - if (!nameFile.exists() || !nameFile.canRead()) { - return null; + private static Optional extractContainerId(Path cgroupFilePath) { + if (!Files.exists(cgroupFilePath) || !Files.isReadable(cgroupFilePath)) { + return Optional.empty(); } - try (BufferedReader reader = new BufferedReader(new FileReader(nameFile))) { - String line; - while ((line = reader.readLine()) != null) { - if (line.isEmpty()) { - continue; - } - // This cgroup output line should have the container id in it - String[] sections = line.split(File.separator); - if (sections.length <= 1) { - continue; - } - String lastSection = sections[sections.length - 1]; - Matcher matcher = HEX_EXTRACTOR.matcher(lastSection); - if (matcher.matches() && matcher.group(2) != null && !matcher.group(2).isEmpty()) { - return matcher.group(2); - } - } + try (Stream lines = Files.lines(cgroupFilePath)) { + return lines + .filter(line -> !line.isEmpty()) + .map(line -> getIdFromLine(line)) + .filter(Objects::nonNull) + .findFirst(); } catch (IOException e) { logger.log(Level.WARNING, "Unable to read file: " + e.getMessage()); } + return Optional.empty(); + } + + @SuppressWarnings("SystemOut") + private static String getIdFromLine(String line) { + // This cgroup output line should have the container id in it + System.out.println("Processing: " + line); + String[] sections = line.split(File.separator); + if (sections.length <= 1) { + return null; + } + + String lastSection = sections[sections.length - 1]; + int startIdx = lastSection.indexOf("-"); + int endIdx = lastSection.lastIndexOf("."); + + Matcher matcher = + HEX_EXTRACTOR.matcher( + lastSection.substring( + startIdx == -1 ? 0 : startIdx + 1, endIdx == -1 ? lastSection.length() : endIdx)); + if (matcher.matches() && matcher.group(1) != null && !matcher.group(1).isEmpty()) { + return matcher.group(1); + } return null; } diff --git a/sdk-extensions/resources/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider b/sdk-extensions/resources/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider index 4f1b3ae5e08..8ac23c697a4 100644 --- a/sdk-extensions/resources/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider +++ b/sdk-extensions/resources/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider @@ -1,5 +1,5 @@ +io.opentelemetry.sdk.extension.resources.ContainerResourceProvider io.opentelemetry.sdk.extension.resources.HostResourceProvider io.opentelemetry.sdk.extension.resources.OsResourceProvider io.opentelemetry.sdk.extension.resources.ProcessResourceProvider io.opentelemetry.sdk.extension.resources.ProcessRuntimeResourceProvider -io.opentelemetry.sdk.extension.resources.ContainerResourceProvider \ No newline at end of file diff --git a/sdk-extensions/resources/src/test/java/io/opentelemetry/sdk/extension/resources/ContainerResourceTest.java b/sdk-extensions/resources/src/test/java/io/opentelemetry/sdk/extension/resources/ContainerResourceTest.java index 94543b729f8..09fdf01cdd0 100644 --- a/sdk-extensions/resources/src/test/java/io/opentelemetry/sdk/extension/resources/ContainerResourceTest.java +++ b/sdk-extensions/resources/src/test/java/io/opentelemetry/sdk/extension/resources/ContainerResourceTest.java @@ -5,13 +5,15 @@ package io.opentelemetry.sdk.extension.resources; -import static io.opentelemetry.sdk.extension.resources.ContainerResource.extractContainerId; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static io.opentelemetry.sdk.extension.resources.ContainerResource.buildResource; +import static org.assertj.core.api.Assertions.assertThat; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; @@ -39,39 +41,44 @@ public class ContainerResourceTest { // with prefix private static final String CGROUP_LINE_4 = - "13:name=systemd:/podruntime/docker/kubepods/docker-dc579f8a8319c8cf7d38e1adf263bc08d23"; - private static final String EXPECTED_CGROUP_4 = "dc579f8a8319c8cf7d38e1adf263bc08d23"; + "//\n" + + "1:name=systemd:/podruntime/docker/kubepods/docker-dc579f8a8319c8cf7d38e1adf263bc08d23" + + "2:name=systemd:/podruntime/docker/kubepods/docker-dc579f8a8319c8cf7d38e1adf263bc08d23" + + "3:name=systemd:/podruntime/docker/kubepods/docker-dc579f8a8319c8cf7d38e1adf263bc08d23"; - @SuppressWarnings("DefaultCharset") - public File createCGroup(File file, String line) throws IOException { - file.createNewFile(); - try (FileWriter os = new FileWriter(file); - BufferedWriter bw = new BufferedWriter(os)) { - bw.write(""); - bw.write(line); - bw.flush(); - } - return file; - } + private static final String EXPECTED_CGROUP_4 = "dc579f8a8319c8cf7d38e1adf263bc08d23"; @Test - public void testInvalidContainer(@TempDir File tempFolder) throws IOException { - File cgroup = createCGroup(new File(tempFolder, "cgroup1"), INVALID_CGROUP_LINE_1); - assertEquals(null, extractContainerId(cgroup.getPath())); + public void testNegativeCases(@TempDir Path tempFolder) throws IOException { + // invalid containerId (non-hex) + Path cgroup = createCGroup(tempFolder.resolve("cgroup1"), INVALID_CGROUP_LINE_1); + assertThat(buildResource(cgroup)).isEqualTo(Resource.empty()); + + // test invalid file + cgroup = tempFolder.resolve("DoesNotExist"); + assertThat(buildResource(cgroup)).isEqualTo(Resource.empty()); } @Test - public void testContainer(@TempDir File tempFolder) throws IOException { - File cgroup = createCGroup(new File(tempFolder, "cgroup1"), CGROUP_LINE_1); - assertEquals(EXPECTED_CGROUP_1, extractContainerId(cgroup.getPath())); + public void testContainer(@TempDir Path tempFolder) throws IOException { + Path cgroup = createCGroup(tempFolder.resolve("cgroup1"), CGROUP_LINE_1); + assertThat(getContainerId(buildResource(cgroup))).isEqualTo(EXPECTED_CGROUP_1); - File cgroup2 = createCGroup(new File(tempFolder, "cgroup2"), CGROUP_LINE_2); - assertEquals(EXPECTED_CGROUP_2, extractContainerId(cgroup2.getPath())); + Path cgroup2 = createCGroup(tempFolder.resolve("cgroup2"), CGROUP_LINE_2); + assertThat(getContainerId(buildResource(cgroup2))).isEqualTo(EXPECTED_CGROUP_2); - File cgroup3 = createCGroup(new File(tempFolder, "cgroup3"), CGROUP_LINE_3); - assertEquals(EXPECTED_CGROUP_3, extractContainerId(cgroup3.getPath())); + Path cgroup3 = createCGroup(tempFolder.resolve("cgroup3"), CGROUP_LINE_3); + assertThat(getContainerId(buildResource(cgroup3))).isEqualTo(EXPECTED_CGROUP_3); + + Path cgroup4 = createCGroup(tempFolder.resolve("cgroup4"), CGROUP_LINE_4); + assertThat(getContainerId(buildResource(cgroup4))).isEqualTo(EXPECTED_CGROUP_4); + } + + private static String getContainerId(Resource resource) { + return resource.getAttributes().get(ResourceAttributes.CONTAINER_ID); + } - File cgroup4 = createCGroup(new File(tempFolder, "cgroup4"), CGROUP_LINE_4); - assertEquals(EXPECTED_CGROUP_4, extractContainerId(cgroup4.getPath())); + public static Path createCGroup(Path path, String line) throws IOException { + return Files.write(path, line.getBytes(StandardCharsets.UTF_8)); } }