From 4fd7b8a2ebdbac7f2c65ae02cc4ae6e7ba57992c Mon Sep 17 00:00:00 2001 From: fb Date: Sun, 16 Oct 2016 21:54:49 +0200 Subject: [PATCH 1/6] implement new docker config file format for authentication add auth option to create image command --- .../api/command/CreateContainerCmd.java | 5 + .../dockerjava/core/AuthConfigFile.java | 210 ------------------ .../core/DefaultDockerClientConfig.java | 108 +++------ .../dockerjava/core/DockerClientImpl.java | 3 +- .../dockerjava/core/DockerConfigFile.java | 209 +++++++++++++++++ .../core/command/CreateContainerCmdImpl.java | 20 +- .../jaxrs/CreateContainerCmdExec.java | 25 ++- .../core/DefaultDockerClientConfigTest.java | 2 +- ...ileTest.java => DockerConfigFileTest.java} | 49 ++-- .../{emptyFile => emptyFile/.dockercfg} | 0 .../.dockercfg} | 0 .../.dockercfg} | 0 .../.dockercfg} | 0 .../.dockercfg} | 0 .../{tooSmallFile => tooSmallFile/.dockercfg} | 0 .../validDockerConfig/config.json | 12 + .../testAuthConfigFile/validJson/.dockercfg | 1 + .../{validLegacy => validLegacy/.dockercfg} | 0 18 files changed, 338 insertions(+), 306 deletions(-) delete mode 100644 src/main/java/com/github/dockerjava/core/AuthConfigFile.java create mode 100644 src/main/java/com/github/dockerjava/core/DockerConfigFile.java rename src/test/java/com/github/dockerjava/core/{AuthConfigFileTest.java => DockerConfigFileTest.java} (68%) rename src/test/resources/testAuthConfigFile/{emptyFile => emptyFile/.dockercfg} (100%) rename src/test/resources/testAuthConfigFile/{invalidJsonInvalidAuth => invalidJsonInvalidAuth/.dockercfg} (100%) rename src/test/resources/testAuthConfigFile/{invalidLegacyAuthLine => invalidLegacyAuthLine/.dockercfg} (100%) rename src/test/resources/testAuthConfigFile/{invalidLegacyEmailLine => invalidLegacyEmailLine/.dockercfg} (100%) rename src/test/resources/testAuthConfigFile/{invalidLegacyInvalidAuth => invalidLegacyInvalidAuth/.dockercfg} (100%) rename src/test/resources/testAuthConfigFile/{tooSmallFile => tooSmallFile/.dockercfg} (100%) create mode 100644 src/test/resources/testAuthConfigFile/validDockerConfig/config.json create mode 100644 src/test/resources/testAuthConfigFile/validJson/.dockercfg rename src/test/resources/testAuthConfigFile/{validLegacy => validLegacy/.dockercfg} (100%) diff --git a/src/main/java/com/github/dockerjava/api/command/CreateContainerCmd.java b/src/main/java/com/github/dockerjava/api/command/CreateContainerCmd.java index 77bf100ac..0b1c62229 100644 --- a/src/main/java/com/github/dockerjava/api/command/CreateContainerCmd.java +++ b/src/main/java/com/github/dockerjava/api/command/CreateContainerCmd.java @@ -2,6 +2,7 @@ import com.github.dockerjava.api.exception.ConflictException; import com.github.dockerjava.api.exception.NotFoundException; +import com.github.dockerjava.api.model.AuthConfig; import com.github.dockerjava.api.model.Bind; import com.github.dockerjava.api.model.Capability; import com.github.dockerjava.api.model.Device; @@ -23,6 +24,8 @@ public interface CreateContainerCmd extends SyncDockerCmd { + AuthConfig getAuthConfig(); + @CheckForNull List getAliases(); @@ -197,6 +200,8 @@ public interface CreateContainerCmd extends SyncDockerCmd> CONFIG_CFG_MAP_TYPE = - new TypeReference>() { - }; - - private static final TypeReference>> CONFIG_JSON_MAP_TYPE = - new TypeReference>>() { - }; - private static final String AUTHS_PROPERTY = "auths"; - - private final Map authConfigMap; - - public AuthConfigFile() { - authConfigMap = new HashMap<>(); - } - - void addConfig(AuthConfig config) { - authConfigMap.put(config.getRegistryAddress(), config); - } - - public AuthConfig resolveAuthConfig(String hostname) { - if (StringUtils.isEmpty(hostname) || AuthConfig.DEFAULT_SERVER_ADDRESS.equals(hostname)) { - return authConfigMap.get(AuthConfig.DEFAULT_SERVER_ADDRESS); - } - AuthConfig c = authConfigMap.get(hostname); - if (c != null) { - return c; - } - - // Maybe they have a legacy config file, we will iterate the keys converting - // them to the new format and testing - String normalizedHostname = convertToHostname(hostname); - for (Map.Entry entry : authConfigMap.entrySet()) { - String registry = entry.getKey(); - AuthConfig config = entry.getValue(); - if (convertToHostname(registry).equals(normalizedHostname)) { - return config; - } - } - return null; - } - - public AuthConfigurations getAuthConfigurations() { - final AuthConfigurations authConfigurations = new AuthConfigurations(); - for (Map.Entry authConfigEntry : authConfigMap.entrySet()) { - authConfigurations.addConfig(authConfigEntry.getValue()); - } - - return authConfigurations; - } - - // CHECKSTYLE:OFF - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((authConfigMap == null) ? 0 : authConfigMap.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - AuthConfigFile other = (AuthConfigFile) obj; - if (authConfigMap == null) { - if (other.authConfigMap != null) - return false; - } else if (!authConfigMap.equals(other.authConfigMap)) - return false; - return true; - } - - // CHECKSTYLE:ON - - @Override - public String toString() { - return "AuthConfigFile [authConfigMap=" + authConfigMap + "]"; - } - - public static AuthConfigFile loadConfig(File confFile) throws IOException { - AuthConfigFile configFile = new AuthConfigFile(); - if (!confFile.exists()) { - return new AuthConfigFile(); - } - - Map configMap = null; - if (isJSONFile(confFile)) { - /* - Registry v2 expects config expects config.json while v2 expects .dockercfg - The only difference between them is that config.json wraps "auths" around the AuthConfig - */ - try { - // try registry version 2 - final ObjectNode node = filterNonAuthsFromJSON(confFile); - Map> configJson = MAPPER.convertValue(node, CONFIG_JSON_MAP_TYPE); - if (configJson != null && !configJson.isEmpty()) { - configMap = configJson.get(AUTHS_PROPERTY); - } - - } catch (IOException e1) { - try { - // try registry version 1 - configMap = MAPPER.readValue(confFile, CONFIG_CFG_MAP_TYPE); - } catch (IOException e2) { - // we know it is JSON so it does not make sense to check for the old format - // probably Docker writes some unstructured contents here, see #806 - // at least it is not worse than the totally absent file - return new AuthConfigFile(); - } - } - } - - if (configMap != null) { - for (Map.Entry entry : configMap.entrySet()) { - AuthConfig authConfig = entry.getValue(); - final String auth = authConfig.getAuth(); - if (auth == null) continue; - - decodeAuth(auth, authConfig); - authConfig.withAuth(null); - authConfig.withRegistryAddress(entry.getKey()); - configFile.addConfig(authConfig); - } - } else { - List authFileContent = FileUtils.readLines(confFile); - if (authFileContent.size() < 2) { - throw new IOException("The Auth Config file is empty"); - } - AuthConfig config = new AuthConfig(); - String[] origAuth = authFileContent.get(0).split(" = "); - if (origAuth.length != 2) { - throw new IOException("Invalid Auth config file"); - } - decodeAuth(origAuth[1], config); - - String[] origEmail = authFileContent.get(1).split(" = "); - if (origEmail.length != 2) { - throw new IOException("Invalid Auth config file"); - } - config.withEmail(origEmail[1]); - configFile.addConfig(config); - } - return configFile; - - } - - private static boolean isJSONFile(final File confFile) { - try { - return MAPPER.readValue(confFile, ObjectNode.class) != null; - } catch (IOException e) { - return false; - } - } - - private static ObjectNode filterNonAuthsFromJSON(final File confFile) throws IOException { - final ObjectNode node = MAPPER.readValue(confFile, ObjectNode.class); - if (!node.has(AUTHS_PROPERTY)) { - throw new IOException("No Auth Config contained"); - } - node.retain(AUTHS_PROPERTY); - return node; - } - - static void decodeAuth(String auth, AuthConfig config) throws IOException { - String str = new String(Base64.decodeBase64(auth), Charset.forName("UTF-8")); - String[] parts = str.split(":", 2); - if (parts.length != 2) { - throw new IOException("Invalid auth configuration file"); - } - config.withUsername(parts[0]); - config.withPassword(parts[1]); - } - - static String convertToHostname(String server) { - String stripped = server; - if (server.startsWith("http://")) { - stripped = server.substring(7); - } else if (server.startsWith("https://")) { - stripped = server.substring(8); - } - String[] numParts = stripped.split("/", 2); - return numParts[0]; - } -} diff --git a/src/main/java/com/github/dockerjava/core/DefaultDockerClientConfig.java b/src/main/java/com/github/dockerjava/core/DefaultDockerClientConfig.java index 5857227ae..8517d89a9 100644 --- a/src/main/java/com/github/dockerjava/core/DefaultDockerClientConfig.java +++ b/src/main/java/com/github/dockerjava/core/DefaultDockerClientConfig.java @@ -1,7 +1,15 @@ package com.github.dockerjava.core; -import static com.google.common.base.Preconditions.checkNotNull; -import static org.apache.commons.lang.BooleanUtils.isTrue; +import com.github.dockerjava.api.exception.DockerClientException; +import com.github.dockerjava.api.model.AuthConfig; +import com.github.dockerjava.api.model.AuthConfigurations; +import com.github.dockerjava.core.NameParser.HostnameReposName; +import com.github.dockerjava.core.NameParser.ReposTag; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; +import org.apache.commons.lang.builder.ToStringBuilder; +import org.apache.commons.lang.builder.ToStringStyle; import java.io.File; import java.io.FileInputStream; @@ -9,25 +17,13 @@ import java.io.InputStream; import java.io.Serializable; import java.net.URI; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; import java.util.HashSet; import java.util.Map; import java.util.Properties; import java.util.Set; -import org.apache.commons.lang.StringUtils; -import org.apache.commons.lang.builder.EqualsBuilder; -import org.apache.commons.lang.builder.HashCodeBuilder; -import org.apache.commons.lang.builder.ToStringBuilder; -import org.apache.commons.lang.builder.ToStringStyle; - -import com.github.dockerjava.api.exception.DockerClientException; -import com.github.dockerjava.api.model.AuthConfig; -import com.github.dockerjava.api.model.AuthConfigurations; -import com.github.dockerjava.core.NameParser.HostnameReposName; -import com.github.dockerjava.core.NameParser.ReposTag; +import static com.google.common.base.Preconditions.checkNotNull; +import static org.apache.commons.lang.BooleanUtils.isTrue; /** * Respects some of the docker CLI options. See https://docs.docker.com/engine/reference/commandline/cli/#environment-variables @@ -56,10 +52,6 @@ public class DefaultDockerClientConfig implements Serializable, DockerClientConf private static final String DOCKER_JAVA_PROPERTIES = "docker-java.properties"; - private static final String DOCKER_CFG = ".dockercfg"; - - private static final String CONFIG_JSON = "config.json"; - private static final Set CONFIG_KEYS = new HashSet(); static { @@ -76,16 +68,18 @@ public class DefaultDockerClientConfig implements Serializable, DockerClientConf private final URI dockerHost; - private final String registryUsername, registryPassword, registryEmail, registryUrl, dockerConfig; + private final String registryUsername, registryPassword, registryEmail, registryUrl, dockerConfigPath; private final SSLConfig sslConfig; private final RemoteApiVersion apiVersion; - DefaultDockerClientConfig(URI dockerHost, String dockerConfig, String apiVersion, String registryUrl, + private DockerConfigFile dockerConfig = null; + + DefaultDockerClientConfig(URI dockerHost, String dockerConfigPath, String apiVersion, String registryUrl, String registryUsername, String registryPassword, String registryEmail, SSLConfig sslConfig) { this.dockerHost = checkDockerHostScheme(dockerHost); - this.dockerConfig = dockerConfig; + this.dockerConfigPath = dockerConfigPath; this.apiVersion = RemoteApiVersion.parseConfigWithDefault(apiVersion); this.sslConfig = sslConfig; this.registryUsername = registryUsername; @@ -237,7 +231,18 @@ public String getRegistryUrl() { return registryUrl; } - public String getDockerConfig() { + public String getDockerConfigPath() { + return dockerConfigPath; + } + + public DockerConfigFile getDockerConfig() { + if (dockerConfig == null) { + try { + dockerConfig = DockerConfigFile.loadConfig(new File(getDockerConfigPath())); + } catch (IOException e) { + throw new DockerClientException("Failed to parse docker configuration file", e); + } + } return dockerConfig; } @@ -256,62 +261,23 @@ && getRegistryUrl() != null) { @Override public AuthConfig effectiveAuthConfig(String imageName) { - AuthConfig authConfig = null; - - File dockerCfgFile = getDockerConfigFile(); + AuthConfig authConfig = getAuthConfig(); - if (dockerCfgFile != null) { - AuthConfigFile authConfigFile; - try { - authConfigFile = AuthConfigFile.loadConfig(dockerCfgFile); - } catch (IOException e) { - throw new DockerClientException("Failed to parse dockerCfgFile", e); - } - ReposTag reposTag = NameParser.parseRepositoryTag(imageName); - HostnameReposName hostnameReposName = NameParser.resolveRepositoryName(reposTag.repos); - - authConfig = authConfigFile.resolveAuthConfig(hostnameReposName.hostname); + if (authConfig != null) { + return authConfig; } - AuthConfig otherAuthConfig = getAuthConfig(); + DockerConfigFile dockerCfg = getDockerConfig(); - if (otherAuthConfig != null) { - authConfig = otherAuthConfig; - } + ReposTag reposTag = NameParser.parseRepositoryTag(imageName); + HostnameReposName hostnameReposName = NameParser.resolveRepositoryName(reposTag.repos); - return authConfig; + return dockerCfg.resolveAuthConfig(hostnameReposName.hostname); } @Override public AuthConfigurations getAuthConfigurations() { - File dockerCfgFile = getDockerConfigFile(); - - if (dockerCfgFile != null) { - AuthConfigFile authConfigFile; - try { - authConfigFile = AuthConfigFile.loadConfig(dockerCfgFile); - } catch (IOException e) { - throw new DockerClientException("Failed to parse dockerCfgFile: " + - dockerCfgFile.getAbsolutePath(), e); - } - - return authConfigFile.getAuthConfigurations(); - } - - return new AuthConfigurations(); - } - - private File getDockerConfigFile() { - final Path configJson = Paths.get(getDockerConfig(), CONFIG_JSON); - final Path dockerCfg = Paths.get(getDockerConfig(), DOCKER_CFG); - - if (Files.exists(configJson)) { - return configJson.toFile(); - } else if (Files.exists(dockerCfg)) { - return dockerCfg.toFile(); - } else { - return null; - } + return getDockerConfig().getAuthConfigurations(); } @Override diff --git a/src/main/java/com/github/dockerjava/core/DockerClientImpl.java b/src/main/java/com/github/dockerjava/core/DockerClientImpl.java index 3abfdbe38..230fe07df 100644 --- a/src/main/java/com/github/dockerjava/core/DockerClientImpl.java +++ b/src/main/java/com/github/dockerjava/core/DockerClientImpl.java @@ -308,7 +308,8 @@ public ListContainersCmd listContainersCmd() { @Override public CreateContainerCmd createContainerCmd(String image) { - return new CreateContainerCmdImpl(getDockerCmdExecFactory().createCreateContainerCmdExec(), image); + return new CreateContainerCmdImpl(getDockerCmdExecFactory() + .createCreateContainerCmdExec(), dockerClientConfig.effectiveAuthConfig(image), image); } @Override diff --git a/src/main/java/com/github/dockerjava/core/DockerConfigFile.java b/src/main/java/com/github/dockerjava/core/DockerConfigFile.java new file mode 100644 index 000000000..80c45fe15 --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/DockerConfigFile.java @@ -0,0 +1,209 @@ +package com.github.dockerjava.core; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.dockerjava.api.model.AuthConfig; +import com.github.dockerjava.api.model.AuthConfigurations; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang.StringUtils; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@JsonIgnoreProperties(ignoreUnknown = true) +public class DockerConfigFile { + private static final String DOCKER_LEGACY_CFG = ".dockercfg"; + private static final String DOCKER_CFG = "config.json"; + + private static final ObjectMapper MAPPER = new ObjectMapper(); + private static final TypeReference> CONFIG_MAP_TYPE = new TypeReference>() { + }; + + @JsonProperty() + private final Map auths; + + public DockerConfigFile() { + this(new HashMap()); + } + + private DockerConfigFile(Map authConfigMap) { + auths = authConfigMap; + } + + public Map getAuths() { + return auths; + } + + void addAuthConfig(AuthConfig config) { + auths.put(config.getRegistryAddress(), config); + } + + public AuthConfig resolveAuthConfig(String hostname) { + if (StringUtils.isEmpty(hostname) || AuthConfig.DEFAULT_SERVER_ADDRESS.equals(hostname)) { + return auths.get(AuthConfig.DEFAULT_SERVER_ADDRESS); + } + + AuthConfig c = auths.get(hostname); + if (c != null) { + return c; + } + + // Maybe they have a legacy config file, we will iterate the keys converting + // them to the new format and testing + String normalizedHostname = convertToHostname(hostname); + for (Map.Entry entry : auths.entrySet()) { + String registry = entry.getKey(); + AuthConfig config = entry.getValue(); + if (convertToHostname(registry).equals(normalizedHostname)) { + return config; + } + } + + return null; + } + + public AuthConfigurations getAuthConfigurations() { + final AuthConfigurations authConfigurations = new AuthConfigurations(); + for (Map.Entry authConfigEntry : auths.entrySet()) { + authConfigurations.addConfig(authConfigEntry.getValue()); + } + + return authConfigurations; + } + + // CHECKSTYLE:OFF + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((auths == null) ? 0 : auths.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + DockerConfigFile other = (DockerConfigFile) obj; + if (auths == null) { + if (other.auths != null) + return false; + } else if (!auths.equals(other.auths)) + return false; + return true; + } + + // CHECKSTYLE:ON + + @Override + public String toString() { + return "DockerConfigFile [auths=" + auths + "]"; + } + + public static DockerConfigFile loadConfig(File dockerConfigPath) throws IOException { + //parse new docker config file format + DockerConfigFile dockerConfig = loadCurrentConfig(dockerConfigPath); + + //parse old auth config file format + if (dockerConfig == null) { + dockerConfig = loadLegacyConfig(dockerConfigPath); + } + + //otherwise create default config + if (dockerConfig == null) { + dockerConfig = new DockerConfigFile(); + } + + for (Map.Entry entry : dockerConfig.getAuths().entrySet()) { + AuthConfig authConfig = entry.getValue(); + decodeAuth(authConfig); + authConfig.withAuth(null); + authConfig.withRegistryAddress(entry.getKey()); + } + + return dockerConfig; + } + + private static DockerConfigFile loadCurrentConfig(File dockerConfigPath) throws IOException { + File dockerCfgFile = new File(dockerConfigPath, File.separator + DOCKER_CFG); + + if (!dockerCfgFile.exists() || !dockerCfgFile.isFile()) { + return null; + } + + try { + return MAPPER.readValue(dockerCfgFile, DockerConfigFile.class); + } catch (IOException e) { + throw new IOException("Failed to parse docker " + DOCKER_CFG, e); + } + } + + private static DockerConfigFile loadLegacyConfig(File dockerConfigPath) throws IOException { + File dockerLegacyCfgFile = new File(dockerConfigPath, File.separator + DOCKER_LEGACY_CFG); + + if (!dockerLegacyCfgFile.exists() || !dockerLegacyCfgFile.isFile()) { + return null; + } + + //parse legacy auth config file format + try { + return new DockerConfigFile(MAPPER.>readValue(dockerLegacyCfgFile, CONFIG_MAP_TYPE)); + } catch (IOException e) { + // pass + } + + List authFileContent = FileUtils.readLines(dockerLegacyCfgFile, StandardCharsets.UTF_8); + if (authFileContent.size() < 2) { + throw new IOException("The Auth Config file is empty"); + } + + AuthConfig config = new AuthConfig(); + String[] origAuth = authFileContent.get(0).split(" = "); + if (origAuth.length != 2) { + throw new IOException("Invalid Auth config file"); + } + + config.withAuth(origAuth[1]); + + String[] origEmail = authFileContent.get(1).split(" = "); + if (origEmail.length != 2) { + throw new IOException("Invalid Auth config file"); + } + config.withEmail(origEmail[1]); + + return new DockerConfigFile(new HashMap<>(Collections.singletonMap(config.getRegistryAddress(), config))); + } + + private static void decodeAuth(AuthConfig config) throws IOException { + String str = new String(Base64.decodeBase64(config.getAuth()), StandardCharsets.UTF_8); + String[] parts = str.split(":", 2); + if (parts.length != 2) { + throw new IOException("Invalid auth configuration file"); + } + config.withUsername(parts[0]); + config.withPassword(parts[1]); + } + + static String convertToHostname(String server) { + String stripped = server; + if (server.startsWith("http://")) { + stripped = server.substring(7); + } else if (server.startsWith("https://")) { + stripped = server.substring(8); + } + String[] numParts = stripped.split("/", 2); + return numParts[0]; + } +} diff --git a/src/main/java/com/github/dockerjava/core/command/CreateContainerCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/CreateContainerCmdImpl.java index f3cfefa05..4bccb1b79 100644 --- a/src/main/java/com/github/dockerjava/core/command/CreateContainerCmdImpl.java +++ b/src/main/java/com/github/dockerjava/core/command/CreateContainerCmdImpl.java @@ -8,6 +8,7 @@ import com.github.dockerjava.api.command.CreateContainerResponse; import com.github.dockerjava.api.exception.ConflictException; import com.github.dockerjava.api.exception.NotFoundException; +import com.github.dockerjava.api.model.AuthConfig; import com.github.dockerjava.api.model.Bind; import com.github.dockerjava.api.model.Capability; import com.github.dockerjava.api.model.ContainerNetwork; @@ -127,12 +128,29 @@ public class CreateContainerCmdImpl extends AbstrDockerCmd aliases = null; - public CreateContainerCmdImpl(CreateContainerCmd.Exec exec, String image) { + private AuthConfig authConfig; + + public CreateContainerCmdImpl(CreateContainerCmd.Exec exec, AuthConfig authConfig, String image) { super(exec); checkNotNull(image, "image was not specified"); + withOptionalAuthConfig(authConfig); withImage(image); } + public AuthConfig getAuthConfig() { + return authConfig; + } + + public CreateContainerCmd withAuthConfig(AuthConfig authConfig) { + checkNotNull(authConfig, "authConfig was not specified"); + return withOptionalAuthConfig(authConfig); + } + + private CreateContainerCmd withOptionalAuthConfig(AuthConfig authConfig) { + this.authConfig = authConfig; + return this; + } + /** * @throws NotFoundException * No such container diff --git a/src/main/java/com/github/dockerjava/jaxrs/CreateContainerCmdExec.java b/src/main/java/com/github/dockerjava/jaxrs/CreateContainerCmdExec.java index 651362b1c..211023987 100644 --- a/src/main/java/com/github/dockerjava/jaxrs/CreateContainerCmdExec.java +++ b/src/main/java/com/github/dockerjava/jaxrs/CreateContainerCmdExec.java @@ -1,16 +1,17 @@ package com.github.dockerjava.jaxrs; -import static javax.ws.rs.client.Entity.entity; +import com.github.dockerjava.api.command.CreateContainerCmd; +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.model.AuthConfig; +import com.github.dockerjava.core.DockerClientConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import javax.ws.rs.client.Invocation; import javax.ws.rs.client.WebTarget; import javax.ws.rs.core.MediaType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.github.dockerjava.api.command.CreateContainerCmd; -import com.github.dockerjava.api.command.CreateContainerResponse; -import com.github.dockerjava.core.DockerClientConfig; +import static javax.ws.rs.client.Entity.entity; public class CreateContainerCmdExec extends AbstrSyncDockerCmdExec implements CreateContainerCmd.Exec { @@ -21,6 +22,14 @@ public CreateContainerCmdExec(WebTarget baseResource, DockerClientConfig dockerC super(baseResource, dockerClientConfig); } + private Invocation.Builder resourceWithOptionalAuthConfig(CreateContainerCmd command, Invocation.Builder request) { + AuthConfig authConfig = command.getAuthConfig(); + if (authConfig != null) { + request = request.header("X-Registry-Auth", registryAuth(authConfig)); + } + return request; + } + @Override protected CreateContainerResponse execute(CreateContainerCmd command) { WebTarget webResource = getBaseResource().path("/containers/create"); @@ -30,7 +39,7 @@ protected CreateContainerResponse execute(CreateContainerCmd command) { } LOGGER.trace("POST: {} ", webResource); - return webResource.request().accept(MediaType.APPLICATION_JSON) + return resourceWithOptionalAuthConfig(command, webResource.request()).accept(MediaType.APPLICATION_JSON) .post(entity(command, MediaType.APPLICATION_JSON), CreateContainerResponse.class); } diff --git a/src/test/java/com/github/dockerjava/core/DefaultDockerClientConfigTest.java b/src/test/java/com/github/dockerjava/core/DefaultDockerClientConfigTest.java index 0001c96b2..037a82690 100644 --- a/src/test/java/com/github/dockerjava/core/DefaultDockerClientConfigTest.java +++ b/src/test/java/com/github/dockerjava/core/DefaultDockerClientConfigTest.java @@ -110,7 +110,7 @@ public void defaults() throws Exception { assertEquals(config.getRegistryUsername(), "someUserName"); assertEquals(config.getRegistryUrl(), AuthConfig.DEFAULT_SERVER_ADDRESS); assertEquals(config.getApiVersion(), RemoteApiVersion.unknown()); - assertEquals(config.getDockerConfig(), homeDir() + "/.docker"); + assertEquals(config.getDockerConfigPath(), homeDir() + "/.docker"); assertNull(config.getSSLConfig()); } diff --git a/src/test/java/com/github/dockerjava/core/AuthConfigFileTest.java b/src/test/java/com/github/dockerjava/core/DockerConfigFileTest.java similarity index 68% rename from src/test/java/com/github/dockerjava/core/AuthConfigFileTest.java rename to src/test/java/com/github/dockerjava/core/DockerConfigFileTest.java index 47bca37c8..cef91b664 100644 --- a/src/test/java/com/github/dockerjava/core/AuthConfigFileTest.java +++ b/src/test/java/com/github/dockerjava/core/DockerConfigFileTest.java @@ -13,7 +13,7 @@ import static org.junit.Assert.assertEquals; -public class AuthConfigFileTest { +public class DockerConfigFileTest { @Rule public ExpectedException expectedEx = ExpectedException.none(); @@ -82,11 +82,11 @@ public void validJson() throws IOException { .withPassword("bar1") .withRegistryAddress(AuthConfig.DEFAULT_SERVER_ADDRESS); - AuthConfigFile expected = new AuthConfigFile(); - expected.addConfig(authConfig1); - expected.addConfig(authConfig2); + DockerConfigFile expected = new DockerConfigFile(); + expected.addAuthConfig(authConfig1); + expected.addAuthConfig(authConfig2); - assertEquals(runTest("validJson.json"), expected); + assertEquals(expected, runTest("validJson")); } @@ -98,15 +98,15 @@ public void validJsonWithUnknown() throws IOException { .withPassword("bar") .withRegistryAddress("quay.io"); - AuthConfigFile expected = new AuthConfigFile(); - expected.addConfig(authConfig1); + DockerConfigFile expected = new DockerConfigFile(); + expected.addAuthConfig(authConfig1); runTest("validJsonWithUnknown.json"); } @Test public void validJsonWithOnlyUnknown() throws IOException { - AuthConfigFile expected = new AuthConfigFile(); - AuthConfigFile actual = runTest("validJsonWithOnlyUnknown.json"); + DockerConfigFile expected = new DockerConfigFile(); + DockerConfigFile actual = runTest("validJsonWithOnlyUnknown.json"); assertEquals(actual, expected); } @@ -118,20 +118,41 @@ public void validLegacy() throws IOException { .withPassword("bar") .withRegistryAddress(AuthConfig.DEFAULT_SERVER_ADDRESS); - AuthConfigFile expected = new AuthConfigFile(); - expected.addConfig(authConfig); + DockerConfigFile expected = new DockerConfigFile(); + expected.addAuthConfig(authConfig); assertEquals(runTest("validLegacy"), expected); } + @Test + public void validDockerConfig() throws IOException { + AuthConfig authConfig1 = new AuthConfig() + .withEmail("foo@example.com") + .withUsername("foo") + .withPassword("bar") + .withRegistryAddress("quay.io"); + + AuthConfig authConfig2 = new AuthConfig() + .withEmail("moo@example.com") + .withUsername("foo1") + .withPassword("bar1") + .withRegistryAddress(AuthConfig.DEFAULT_SERVER_ADDRESS); + + DockerConfigFile expected = new DockerConfigFile(); + expected.addAuthConfig(authConfig1); + expected.addAuthConfig(authConfig2); + + assertEquals(runTest("validDockerConfig"), expected); + } + @Test public void nonExistent() throws IOException { - AuthConfigFile expected = new AuthConfigFile(); + DockerConfigFile expected = new DockerConfigFile(); assertEquals(runTest("idontexist"), expected); } - private AuthConfigFile runTest(String testFileName) throws IOException { - return AuthConfigFile.loadConfig(new File(FILESROOT, testFileName)); + private DockerConfigFile runTest(String testFileName) throws IOException { + return DockerConfigFile.loadConfig(new File(FILESROOT, testFileName)); } } diff --git a/src/test/resources/testAuthConfigFile/emptyFile b/src/test/resources/testAuthConfigFile/emptyFile/.dockercfg similarity index 100% rename from src/test/resources/testAuthConfigFile/emptyFile rename to src/test/resources/testAuthConfigFile/emptyFile/.dockercfg diff --git a/src/test/resources/testAuthConfigFile/invalidJsonInvalidAuth b/src/test/resources/testAuthConfigFile/invalidJsonInvalidAuth/.dockercfg similarity index 100% rename from src/test/resources/testAuthConfigFile/invalidJsonInvalidAuth rename to src/test/resources/testAuthConfigFile/invalidJsonInvalidAuth/.dockercfg diff --git a/src/test/resources/testAuthConfigFile/invalidLegacyAuthLine b/src/test/resources/testAuthConfigFile/invalidLegacyAuthLine/.dockercfg similarity index 100% rename from src/test/resources/testAuthConfigFile/invalidLegacyAuthLine rename to src/test/resources/testAuthConfigFile/invalidLegacyAuthLine/.dockercfg diff --git a/src/test/resources/testAuthConfigFile/invalidLegacyEmailLine b/src/test/resources/testAuthConfigFile/invalidLegacyEmailLine/.dockercfg similarity index 100% rename from src/test/resources/testAuthConfigFile/invalidLegacyEmailLine rename to src/test/resources/testAuthConfigFile/invalidLegacyEmailLine/.dockercfg diff --git a/src/test/resources/testAuthConfigFile/invalidLegacyInvalidAuth b/src/test/resources/testAuthConfigFile/invalidLegacyInvalidAuth/.dockercfg similarity index 100% rename from src/test/resources/testAuthConfigFile/invalidLegacyInvalidAuth rename to src/test/resources/testAuthConfigFile/invalidLegacyInvalidAuth/.dockercfg diff --git a/src/test/resources/testAuthConfigFile/tooSmallFile b/src/test/resources/testAuthConfigFile/tooSmallFile/.dockercfg similarity index 100% rename from src/test/resources/testAuthConfigFile/tooSmallFile rename to src/test/resources/testAuthConfigFile/tooSmallFile/.dockercfg diff --git a/src/test/resources/testAuthConfigFile/validDockerConfig/config.json b/src/test/resources/testAuthConfigFile/validDockerConfig/config.json new file mode 100644 index 000000000..ee32e91ed --- /dev/null +++ b/src/test/resources/testAuthConfigFile/validDockerConfig/config.json @@ -0,0 +1,12 @@ +{ + "auths": { + "quay.io" : { + "auth" : "Zm9vOmJhcg==", + "email" :"foo@example.com" + }, + "https://index.docker.io/v1/" : { + "auth" : "Zm9vMTpiYXIx", + "email" : "moo@example.com" + } + } +} \ No newline at end of file diff --git a/src/test/resources/testAuthConfigFile/validJson/.dockercfg b/src/test/resources/testAuthConfigFile/validJson/.dockercfg new file mode 100644 index 000000000..2b47e3822 --- /dev/null +++ b/src/test/resources/testAuthConfigFile/validJson/.dockercfg @@ -0,0 +1 @@ +{"quay.io" : { "auth" : "Zm9vOmJhcg==", "email" :"foo@example.com"}, "https://index.docker.io/v1/" : {"auth" : "Zm9vMTpiYXIx", "email" : "moo@example.com"}} \ No newline at end of file diff --git a/src/test/resources/testAuthConfigFile/validLegacy b/src/test/resources/testAuthConfigFile/validLegacy/.dockercfg similarity index 100% rename from src/test/resources/testAuthConfigFile/validLegacy rename to src/test/resources/testAuthConfigFile/validLegacy/.dockercfg From cc0b2e0954991db69dfd0654a883d855726315aa Mon Sep 17 00:00:00 2001 From: fb Date: Sun, 5 Nov 2017 00:09:45 +0100 Subject: [PATCH 2/6] Add more config and auth tests for push / pull and container creation in swarm --- .../github/dockerjava/api/DockerClient.java | 2 +- .../api/command/CreateContainerCmd.java | 6 + .../dockerjava/core/DockerClientImpl.java | 4 +- .../dockerjava/core/DockerConfigFile.java | 4 + .../dockerjava/jaxrs/AbstrDockerCmdExec.java | 33 +++-- .../jaxrs/CreateContainerCmdExec.java | 12 +- .../dockerjava/jaxrs/PullImageCmdExec.java | 29 ++--- .../dockerjava/jaxrs/PushImageCmdExec.java | 20 ++- .../netty/exec/AbstrDockerCmdExec.java | 29 +++-- .../netty/exec/CreateContainerCmdExec.java | 6 +- .../netty/exec/PullImageCmdExec.java | 17 +-- .../netty/exec/PushImageCmdExec.java | 3 +- .../dockerjava/cmd/BuildImageCmdIT.java | 71 +++-------- .../dockerjava/cmd/CreateContainerCmdIT.java | 43 +++++++ .../github/dockerjava/cmd/PullImageCmdIT.java | 59 +++++++++ .../github/dockerjava/cmd/PushImageCmdIT.java | 65 ++++++++-- .../dockerjava/core/DockerConfigFileTest.java | 30 +++-- .../dockerjava/utils/RegistryUtils.java | 116 ++++++++++++++++++ .../testAuthConfigFile/validJson.json | 10 -- .../config.json} | 0 .../config.json} | 0 .../{validJson => validLegacyJson}/.dockercfg | 0 22 files changed, 390 insertions(+), 169 deletions(-) create mode 100644 src/test/java/com/github/dockerjava/utils/RegistryUtils.java delete mode 100644 src/test/resources/testAuthConfigFile/validJson.json rename src/test/resources/testAuthConfigFile/{validJsonWithOnlyUnknown.json => validJsonWithOnlyUnknown/config.json} (100%) rename src/test/resources/testAuthConfigFile/{validJsonWithUnknown.json => validJsonWithUnknown/config.json} (100%) rename src/test/resources/testAuthConfigFile/{validJson => validLegacyJson}/.dockercfg (100%) diff --git a/src/main/java/com/github/dockerjava/api/DockerClient.java b/src/main/java/com/github/dockerjava/api/DockerClient.java index 8fe841cce..2777682c8 100644 --- a/src/main/java/com/github/dockerjava/api/DockerClient.java +++ b/src/main/java/com/github/dockerjava/api/DockerClient.java @@ -236,7 +236,7 @@ public interface DockerClient extends Closeable { TopContainerCmd topContainerCmd(String containerId); - TagImageCmd tagImageCmd(String imageId, String repository, String tag); + TagImageCmd tagImageCmd(String imageId, String imageNameWithRepository, String tag); PauseContainerCmd pauseContainerCmd(String containerId); diff --git a/src/main/java/com/github/dockerjava/api/command/CreateContainerCmd.java b/src/main/java/com/github/dockerjava/api/command/CreateContainerCmd.java index 0b1c62229..62d5cfee3 100644 --- a/src/main/java/com/github/dockerjava/api/command/CreateContainerCmd.java +++ b/src/main/java/com/github/dockerjava/api/command/CreateContainerCmd.java @@ -200,6 +200,12 @@ public interface CreateContainerCmd extends SyncDockerCmd implements PullImageCmd.Exec { @@ -28,14 +25,6 @@ public PullImageCmdExec(WebTarget baseResource, DockerClientConfig dockerClientC super(baseResource, dockerClientConfig); } - private Invocation.Builder resourceWithOptionalAuthConfig(PullImageCmd command, Invocation.Builder request) { - AuthConfig authConfig = command.getAuthConfig(); - if (authConfig != null) { - request = request.header("X-Registry-Auth", registryAuth(authConfig)); - } - return request; - } - @Override protected AbstractCallbackNotifier callbackNotifier(PullImageCmd command, ResultCallback resultCallback) { @@ -44,7 +33,7 @@ protected AbstractCallbackNotifier callbackNotifier(PullImageC .queryParam("fromImage", command.getRepository()).queryParam("registry", command.getRegistry()); LOGGER.trace("POST: {}", webResource); - Builder builder = resourceWithOptionalAuthConfig(command, webResource.request()).accept( + Builder builder = resourceWithOptionalAuthConfig(command.getAuthConfig(), webResource.request()).accept( MediaType.APPLICATION_OCTET_STREAM_TYPE); return new POSTCallbackNotifier(new JsonStreamProcessor( diff --git a/src/main/java/com/github/dockerjava/jaxrs/PushImageCmdExec.java b/src/main/java/com/github/dockerjava/jaxrs/PushImageCmdExec.java index 2943ef7e8..906487c6c 100644 --- a/src/main/java/com/github/dockerjava/jaxrs/PushImageCmdExec.java +++ b/src/main/java/com/github/dockerjava/jaxrs/PushImageCmdExec.java @@ -1,14 +1,5 @@ package com.github.dockerjava.jaxrs; -import static javax.ws.rs.client.Entity.entity; - -import javax.ws.rs.client.Invocation.Builder; -import javax.ws.rs.client.WebTarget; -import javax.ws.rs.core.MediaType; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import com.github.dockerjava.api.async.ResultCallback; import com.github.dockerjava.api.command.PushImageCmd; import com.github.dockerjava.api.model.AuthConfig; @@ -17,6 +8,14 @@ import com.github.dockerjava.core.async.JsonStreamProcessor; import com.github.dockerjava.jaxrs.async.AbstractCallbackNotifier; import com.github.dockerjava.jaxrs.async.POSTCallbackNotifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.ws.rs.client.Invocation.Builder; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.MediaType; + +import static javax.ws.rs.client.Entity.entity; public class PushImageCmdExec extends AbstrAsyncDockerCmdExec implements PushImageCmd.Exec { @@ -40,10 +39,9 @@ protected AbstractCallbackNotifier callbackNotifier(PushImageC WebTarget webResource = getBaseResource().path("/images/" + name(command) + "/push").queryParam("tag", command.getTag()); - final String registryAuth = registryAuth(command.getAuthConfig()); LOGGER.trace("POST: {}", webResource); - Builder builder = webResource.request().header("X-Registry-Auth", registryAuth) + Builder builder = resourceWithAuthConfig(command.getAuthConfig(), webResource.request()) .accept(MediaType.APPLICATION_JSON); return new POSTCallbackNotifier(new JsonStreamProcessor( diff --git a/src/main/java/com/github/dockerjava/netty/exec/AbstrDockerCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/AbstrDockerCmdExec.java index 85763b403..63e3bff12 100644 --- a/src/main/java/com/github/dockerjava/netty/exec/AbstrDockerCmdExec.java +++ b/src/main/java/com/github/dockerjava/netty/exec/AbstrDockerCmdExec.java @@ -1,20 +1,20 @@ package com.github.dockerjava.netty.exec; -import static com.github.dockerjava.core.RemoteApiVersion.UNKNOWN_VERSION; -import static com.github.dockerjava.core.RemoteApiVersion.VERSION_1_19; -import static com.google.common.base.Preconditions.checkNotNull; - -import java.io.IOException; - -import com.fasterxml.jackson.databind.node.ObjectNode; -import org.apache.commons.codec.binary.Base64; - import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; import com.github.dockerjava.api.model.AuthConfig; import com.github.dockerjava.api.model.AuthConfigurations; import com.github.dockerjava.core.DockerClientConfig; import com.github.dockerjava.core.RemoteApiVersion; +import com.github.dockerjava.netty.InvocationBuilder; import com.github.dockerjava.netty.WebTarget; +import org.apache.commons.codec.binary.Base64; + +import java.io.IOException; + +import static com.github.dockerjava.core.RemoteApiVersion.UNKNOWN_VERSION; +import static com.github.dockerjava.core.RemoteApiVersion.VERSION_1_19; +import static com.google.common.base.Preconditions.checkNotNull; public abstract class AbstrDockerCmdExec { @@ -68,6 +68,17 @@ protected String registryConfigs(AuthConfigurations authConfigs) { } } + protected InvocationBuilder resourceWithAuthConfig(AuthConfig authConfig, InvocationBuilder request) { + return request.header("X-Registry-Auth", registryAuth(authConfig)); + } + + protected InvocationBuilder resourceWithOptionalAuthConfig(AuthConfig authConfig, InvocationBuilder request) { + if (authConfig != null) { + request = resourceWithAuthConfig(authConfig, request); + } + return request; + } + protected boolean bool(Boolean bool) { return bool != null && bool; } diff --git a/src/main/java/com/github/dockerjava/netty/exec/CreateContainerCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/CreateContainerCmdExec.java index f7e4e39d9..7764d4729 100644 --- a/src/main/java/com/github/dockerjava/netty/exec/CreateContainerCmdExec.java +++ b/src/main/java/com/github/dockerjava/netty/exec/CreateContainerCmdExec.java @@ -28,8 +28,8 @@ protected CreateContainerResponse execute(CreateContainerCmd command) { } LOGGER.trace("POST: {} ", webResource); - return webResource.request().accept(MediaType.APPLICATION_JSON) - .post(command, new TypeReference() { - }); + return resourceWithOptionalAuthConfig(command.getAuthConfig(), webResource.request()) + .accept(MediaType.APPLICATION_JSON) + .post(command, new TypeReference() { }); } } diff --git a/src/main/java/com/github/dockerjava/netty/exec/PullImageCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/PullImageCmdExec.java index 7f15b4e62..0f75f1918 100644 --- a/src/main/java/com/github/dockerjava/netty/exec/PullImageCmdExec.java +++ b/src/main/java/com/github/dockerjava/netty/exec/PullImageCmdExec.java @@ -1,17 +1,14 @@ package com.github.dockerjava.netty.exec; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import com.fasterxml.jackson.core.type.TypeReference; import com.github.dockerjava.api.async.ResultCallback; import com.github.dockerjava.api.command.PullImageCmd; -import com.github.dockerjava.api.model.AuthConfig; import com.github.dockerjava.api.model.PullResponseItem; import com.github.dockerjava.core.DockerClientConfig; -import com.github.dockerjava.netty.InvocationBuilder; import com.github.dockerjava.netty.MediaType; import com.github.dockerjava.netty.WebTarget; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class PullImageCmdExec extends AbstrAsyncDockerCmdExec implements PullImageCmd.Exec { @@ -22,14 +19,6 @@ public PullImageCmdExec(WebTarget baseResource, DockerClientConfig dockerClientC super(baseResource, dockerClientConfig); } - private InvocationBuilder resourceWithOptionalAuthConfig(PullImageCmd command, InvocationBuilder request) { - AuthConfig authConfig = command.getAuthConfig(); - if (authConfig != null) { - request = request.header("X-Registry-Auth", registryAuth(authConfig)); - } - return request; - } - @Override protected Void execute0(PullImageCmd command, ResultCallback resultCallback) { @@ -37,7 +26,7 @@ protected Void execute0(PullImageCmd command, ResultCallback r .queryParam("fromImage", command.getRepository()).queryParam("registry", command.getRegistry()); LOGGER.trace("POST: {}", webResource); - resourceWithOptionalAuthConfig(command, webResource.request()).accept(MediaType.APPLICATION_OCTET_STREAM).post( + resourceWithOptionalAuthConfig(command.getAuthConfig(), webResource.request()).accept(MediaType.APPLICATION_OCTET_STREAM).post( null, new TypeReference() { }, resultCallback); diff --git a/src/main/java/com/github/dockerjava/netty/exec/PushImageCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/PushImageCmdExec.java index d6dcdbd00..964928e8f 100644 --- a/src/main/java/com/github/dockerjava/netty/exec/PushImageCmdExec.java +++ b/src/main/java/com/github/dockerjava/netty/exec/PushImageCmdExec.java @@ -34,10 +34,9 @@ protected Void execute0(PushImageCmd command, ResultCallback r WebTarget webResource = getBaseResource().path("/images/" + name(command) + "/push").queryParam("tag", command.getTag()); - final String registryAuth = registryAuth(command.getAuthConfig()); LOGGER.trace("POST: {}", webResource); - InvocationBuilder builder = webResource.request().header("X-Registry-Auth", registryAuth) + InvocationBuilder builder = resourceWithAuthConfig(command.getAuthConfig(), webResource.request()) .accept(MediaType.APPLICATION_JSON); builder.post(null, new TypeReference() { diff --git a/src/test/java/com/github/dockerjava/cmd/BuildImageCmdIT.java b/src/test/java/com/github/dockerjava/cmd/BuildImageCmdIT.java index 09e008c30..740395ae6 100644 --- a/src/test/java/com/github/dockerjava/cmd/BuildImageCmdIT.java +++ b/src/test/java/com/github/dockerjava/cmd/BuildImageCmdIT.java @@ -6,13 +6,11 @@ import com.github.dockerjava.api.exception.DockerClientException; import com.github.dockerjava.api.model.AuthConfig; import com.github.dockerjava.api.model.AuthConfigurations; -import com.github.dockerjava.api.model.ExposedPort; -import com.github.dockerjava.api.model.PortBinding; -import com.github.dockerjava.api.model.Ports; import com.github.dockerjava.core.command.BuildImageResultCallback; import com.github.dockerjava.core.command.PushImageResultCallback; import com.github.dockerjava.core.command.WaitContainerResultCallback; import com.github.dockerjava.core.util.CompressArchiveUtil; +import com.github.dockerjava.utils.RegistryUtils; import net.jcip.annotations.NotThreadSafe; import org.apache.commons.io.FileUtils; import org.apache.commons.io.filefilter.TrueFileFilter; @@ -30,8 +28,8 @@ import java.util.Collections; import java.util.HashSet; import java.util.UUID; +import java.util.concurrent.TimeUnit; -import static com.github.dockerjava.cmd.CmdIT.FactoryType.JERSEY; import static com.github.dockerjava.core.RemoteApiVersion.VERSION_1_21; import static com.github.dockerjava.core.RemoteApiVersion.VERSION_1_23; import static com.github.dockerjava.core.RemoteApiVersion.VERSION_1_27; @@ -197,61 +195,29 @@ public void env() throws Exception { @Test public void fromPrivateRegistry() throws Exception { - int port = getFactoryType() == JERSEY ? 5001 : 5002; -// int port = 5050; + AuthConfig authConfig = RegistryUtils.runPrivateRegistry(dockerRule.getClient()); + String imgName = authConfig.getRegistryAddress() + "/testuser/busybox"; File dockerfile = folder.newFile("Dockerfile"); - writeStringToFile(dockerfile, String.format("FROM localhost:%d/testuser/busybox:latest", port)); + writeStringToFile(dockerfile, "FROM " + imgName); - - String containerName = "registryy" + dockerRule.getKind(); - String imageName = "testregistryy" + dockerRule.getKind(); - dockerRule.ensureContainerRemoved(containerName); - dockerRule.ensureImageRemoved(imageName); - - File baseDir = new File(Thread.currentThread().getContextClassLoader().getResource("privateRegistry").getFile()); - - String imageId = dockerRule.buildImage(baseDir); - - InspectImageResponse inspectImageResponse = dockerRule.getClient().inspectImageCmd(imageId).exec(); - assertThat(inspectImageResponse, not(nullValue())); - LOG.info("Image Inspect: {}", inspectImageResponse.toString()); - - dockerRule.getClient().tagImageCmd(imageId, imageName, "2") - .withForce().exec(); - - // see https://github.com/docker/distribution/blob/master/docs/deploying.md#native-basic-auth - CreateContainerResponse testregistry = dockerRule.getClient() - .createContainerCmd(imageName + ":2") - .withName(containerName) - .withPortBindings(new PortBinding(Ports.Binding.bindPort(port), ExposedPort.tcp(5000))) - .withEnv("REGISTRY_AUTH=htpasswd", "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm", - "REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd", "REGISTRY_LOG_LEVEL=debug", - "REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt", "REGISTRY_HTTP_TLS_KEY=/certs/domain.key") - .exec(); - - dockerRule.getClient().startContainerCmd(testregistry.getId()).exec(); - - // wait for registry to boot - Thread.sleep(3000); - - // credentials as configured in /auth/htpasswd - AuthConfig authConfig = new AuthConfig() - .withUsername("testuser") - .withPassword("testpassword") - .withEmail("foo@bar.de") - .withRegistryAddress("localhost:" + port); + File baseDir; + InspectImageResponse inspectImageResponse; dockerRule.getClient().authCmd().withAuthConfig(authConfig).exec(); - dockerRule.getClient().tagImageCmd("busybox:latest", String.format("localhost:%d/testuser/busybox", port), "latest").withForce().exec(); + dockerRule.getClient().tagImageCmd("busybox:latest", imgName, "latest") + .withForce() + .exec(); - dockerRule.getClient().pushImageCmd(String.format("localhost:%d/testuser/busybox", port)) + dockerRule.getClient().pushImageCmd(imgName) .withTag("latest") .withAuthConfig(authConfig) .exec(new PushImageResultCallback()) - .awaitSuccess(); + .awaitCompletion(30, TimeUnit.SECONDS); - dockerRule.getClient().removeImageCmd(String.format("localhost:%d/testuser/busybox", port)).withForce(true).exec(); + dockerRule.getClient().removeImageCmd(imgName) + .withForce(true) + .exec(); // baseDir = fileFromBuildTestResource("FROM/privateRegistry"); baseDir = folder.getRoot(); @@ -259,8 +225,8 @@ public void fromPrivateRegistry() throws Exception { AuthConfigurations authConfigurations = new AuthConfigurations(); authConfigurations.addConfig(authConfig); - imageId = dockerRule.getClient().buildImageCmd(baseDir). - withNoCache(true) + String imageId = dockerRule.getClient().buildImageCmd(baseDir) + .withNoCache(true) .withBuildAuthConfigs(authConfigurations) .exec(new BuildImageResultCallback()) .awaitImageId(); @@ -268,9 +234,6 @@ public void fromPrivateRegistry() throws Exception { inspectImageResponse = dockerRule.getClient().inspectImageCmd(imageId).exec(); assertThat(inspectImageResponse, not(nullValue())); LOG.info("Image Inspect: {}", inspectImageResponse.toString()); - - dockerRule.ensureContainerRemoved(containerName); -// dockerRule.ensureImageRemoved(imageId); } @Test diff --git a/src/test/java/com/github/dockerjava/cmd/CreateContainerCmdIT.java b/src/test/java/com/github/dockerjava/cmd/CreateContainerCmdIT.java index c6905ef5e..a38cd7885 100644 --- a/src/test/java/com/github/dockerjava/cmd/CreateContainerCmdIT.java +++ b/src/test/java/com/github/dockerjava/cmd/CreateContainerCmdIT.java @@ -6,6 +6,9 @@ import com.github.dockerjava.api.command.InspectContainerResponse; import com.github.dockerjava.api.exception.ConflictException; import com.github.dockerjava.api.exception.DockerException; +import com.github.dockerjava.api.exception.InternalServerErrorException; +import com.github.dockerjava.api.exception.NotFoundException; +import com.github.dockerjava.api.model.AuthConfig; import com.github.dockerjava.api.model.Bind; import com.github.dockerjava.api.model.ContainerNetwork; import com.github.dockerjava.api.model.Device; @@ -22,10 +25,14 @@ import com.github.dockerjava.api.model.Volume; import com.github.dockerjava.api.model.VolumesFrom; import com.github.dockerjava.core.command.LogContainerResultCallback; +import com.github.dockerjava.junit.DockerAssume; +import com.github.dockerjava.utils.RegistryUtils; +import com.github.dockerjava.utils.TestUtils; import net.jcip.annotations.NotThreadSafe; import org.apache.commons.io.FileUtils; import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -55,6 +62,7 @@ import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasItemInArray; +import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.isEmptyString; import static org.hamcrest.Matchers.not; @@ -70,6 +78,9 @@ public class CreateContainerCmdIT extends CmdIT { @Rule public TemporaryFolder tempDir = new TemporaryFolder(new File("target/")); + @Rule + public ExpectedException exception = ExpectedException.none(); + @Test(expected = ConflictException.class) public void createContainerWithExistingName() throws DockerException { @@ -823,4 +834,36 @@ public void createContainerWithNetworkID() { } assertThat(containerNetwork, notNullValue()); } + + @Test + public void createContainerFromPrivateRegistryWithValidAuth() throws Exception { + DockerAssume.assumeSwarm(dockerRule.getClient()); + + AuthConfig authConfig = RegistryUtils.runPrivateRegistry(dockerRule.getClient()); + + String imgName = RegistryUtils.createPrivateImage(dockerRule, "create-container-with-valid-auth"); + + CreateContainerResponse container = dockerRule.getClient().createContainerCmd(imgName) + .withAuthConfig(authConfig) + .exec(); + + assertThat(container.getId(), is(notNullValue())); + } + + @Test + public void createContainerFromPrivateRegistryWithNoAuth() throws Exception { + AuthConfig authConfig = RegistryUtils.runPrivateRegistry(dockerRule.getClient()); + + String imgName = RegistryUtils.createPrivateImage(dockerRule, "create-container-with-no-auth"); + + if (TestUtils.isSwarm(dockerRule.getClient())) { + exception.expect(instanceOf(InternalServerErrorException.class)); + } else { + exception.expect(instanceOf(NotFoundException.class)); + } + + dockerRule.getClient().createContainerCmd(imgName) + .exec(); + } + } diff --git a/src/test/java/com/github/dockerjava/cmd/PullImageCmdIT.java b/src/test/java/com/github/dockerjava/cmd/PullImageCmdIT.java index 05dd431e8..6b10ddd38 100644 --- a/src/test/java/com/github/dockerjava/cmd/PullImageCmdIT.java +++ b/src/test/java/com/github/dockerjava/cmd/PullImageCmdIT.java @@ -4,12 +4,15 @@ import com.github.dockerjava.api.command.InspectImageResponse; import com.github.dockerjava.api.command.PullImageCmd; import com.github.dockerjava.api.exception.DockerClientException; +import com.github.dockerjava.api.exception.InternalServerErrorException; import com.github.dockerjava.api.exception.NotFoundException; +import com.github.dockerjava.api.model.AuthConfig; import com.github.dockerjava.api.model.Info; import com.github.dockerjava.api.model.PullResponseItem; import com.github.dockerjava.core.RemoteApiVersion; import com.github.dockerjava.core.command.PullImageCmdImpl; import com.github.dockerjava.core.command.PullImageResultCallback; +import com.github.dockerjava.utils.RegistryUtils; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -109,4 +112,60 @@ public void testPullNonExistingImage() throws Exception { .exec(new PullImageResultCallback()) .awaitCompletion(30, TimeUnit.SECONDS); } + + @Test + public void testPullImageWithValidAuth() throws Exception { + AuthConfig authConfig = RegistryUtils.runPrivateRegistry(dockerRule.getClient()); + + String imgName = RegistryUtils.createPrivateImage(dockerRule, "pull-image-with-valid-auth"); + + // stream needs to be fully read in order to close the underlying connection + dockerRule.getClient().pullImageCmd(imgName) + .withAuthConfig(authConfig) + .exec(new PullImageResultCallback()) + .awaitCompletion(30, TimeUnit.SECONDS); + } + + @Test + public void testPullImageWithNoAuth() throws Exception { + RegistryUtils.runPrivateRegistry(dockerRule.getClient()); + + String imgName = RegistryUtils.createPrivateImage(dockerRule, "pull-image-with-no-auth"); + + if (isNotSwarm(dockerRule.getClient())) { + exception.expect(InternalServerErrorException.class); + } else { + exception.expect(DockerClientException.class); + } + + // stream needs to be fully read in order to close the underlying connection + dockerRule.getClient().pullImageCmd(imgName) + .exec(new PullImageResultCallback()) + .awaitCompletion(30, TimeUnit.SECONDS); + } + + @Test + public void testPullImageWithInvalidAuth() throws Exception { + AuthConfig validAuthConfig = RegistryUtils.runPrivateRegistry(dockerRule.getClient()); + + AuthConfig authConfig = new AuthConfig() + .withUsername("testuser") + .withPassword("testwrongpassword") + .withEmail("foo@bar.de") + .withRegistryAddress(validAuthConfig.getRegistryAddress()); + + String imgName = RegistryUtils.createPrivateImage(dockerRule, "pull-image-with-invalid-auth"); + + if (isNotSwarm(dockerRule.getClient())) { + exception.expect(InternalServerErrorException.class); + } else { + exception.expect(DockerClientException.class); + } + + // stream needs to be fully read in order to close the underlying connection + dockerRule.getClient().pullImageCmd(imgName) + .withAuthConfig(authConfig) + .exec(new PullImageResultCallback()) + .awaitCompletion(30, TimeUnit.SECONDS); + } } diff --git a/src/test/java/com/github/dockerjava/cmd/PushImageCmdIT.java b/src/test/java/com/github/dockerjava/cmd/PushImageCmdIT.java index 7bad7b403..82f2dda37 100644 --- a/src/test/java/com/github/dockerjava/cmd/PushImageCmdIT.java +++ b/src/test/java/com/github/dockerjava/cmd/PushImageCmdIT.java @@ -3,14 +3,14 @@ import com.github.dockerjava.api.command.CreateContainerResponse; import com.github.dockerjava.api.exception.DockerClientException; import com.github.dockerjava.api.exception.NotFoundException; +import com.github.dockerjava.api.model.AuthConfig; import com.github.dockerjava.core.RemoteApiVersion; import com.github.dockerjava.core.command.PullImageResultCallback; import com.github.dockerjava.core.command.PushImageResultCallback; -import com.github.dockerjava.junit.category.AuthIntegration; +import com.github.dockerjava.utils.RegistryUtils; import org.junit.Before; import org.junit.Rule; import org.junit.Test; -import org.junit.experimental.categories.Category; import org.junit.rules.ExpectedException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -31,33 +31,40 @@ public class PushImageCmdIT extends CmdIT { @Rule public ExpectedException exception = ExpectedException.none(); + private AuthConfig authConfig; @Before public void beforeTest() throws Exception { username = dockerRule.getClient().authConfig().getUsername(); + authConfig = RegistryUtils.runPrivateRegistry(dockerRule.getClient()); } - @Category(AuthIntegration.class) @Test public void pushLatest() throws Exception { - CreateContainerResponse container = dockerRule.getClient().createContainerCmd("busybox").withCmd("true").exec(); LOG.info("Created container {}", container.toString()); - assertThat(container.getId(), not(isEmptyString())); LOG.info("Committing container: {}", container.toString()); - String imageId = dockerRule.getClient().commitCmd(container.getId()).withRepository(username + "/busybox").exec(); + String imgName = authConfig.getRegistryAddress() + "/" + dockerRule.getKind() + "-push-latest"; + String imageId = dockerRule.getClient().commitCmd(container.getId()) + .withRepository(imgName) + .exec(); // we have to block until image is pushed - dockerRule.getClient().pushImageCmd(username + "/busybox").exec(new PushImageResultCallback()) + dockerRule.getClient().pushImageCmd(imgName) + .withAuthConfig(authConfig) + .exec(new PushImageResultCallback()) .awaitCompletion(30, TimeUnit.SECONDS); LOG.info("Removing image: {}", imageId); dockerRule.getClient().removeImageCmd(imageId).exec(); - dockerRule.getClient().pullImageCmd(username + "/busybox").exec(new PullImageResultCallback()) + dockerRule.getClient().pullImageCmd(imgName) + .withTag("latest") + .withAuthConfig(authConfig) + .exec(new PullImageResultCallback()) .awaitCompletion(30, TimeUnit.SECONDS); } @@ -76,4 +83,46 @@ public void pushNonExistentImage() throws Exception { .awaitCompletion(30, TimeUnit.SECONDS); // exclude infinite await sleep } + + @Test + public void testPushImageWithValidAuth() throws Exception { + String imgName = RegistryUtils.createTestImage(dockerRule, "push-image-with-valid-auth"); + + // stream needs to be fully read in order to close the underlying connection + dockerRule.getClient().pushImageCmd(imgName) + .withAuthConfig(authConfig) + .exec(new PushImageResultCallback()) + .awaitCompletion(30, TimeUnit.SECONDS); + } + + @Test + public void testPushImageWithNoAuth() throws Exception { + String imgName = RegistryUtils.createTestImage(dockerRule, "push-image-with-no-auth"); + + exception.expect(DockerClientException.class); + + // stream needs to be fully read in order to close the underlying connection + dockerRule.getClient().pushImageCmd(imgName) + .exec(new PushImageResultCallback()) + .awaitCompletion(30, TimeUnit.SECONDS); + } + + @Test + public void testPushImageWithInvalidAuth() throws Exception { + AuthConfig invalidAuthConfig = new AuthConfig() + .withUsername("testuser") + .withPassword("testwrongpassword") + .withEmail("foo@bar.de") + .withRegistryAddress(authConfig.getRegistryAddress()); + + String imgName = RegistryUtils.createTestImage(dockerRule, "push-image-with-invalid-auth"); + + exception.expect(DockerClientException.class); + + // stream needs to be fully read in order to close the underlying connection + dockerRule.getClient().pushImageCmd(imgName) + .withAuthConfig(invalidAuthConfig) + .exec(new PushImageResultCallback()) + .awaitCompletion(30, TimeUnit.SECONDS); + } } diff --git a/src/test/java/com/github/dockerjava/core/DockerConfigFileTest.java b/src/test/java/com/github/dockerjava/core/DockerConfigFileTest.java index cef91b664..7f121c69a 100644 --- a/src/test/java/com/github/dockerjava/core/DockerConfigFileTest.java +++ b/src/test/java/com/github/dockerjava/core/DockerConfigFileTest.java @@ -11,14 +11,14 @@ import java.io.File; import java.io.IOException; -import static org.junit.Assert.assertEquals; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; public class DockerConfigFileTest { @Rule public ExpectedException expectedEx = ExpectedException.none(); - private final File FILESROOT = new File(Thread.currentThread().getContextClassLoader() - .getResource("testAuthConfigFile").getFile()); + private final File FILESROOT = new File(DockerConfigFileTest.class.getResource("/testAuthConfigFile").getFile()); @Test public void emptyFile() throws IOException { @@ -69,7 +69,7 @@ public void invalidLegacyEmailLine() throws IOException { } @Test - public void validJson() throws IOException { + public void validLegacyJson() throws IOException { AuthConfig authConfig1 = new AuthConfig() .withEmail("foo@example.com") .withUsername("foo") @@ -86,28 +86,32 @@ public void validJson() throws IOException { expected.addAuthConfig(authConfig1); expected.addAuthConfig(authConfig2); - assertEquals(expected, runTest("validJson")); - + assertThat(runTest("validLegacyJson"), is(expected)); } @Test public void validJsonWithUnknown() throws IOException { AuthConfig authConfig1 = new AuthConfig() + .withRegistryAddress("192.168.99.100:32768"); + + AuthConfig authConfig2 = new AuthConfig() .withEmail("foo@example.com") .withUsername("foo") .withPassword("bar") - .withRegistryAddress("quay.io"); + .withRegistryAddress("https://index.docker.io/v1/"); DockerConfigFile expected = new DockerConfigFile(); expected.addAuthConfig(authConfig1); - runTest("validJsonWithUnknown.json"); + expected.addAuthConfig(authConfig2); + DockerConfigFile actual = runTest("validJsonWithUnknown"); + assertThat(actual, is(expected)); } @Test public void validJsonWithOnlyUnknown() throws IOException { DockerConfigFile expected = new DockerConfigFile(); - DockerConfigFile actual = runTest("validJsonWithOnlyUnknown.json"); - assertEquals(actual, expected); + DockerConfigFile actual = runTest("validJsonWithOnlyUnknown"); + assertThat(actual, is(expected)); } @Test @@ -121,7 +125,7 @@ public void validLegacy() throws IOException { DockerConfigFile expected = new DockerConfigFile(); expected.addAuthConfig(authConfig); - assertEquals(runTest("validLegacy"), expected); + assertThat(runTest("validLegacy"), is(expected)); } @Test @@ -142,13 +146,13 @@ public void validDockerConfig() throws IOException { expected.addAuthConfig(authConfig1); expected.addAuthConfig(authConfig2); - assertEquals(runTest("validDockerConfig"), expected); + assertThat(runTest("validDockerConfig"), is(expected)); } @Test public void nonExistent() throws IOException { DockerConfigFile expected = new DockerConfigFile(); - assertEquals(runTest("idontexist"), expected); + assertThat(runTest("idontexist"), is(expected)); } private DockerConfigFile runTest(String testFileName) throws IOException { diff --git a/src/test/java/com/github/dockerjava/utils/RegistryUtils.java b/src/test/java/com/github/dockerjava/utils/RegistryUtils.java new file mode 100644 index 000000000..6e178df56 --- /dev/null +++ b/src/test/java/com/github/dockerjava/utils/RegistryUtils.java @@ -0,0 +1,116 @@ +package com.github.dockerjava.utils; + +import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.command.CreateContainerResponse; +import com.github.dockerjava.api.command.InspectImageResponse; +import com.github.dockerjava.api.exception.NotFoundException; +import com.github.dockerjava.api.model.AuthConfig; +import com.github.dockerjava.api.model.ExposedPort; +import com.github.dockerjava.api.model.PortBinding; +import com.github.dockerjava.api.model.Ports; +import com.github.dockerjava.core.command.BuildImageResultCallback; +import com.github.dockerjava.core.command.PushImageResultCallback; +import com.github.dockerjava.junit.DockerRule; + +import java.io.File; +import java.util.concurrent.TimeUnit; + +import static com.github.dockerjava.junit.DockerRule.DEFAULT_IMAGE; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.nullValue; + +public class RegistryUtils { + + private static AuthConfig privateRegistryAuthConfig; + + /** + * Starts a local test registry when it is not already started and returns the auth configuration for it + * This method is synchronized so that only the first invocation starts the registry + * @return The auth configuration for the started private docker registry + * @throws Exception + */ + public static synchronized AuthConfig runPrivateRegistry(DockerClient dockerClient) throws Exception { + if (privateRegistryAuthConfig == null) { + int port = 5050; + + String containerName = "private-registry"; + String imageName = "private-registry-image"; + + File baseDir = new File(DockerRule.class.getResource("/privateRegistry").getFile()); + + try { + dockerClient.removeContainerCmd(containerName) + .withForce(true) + .withRemoveVolumes(true) + .exec(); + } catch (NotFoundException ex) { + // ignore + } + + String registryImageId = dockerClient.buildImageCmd(baseDir) + .withNoCache(true) + .exec(new BuildImageResultCallback()) + .awaitImageId(); + + InspectImageResponse inspectImageResponse = dockerClient.inspectImageCmd(registryImageId).exec(); + assertThat(inspectImageResponse, not(nullValue())); + DockerRule.LOG.info("Image Inspect: {}", inspectImageResponse.toString()); + + dockerClient.tagImageCmd(registryImageId, imageName, "2") + .withForce().exec(); + + // see https://github.com/docker/distribution/blob/master/docs/deploying.md#native-basic-auth + CreateContainerResponse testregistry = dockerClient + .createContainerCmd(imageName + ":2") + .withName(containerName) + .withPortBindings(new PortBinding(Ports.Binding.bindPort(port), ExposedPort.tcp(5000))) + .withEnv("REGISTRY_AUTH=htpasswd", "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm", + "REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd", "REGISTRY_LOG_LEVEL=debug", + "REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt", "REGISTRY_HTTP_TLS_KEY=/certs/domain.key") + .exec(); + + dockerClient.startContainerCmd(testregistry.getId()).exec(); + + // wait for registry to boot + Thread.sleep(3000); + + // credentials as configured in /auth/htpasswd + privateRegistryAuthConfig = new AuthConfig() + .withUsername("testuser") + .withPassword("testpassword") + .withEmail("foo@bar.de") + .withRegistryAddress("localhost:" + port); + } + + return privateRegistryAuthConfig; + } + + public static String createPrivateImage(DockerRule dockerRule, String tagName) throws InterruptedException { + if (privateRegistryAuthConfig == null) + throw new IllegalStateException("Ensure that you have invoked runPrivateRegistry beforehand."); + + String imgNameWithTag = createTestImage(dockerRule, tagName); + + dockerRule.getClient().pushImageCmd(imgNameWithTag) + .withAuthConfig(privateRegistryAuthConfig) + .exec(new PushImageResultCallback()) + .awaitCompletion(30, TimeUnit.SECONDS); + + Thread.sleep(5000); //ensures that the image is also purged from the swarm cache + dockerRule.getClient().removeImageCmd(imgNameWithTag) + .exec(); + + return imgNameWithTag; + } + + public static String createTestImage(DockerRule dockerRule, String tagName) { + String tag = dockerRule.getKind() + "-" + tagName; + String imgName = privateRegistryAuthConfig.getRegistryAddress() + "/busybox"; + String imgNameWithTag = imgName + ":" + tag; + + dockerRule.getClient().tagImageCmd(DEFAULT_IMAGE, imgName, tag) + .exec(); + return imgNameWithTag; + } +} diff --git a/src/test/resources/testAuthConfigFile/validJson.json b/src/test/resources/testAuthConfigFile/validJson.json deleted file mode 100644 index c94acce0d..000000000 --- a/src/test/resources/testAuthConfigFile/validJson.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "quay.io": { - "auth": "Zm9vOmJhcg==", - "email": "foo@example.com" - }, - "https://index.docker.io/v1/": { - "auth": "Zm9vMTpiYXIx", - "email": "moo@example.com" - } -} diff --git a/src/test/resources/testAuthConfigFile/validJsonWithOnlyUnknown.json b/src/test/resources/testAuthConfigFile/validJsonWithOnlyUnknown/config.json similarity index 100% rename from src/test/resources/testAuthConfigFile/validJsonWithOnlyUnknown.json rename to src/test/resources/testAuthConfigFile/validJsonWithOnlyUnknown/config.json diff --git a/src/test/resources/testAuthConfigFile/validJsonWithUnknown.json b/src/test/resources/testAuthConfigFile/validJsonWithUnknown/config.json similarity index 100% rename from src/test/resources/testAuthConfigFile/validJsonWithUnknown.json rename to src/test/resources/testAuthConfigFile/validJsonWithUnknown/config.json diff --git a/src/test/resources/testAuthConfigFile/validJson/.dockercfg b/src/test/resources/testAuthConfigFile/validLegacyJson/.dockercfg similarity index 100% rename from src/test/resources/testAuthConfigFile/validJson/.dockercfg rename to src/test/resources/testAuthConfigFile/validLegacyJson/.dockercfg From fe2c5eeff3d5f2f00ab27f4ce272580483adb2c5 Mon Sep 17 00:00:00 2001 From: fb Date: Tue, 7 Nov 2017 12:55:44 +0100 Subject: [PATCH 3/6] More Test fixes --- src/test/java/com/github/dockerjava/cmd/PullImageCmdIT.java | 6 ++++-- .../github/dockerjava/core/util/CertificateUtilsTest.java | 3 ++- .../java/com/github/dockerjava/utils/RegistryUtils.java | 4 +++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/test/java/com/github/dockerjava/cmd/PullImageCmdIT.java b/src/test/java/com/github/dockerjava/cmd/PullImageCmdIT.java index 6b10ddd38..8dfd65744 100644 --- a/src/test/java/com/github/dockerjava/cmd/PullImageCmdIT.java +++ b/src/test/java/com/github/dockerjava/cmd/PullImageCmdIT.java @@ -132,7 +132,8 @@ public void testPullImageWithNoAuth() throws Exception { String imgName = RegistryUtils.createPrivateImage(dockerRule, "pull-image-with-no-auth"); - if (isNotSwarm(dockerRule.getClient())) { + if (isNotSwarm(dockerRule.getClient()) && getVersion(dockerRule.getClient()) + .isGreater(RemoteApiVersion.VERSION_1_27)) { exception.expect(InternalServerErrorException.class); } else { exception.expect(DockerClientException.class); @@ -156,7 +157,8 @@ public void testPullImageWithInvalidAuth() throws Exception { String imgName = RegistryUtils.createPrivateImage(dockerRule, "pull-image-with-invalid-auth"); - if (isNotSwarm(dockerRule.getClient())) { + if (isNotSwarm(dockerRule.getClient()) && getVersion(dockerRule.getClient()) + .isGreater(RemoteApiVersion.VERSION_1_27)) { exception.expect(InternalServerErrorException.class); } else { exception.expect(DockerClientException.class); diff --git a/src/test/java/com/github/dockerjava/core/util/CertificateUtilsTest.java b/src/test/java/com/github/dockerjava/core/util/CertificateUtilsTest.java index a19dd2cc4..28818d24c 100644 --- a/src/test/java/com/github/dockerjava/core/util/CertificateUtilsTest.java +++ b/src/test/java/com/github/dockerjava/core/util/CertificateUtilsTest.java @@ -5,6 +5,7 @@ import org.junit.BeforeClass; import org.junit.Test; +import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; @@ -86,6 +87,6 @@ public void readMultipleCaCerts() throws Exception { } private String readFileAsString(String path) throws IOException { - return new String(Files.readAllBytes(Paths.get(baseDir + path))); + return new String(Files.readAllBytes(Paths.get(new File(baseDir + path).getPath()))); } } diff --git a/src/test/java/com/github/dockerjava/utils/RegistryUtils.java b/src/test/java/com/github/dockerjava/utils/RegistryUtils.java index 6e178df56..e0f98793f 100644 --- a/src/test/java/com/github/dockerjava/utils/RegistryUtils.java +++ b/src/test/java/com/github/dockerjava/utils/RegistryUtils.java @@ -97,10 +97,12 @@ public static String createPrivateImage(DockerRule dockerRule, String tagName) t .exec(new PushImageResultCallback()) .awaitCompletion(30, TimeUnit.SECONDS); - Thread.sleep(5000); //ensures that the image is also purged from the swarm cache dockerRule.getClient().removeImageCmd(imgNameWithTag) .exec(); + //ensures that the image is available + Thread.sleep(5000); + return imgNameWithTag; } From 9a937a3d0242b1a95b46ab84d4df62a172b1f135 Mon Sep 17 00:00:00 2001 From: fb Date: Tue, 7 Nov 2017 13:21:12 +0100 Subject: [PATCH 4/6] Adjust the docker api version exception type --- .../java/com/github/dockerjava/core/RemoteApiVersion.java | 1 + src/test/java/com/github/dockerjava/cmd/PullImageCmdIT.java | 4 ++-- src/test/java/com/github/dockerjava/utils/RegistryUtils.java | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/github/dockerjava/core/RemoteApiVersion.java b/src/main/java/com/github/dockerjava/core/RemoteApiVersion.java index 1525d2c28..62da159d7 100644 --- a/src/main/java/com/github/dockerjava/core/RemoteApiVersion.java +++ b/src/main/java/com/github/dockerjava/core/RemoteApiVersion.java @@ -80,6 +80,7 @@ public class RemoteApiVersion implements Serializable { public static final RemoteApiVersion VERSION_1_26 = RemoteApiVersion.create(1, 26); public static final RemoteApiVersion VERSION_1_27 = RemoteApiVersion.create(1, 27); public static final RemoteApiVersion VERSION_1_29 = RemoteApiVersion.create(1, 29); + public static final RemoteApiVersion VERSION_1_30 = RemoteApiVersion.create(1, 30); /** diff --git a/src/test/java/com/github/dockerjava/cmd/PullImageCmdIT.java b/src/test/java/com/github/dockerjava/cmd/PullImageCmdIT.java index 8dfd65744..9f728b860 100644 --- a/src/test/java/com/github/dockerjava/cmd/PullImageCmdIT.java +++ b/src/test/java/com/github/dockerjava/cmd/PullImageCmdIT.java @@ -133,7 +133,7 @@ public void testPullImageWithNoAuth() throws Exception { String imgName = RegistryUtils.createPrivateImage(dockerRule, "pull-image-with-no-auth"); if (isNotSwarm(dockerRule.getClient()) && getVersion(dockerRule.getClient()) - .isGreater(RemoteApiVersion.VERSION_1_27)) { + .isGreaterOrEqual(RemoteApiVersion.VERSION_1_30)) { exception.expect(InternalServerErrorException.class); } else { exception.expect(DockerClientException.class); @@ -158,7 +158,7 @@ public void testPullImageWithInvalidAuth() throws Exception { String imgName = RegistryUtils.createPrivateImage(dockerRule, "pull-image-with-invalid-auth"); if (isNotSwarm(dockerRule.getClient()) && getVersion(dockerRule.getClient()) - .isGreater(RemoteApiVersion.VERSION_1_27)) { + .isGreaterOrEqual(RemoteApiVersion.VERSION_1_30)) { exception.expect(InternalServerErrorException.class); } else { exception.expect(DockerClientException.class); diff --git a/src/test/java/com/github/dockerjava/utils/RegistryUtils.java b/src/test/java/com/github/dockerjava/utils/RegistryUtils.java index e0f98793f..ea78795a2 100644 --- a/src/test/java/com/github/dockerjava/utils/RegistryUtils.java +++ b/src/test/java/com/github/dockerjava/utils/RegistryUtils.java @@ -100,7 +100,7 @@ public static String createPrivateImage(DockerRule dockerRule, String tagName) t dockerRule.getClient().removeImageCmd(imgNameWithTag) .exec(); - //ensures that the image is available + //ensures that the image is available, the private registry needs some time to reflect a tag push Thread.sleep(5000); return imgNameWithTag; From 5139cd84647a08051e65720e72146651585eb41f Mon Sep 17 00:00:00 2001 From: fb Date: Sat, 18 Nov 2017 17:27:27 +0100 Subject: [PATCH 5/6] Normalize the authConfig handling in the different commands --- .../dockerjava/api/command/AuthCmd.java | 7 ++-- .../api/command/CreateContainerCmd.java | 1 + .../dockerjava/api/command/PullImageCmd.java | 7 ++-- .../dockerjava/core/DockerClientImpl.java | 8 +--- .../core/command/AbstrAuthCfgDockerCmd.java | 37 ------------------- .../dockerjava/core/command/AuthCmdImpl.java | 13 ++++++- .../core/command/CreateContainerCmdImpl.java | 7 +--- .../core/command/PullImageCmdImpl.java | 11 ++---- .../core/command/PushImageCmdImpl.java | 9 +---- 9 files changed, 28 insertions(+), 72 deletions(-) delete mode 100644 src/main/java/com/github/dockerjava/core/command/AbstrAuthCfgDockerCmd.java diff --git a/src/main/java/com/github/dockerjava/api/command/AuthCmd.java b/src/main/java/com/github/dockerjava/api/command/AuthCmd.java index a92b1593b..f9ffbf859 100644 --- a/src/main/java/com/github/dockerjava/api/command/AuthCmd.java +++ b/src/main/java/com/github/dockerjava/api/command/AuthCmd.java @@ -1,12 +1,11 @@ package com.github.dockerjava.api.command; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; - import com.github.dockerjava.api.exception.UnauthorizedException; import com.github.dockerjava.api.model.AuthConfig; import com.github.dockerjava.api.model.AuthResponse; +import javax.annotation.CheckForNull; + /** * * Authenticate with the server, useful for checking authentication. @@ -17,7 +16,7 @@ public interface AuthCmd extends SyncDockerCmd { @CheckForNull AuthConfig getAuthConfig(); - AuthCmd withAuthConfig(@Nonnull AuthConfig authConfig); + AuthCmd withAuthConfig(AuthConfig authConfig); /** * @return The status. Based on it's value you may mean you need to authorise your account, e.g.: "Account created. Please see the diff --git a/src/main/java/com/github/dockerjava/api/command/CreateContainerCmd.java b/src/main/java/com/github/dockerjava/api/command/CreateContainerCmd.java index 62d5cfee3..34682a14f 100644 --- a/src/main/java/com/github/dockerjava/api/command/CreateContainerCmd.java +++ b/src/main/java/com/github/dockerjava/api/command/CreateContainerCmd.java @@ -24,6 +24,7 @@ public interface CreateContainerCmd extends SyncDockerCmd { + @CheckForNull AuthConfig getAuthConfig(); @CheckForNull diff --git a/src/main/java/com/github/dockerjava/api/command/PullImageCmd.java b/src/main/java/com/github/dockerjava/api/command/PullImageCmd.java index 8127f7b1f..81da24e61 100644 --- a/src/main/java/com/github/dockerjava/api/command/PullImageCmd.java +++ b/src/main/java/com/github/dockerjava/api/command/PullImageCmd.java @@ -1,11 +1,11 @@ package com.github.dockerjava.api.command; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; - import com.github.dockerjava.api.model.AuthConfig; import com.github.dockerjava.api.model.PullResponseItem; +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; + /** * * Pull image from repository. @@ -22,6 +22,7 @@ public interface PullImageCmd extends AsyncDockerCmd, RES_T> extends AbstrDockerCmd { - - public AbstrAuthCfgDockerCmd(DockerCmdSyncExec execution, AuthConfig authConfig) { - super(execution); - withOptionalAuthConfig(authConfig); - } - - public AbstrAuthCfgDockerCmd(DockerCmdSyncExec execution) { - super(execution); - } - - private AuthConfig authConfig; - - public AuthConfig getAuthConfig() { - return authConfig; - } - - public T withAuthConfig(AuthConfig authConfig) { - checkNotNull(authConfig, "authConfig was not specified"); - return withOptionalAuthConfig(authConfig); - } - - @SuppressWarnings("unchecked") - private T withOptionalAuthConfig(AuthConfig authConfig) { - this.authConfig = authConfig; - return (T) this; - } - -} diff --git a/src/main/java/com/github/dockerjava/core/command/AuthCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/AuthCmdImpl.java index e0a790c32..e22ad67f2 100644 --- a/src/main/java/com/github/dockerjava/core/command/AuthCmdImpl.java +++ b/src/main/java/com/github/dockerjava/core/command/AuthCmdImpl.java @@ -10,7 +10,9 @@ * Authenticate with the server, useful for checking authentication. * */ -public class AuthCmdImpl extends AbstrAuthCfgDockerCmd implements AuthCmd { +public class AuthCmdImpl extends AbstrDockerCmd implements AuthCmd { + + private AuthConfig authConfig; public AuthCmdImpl(AuthCmd.Exec exec, AuthConfig authConfig) { super(exec); @@ -21,4 +23,13 @@ public AuthCmdImpl(AuthCmd.Exec exec, AuthConfig authConfig) { public AuthResponse exec() throws UnauthorizedException { return super.exec(); } + + public AuthConfig getAuthConfig() { + return authConfig; + } + + public AuthCmd withAuthConfig(AuthConfig authConfig) { + this.authConfig = authConfig; + return this; + } } diff --git a/src/main/java/com/github/dockerjava/core/command/CreateContainerCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/CreateContainerCmdImpl.java index 4bccb1b79..54e41b2fd 100644 --- a/src/main/java/com/github/dockerjava/core/command/CreateContainerCmdImpl.java +++ b/src/main/java/com/github/dockerjava/core/command/CreateContainerCmdImpl.java @@ -133,7 +133,7 @@ public class CreateContainerCmdImpl extends AbstrDockerCmd Date: Sat, 18 Nov 2017 17:57:52 +0100 Subject: [PATCH 6/6] Small test fixes --- .../dockerjava/cmd/CreateContainerCmdIT.java | 14 +++++++------- .../com/github/dockerjava/cmd/PullImageCmdIT.java | 15 --------------- 2 files changed, 7 insertions(+), 22 deletions(-) diff --git a/src/test/java/com/github/dockerjava/cmd/CreateContainerCmdIT.java b/src/test/java/com/github/dockerjava/cmd/CreateContainerCmdIT.java index a38cd7885..51f003a75 100644 --- a/src/test/java/com/github/dockerjava/cmd/CreateContainerCmdIT.java +++ b/src/test/java/com/github/dockerjava/cmd/CreateContainerCmdIT.java @@ -148,19 +148,18 @@ public void createContainerWithVolumesFrom() throws DockerException { String container1Name = UUID.randomUUID().toString(); CreateVolumeResponse volume1Info = dockerRule.getClient().createVolumeCmd().exec(); CreateVolumeResponse volume2Info = dockerRule.getClient().createVolumeCmd().exec(); - String mountpoint1 = volume1Info.getMountpoint(); - String mountpoint2 = volume2Info.getMountpoint(); - Volume volume1 = new Volume(mountpoint1); - Volume volume2 = new Volume(mountpoint2); - Bind bind1 = new Bind("/src/webapp1", volume1); - Bind bind2 = new Bind("/src/webapp2", volume2); + Volume volume1 = new Volume("/src/webapp1"); + Volume volume2 = new Volume("/src/webapp2"); + Bind bind1 = new Bind(volume1Info.getName(), volume1); + Bind bind2 = new Bind(volume2Info.getName(), volume2); // create a running container with bind mounts CreateContainerResponse container1 = dockerRule.getClient().createContainerCmd(DEFAULT_IMAGE) .withCmd("sleep", "9999") .withName(container1Name) - .withBinds(bind1, bind2).exec(); + .withBinds(bind1, bind2) + .exec(); LOG.info("Created container1 {}", container1.toString()); @@ -185,6 +184,7 @@ public void createContainerWithVolumesFrom() throws DockerException { // No volumes are created, the information is just stored in .HostConfig.VolumesFrom assertThat(inspectContainerResponse2.getHostConfig().getVolumesFrom(), hasItemInArray(new VolumesFrom(container1Name))); + assertThat(inspectContainerResponse1, mountedVolumes(containsInAnyOrder(volume1, volume2))); // To ensure that the information stored in VolumesFrom really is considered // when starting the container, we start it and verify that it has the same diff --git a/src/test/java/com/github/dockerjava/cmd/PullImageCmdIT.java b/src/test/java/com/github/dockerjava/cmd/PullImageCmdIT.java index 9f728b860..4d7ed98b1 100644 --- a/src/test/java/com/github/dockerjava/cmd/PullImageCmdIT.java +++ b/src/test/java/com/github/dockerjava/cmd/PullImageCmdIT.java @@ -10,7 +10,6 @@ import com.github.dockerjava.api.model.Info; import com.github.dockerjava.api.model.PullResponseItem; import com.github.dockerjava.core.RemoteApiVersion; -import com.github.dockerjava.core.command.PullImageCmdImpl; import com.github.dockerjava.core.command.PullImageResultCallback; import com.github.dockerjava.utils.RegistryUtils; import org.junit.Rule; @@ -26,7 +25,6 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.lessThanOrEqualTo; import static org.hamcrest.Matchers.notNullValue; -import static org.junit.Assert.assertEquals; public class PullImageCmdIT extends CmdIT { private static final Logger LOG = LoggerFactory.getLogger(PullImageCmdIT.class); @@ -40,19 +38,6 @@ public Void exec(PullImageCmd command, ResultCallback resultCa }; }; - @Test - public void nullAuthConfig() throws Exception { - PullImageCmdImpl pullImageCmd = new PullImageCmdImpl(NOP_EXEC, null, ""); - try { - pullImageCmd.withAuthConfig(null); - throw new AssertionError(); - } catch (Exception e) { - assertEquals(e.getMessage(), "authConfig was not specified"); - } finally { - pullImageCmd.close(); - } - } - @Test public void testPullImage() throws Exception { Info info = dockerRule.getClient().infoCmd().exec();