FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Add registry authentication to service updates by novofrank · Pull Request #2693 · docker-java/docker-java · GitHub

Repository navigation

Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension .java  (6) All 1 file type selected
Viewed files
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Unified
Split
Hide whitespace
Diff view
Unified
Split
Hide whitespace
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.github.dockerjava.api.command;

import com.github.dockerjava.api.model.AuthConfig;
import com.github.dockerjava.api.model.ServiceSpec;

import javax.annotation.CheckForNull;
Expand All @@ -19,6 +20,12 @@ public interface UpdateServiceCmd extends SyncDockerCmd<Void> {

UpdateServiceCmd withServiceSpec(ServiceSpec serviceSpec);

@CheckForNull
AuthConfig getAuthConfig();

@Nonnull
UpdateServiceCmd withAuthConfig(@Nonnull AuthConfig authConfig);

@CheckForNull
Long getVersion();

Expand Down
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.github.dockerjava.api.command.UpdateServiceCmd;
import com.github.dockerjava.api.exception.NotFoundException;
import com.github.dockerjava.api.model.AuthConfig;
import com.github.dockerjava.api.model.ServiceSpec;
import com.github.dockerjava.core.RemoteApiVersion;
import org.apache.commons.lang3.builder.EqualsBuilder;
Expand All @@ -11,6 +12,7 @@

import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import java.util.Objects;

/**
* @since {@link RemoteApiVersion#VERSION_1_24}
Expand All @@ -28,6 +30,13 @@ public class UpdateServiceCmdImpl extends AbstrDockerCmd<UpdateServiceCmd, Void>
*/
private ServiceSpec serviceSpec;

/**
* Registry authentication sent with the service update request.
*
* @since {@link RemoteApiVersion#VERSION_1_24}
*/
private AuthConfig authConfig;

/**
* @since 1.24
*/
Expand Down Expand Up @@ -71,6 +80,25 @@ public UpdateServiceCmd withServiceSpec(ServiceSpec serviceSpec) {
return this;
}

/**
* @see #authConfig
*/
@Override
@CheckForNull
public AuthConfig getAuthConfig() {
return authConfig;
}

/**
* @see #authConfig
*/
@Override
@Nonnull
public UpdateServiceCmd withAuthConfig(@Nonnull AuthConfig authConfig) {
this.authConfig = Objects.requireNonNull(authConfig, "authConfig was not specified");
return this;
}

/**
* @see #version
*/
Expand Down
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.github.dockerjava.api.command.UpdateServiceCmd;
import com.github.dockerjava.core.DockerClientConfig;
import com.github.dockerjava.core.InvocationBuilder;
import com.github.dockerjava.core.MediaType;
import com.github.dockerjava.core.WebTarget;
import org.slf4j.Logger;
Expand All @@ -27,8 +28,10 @@ protected Void execute(UpdateServiceCmd command) {
.queryParam("version", command.getVersion());

LOGGER.trace("POST: {}", webResource);
InvocationBuilder builder = resourceWithOptionalAuthConfig(command.getAuthConfig(), webResource.request())
.accept(MediaType.APPLICATION_JSON);
try {
webResource.request().accept(MediaType.APPLICATION_JSON).post(command.getServiceSpec()).close();
builder.post(command.getServiceSpec()).close();
} catch (IOException e) {
throw new RuntimeException(e);
}
Expand Down
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,23 @@
import com.github.dockerjava.api.model.ServiceModeConfig;
import com.github.dockerjava.api.model.ServiceReplicatedModeOptions;
import com.github.dockerjava.api.model.ServiceSpec;
import com.github.dockerjava.api.model.Task;
import com.github.dockerjava.api.model.TaskSpec;
import com.github.dockerjava.api.model.TaskState;
import com.github.dockerjava.junit.PrivateRegistryRule;
import com.google.common.collect.Lists;
import org.junit.Test;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

import static com.github.dockerjava.core.DockerRule.DEFAULT_IMAGE;
import static org.awaitility.Awaitility.await;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.startsWith;

public class UpdateSwarmServiceIT extends SwarmCmdIT {
@Test
Expand Down Expand Up @@ -46,4 +53,45 @@ public void testUpdateServiceReplicate() {
assertThat(updateService.getSpec().getMode().getReplicated().getReplicas(), is(2L));
});
}

@Test
public void testUpdateServiceWithRegistryAuth() throws InterruptedException {
DockerClient dockerClient = startSwarm();
ServiceSpec serviceSpec = new ServiceSpec()
.withName("authenticated-worker")
.withMode(new ServiceModeConfig().withReplicated(new ServiceReplicatedModeOptions().withReplicas(1)))
.withTaskTemplate(new TaskSpec().withContainerSpec(
new ContainerSpec().withImage(DEFAULT_IMAGE).withArgs(Arrays.asList("sleep", "3600"))));
String serviceId = dockerClient.createServiceCmd(serviceSpec).exec().getId();

await().atMost(60, TimeUnit.SECONDS).untilAsserted(() -> {
List<Task> tasks = dockerClient.listTasksCmd()
.withServiceFilter(serviceId)
.withStateFilter(TaskState.RUNNING)
.exec();
assertThat(tasks, hasSize(1));
});

try (PrivateRegistryRule registry = new PrivateRegistryRule(dockerClient)) {
registry.start();
String privateImage = registry.createPrivateImage("update-service");
Service service = dockerClient.inspectServiceCmd(serviceId).exec();
ServiceSpec updatedServiceSpec = service.getSpec();
updatedServiceSpec.getTaskTemplate().getContainerSpec().withImage(privateImage);

dockerClient.updateServiceCmd(serviceId, updatedServiceSpec)
.withVersion(service.getVersion().getIndex())
.withAuthConfig(registry.getAuthConfig())
.exec();

await().atMost(60, TimeUnit.SECONDS).untilAsserted(() -> {
List<Task> tasks = dockerClient.listTasksCmd()
.withServiceFilter(serviceId)
.withStateFilter(TaskState.RUNNING)
.exec();
assertThat(tasks, hasSize(1));
assertThat(tasks.get(0).getSpec().getContainerSpec().getImage(), startsWith(privateImage));
});
}
}
}
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package com.github.dockerjava.core.exec;

