Skip to content

Commit d7db365

Browse files
authored
Support copyUIDGID option in CopyArchiveToContainerCmd (#1963)
Fix #1258 Signed-off-by: kwall <[email protected]>
1 parent 3951333 commit d7db365

File tree

4 files changed

+99
-3
lines changed

4 files changed

+99
-3
lines changed

docker-java-api/src/main/java/com/github/dockerjava/api/command/CopyArchiveToContainerCmd.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ public interface CopyArchiveToContainerCmd extends SyncDockerCmd<Void> {
1616

1717
boolean isDirChildrenOnly();
1818

19+
boolean isCopyUIDGID();
1920
/**
2021
* Set container's id
2122
*
@@ -49,6 +50,14 @@ public interface CopyArchiveToContainerCmd extends SyncDockerCmd<Void> {
4950
*/
5051
CopyArchiveToContainerCmd withNoOverwriteDirNonDir(boolean noOverwriteDirNonDir);
5152

53+
/**
54+
* If set to true then ownership is set to the user and primary group at the destination
55+
*
56+
* @param copyUIDGID
57+
* flag to know if ownership should be set to the user and primary group at the destination
58+
*/
59+
CopyArchiveToContainerCmd withCopyUIDGID(boolean copyUIDGID);
60+
5261
/**
5362
* If this flag is set to true, all children of the local directory will be copied to the remote without the root directory. For ex: if
5463
* I have root/titi and root/tata and the remote path is /var/data. dirChildrenOnly = true will create /var/data/titi and /var/data/tata

docker-java-core/src/main/java/com/github/dockerjava/core/command/CopyArchiveToContainerCmdImpl.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ public class CopyArchiveToContainerCmdImpl extends AbstrDockerCmd<CopyArchiveToC
3131

3232
private boolean dirChildrenOnly = false;
3333

34+
private boolean copyUIDGID = false;
35+
3436
public CopyArchiveToContainerCmdImpl(CopyArchiveToContainerCmd.Exec exec, String containerId) {
3537
super(exec);
3638
withContainerId(containerId);
@@ -56,6 +58,12 @@ public CopyArchiveToContainerCmd withNoOverwriteDirNonDir(boolean noOverwriteDir
5658
return this;
5759
}
5860

61+
@Override
62+
public CopyArchiveToContainerCmd withCopyUIDGID(boolean copyUIDGID) {
63+
this.copyUIDGID = copyUIDGID;
64+
return this;
65+
}
66+
5967
@Override
6068
public CopyArchiveToContainerCmd withRemotePath(String remotePath) {
6169
checkNotNull(remotePath, "remotePath was not specified");
@@ -106,10 +114,15 @@ public boolean isDirChildrenOnly() {
106114
return this.dirChildrenOnly;
107115
}
108116

117+
@Override
118+
public boolean isCopyUIDGID() {
119+
return this.copyUIDGID;
120+
}
121+
109122
@Override
110123
public String toString() {
111-
return new ToStringBuilder(this).append("cp ").append(hostResource).append(" ").append(containerId).append(":")
112-
.append(remotePath).toString();
124+
return new ToStringBuilder(this).append("cp ").append(String.format("-a=%b ", isCopyUIDGID()))
125+
.append(hostResource).append(" ").append(containerId).append(":").append(remotePath).toString();
113126
}
114127

115128
/**

docker-java-core/src/main/java/com/github/dockerjava/core/exec/CopyArchiveToContainerCmdExec.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ protected Void execute(CopyArchiveToContainerCmd command) {
2828
InputStream streamToUpload = command.getTarInputStream();
2929

3030
webResource.queryParam("path", command.getRemotePath())
31-
.queryParam("noOverwriteDirNonDir", command.isNoOverwriteDirNonDir()).request()
31+
.queryParam("noOverwriteDirNonDir", command.isNoOverwriteDirNonDir())
32+
.queryParam("copyUIDGID", command.isCopyUIDGID())
33+
.request()
3234
.put(streamToUpload, MediaType.APPLICATION_X_TAR);
3335

3436
return null;

docker-java/src/test/java/com/github/dockerjava/cmd/CopyArchiveToContainerCmdIT.java

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.github.dockerjava.api.command.CreateContainerResponse;
55
import com.github.dockerjava.api.exception.NotFoundException;
66
import com.github.dockerjava.core.util.CompressArchiveUtil;
7+
import com.github.dockerjava.utils.LogContainerTestCallback;
78
import org.apache.commons.io.FileUtils;
89
import org.junit.Test;
910
import org.slf4j.Logger;
@@ -14,12 +15,16 @@
1415
import java.nio.file.Files;
1516
import java.nio.file.Path;
1617
import java.nio.file.Paths;
18+
import java.util.concurrent.TimeUnit;
1719

1820
import static org.hamcrest.MatcherAssert.assertThat;
21+
import static org.hamcrest.Matchers.containsString;
1922
import static org.hamcrest.Matchers.equalTo;
2023
import static org.hamcrest.Matchers.isEmptyOrNullString;
2124
import static org.hamcrest.Matchers.not;
25+
import static org.hamcrest.Matchers.notNullValue;
2226
import static org.junit.Assert.assertTrue;
27+
import static org.junit.Assume.assumeThat;
2328

2429
public class CopyArchiveToContainerCmdIT extends CmdIT {
2530
public static final Logger LOG = LoggerFactory.getLogger(CopyArchiveToContainerCmdIT.class);
@@ -143,4 +148,71 @@ public void copyFileWithExecutePermission() throws Exception {
143148
assertThat(exitCode, equalTo(0));
144149
}
145150

151+
@Test
152+
public void copyFileWithUIDGID() throws Exception {
153+
Path with = Files.createFile(Files.createTempDirectory("copyFileWithUIDGID").resolve("uidgid.with"));
154+
Files.write(with, "with".getBytes());
155+
156+
Path without = Files.createFile(Files.createTempDirectory("copyFileWithUIDGID").resolve("uidgid.without"));
157+
Files.write(without, "without".getBytes());
158+
159+
String containerCmd = "while [ ! -f /home/uidgid.with ]; do true; done && stat -c %n:%u /home/uidgid.with /home/uidgid.without";
160+
Long syncUserUid = 4L; // sync user in busybox uses uid=4
161+
CreateContainerResponse container = dockerRule.getClient().createContainerCmd("busybox")
162+
.withName("copyFileWithUIDGID")
163+
.withCmd("/bin/sh", "-c", containerCmd)
164+
.withUser(syncUserUid.toString())
165+
.exec();
166+
// start the container
167+
dockerRule.getClient().startContainerCmd(container.getId()).exec();
168+
169+
dockerRule.getClient().copyArchiveToContainerCmd(container.getId())
170+
.withRemotePath("/home/")
171+
.withHostResource(without.toString())
172+
.withCopyUIDGID(false)
173+
.exec();
174+
dockerRule.getClient().copyArchiveToContainerCmd(container.getId())
175+
.withRemotePath("/home/")
176+
.withHostResource(with.toString())
177+
.withCopyUIDGID(true)
178+
.exec();
179+
180+
// await exit code
181+
int exitCode = dockerRule.getClient().waitContainerCmd(container.getId())
182+
.start()
183+
.awaitStatusCode();
184+
// check result
185+
assertThat(exitCode, equalTo(0));
186+
187+
LogContainerTestCallback loggingCallback = new LogContainerTestCallback(true);
188+
189+
dockerRule.getClient().logContainerCmd(container.getId())
190+
.withStdOut(true)
191+
.withTailAll()
192+
.exec(loggingCallback);
193+
194+
loggingCallback.awaitCompletion(3, TimeUnit.SECONDS);
195+
String containerOutput = loggingCallback.toString();
196+
197+
assertThat(containerOutput, containsString(String.format("/home/uidgid.with:%d", syncUserUid)));
198+
199+
Long hostUid = getHostUidIfPossible();
200+
assumeThat("could not get the uid on host platform", hostUid, notNullValue(Long.class));
201+
assertThat(containerOutput, containsString(String.format("/home/uidgid.without:%d", hostUid)));
202+
}
203+
204+
private static Long getHostUidIfPossible() {
205+
try {
206+
Class<?> unixSystemClazz = Class.forName("com.sun.security.auth.module.UnixSystem");
207+
Object unixSystem = unixSystemClazz.newInstance();
208+
Object uid = unixSystemClazz.getMethod("getUid").invoke(unixSystem);
209+
if (uid == null) {
210+
return null;
211+
}
212+
213+
return uid instanceof Long ? (Long) uid : Long.parseLong(uid.toString());
214+
} catch (Exception e) {
215+
return null;
216+
}
217+
}
146218
}

0 commit comments

Comments
 (0)