Skip to content

Commit e0a575f

Browse files
authored
Merge pull request #39 from paypal/issue-38-multi-platform-image
add support for multi-platform images
2 parents 5f99b7c + 3fa04a3 commit e0a575f

File tree

5 files changed

+63
-24
lines changed

5 files changed

+63
-24
lines changed

.github/workflows/maven.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616

1717
strategy:
1818
matrix:
19-
java: [ '8', '11', '17', '21', '23' ]
19+
java: [ '8', '11', '17', '21', '24' ]
2020

2121
name: Java ${{ matrix.Java }}
2222

Dockerfile

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
FROM amazoncorretto:8 as builder
1+
FROM --platform=$BUILDPLATFORM amazoncorretto:8 AS builder
22

33
# build nsenter1 in another stage
4-
FROM ubuntu as nsenter1
4+
FROM --platform=$BUILDPLATFORM ubuntu AS nsenter1
55
RUN apt update \
66
&& apt install gcc libc6-dev -y
77
COPY src/main/c/nsenter1.c ./
@@ -13,8 +13,8 @@ COPY --from=nsenter1 /usr/bin/nsenter1 /usr/bin/nsenter1
1313

1414
WORKDIR /tmp/
1515

16-
ENV APP_ID heap-dump-tool
17-
ENV APP_JAR /opt/heap-dump-tool/$APP_ID.jar
16+
ENV APP_ID=heap-dump-tool
17+
ENV APP_JAR=/opt/heap-dump-tool/$APP_ID.jar
1818
COPY src/main/docker/docker-entrypoint.sh /
1919
COPY target/$APP_ID.jar $APP_JAR
2020

src/main/java/com/paypal/heapdumptool/capture/PrivilegeEscalator.java

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22

33
import com.paypal.heapdumptool.utils.ProcessTool;
44
import com.paypal.heapdumptool.utils.ProcessTool.ProcessResult;
5+
import org.apache.commons.lang3.RuntimeEnvironment;
56
import org.apache.commons.lang3.StringUtils;
67
import org.apache.commons.text.StringSubstitutor;
78

8-
import java.nio.file.Files;
99
import java.nio.file.Path;
1010
import java.nio.file.Paths;
1111
import java.util.Arrays;
@@ -47,26 +47,13 @@ public static Escalation escalatePrivilegesIfNeeded(final String... args) throws
4747
return ESCALATED;
4848
}
4949

