Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit d3f6854

Browse filesBrowse files
authored
Merge pull request #39 from jameshilliard/root-unshare
Allow running under root on Linux when unshare is available
2 parents 5917444 + 97f7211 commit d3f6854
Copy full SHA for d3f6854

File tree

Expand file treeCollapse file tree

5 files changed

+122
-50
lines changed
Filter options
Expand file treeCollapse file tree

5 files changed

+122
-50
lines changed

‎.github/workflows/maven.yml

Copy file name to clipboardExpand all lines: .github/workflows/maven.yml
+1-1Lines 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

‎README.md

Copy file name to clipboardExpand all lines: README.md
+9-5Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -150,17 +150,21 @@ Since `PostgreSQL 10.0`, there are additional artifacts with `alpine-lite` suffi
150150

151151
### Process [/tmp/embedded-pg/PG-XYZ/bin/initdb, ...] failed
152152

153-
Try to remove `/tmp/embedded-pg/PG-XYZ` directory containing temporary binaries of the embedded postgres database. That should solve the problem.
153+
Check the console output for an `initdb: cannot be run as root` message. If the error is present, try to upgrade to a newer version of the library (1.2.8+), or ensure the build process to be running as a non-root user.
154+
155+
If the error is not present, try to clean up the `/tmp/embedded-pg/PG-XYZ` directory containing temporary binaries of the embedded database.
154156

155157
### Running tests on Windows does not work
156158

