* Support adding container.id to resource metadta * Address review comments * Address review feedback * Change containerResource to cached Resource factory * Make method private * Change to Paths and Files * Fix bad merge and change regex * Remove debug code, remove regex * Add nullable annotation Co-authored-by: Anuraag Agrawal <aanuraag@amazon.co.jp>
This commit is contained in:
parent
0d2b7b03b7
commit
2cb461d4ae
|
|
@ -1,2 +1,10 @@
|
|||
Comparing source compatibility of against
|
||||
No changes.
|
||||
+++ 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(+) 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)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* 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.api.internal.OtelEncodingUtils;
|
||||
import io.opentelemetry.sdk.resources.Resource;
|
||||
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
|
||||
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.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 Resource INSTANCE = buildSingleton(UNIQUE_HOST_NAME_FILE_NAME);
|
||||
|
||||
@IgnoreJRERequirement
|
||||
private static Resource buildSingleton(String uniqueHostNameFileName) {
|
||||
// can't initialize this statically without running afoul of animalSniffer on paths
|
||||
return buildResource(Paths.get(uniqueHostNameFileName));
|
||||
}
|
||||
|
||||
// package private for testing
|
||||
static Resource buildResource(Path path) {
|
||||
String containerId = extractContainerId(path);
|
||||
|
||||
if (containerId == null || containerId.isEmpty()) {
|
||||
return Resource.empty();
|
||||
} else {
|
||||
return Resource.create(Attributes.of(ResourceAttributes.CONTAINER_ID, containerId));
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns resource with container information. */
|
||||
public static Resource get() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 '.' (at beginning) or '-' (at end)
|
||||
*
|
||||
* @return containerId
|
||||
*/
|
||||
@IgnoreJRERequirement
|
||||
@Nullable
|
||||
private static String extractContainerId(Path cgroupFilePath) {
|
||||
if (!Files.exists(cgroupFilePath) || !Files.isReadable(cgroupFilePath)) {
|
||||
return null;
|
||||
}
|
||||
try (Stream<String> lines = Files.lines(cgroupFilePath)) {
|
||||
Optional<String> value =
|
||||
lines
|
||||
.filter(line -> !line.isEmpty())
|
||||
.map(line -> getIdFromLine(line))
|
||||
.filter(Objects::nonNull)
|
||||
.findFirst();
|
||||
if (value.isPresent()) {
|
||||
return value.get();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.log(Level.WARNING, "Unable to read file: " + e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getIdFromLine(String line) {
|
||||
// This cgroup output line should have the container id in it
|
||||
int lastSlashIdx = line.lastIndexOf("/");
|
||||
if (lastSlashIdx < 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String lastSection = line.substring(lastSlashIdx + 1);
|
||||
int startIdx = lastSection.indexOf("-");
|
||||
int endIdx = lastSection.lastIndexOf(".");
|
||||
|
||||
String containerId =
|
||||
lastSection.substring(
|
||||
startIdx == -1 ? 0 : startIdx + 1, endIdx == -1 ? lastSection.length() : endIdx);
|
||||
if (OtelEncodingUtils.isValidBase16String(containerId) && !containerId.isEmpty()) {
|
||||
return containerId;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private ContainerResource() {}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
io.opentelemetry.sdk.extension.resources.ContainerResourceProvider
|
||||
io.opentelemetry.sdk.extension.resources.HostResourceProvider
|
||||
io.opentelemetry.sdk.extension.resources.OsResourceProvider
|
||||
io.opentelemetry.sdk.extension.resources.ProcessResourceProvider
|
||||
|
|
|
|||
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.sdk.extension.resources;
|
||||
|
||||
import static io.opentelemetry.sdk.extension.resources.ContainerResource.buildResource;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
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;
|
||||
|
||||
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 =
|
||||
"//\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";
|
||||
|
||||
private static final String EXPECTED_CGROUP_4 = "dc579f8a8319c8cf7d38e1adf263bc08d23";
|
||||
|
||||
@Test
|
||||
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 Path tempFolder) throws IOException {
|
||||
Path cgroup = createCGroup(tempFolder.resolve("cgroup1"), CGROUP_LINE_1);
|
||||
assertThat(getContainerId(buildResource(cgroup))).isEqualTo(EXPECTED_CGROUP_1);
|
||||
|
||||
Path cgroup2 = createCGroup(tempFolder.resolve("cgroup2"), CGROUP_LINE_2);
|
||||
assertThat(getContainerId(buildResource(cgroup2))).isEqualTo(EXPECTED_CGROUP_2);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
private static Path createCGroup(Path path, String line) throws IOException {
|
||||
return Files.write(path, line.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue