Skip to content

Commit

Permalink
Support for swarm jobs (fixes #341)
Browse files Browse the repository at this point in the history
- Support for max-concurrent in replicated services
- Workaround listTasks assertions
  • Loading branch information
dmandalidis committed Aug 14, 2021
1 parent 8d4743b commit a9e085c
Show file tree
Hide file tree
Showing 5 changed files with 160 additions and 11 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*-
* -\-\-
* docker-client
* --
* Copyright (C) 2016 Spotify AB
* Copyright (C) 9/2019 - 2020 Dimitris Mandalidis
* --
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* -/-/-
*/

package org.mandas.docker.client.messages.swarm;

import org.immutables.value.Value.Immutable;

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;

@JsonDeserialize(builder = ImmutableGlobalJob.Builder.class)
@Immutable
public interface GlobalJob {

public static Builder builder() {
return ImmutableGlobalJob.builder();
}

interface Builder {
GlobalJob build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*-
* -\-\-
* docker-client
* --
* Copyright (C) 2016 Spotify AB
* Copyright (C) 9/2019 - 2020 Dimitris Mandalidis
* --
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* -/-/-
*/

package org.mandas.docker.client.messages.swarm;

import org.immutables.value.Value.Immutable;
import org.mandas.docker.Nullable;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;


@JsonDeserialize(builder = ImmutableReplicatedJob.Builder.class)
@Immutable
public interface ReplicatedJob {

@Nullable
@JsonProperty("MaxConcurrent")
Long maxConcurrent();

@Nullable
@JsonProperty("TotalCompletions")
Long totalCompletions();

interface Builder {

Builder maxConcurrent(Long maxConcurrent);

Builder totalCompletions(Long totalCompletions);

ReplicatedJob build();
}

public static Builder builder() {
return ImmutableReplicatedJob.builder();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,16 @@ public interface ReplicatedService {
@Nullable
@JsonProperty("Replicas")
Long replicas();

@Nullable
@JsonProperty("MaxConcurrent")
Long maxConcurrent();

interface Builder {

Builder replicas(Long replicas);

Builder maxConcurrent(Long maxConcurrent);

ReplicatedService build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,26 +35,60 @@ public interface ServiceMode {
@JsonProperty("Replicated")
ReplicatedService replicated();

@Nullable
@JsonProperty("ReplicatedJob")
ReplicatedJob replicatedJob();

@Nullable
@JsonProperty("Global")
GlobalService global();

@Nullable
@JsonProperty("GlobalJob")
GlobalJob globalJob();

public static ServiceMode withReplicas(final long replicas) {
return ImmutableServiceMode.builder()
.replicated(ReplicatedService.builder().replicas(replicas).build())
.build();
}

public static ServiceMode withReplicas(final long replicas, final long maxConcurrent) {
return ImmutableServiceMode.builder()
.replicated(ReplicatedService.builder().replicas(replicas).maxConcurrent(maxConcurrent).build())
.build();
}

public static ServiceMode withJobReplicas(final long totalCompletions) {
return ImmutableServiceMode.builder()
.replicatedJob(ReplicatedJob.builder().totalCompletions(totalCompletions).build())
.build();
}

public static ServiceMode withJobReplicas(final long totalCompletions, final long maxConcurrent) {
return ImmutableServiceMode.builder()
.replicatedJob(ReplicatedJob.builder().totalCompletions(totalCompletions).maxConcurrent(maxConcurrent).build())
.build();
}

public static ServiceMode withGlobal() {
return ImmutableServiceMode.builder().global(GlobalService.builder().build()).build();
}

public static ServiceMode withGlobalJob() {
return ImmutableServiceMode.builder().globalJob(GlobalJob.builder().build()).build();
}

interface Builder {

Builder replicated(ReplicatedService replicated);

Builder replicatedJob(ReplicatedJob replicated);

Builder global(GlobalService global);

Builder globalJob(GlobalJob global);

ServiceMode build();
}

Expand Down
36 changes: 25 additions & 11 deletions src/test/java/org/mandas/docker/client/DefaultDockerClientTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@
import static org.mandas.docker.client.messages.RemovedImage.Type.UNTAGGED;
import static org.mandas.docker.client.messages.swarm.PortConfig.PROTOCOL_TCP;
import static org.mandas.docker.client.messages.swarm.RestartPolicy.RESTART_POLICY_ANY;
import static org.mandas.docker.client.messages.swarm.ServiceMode.withGlobalJob;
import static org.mandas.docker.client.messages.swarm.ServiceMode.withJobReplicas;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
Expand Down Expand Up @@ -4321,6 +4323,22 @@ public void testCreateService() throws Exception {
final ServiceCreateResponse response = sut.createService(spec);
assertThat(response.id(), is(notNullValue()));
}

@Test
public void testCreateServiceSwarmJobs() throws Exception {
// global job
final ServiceSpec globalSpec = createServiceSpec(randomName(), Collections.emptyMap(), withGlobalJob());
final ServiceCreateResponse globalResponse = sut.createService(globalSpec);
assertThat(globalResponse.id(), is(notNullValue()));
Service globalJob = sut.inspectService(globalResponse.id());
assertThat(globalJob.spec().mode(), is(withGlobalJob()));
// replicated job
final ServiceSpec replicatedSpec = createServiceSpec(randomName(), Collections.emptyMap(), withJobReplicas(3));
final ServiceCreateResponse replicatedResponse = sut.createService(replicatedSpec);
assertThat(replicatedResponse.id(), is(notNullValue()));
Service replicatedJob = sut.inspectService(replicatedResponse.id());
assertThat(replicatedJob.spec().mode(), is(withJobReplicas(3, 1)));
}

@Test
public void testSecretOperations() throws Exception {
Expand Down Expand Up @@ -4543,8 +4561,6 @@ public void testCreateServiceWithDefaults() throws Exception {
final ServiceCreateResponse response = sut.createService(serviceSpec);
assertThat(response.id(), is(notNullValue()));

sut.listTasks();

final Service service = sut.inspectService(serviceName);
final ServiceSpec actualServiceSpec = service.spec();
assertThat(actualServiceSpec.mode().replicated().replicas(), equalTo(1L));
Expand Down Expand Up @@ -4852,7 +4868,7 @@ public void testListServicesFilterByLabel() throws Exception {
Map<String, String> labels = new HashMap<>();
labels.put("foo", "bar");

final ServiceSpec spec = createServiceSpec(serviceName, labels);
final ServiceSpec spec = createServiceSpec(serviceName, labels, ServiceMode.withReplicas(4));
sut.createService(spec);

final List<Service> services = sut.listServices(Service.find().addLabel("foo", "bar").build());
Expand All @@ -4877,10 +4893,10 @@ public void testRemoveService() throws Exception {
@Test
public void testListTasks() throws Exception {
final ServiceSpec spec = createServiceSpec(randomName());
assertThat(sut.listTasks().size(), is(0));
final int startingNumTasks = sut.listTasks().size();
sut.createService(spec);
await().until(numberOfTasks(sut), is(greaterThan(0)));
assertThat(sut.listTasks().size(), is(4));
await().until(numberOfTasks(sut), is(greaterThan(startingNumTasks)));
assertThat(sut.listTasks().size(), is(startingNumTasks + 4));
}

@Test
Expand Down Expand Up @@ -4987,22 +5003,20 @@ public void testMountTmpfsOptions() throws Exception {
}

private ServiceSpec createServiceSpec(final String serviceName) {
return this.createServiceSpec(serviceName, new HashMap<String, String>());
return this.createServiceSpec(serviceName, new HashMap<String, String>(), ServiceMode.withReplicas(4));
}

private ServiceSpec createServiceSpec(final String serviceName,
final Map<String, String> labels) {
final Map<String, String> labels, ServiceMode mode) {

final TaskSpec taskSpec = TaskSpec
.builder()
.containerSpec(ContainerSpec.builder().image("alpine")
.command("ping", "-c1000", "localhost").build())
.build();

final ServiceMode serviceMode = ServiceMode.withReplicas(4);

return ServiceSpec.builder().name(serviceName).taskTemplate(taskSpec)
.mode(serviceMode)
.mode(mode)
.labels(labels)
.build();
}
Expand Down

0 comments on commit a9e085c

Please sign in to comment.