Skip to content

Commit 12e7bd6

Browse files
committed
Allow running under root on Linux when unshare is available
1 parent 5917444 commit 12e7bd6

File tree

4 files changed

+90
-37
lines changed

4 files changed

+90
-37
lines changed

.github/workflows/maven.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ jobs:
66
runs-on: ubuntu-latest
77
strategy:
88
matrix:
9-
java: [8, 11, 13]
9+
java: [8, 11, 13, 14]
1010
steps:
1111
- name: Checkout project
1212
uses: actions/checkout@v1

pom.xml

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -109,12 +109,12 @@
109109
<dependency>
110110
<groupId>org.apache.commons</groupId>
111111
<artifactId>commons-lang3</artifactId>
112-
<version>3.6</version>
112+
<version>3.10</version>
113113
</dependency>
114114
<dependency>
115115
<groupId>org.apache.commons</groupId>
116116
<artifactId>commons-compress</artifactId>
117-
<version>1.19</version>
117+
<version>1.20</version>
118118
</dependency>
119119
<dependency>
120120
<groupId>org.tukaani</groupId>
@@ -124,29 +124,29 @@
124124
<dependency>
125125
<groupId>commons-io</groupId>
126126
<artifactId>commons-io</artifactId>
127-
<version>2.6</version>
127+
<version>2.7</version>
128128
</dependency>
129129
<dependency>
130130
<groupId>commons-codec</groupId>
131131
<artifactId>commons-codec</artifactId>
132-
<version>1.11</version>
132+
<version>1.14</version>
133133
</dependency>
134134
<dependency>
135135
<groupId>org.flywaydb</groupId>
136136
<artifactId>flyway-core</artifactId>
137-
<version>6.0.8</version>
137+
<version>6.5.1</version>
138138
<optional>true</optional>
139139
</dependency>
140140
<dependency>
141141
<groupId>org.liquibase</groupId>
142142
<artifactId>liquibase-core</artifactId>
143-
<version>3.6.3</version>
143+
<version>4.0.0</version>
144144
<optional>true</optional>
145145
</dependency>
146146
<dependency>
147147
<groupId>org.postgresql</groupId>
148148
<artifactId>postgresql</artifactId>
149-
<version>42.2.5</version>
149+
<version>42.2.14</version>
150150
</dependency>
151151
<dependency>
152152
<groupId>junit</groupId>
@@ -158,21 +158,21 @@
158158
<dependency>
159159
<groupId>org.junit.jupiter</groupId>
160160
<artifactId>junit-jupiter-api</artifactId>
161-
<version>5.3.2</version>
161+
<version>5.6.2</version>
162162
<scope>provided</scope>
163163
<optional>true</optional>
164164
</dependency>
165165

166166
<dependency>
167167
<groupId>org.slf4j</groupId>
168168
<artifactId>slf4j-simple</artifactId>
169-
<version>1.7.25</version>
169+
<version>1.7.30</version>
170170
<scope>test</scope>
171171
</dependency>
172172
<dependency>
173173
<groupId>org.mockito</groupId>
174174
<artifactId>mockito-core</artifactId>
175-
<version>2.13.0</version>
175+
<version>3.4.0</version>
176176
<scope>test</scope>
177177
</dependency>
178178
</dependencies>
@@ -181,7 +181,7 @@
181181
<plugins>
182182
<plugin>
183183
<artifactId>maven-pmd-plugin</artifactId>
184-
<version>3.8</version>
184+
<version>3.13.0</version>
185185
<executions>
186186
<execution>
187187
<phase>verify</phase>
@@ -194,13 +194,13 @@
194194
<dependency>
195195
<groupId>net.sourceforge.pmd</groupId>
196196
<artifactId>pmd-core</artifactId>
197-
<version>5.6.1</version>
197+
<version>6.25.0</version>
198198
<scope>compile</scope>
199199
</dependency>
200200
<dependency>
201201
<groupId>net.sourceforge.pmd</groupId>
202202
<artifactId>pmd-java</artifactId>
203-
<version>5.6.1</version>
203+
<version>6.25.0</version>
204204
<scope>compile</scope>
205205
</dependency>
206206
</dependencies>
@@ -216,7 +216,7 @@
216216
<plugin>
217217
<groupId>org.apache.maven.plugins</groupId>
218218
<artifactId>maven-source-plugin</artifactId>
219-
<version>3.0.1</version>
219+
<version>3.2.1</version>
220220
<executions>
221221
<execution>
222222
<id>attach-sources</id>
@@ -229,7 +229,7 @@
229229
<plugin>
230230
<groupId>org.apache.maven.plugins</groupId>
231231
<artifactId>maven-javadoc-plugin</artifactId>
232-
<version>2.10.4</version>
232+
<version>3.2.0</version>
233233
<executions>
234234
<execution>
235235
<id>attach-javadocs</id>
@@ -263,7 +263,7 @@
263263
<plugin>
264264
<groupId>org.apache.maven.plugins</groupId>
265265
<artifactId>maven-gpg-plugin</artifactId>
266-
<version>1.5</version>
266+
<version>1.6</version>
267267
<executions>
268268
<execution>
269269
<id>sign-artifacts</id>