157-
You probably need to install the [Microsoft Visual C++ 2013 Redistributable Package](https://support.microsoft.com/en-us/help/3179560/update-for-visual-c-2013-and-visual-c-redistributable-package). The version 2013 is important, installation of other versions will not help. More detailed is the problem discussed [here](https://github.com/opentable/otj-pg-embedded/issues/65).
159+
You probably need to install [Microsoft Visual C++ 2013 Redistributable Package](https://support.microsoft.com/en-us/help/3179560/update-for-visual-c-2013-and-visual-c-redistributable-package). The version 2013 is important, installation of other versions will not help. More detailed is the problem discussed [here](https://github.com/opentable/otj-pg-embedded/issues/65).
160+
161+
### Running tests in Docker does not work
158162

159-
### Running tests inside Docker does not work
163+
Running builds inside a Docker container is fully supported, including Alpine Linux. However, PostgreSQL has a restriction the database process must run under a non-root user. Otherwise, the database does not start and fails with an error.
160164

161-
Running build inside Docker is fully supported, including Alpine Linux. But you must keep in mind that the **PostgreSQL database must be run under a non-root user**. Otherwise, the database does not start and fails with an error.
165+
So be sure to use a docker image that uses a non-root user. Or, since version `1.2.8` you can run the docker container with `--privileged` option, which allows taking advantage of `unshare` command to run the database process in a separate namespace.
162166

163-
So be sure to use a docker image that uses a non-root user, or you can use any of the following Dockerfiles to prepare your own image.
167+
Below are some examples of how to prepare a docker image running with a non-root user:
164168

165169
<details>
166170
<summary>Standard Dockerfile</summary>

‎pom.xml

Copy file name to clipboardExpand all lines: pom.xml
+16-16Lines 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

Copy file name to clipboardExpand all lines: src/main/java/io/zonky/test/db/postgres/embedded/EmbeddedPostgres.java
+57-28Lines changed: 57 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
*/
1414
package io.zonky.test.db.postgres.embedded;
1515

16-
1716
import java.io.ByteArrayInputStream;
1817
import java.io.Closeable;
1918
import java.io.File;
@@ -55,7 +54,6 @@
5554
import java.util.concurrent.atomic.AtomicBoolean;
5655
import java.util.concurrent.locks.Lock;
5756
import java.util.concurrent.locks.ReentrantLock;
58-
import java.util.stream.Collectors;
5957

6058
import javax.sql.DataSource;
6159

@@ -72,6 +70,8 @@
7270
import org.slf4j.LoggerFactory;
7371
import org.tukaani.xz.XZInputStream;
7472

73+
import io.zonky.test.db.postgres.util.LinuxUtils;
74+
7575
import static java.nio.file.StandardOpenOption.CREATE;
7676
import static java.nio.file.StandardOpenOption.WRITE;
7777
import static java.util.Collections.unmodifiableMap;
@@ -241,12 +241,12 @@ private void initdb()
241241
{
242242
final StopWatch watch = new StopWatch();
243243
watch.start();
244-
List<String> command = new ArrayList<>();
245-
command.addAll(Arrays.asList(
246-
pgBin("initdb"), "-A", "trust", "-U", PG_SUPERUSER,
244+
List<String> args = new ArrayList<>();
245+
args.addAll(Arrays.asList(
246+
"-A", "trust", "-U", PG_SUPERUSER,
247247
"-D", dataDirectory.getPath(), "-E", "UTF-8"));
248-
command.addAll(createLocaleOptions());
249-
system(command.toArray(new String[command.size()]));
248+
args.addAll(createLocaleOptions());
249+
system(INIT_DB, args);
250250
LOG.info("{} initdb completed in {}", instanceId, watch);
251251
}
252252

@@ -259,23 +259,19 @@ private void startPostmaster() throws IOException
259259
}
260260

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

270-
final ProcessBuilder builder = new ProcessBuilder(args);
265+
final ProcessBuilder builder = new ProcessBuilder();
266+
POSTGRES.applyTo(builder, args);
271267

272268
builder.redirectErrorStream(true);
273269
builder.redirectError(errorRedirector);
274270
builder.redirectOutput(outputRedirector);
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.processName());
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(PG_CTL, args);
418420
}
419421

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

464-
private String pgBin(String binaryName)
465-
{
466-
final String extension = SystemUtils.IS_OS_WINDOWS ? ".exe" : "";
467-
return new File(pgDir, "bin/" + binaryName + extension).getPath();
468-
}
469-
470466
private static File getWorkingDirectory()
471467
{
472468
final File tempWorkingDirectory = new File(System.getProperty("java.io.tmpdir"), "embedded-pg");
@@ -614,21 +610,23 @@ public int hashCode() {
614610
}
615611
}
616612

617-
private void system(String... command)
613+
private void system(Command command, List<String> args)
618614
{
619615
try {
620-
final ProcessBuilder builder = new ProcessBuilder(command);
616+
final ProcessBuilder builder = new ProcessBuilder();
617+
618+
command.applyTo(builder, args);
621619
builder.redirectErrorStream(true);
622620
builder.redirectError(errorRedirector);
623621
builder.redirectOutput(outputRedirector);
622+
624623
final Process process = builder.start();
625624

626625
if (outputRedirector.type() == ProcessBuilder.Redirect.Type.PIPE) {
627-
String processName = command[0].replaceAll("^.*[\\\\/](\\w+)(\\.exe)?$", "$1");
628-
ProcessOutputLogger.logOutput(LOG, process, processName);
626+
ProcessOutputLogger.logOutput(LOG, process, command.processName());
629627
}
630628
if (0 != process.waitFor()) {
631-
throw new IllegalStateException(String.format("Process %s failed", Arrays.asList(command)));
629+
throw new IllegalStateException(String.format("Process %s failed", builder.command()));
632630
}
633631
} catch (final RuntimeException e) { // NOPMD
634632
throw e;
@@ -841,4 +839,35 @@ public String toString()
841839
{
842840
return "EmbeddedPG-" + instanceId;
843841
}
842+
843+
private final Command INIT_DB = new Command("initdb");
844+
private final Command POSTGRES = new Command("postgres");
845+
private final Command PG_CTL = new Command("pg_ctl");
846+
847+
private class Command {
848+
849+
private final String commandName;
850+
851+
private Command(String commandName) {
852+
this.commandName = commandName;
853+
}
854+
855+
public String processName() {
856+
return commandName;
857+
}
858+
859+
public void applyTo(ProcessBuilder builder, List<String> arguments) {
860+
List<String> command = new ArrayList<>();
861+
862+
if (LinuxUtils.isUnshareAvailable()) {
863+
command.addAll(Arrays.asList("unshare", "-U"));
864+
}
865+
866+
String extension = SystemUtils.IS_OS_WINDOWS ? ".exe" : "";
867+
command.add(new File(pgDir, "bin/" + commandName + extension).getPath());
868+
command.addAll(arguments);
869+
870+
builder.command(command);
871+
}
872+
}
844873
}

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

Copy file name to clipboardExpand all lines: src/main/java/io/zonky/test/db/postgres/util/LinuxUtils.java
+39Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.io.IOException;
2323
import java.io.InputStream;
2424
import java.io.InputStreamReader;
25+
import java.lang.reflect.Method;
2526
import java.nio.file.Files;
2627
import java.nio.file.Path;
2728

@@ -33,13 +34,18 @@ public final class LinuxUtils {
3334
private static final Logger logger = LoggerFactory.getLogger(LinuxUtils.class);
3435

3536
private static final String DISTRIBUTION_NAME = resolveDistributionName();
37+
private static final boolean UNSHARE_AVAILABLE = unshareAvailable();
3638

3739
private LinuxUtils() {}
3840

3941
public static String getDistributionName() {
4042
return DISTRIBUTION_NAME;
4143
}
4244

45+
public static boolean isUnshareAvailable() {
46+
return UNSHARE_AVAILABLE;
47+
}
48+
4349
private static String resolveDistributionName() {
4450
if (!SystemUtils.IS_OS_LINUX) {
4551
return null;
@@ -85,4 +91,37 @@ private static String resolveDistributionName() {
8591
return null;
8692
}
8793
}
94+
95+
private static boolean unshareAvailable() {
96+
if (!SystemUtils.IS_OS_LINUX) {
97+
return false;
98+
}
99+
100+
try {
101+
Class<?> clazz = Class.forName("com.sun.security.auth.module.UnixSystem");
102+
Object instance = clazz.getDeclaredConstructor().newInstance();
103+
Method method = clazz.getDeclaredMethod("getUid");
104+
int uid = ((Number) method.invoke(instance)).intValue();
105+
106+
if (uid != 0) {
107+
return false;
108+
}
109+
110+
ProcessBuilder builder = new ProcessBuilder();
111+
builder.command("unshare", "-U", "id", "-u");
112+
113+
Process process = builder.start();
114+
process.waitFor();
115+
116+
try (BufferedReader outputReader = new BufferedReader(new InputStreamReader(process.getInputStream(), UTF_8))) {
117+
if (process.exitValue() == 0 && !"0".equals(outputReader.readLine())) {
118+
return true;
119+
}
120+
}
121+
122+
return false;
123+
} catch (Exception e) {
124+
return false;
125+
}
126+
}
88127
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.