Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cgroupsv2 container #1529

Merged
merged 5 commits into from
Sep 29, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.newrelic.agent.utilization;

public enum CGroup {
V1,
V2
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,52 +20,60 @@
import java.util.regex.Pattern;

/*
* Grabs the docker container id from the file /proc/self/cgroup. The lines look something like below:
* Grabs the docker container id.
* For newer Linux systems running cgroup v2, this is taken from /proc/self/mountinfo. The line should look like:
*
* 594 576 254:1 /docker/containers/f37a7e4d17017e7bf994656b19ca4360c6cdc4951c86700a464101d0d9ce97ef/hosts /etc/hosts rw,relatime - ext4 /dev/vda1 rw,discard
*
* For older Linux systems running cgroup v1, this is taken from /proc/self/cgroup. The line should look like:
*
* +4:cpu:/docker/3ccfa00432798ff38f85839de1e396f771b4acbe9f4ddea0a761c39b9790a782
*
* We should grab the "cpu" line. The long id number is the number we want.
* This is the full docker id, not the short id that appears when you run a "docker ps".
*
* In either case, this is the full docker id, not the short id that appears when you run a "docker ps".
*/
public class DockerData {

private static final String FILE_WITH_CONTAINER_ID = "/proc/self/cgroup";
private static final String FILE_WITH_CONTAINER_ID_V1 = "/proc/self/cgroup";
private static final String FILE_WITH_CONTAINER_ID_V2 = "/proc/self/mountinfo";
private static final String CPU = "cpu";

private static final Pattern VALID_CONTAINER_ID = Pattern.compile("^[0-9a-f]{64}$");
private static final Pattern DOCKER_CONTAINER_STRING = Pattern.compile("^.*[^0-9a-f]+([0-9a-f]{64,}).*");
private static final Pattern DOCKER_CONTAINER_STRING_V1 = Pattern.compile("^.*[^0-9a-f]+([0-9a-f]{64,}).*");
private static final Pattern DOCKER_CONTAINER_STRING_V2 = Pattern.compile(".*/docker/containers/([0-9a-f]{64,}).*");

public String getDockerContainerId(boolean isLinux) {
if (isLinux) {
File cpuInfoFile;
cpuInfoFile = new File(FILE_WITH_CONTAINER_ID);
return getDockerIdFromFile(cpuInfoFile);
//try to get the container id from the v2 location
File containerIdFileV2 = new File(FILE_WITH_CONTAINER_ID_V2);
String idResultV2 = getDockerIdFromFile(containerIdFileV2, CGroup.V2);
if (idResultV2 != null) {
return idResultV2;
}
//try to get container id from the v1 location
File containerIdFileV1 = new File(FILE_WITH_CONTAINER_ID_V1);
return getDockerIdFromFile(containerIdFileV1, CGroup.V1);
}
return null;
}

String getDockerIdFromFile(File cpuInfoFile) {
if (cpuInfoFile.exists() && cpuInfoFile.canRead()) {
String getDockerIdFromFile(File mountInfoFile, CGroup cgroup) {
if (mountInfoFile.exists() && mountInfoFile.canRead()) {
try {
return readFile(new FileReader(cpuInfoFile));
return readFile(new FileReader(mountInfoFile), cgroup);
} catch (FileNotFoundException e) {
}
}
return null;
}

/*
* protected for testing.
*
* Returns the docker id as a string, or null if this value could not be read, or failed validation.
*/
String readFile(Reader reader) {
String readFile(Reader reader, CGroup cgroup) {
try (BufferedReader bReader = new BufferedReader(reader)) {

String line;
StringBuilder resultGoesHere = new StringBuilder();
while ((line = bReader.readLine()) != null) {
if (checkLineAndGetResult(line, resultGoesHere)) {
if (checkLineAndGetResult(line, resultGoesHere, cgroup)) {
String value = resultGoesHere.toString().trim();
if (isInvalidDockerValue(value)) {
Agent.LOG.log(Level.WARNING, MessageFormat.format("Failed to validate Docker value {0}", value));
Expand All @@ -80,28 +88,46 @@ String readFile(Reader reader) {
return null;
}

boolean isInvalidDockerValue(String value) {
/*
* Value should be exactly 64 characters.
*
* Value is expected to include only [0-9a-f]
*/
return (value == null) || (!VALID_CONTAINER_ID.matcher(value).matches());
boolean checkLineAndGetResult(String line, StringBuilder resultGoesHere, CGroup cgroup) {
if (cgroup == CGroup.V1) {
return checkLineAndGetResultV1(line, resultGoesHere);
} else if (cgroup == CGroup.V2) {
return checkLineAndGetResultV2(line, resultGoesHere);
}
return false;
}

// protected for testing
boolean checkLineAndGetResult(String line, StringBuilder resultGoesHere) {
private boolean checkLineAndGetResultV1(String line, StringBuilder resultGoesHere) {
String[] parts = line.split(":");
if (parts.length == 3 && validCpuLine(parts[1])) {
String mayContainId = parts[2];
if (checkAndGetMatch(DOCKER_CONTAINER_STRING, resultGoesHere, mayContainId)) {
if (checkAndGetMatch(DOCKER_CONTAINER_STRING_V1, resultGoesHere, mayContainId)) {
return true;
} else if (!mayContainId.equals("/")) {
Agent.LOG.log(Level.FINE, "Docker Data: Ignoring unrecognized cgroup ID format: {0}", mayContainId);
}
}
return false;
}
private boolean checkLineAndGetResultV2(String line, StringBuilder resultGoesHere) {
String[] parts = line.split(" ");
if (parts.length >= 4 ) {
String mayContainId = parts[3];
if (checkAndGetMatch(DOCKER_CONTAINER_STRING_V2, resultGoesHere, mayContainId)) {
return true;
}
}
return false;
}

boolean isInvalidDockerValue(String value) {
/*
* Value should be exactly 64 characters.
*
* Value is expected to include only [0-9a-f]
*/
return (value == null) || (!VALID_CONTAINER_ID.matcher(value).matches());
}

private boolean validCpuLine(String segment) {
if (segment != null) {
Expand All @@ -115,6 +141,7 @@ private boolean validCpuLine(String segment) {
return false;
}

//Get the match for the capture group of a single-capture-group regex expression.
kanderson250 marked this conversation as resolved.
Show resolved Hide resolved
private boolean checkAndGetMatch(Pattern p, StringBuilder result, String segment) {
Matcher m = p.matcher(segment);
if (m.matches() && m.groupCount() == 1) {
Expand All @@ -123,4 +150,74 @@ private boolean checkAndGetMatch(Pattern p, StringBuilder result, String segment
}
return false;
}

kanderson250 marked this conversation as resolved.
Show resolved Hide resolved
//CGROUPS v1 logic

// String getDockerIdFromFile(File cpuInfoFile) {
// if (cpuInfoFile.exists() && cpuInfoFile.canRead()) {
// try {
// return readFile(new FileReader(cpuInfoFile));
// } catch (FileNotFoundException e) {
// }
// }
// return null;
// }

/*
* protected for testing.
*
* Returns the docker id as a string, or null if this value could not be read, or failed validation.
// */
// String readFile(Reader reader) {
// try (BufferedReader bReader = new BufferedReader(reader)) {
//
// String line;
// StringBuilder resultGoesHere = new StringBuilder();
// while ((line = bReader.readLine()) != null) {
// if (checkLineAndGetResult(line, resultGoesHere)) {
// String value = resultGoesHere.toString().trim();
// if (isInvalidDockerValue(value)) {
// Agent.LOG.log(Level.WARNING, MessageFormat.format("Failed to validate Docker value {0}", value));
// return null;
// }
// return value;
// }
// }
// } catch (Throwable e) {
// Agent.LOG.log(Level.FINEST, e, "Exception occurred when reading docker file.");
// }
// return null;
// }
// String cgroupV2GetDockerIdFromFile(File mountInfoFile) {
// if (mountInfoFile.exists() && mountInfoFile.canRead()) {
// try {
// return cgroupV2ReadFile(new FileReader(mountInfoFile));
// } catch (FileNotFoundException e) {
// }
// }
// return null;
// }
//
// String cgroupV2ReadFile(Reader reader) {
// try (BufferedReader bReader = new BufferedReader(reader)) {
// String line;
// StringBuilder resultGoesHere = new StringBuilder();
// while ((line = bReader.readLine()) != null) {
// //check the first line with the docker/containers/ tag.
// if (cgroupV2CheckLineAndGetResult(line, resultGoesHere)) {
// String value = resultGoesHere.toString().trim();
// if (isInvalidDockerValue(value)) {
// Agent.LOG.log(Level.WARNING, MessageFormat.format("Failed to validate Docker value {0}", value));
// return null;
// }
// return value;
// }
// }
// } catch (Throwable e) {
// Agent.LOG.log(Level.FINEST, e, "Exception occurred when reading docker file.");
// }
// return null;
// }


}
Loading