src/main/java/io/zonky/test/db/postgres/embedded/EmbeddedPostgres.java

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,7 @@
1414
package io.zonky.test.db.postgres.embedded;
1515

1616

17-
import java.io.ByteArrayInputStream;
18-
import java.io.Closeable;
19-
import java.io.File;
20-
import java.io.FileOutputStream;
21-
import java.io.IOException;
22-
import java.io.InputStream;
17+
import java.io.*;
2318
import java.net.InetAddress;
2419
import java.net.InetSocketAddress;
2520
import java.net.ServerSocket;
@@ -55,7 +50,6 @@
5550
import java.util.concurrent.atomic.AtomicBoolean;
5651
import java.util.concurrent.locks.Lock;
5752
import java.util.concurrent.locks.ReentrantLock;
58-
import java.util.stream.Collectors;
5953

6054
import javax.sql.DataSource;
6155

@@ -72,6 +66,7 @@
7266
import org.slf4j.LoggerFactory;
7367
import org.tukaani.xz.XZInputStream;
7468

69+
import static io.zonky.test.db.postgres.util.LinuxUtils.isUnshareUseable;
7570
import static java.nio.file.StandardOpenOption.CREATE;
7671
import static java.nio.file.StandardOpenOption.WRITE;
7772
import static java.util.Collections.unmodifiableMap;
@@ -104,6 +99,7 @@ public class EmbeddedPostgres implements Closeable
10499
private volatile FileOutputStream lockStream;
105100
private volatile FileLock lock;
106101
private final boolean cleanDataDirectory;
102+
private static boolean useUnshare;
107103

108104
private final ProcessBuilder.Redirect errorRedirector;
109105
private final ProcessBuilder.Redirect outputRedirector;
@@ -133,6 +129,8 @@ public class EmbeddedPostgres implements Closeable
133129
this.pgStartupWait = pgStartupWait;
134130
Objects.requireNonNull(this.pgStartupWait, "Wait time cannot be null");
135131

132+
useUnshare = isUnshareUseable();
133+
136134
if (parentDirectory != null) {
137135
mkdirs(parentDirectory);
138136
cleanOldDataDirectories(parentDirectory);
@@ -243,10 +241,10 @@ private void initdb()
243241
watch.start();
244242
List<String> command = new ArrayList<>();
245243
command.addAll(Arrays.asList(
246-
pgBin("initdb"), "-A", "trust", "-U", PG_SUPERUSER,
244+
"-A", "trust", "-U", PG_SUPERUSER,
247245
"-D", dataDirectory.getPath(), "-E", "UTF-8"));
248246
command.addAll(createLocaleOptions());
249-
system(command.toArray(new String[command.size()]));
247+
system(pgBin("initdb"), command);
250248
LOG.info("{} initdb completed in {}", instanceId, watch);
251249
}
252250

@@ -259,13 +257,11 @@ private void startPostmaster() throws IOException
259257
}
260258

261259
final List<String> args = new ArrayList<>();
260+
args.addAll(pgBin("postgres"));
262261
args.addAll(Arrays.asList(
263-
pgBin("pg_ctl"),
264-
"-D", dataDirectory.getPath(),
265-
"-o", createInitOptions().stream().collect(Collectors.joining(" ")),
266-
"-w",
267-
"start"
262+
"-D", dataDirectory.getPath()
268263
));
264+
args.addAll(createInitOptions());
269265

270266
final ProcessBuilder builder = new ProcessBuilder(args);
271267

@@ -275,7 +271,7 @@ private void startPostmaster() throws IOException
275271
final Process postmaster = builder.start();
276272

277273
if (outputRedirector.type() == ProcessBuilder.Redirect.Type.PIPE) {
278-
ProcessOutputLogger.logOutput(LOG, postmaster, "pg_ctl");
274+
ProcessOutputLogger.logOutput(LOG, postmaster, "postgres");
279275
}
280276

281277
LOG.info("{} postmaster started as {} on port {}. Waiting up to {} for server startup to finish.", instanceId, postmaster.toString(), port, pgStartupWait);
@@ -414,7 +410,13 @@ public void close() throws IOException
414410