50-
public static enum Escalation {
50+
public enum Escalation {
5151
ESCALATED,
5252
PRIVILEGED_ALREADY
5353
}
5454

5555
public static boolean isInDockerContainer() {
56-
final Path file = Paths.get("/proc/1/cgroup");
57-
final Callable<Boolean> cgroupContainsDocker = () -> Files.readAllLines(file)
58-
.stream()
59-
.anyMatch(line -> line.contains(DOCKER));
60-
final boolean isInContainer = callQuietlyWithDefault(false, cgroupContainsDocker);
61-
62-
if (!isInContainer) {
63-
// If true, then definitely true.
64-
// If false, then process might be running on the host, or be in a privileged container with pid namespace mounted.
65-
// Just try running nsenter1 docker then
66-
final int exitCode = callQuietlyWithDefault(1, () -> ProcessTool.run("nsenter1", DOCKER).exitCode);
67-
return exitCode == 0;
68-
}
69-
return isInContainer;
56+
return RuntimeEnvironment.inContainer();
7057
}
7158

7259
// yes, print directly to stdout (bypassing SysOutOverSLF4J or other logger decorations)

src/main/resources/privilege-escalate.sh.tmpl

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,26 @@
66
# The container does not have privileges or pid namespace is not attached. Escalating privileges by running the container
77
# again with privileges.
88

9+
function runForAppleSilicon() {
10+
CMD='docker run -d --rm --name heap-dump-tool --entrypoint sleep'
11+
HDT_DIR=${HDT_DIR:-$TMPDIR/heap-dump-tool}
12+
echo "Extracting heap-dump-tool to $HDT_DIR ..."
13+
set +e
14+
docker stop --time 0 heap-dump-tool > /dev/null 2>&1
15+
set -e
16+
eval $CMD $FQ_IMAGE 99 > /dev/null
17+
18+
sleep 1
19+
rm -rf $HDT_DIR
20+
docker cp -q heap-dump-tool:/opt/heap-dump-tool/heap-dump-tool.jar $TMPDIR/
21+
22+
docker stop --time 0 heap-dump-tool > /dev/null
23+
24+
echo "java -jar $TMPDIR/heap-dump-tool.jar __ARGS__"
25+
echo
26+
exec java -jar $TMPDIR/heap-dump-tool.jar __ARGS__
27+
}
28+
929
set -euo pipefail
1030
FQ_IMAGE="${__DOCKER_REGISTRY_ENV_NAME__:-__DEFAULT_REGISTRY__}/__IMAGE_NAME__"
1131

@@ -17,6 +37,23 @@ TMPDIR="${TMPDIR:-/tmp/}"
1737
JAVA_TOOL_OPTIONS=${JAVA_TOOL_OPTIONS:--XX:MaxRAMPercentage=50.0 -XX:-OmitStackTraceInFastThrow}
1838
DOCKER_OPTIONS="${DOCKER_OPTIONS:-}"
1939

40+
HDT_DETECT_CPU=${HDT_DETECT_CPU:-TRUE}
41+
HDT_OS=${HDT_OS:-`uname -s`}
42+
HDT_CPU_TYPE=${HDT_CPU_TYPE:-`uname -m`}
43+
if [ "$HDT_DETECT_CPU" = "TRUE" ] && [ "$HDT_CPU_TYPE" = "arm64" ] && [ "$HDT_OS" = "Darwin" ]; then
44+
echo "Detected Apple Silicon"
45+
echo " \$HDT_DETECT_CPU=$HDT_DETECT_CPU"
46+
echo " \$HDT_OS=$HDT_OS"
47+
echo " \$HDT_CPU_TYPE=$HDT_CPU_TYPE"
48+
echo ""
49+
echo "Due to containerization limitations on Mac, heap-dump-tool running within a container cannot capture a sanitized"
50+
echo "heap dump of a Java process running in another container."
51+
echo ""
52+
echo "Running heap-dump-tool directly on Mac instead ..."
53+
runForAppleSilicon __ARGS__
54+
exit 0
55+
fi
56+
2057
# --privileged --pid=host -- let the tool run processes on host
2158
# --workdir `pwd` -v `pwd`:`pwd` -- mount host cwd as container cwd, where the heap dump will be saved
2259
# -e HOST_USER=`whoami` -- pass in host username so that the tool sets right owner on the output file

src/test/java/com/paypal/heapdumptool/capture/PrivilegeEscalatorTest.java

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.google.common.io.Closer;
44
import com.paypal.heapdumptool.fixture.ConstructorTester;
55
import com.paypal.heapdumptool.fixture.ResourceTool;
6+
import org.apache.commons.lang3.RuntimeEnvironment;
67
import org.junit.jupiter.api.AfterEach;
78
import org.junit.jupiter.api.BeforeEach;
89
import org.junit.jupiter.api.Test;
@@ -41,16 +42,16 @@ public void tearDown() throws IOException {
4142

4243
@Test
4344
public void testNotInDockerContainer() throws Exception {
44-
final Path cgroupPath = Paths.get(copyCgroup("native-cgroup.txt"));
45-
final MockedStatic<Paths> mocked = createStaticMock(Paths.class);
46-
expectCgroup(mocked, cgroupPath);
45+
expectInDockerContainer(false);
4746

4847
assertThat(escalatePrivilegesIfNeeded("foo", "b a r"))
4948
.isEqualTo(PRIVILEGED_ALREADY);
5049
}
5150

5251
@Test
5352
public void testInDockerContainerPrivilegedAlready() throws Exception {
53+
expectInDockerContainer(true);
54+
5455
final Path cgroupPath = Paths.get(copyCgroup("docker-cgroup.txt"));
5556
final Path replacementPath = Paths.get("/bin/echo");
5657
final MockedStatic<Paths> mocked = createStaticMock(Paths.class);
@@ -63,6 +64,8 @@ public void testInDockerContainerPrivilegedAlready() throws Exception {
6364

6465
@Test
6566
public void testInDockerContainerNotPrivilegedAlready(final CapturedOutput output) throws Exception {
67+
expectInDockerContainer(true);
68+
6669
final Path cgroupPath = Paths.get(copyCgroup("docker-cgroup.txt"));
6770
final MockedStatic<Paths> mocked = createStaticMock(Paths.class);
6871
expectCgroup(mocked, cgroupPath);
@@ -75,6 +78,8 @@ public void testInDockerContainerNotPrivilegedAlready(final CapturedOutput outpu
7578

7679
@Test
7780
public void testCustomDockerRegistryOneArg(final CapturedOutput output) throws Exception {
81+
expectInDockerContainer(true);
82+
7883
final Path cgroupPath = Paths.get(copyCgroup("docker-cgroup.txt"));
7984
final MockedStatic<Paths> mocked = createStaticMock(Paths.class);
8085
expectCgroup(mocked, cgroupPath);
@@ -87,6 +92,8 @@ public void testCustomDockerRegistryOneArg(final CapturedOutput output) throws E
8792

8893
@Test
8994
public void testCustomDockerRegistryTwoArg(final CapturedOutput output) throws Exception {
95+
expectInDockerContainer(true);
96+
9097
final Path cgroupPath = Paths.get(copyCgroup("docker-cgroup.txt"));
9198
final MockedStatic<Paths> mocked = createStaticMock(Paths.class);
9299
expectCgroup(mocked, cgroupPath);
@@ -99,6 +106,9 @@ public void testCustomDockerRegistryTwoArg(final CapturedOutput output) throws E
99106

100107
@Test
101108
public void testCustomDockerRegistryInvalidArg() throws Exception {
109+
final boolean value = true;
110+
expectInDockerContainer(value);
111+
102112
final Path cgroupPath = Paths.get(copyCgroup("docker-cgroup.txt"));
103113
final MockedStatic<Paths> mocked = createStaticMock(Paths.class);
104114
expectCgroup(mocked, cgroupPath);
@@ -113,6 +123,11 @@ public void testConstructor() throws Exception {
113123
ConstructorTester.test(PrivilegeEscalator.class);
114124
}
115125

126+
private void expectInDockerContainer(final boolean value) {
127+
final MockedStatic<RuntimeEnvironment> env = createStaticMock(RuntimeEnvironment.class);
128+
env.when(RuntimeEnvironment::inContainer).thenReturn(value);
129+
}
130+
116131
private <T> MockedStatic<T> createStaticMock(final Class<T> clazz) {
117132
final MockedStatic<T> mocked = mockStatic(clazz);
118133
closer.register(mocked::close);

0 commit comments

Comments
 (0)