Skip to content

Commit 5ba770c

Browse files
toureamarcuslinke
authored andcommitted
Add support for private docker registry version 2 (docker-java#756)
Private docker registry version 1 expects the authentication credentials in $DOCKER_HOME/.dockercfg while version 2 expects them in $DOCKER_HOME/config.json. Support for both paths should be added to make docker-java more portable. This pull request has the following modifications: * When resolving the path of the authentication configuration file, $DOCKER_HOME/config.json (v2) is tried before $DOCKER_HOME/.dockercfg (v1) * When parsing the content of the authentication configuration file, the format of config.json (v2) is tried before .dockercfg (v1) * Test for both config.json (v2) and dockercfg (v1) have been added
1 parent 1d9edee commit 5ba770c

File tree

5 files changed

+99
-10
lines changed

5 files changed

+99
-10
lines changed

src/main/java/com/github/dockerjava/core/AuthConfigFile.java

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,13 @@
1919
public class AuthConfigFile {
2020
private static final ObjectMapper MAPPER = new ObjectMapper();
2121

22-
private static final TypeReference<Map<String, AuthConfig>> CONFIG_MAP_TYPE = new TypeReference<Map<String, AuthConfig>>() {
23-
};
22+
private static final TypeReference<Map<String, AuthConfig>> CONFIG_CFG_MAP_TYPE =
23+
new TypeReference<Map<String, AuthConfig>>() {
24+
};
25+
26+
private static final TypeReference<Map<String, Map<String, AuthConfig>>> CONFIG_JSON_MAP_TYPE =
27+
new TypeReference<Map<String, Map<String, AuthConfig>>>() {
28+
};
2429

2530
private final Map<String, AuthConfig> authConfigMap;
2631

@@ -101,12 +106,27 @@ public static AuthConfigFile loadConfig(File confFile) throws IOException {
101106
if (!confFile.exists()) {
102107
return new AuthConfigFile();
103108
}
109+
104110
Map<String, AuthConfig> configMap = null;
111+
/*
112+
Registry v2 expects config expects config.json while v2 expects .dockercfg
113+
The only difference between them is that config.json wraps "auths" around the AuthConfig
114+
*/
105115
try {
106-
configMap = MAPPER.readValue(confFile, CONFIG_MAP_TYPE);
107-
} catch (IOException e) {
108-
// pass
116+
// try registry version 2
117+
Map<String, Map<String, AuthConfig>> configJson = MAPPER.readValue(confFile, CONFIG_JSON_MAP_TYPE);
118+
if (configJson != null) {
119+
configMap = configJson.get("auths");
120+
}
121+
} catch (IOException e1) {
122+
try {
123+
// try registry version 1
124+
configMap = MAPPER.readValue(confFile, CONFIG_CFG_MAP_TYPE);
125+
} catch (IOException e2) {
126+
// pass
127+
}
109128
}
129+
110130
if (configMap != null) {
111131
for (Map.Entry<String, AuthConfig> entry : configMap.entrySet()) {
112132
AuthConfig authConfig = entry.getValue();

src/main/java/com/github/dockerjava/core/DefaultDockerClientConfig.java

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
import java.io.InputStream;
1010
import java.io.Serializable;
1111
import java.net.URI;
12+
import java.nio.file.Files;
13+
import java.nio.file.Path;
14+
import java.nio.file.Paths;
1215
import java.util.HashSet;
1316
import java.util.Map;
1417
import java.util.Properties;
@@ -55,6 +58,8 @@ public class DefaultDockerClientConfig implements Serializable, DockerClientConf
5558

5659
private static final String DOCKER_CFG = ".dockercfg";
5760

61+
private static final String CONFIG_JSON = "config.json";
62+
5863
private static final Set<String> CONFIG_KEYS = new HashSet<String>();
5964

6065
static {
@@ -253,9 +258,9 @@ && getRegistryUrl() != null) {
253258
public AuthConfig effectiveAuthConfig(String imageName) {
254259
AuthConfig authConfig = null;
255260

256-
File dockerCfgFile = new File(getDockerConfig() + File.separator + DOCKER_CFG);
261+
File dockerCfgFile = getDockerConfigFile();
257262

258-
if (dockerCfgFile.exists() && dockerCfgFile.isFile() && imageName != null) {
263+
if (dockerCfgFile != null) {
259264
AuthConfigFile authConfigFile;
260265
try {
261266
authConfigFile = AuthConfigFile.loadConfig(dockerCfgFile);
@@ -279,13 +284,15 @@ public AuthConfig effectiveAuthConfig(String imageName) {
279284

280285
@Override
281286
public AuthConfigurations getAuthConfigurations() {
282-
File dockerCfgFile = new File(getDockerConfig() + File.separator + DOCKER_CFG);
283-
if (dockerCfgFile.exists() && dockerCfgFile.isFile()) {
287+
File dockerCfgFile = getDockerConfigFile();
288+
289+
if (dockerCfgFile != null) {
284290
AuthConfigFile authConfigFile;
285291
try {
286292
authConfigFile = AuthConfigFile.loadConfig(dockerCfgFile);
287293
} catch (IOException e) {
288-
throw new DockerClientException("Failed to parse dockerCfgFile", e);
294+
throw new DockerClientException("Failed to parse dockerCfgFile: " +
295+
dockerCfgFile.getAbsolutePath(), e);
289296
}
290297

291298
return authConfigFile.getAuthConfigurations();
@@ -294,6 +301,19 @@ public AuthConfigurations getAuthConfigurations() {
294301
return new AuthConfigurations();
295302
}
296303

304+
private File getDockerConfigFile() {
305+
final Path configJson = Paths.get(getDockerConfig(), CONFIG_JSON);
306+
final Path dockerCfg = Paths.get(getDockerConfig(), DOCKER_CFG);
307+
308+
if (Files.exists(configJson)) {
309+
return configJson.toFile();
310+
} else if (Files.exists(dockerCfg)) {
311+
return dockerCfg.toFile();
312+
} else {
313+
return null;
314+
}
315+
}
316+
297317
@Override
298318
public SSLConfig getSSLConfig() {
299319
return sslConfig;

src/test/java/com/github/dockerjava/core/DefaultDockerClientConfigTest.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@
22

33
import static org.hamcrest.MatcherAssert.assertThat;
44
import static org.hamcrest.Matchers.equalTo;
5+
import static org.hamcrest.Matchers.notNullValue;
56
import static org.hamcrest.core.Is.is;
67
import static org.testng.Assert.assertEquals;
78
import static org.testng.Assert.assertNull;
89

10+
import java.io.File;
911
import java.lang.reflect.Field;
1012
import java.net.URI;
13+
import java.net.URISyntaxException;
1114
import java.util.Collections;
1215
import java.util.HashMap;
1316
import java.util.Map;
@@ -18,6 +21,8 @@
1821

1922
import com.github.dockerjava.api.exception.DockerClientException;
2023
import com.github.dockerjava.api.model.AuthConfig;
24+
import com.github.dockerjava.api.model.AuthConfigurations;
25+
import com.google.common.io.Resources;
2126

2227
public class DefaultDockerClientConfigTest {
2328

@@ -201,4 +206,36 @@ public void withDockerTlsVerify() throws Exception {
201206
assertThat((Boolean) field.get(builder), is(true));
202207
}
203208

209+
@Test
210+
public void testGetAuthConfigurationsFromDockerCfg() throws URISyntaxException {
211+
File cfgFile = new File(Resources.getResource("com.github.dockerjava.core/registry.v1").toURI());
212+
DefaultDockerClientConfig clientConfig = new DefaultDockerClientConfig(URI.create(
213+
"unix://foo"), cfgFile.getAbsolutePath(), "apiVersion", "registryUrl", "registryUsername", "registryPassword",
214+
"registryEmail", null);
215+
216+
AuthConfigurations authConfigurations = clientConfig.getAuthConfigurations();
217+
assertThat(authConfigurations, notNullValue());
218+
assertThat(authConfigurations.getConfigs().get("https://test.docker.io/v1/"), notNullValue());
219+
220+
AuthConfig authConfig = authConfigurations.getConfigs().get("https://test.docker.io/v1/");
221+
assertThat(authConfig.getUsername(), equalTo("user"));
222+
assertThat(authConfig.getPassword(), equalTo("password"));
223+
}
224+
225+
@Test
226+
public void testGetAuthConfigurationsFromConfigJson() throws URISyntaxException {
227+
File cfgFile = new File(Resources.getResource("com.github.dockerjava.core/registry.v2").toURI());
228+
DefaultDockerClientConfig clientConfig = new DefaultDockerClientConfig(URI.create(
229+
"unix://foo"), cfgFile.getAbsolutePath(), "apiVersion", "registryUrl", "registryUsername", "registryPassword",
230+
"registryEmail", null);
231+
232+
AuthConfigurations authConfigurations = clientConfig.getAuthConfigurations();
233+
assertThat(authConfigurations, notNullValue());
234+
assertThat(authConfigurations.getConfigs().get("https://test.docker.io/v2/"), notNullValue());
235+
236+
AuthConfig authConfig = authConfigurations.getConfigs().get("https://test.docker.io/v2/");
237+
assertThat(authConfig.getUsername(), equalTo("user"));
238+
assertThat(authConfig.getPassword(), equalTo("password"));
239+
}
240+
204241
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"https://test.docker.io/v1/": {
3+
"auth": "dXNlcjpwYXNzd29yZA=="
4+
}
5+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"auths": {
3+
"https://test.docker.io/v2/": {
4+
"auth": "dXNlcjpwYXNzd29yZA=="
5+
}
6+
}
7+
}

0 commit comments

Comments
 (0)