415411
private void pgCtl(File dir, String action)
416412
{
417-
system(pgBin("pg_ctl"), "-D", dir.getPath(), action, "-m", PG_STOP_MODE, "-t", PG_STOP_WAIT_S, "-w");
413+
final List<String> args = new ArrayList<>();
414+
args.addAll(Arrays.asList(
415+
"-D", dir.getPath(), action,
416+
"-m", PG_STOP_MODE, "-t",
417+
PG_STOP_WAIT_S, "-w"
418+
));
419+
system(pgBin("pg_ctl"), args);
418420
}
419421

420422
private void cleanOldDataDirectories(File parentDirectory)
@@ -461,10 +463,17 @@ private void cleanOldDataDirectories(File parentDirectory)
461463
}
462464
}
463465

464-
private String pgBin(String binaryName)
466+
private List<String> pgBin(String binaryName)
465467
{
468+
final List<String> args = new ArrayList<>();
469+
if (useUnshare) {
470+
args.addAll(Arrays.asList(
471+
"unshare", "-U"
472+
));
473+
}
466474
final String extension = SystemUtils.IS_OS_WINDOWS ? ".exe" : "";
467-
return new File(pgDir, "bin/" + binaryName + extension).getPath();
475+
args.add(new File(pgDir, "bin/" + binaryName + extension).getPath());
476+
return args;
468477
}
469478

470479
private static File getWorkingDirectory()
@@ -614,8 +623,11 @@ public int hashCode() {
614623
}
615624
}
616625

617-
private void system(String... command)
626+
private void system(List<String> bin, List<String> args)
618627
{
628+
final List<String> command = new ArrayList<>();
629+
command.addAll(bin);
630+
command.addAll(args);
619631
try {
620632
final ProcessBuilder builder = new ProcessBuilder(command);
621633
builder.redirectErrorStream(true);
@@ -624,7 +636,7 @@ private void system(String... command)
624636
final Process process = builder.start();
625637

626638
if (outputRedirector.type() == ProcessBuilder.Redirect.Type.PIPE) {
627-
String processName = command[0].replaceAll("^.*[\\\\/](\\w+)(\\.exe)?$", "$1");
639+
String processName = bin.get(bin.size() - 1).replaceAll("^.*[\\\\/](\\w+)(\\.exe)?$", "$1");
628640
ProcessOutputLogger.logOutput(LOG, process, processName);
629641
}
630642
if (0 != process.waitFor()) {

src/main/java/io/zonky/test/db/postgres/util/LinuxUtils.java

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424
import java.io.InputStreamReader;
2525
import java.nio.file.Files;
2626
import java.nio.file.Path;
27+
import java.util.ArrayList;
28+
import java.util.Arrays;
29+
import java.util.List;
2730

2831
import static java.nio.charset.StandardCharsets.UTF_8;
2932
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
@@ -34,12 +37,16 @@ public final class LinuxUtils {
3437

3538
private static final String DISTRIBUTION_NAME = resolveDistributionName();
3639

40+
private static final boolean UNSHARE_USEABLE = unshareUseable();
41+
3742
private LinuxUtils() {}
3843

3944
public static String getDistributionName() {
4045
return DISTRIBUTION_NAME;
4146
}
4247

48+
public static boolean isUnshareUseable() { return UNSHARE_USEABLE; }
49+
4350
private static String resolveDistributionName() {
4451
if (!SystemUtils.IS_OS_LINUX) {
4552
return null;
@@ -85,4 +92,38 @@ private static String resolveDistributionName() {
8592
return null;
8693
}
8794
}
95+
96+
private static boolean unshareUseable() {
97+
if (SystemUtils.IS_OS_LINUX) {
98+
int uid = (int) new com.sun.security.auth.module.UnixSystem().getUid();
99+
if (uid == 0) {
100+
final List<String> command = new ArrayList<>();
101+
command.addAll(Arrays.asList(
102+
"unshare", "-U",
103+
"id", "-u"
104+
));
105+
final ProcessBuilder builder = new ProcessBuilder(command);
106+
final Process process;
107+
try {
108+
process = builder.start();
109+
} catch (IOException e) {
110+
return false;
111+
}
112+
BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));
113+
try {
114+
process.waitFor();
115+
} catch (InterruptedException e) {
116+
return false;
117+
}
118+
try {
119+
if (process.exitValue() == 0 && br.readLine() != "0") {
120+
return true;
121+
}
122+
} catch (IOException e) {
123+
return false;
124+
}
125+
}
126+
}
127+
return false;
128+
}
88129
}

0 commit comments

Comments
 (0)