import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.model.AuthConfig;
import com.github.dockerjava.api.model.ServiceSpec;
import com.github.dockerjava.core.DefaultDockerClientConfig;
import com.github.dockerjava.core.DockerClientConfig;
import com.github.dockerjava.core.DockerClientImpl;
import com.github.dockerjava.transport.DockerHttpClient;
import com.google.common.io.BaseEncoding;
import org.junit.Test;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;

public class UpdateServiceCmdExecTest {

private static final String REGISTRY_AUTH_HEADER = "X-Registry-Auth";

@Test
public void sendsRegistryAuthHeader() throws Exception {
AtomicReference<DockerHttpClient.Request> request = new AtomicReference<>();
DockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder().build();
DockerClient dockerClient = DockerClientImpl.getInstance(config, new CapturingDockerHttpClient(request));
AuthConfig authConfig = new AuthConfig()
.withUsername("user")
.withPassword("password")
.withRegistryAddress("registry.example.com");

dockerClient.updateServiceCmd("service-id", new ServiceSpec())
.withVersion(1L)
.withAuthConfig(authConfig)
.exec();

String encodedAuth = request.get().headers().get(REGISTRY_AUTH_HEADER);
assertThat(encodedAuth, notNullValue());
byte[] decodedAuth = BaseEncoding.base64Url().decode(encodedAuth);
AuthConfig sentAuth = config.getObjectMapper().readValue(decodedAuth, AuthConfig.class);
assertThat(sentAuth, is(authConfig));
}

@Test
public void omitsRegistryAuthHeaderWhenNotConfigured() {
AtomicReference<DockerHttpClient.Request> request = new AtomicReference<>();
DockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder().build();
DockerClient dockerClient = DockerClientImpl.getInstance(config, new CapturingDockerHttpClient(request));

dockerClient.updateServiceCmd("service-id", new ServiceSpec())
.withVersion(1L)
.exec();

assertThat(request.get().headers().get(REGISTRY_AUTH_HEADER), nullValue());
}

private static class CapturingDockerHttpClient implements DockerHttpClient {

private final AtomicReference<Request> request;

CapturingDockerHttpClient(AtomicReference<Request> request) {
this.request = request;
}

@Override
public Response execute(Request request) {
this.request.set(request);
return new EmptyResponse();
}

@Override
public void close() throws IOException {
}
}

private static class EmptyResponse implements DockerHttpClient.Response {

@Override
public int getStatusCode() {
return 200;
}

@Override
public Map<String, List<String>> getHeaders() {
return Collections.emptyMap();
}

@Override
public InputStream getBody() {
return new ByteArrayInputStream(new byte[0]);
}

@Override
public void close() {
}
}
}
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.junit.rules.ExternalResource;

import java.io.File;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

import static com.github.dockerjava.api.model.HostConfig.newHostConfig;
Expand All @@ -20,7 +21,7 @@
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue;

public class PrivateRegistryRule extends ExternalResource {
public class PrivateRegistryRule extends ExternalResource implements AutoCloseable {

private final DockerClient dockerClient;

Expand All @@ -29,7 +30,11 @@ public class PrivateRegistryRule extends ExternalResource {
private String containerId;

public PrivateRegistryRule() {
this.dockerClient = CmdIT.createDockerClient(DockerRule.config(null));
this(CmdIT.createDockerClient(DockerRule.config(null)));
}

public PrivateRegistryRule(DockerClient dockerClient) {
this.dockerClient = Objects.requireNonNull(dockerClient);
}

public AuthConfig getAuthConfig() {
Expand Down Expand Up @@ -59,12 +64,20 @@ public String createTestImage(String tagName) {
return imgName + ":" + tagName;
}

public void start() throws InterruptedException {
startRegistry();
}

/**
* 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
*/
@Override
protected void before() throws Throwable {
protected void before() throws InterruptedException {
startRegistry();
}

private void startRegistry() throws InterruptedException {

int port = 5050;

Expand Down Expand Up @@ -116,4 +129,9 @@ protected void after() {
.exec();
}
}

@Override
public void close() {
after();
}
}

Back | FazBrowse Home | New Git URL