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 2eeda23e2..8d290125b 100644 --- a/src/main/java/com/github/dockerjava/api/command/CreateContainerCmd.java +++ b/src/main/java/com/github/dockerjava/api/command/CreateContainerCmd.java @@ -6,6 +6,7 @@ import com.github.dockerjava.api.model.ExposedPort; import com.github.dockerjava.api.model.HostConfig; import com.github.dockerjava.api.model.Volume; +import com.github.dockerjava.api.model.VolumeFrom; public interface CreateContainerCmd extends DockerCmd{ @@ -93,9 +94,9 @@ public interface CreateContainerCmd extends DockerCmd{ public CreateContainerCmd withVolumes(Volume... volumes); - public String[] getVolumesFrom(); + public VolumeFrom[] getVolumesFrom(); - public CreateContainerCmd withVolumesFrom(String... volumesFrom); + public CreateContainerCmd withVolumesFrom(VolumeFrom... volumesFrom); public HostConfig getHostConfig(); diff --git a/src/main/java/com/github/dockerjava/api/model/AccessMode.java b/src/main/java/com/github/dockerjava/api/model/AccessMode.java index e01065368..a1f53f57b 100644 --- a/src/main/java/com/github/dockerjava/api/model/AccessMode.java +++ b/src/main/java/com/github/dockerjava/api/model/AccessMode.java @@ -16,4 +16,5 @@ public enum AccessMode { */ public static final AccessMode DEFAULT = rw; + } diff --git a/src/main/java/com/github/dockerjava/api/model/Bind.java b/src/main/java/com/github/dockerjava/api/model/Bind.java index 2a838c279..eb0b8fd43 100644 --- a/src/main/java/com/github/dockerjava/api/model/Bind.java +++ b/src/main/java/com/github/dockerjava/api/model/Bind.java @@ -1,7 +1,6 @@ package com.github.dockerjava.api.model; -import static com.github.dockerjava.api.model.AccessMode.ro; -import static com.github.dockerjava.api.model.AccessMode.rw; + import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.HashCodeBuilder; @@ -29,14 +28,6 @@ public Bind(String path, Volume volume, AccessMode accessMode) { this.accessMode = accessMode; } - /** - * @deprecated use {@link #Bind(String, Volume, AccessMode)} - */ - @Deprecated - public Bind(String path, Volume volume, boolean readOnly) { - this(path, volume, readOnly ? ro : rw); - } - public String getPath() { return path; } @@ -49,13 +40,6 @@ public AccessMode getAccessMode() { return accessMode; } - /** - * @deprecated use {@link #getAccessMode()} - */ - @Deprecated - public boolean isReadOnly() { - return ro.equals(accessMode); - } /** * Parses a bind mount specification to a {@link Bind}. diff --git a/src/main/java/com/github/dockerjava/api/model/HostConfig.java b/src/main/java/com/github/dockerjava/api/model/HostConfig.java index 3d1865017..dd25e79bd 100644 --- a/src/main/java/com/github/dockerjava/api/model/HostConfig.java +++ b/src/main/java/com/github/dockerjava/api/model/HostConfig.java @@ -33,7 +33,7 @@ public class HostConfig { private String[] dnsSearch; @JsonProperty("VolumesFrom") - private String[] volumesFrom; + private VolumesFrom volumesFrom; @JsonProperty("ContainerIDFile") private String containerIDFile; @@ -57,7 +57,7 @@ public HostConfig() { } public HostConfig(String[] binds, Links links, LxcConf[] lxcConf, Ports portBindings, boolean publishAllPorts, - boolean privileged, String[] dns, String[] dnsSearch, String[] volumesFrom, String containerIDFile, + boolean privileged, String[] dns, String[] dnsSearch, VolumesFrom volumesFrom, String containerIDFile, Capability[] capAdd, Capability[] capDrop, RestartPolicy restartPolicy, String networkMode, Device[] devices) { this.binds = binds; this.links = links; @@ -100,7 +100,7 @@ public String[] getDns() { return dns; } - public String[] getVolumesFrom() { + public VolumesFrom getVolumesFrom() { return volumesFrom; } @@ -168,7 +168,7 @@ public void setDnsSearch(String[] dnsSearch) { this.dnsSearch = dnsSearch; } - public void setVolumesFrom(String[] volumesFrom) { + public void setVolumesFrom(VolumesFrom volumesFrom) { this.volumesFrom = volumesFrom; } diff --git a/src/main/java/com/github/dockerjava/api/model/Volume.java b/src/main/java/com/github/dockerjava/api/model/Volume.java index 1a1890147..3ec5e24c7 100644 --- a/src/main/java/com/github/dockerjava/api/model/Volume.java +++ b/src/main/java/com/github/dockerjava/api/model/Volume.java @@ -30,18 +30,23 @@ public class Volume { private String path; - private boolean readWrite = true; + private AccessMode accessMode = AccessMode.rw; public Volume(String path) { this.path = path; } + + public Volume(String path, AccessMode accessMode) { + this.path = path; + this.accessMode = accessMode; + } public String getPath() { return path; } - public boolean isReadWrite() { - return readWrite; + public AccessMode getAccessMode() { + return accessMode; } public static Volume parse(String serialized) { @@ -64,7 +69,7 @@ public String toString() { public boolean equals(Object obj) { if (obj instanceof Volume) { Volume other = (Volume) obj; - return new EqualsBuilder().append(path, other.getPath()).append(readWrite, other.isReadWrite()) + return new EqualsBuilder().append(path, other.getPath()).append(accessMode, other.getAccessMode()) .isEquals(); } else return super.equals(obj); @@ -72,7 +77,7 @@ public boolean equals(Object obj) { @Override public int hashCode() { - return new HashCodeBuilder().append(path).append(readWrite).toHashCode(); + return new HashCodeBuilder().append(path).append(accessMode).toHashCode(); } public static class Serializer extends JsonSerializer { @@ -84,7 +89,7 @@ public void serialize(Volume volume, JsonGenerator jsonGen, jsonGen.writeStartObject(); jsonGen.writeFieldName(volume.getPath()); - jsonGen.writeString(Boolean.toString(volume.isReadWrite())); + jsonGen.writeString(Boolean.toString(volume.getAccessMode().equals(AccessMode.rw) ? true: false)); jsonGen.writeEndObject(); } diff --git a/src/main/java/com/github/dockerjava/api/model/VolumeFrom.java b/src/main/java/com/github/dockerjava/api/model/VolumeFrom.java new file mode 100644 index 000000000..8554c2f6d --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/model/VolumeFrom.java @@ -0,0 +1,87 @@ +package com.github.dockerjava.api.model; + +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; + +public class VolumeFrom { + + private String container; + + private AccessMode accessMode; + + public VolumeFrom(String container) { + this(container, AccessMode.DEFAULT); + } + + public VolumeFrom(String container, AccessMode accessMode) { + this.container = container; + this.accessMode = accessMode; + } + + public String getContainer() { + return container; + } + + public AccessMode getAccessMode() { + return accessMode; + } + + + /** + * Parses a volume from specification to a {@link VolumeFrom}. + * + * @param serialized the specification, e.g. container:ro + * @return a {@link VolumeFrom} matching the specification + * @throws IllegalArgumentException if the specification cannot be parsed + */ + public static VolumeFrom parse(String serialized) { + try { + String[] parts = serialized.split(":"); + switch (parts.length) { + case 1: { + return new VolumeFrom(parts[0]); + } + case 2: { + return new VolumeFrom(parts[0], AccessMode.valueOf(parts[1])); + } + + default: { + throw new IllegalArgumentException(); + } + } + } catch (Exception e) { + throw new IllegalArgumentException("Error parsing Bind '" + serialized + + "'"); + } + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof VolumeFrom) { + VolumeFrom other = (VolumeFrom) obj; + return new EqualsBuilder().append(container, other.getContainer()) + .append(accessMode, other.getAccessMode()).isEquals(); + } else + return super.equals(obj); + } + + @Override + public int hashCode() { + return new HashCodeBuilder().append(container) + .append(accessMode).toHashCode(); + } + + /** + * Returns a string representation of this {@link VolumeFrom} suitable + * for inclusion in a JSON message. + * The format is <container>:<access mode>, + * like the argument in {@link #parse(String)}. + * + * @return a string representation of this {@link VolumeFrom} + */ + @Override + public String toString() { + return container + ":" + accessMode.toString(); + } + +} diff --git a/src/main/java/com/github/dockerjava/api/model/Volumes.java b/src/main/java/com/github/dockerjava/api/model/Volumes.java index b8a9315bc..69753d6a8 100644 --- a/src/main/java/com/github/dockerjava/api/model/Volumes.java +++ b/src/main/java/com/github/dockerjava/api/model/Volumes.java @@ -48,7 +48,7 @@ public void serialize(Volumes volumes, JsonGenerator jsonGen, jsonGen.writeStartObject(); for (Volume volume : volumes.getVolumes()) { jsonGen.writeFieldName(volume.getPath()); - jsonGen.writeString(Boolean.toString(volume.isReadWrite())); + jsonGen.writeString(Boolean.toString(volume.getAccessMode().equals(AccessMode.rw) ? true: false)); } jsonGen.writeEndObject(); } diff --git a/src/main/java/com/github/dockerjava/api/model/VolumesFrom.java b/src/main/java/com/github/dockerjava/api/model/VolumesFrom.java new file mode 100644 index 000000000..a16e0c679 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/model/VolumesFrom.java @@ -0,0 +1,69 @@ +package com.github.dockerjava.api.model; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.ObjectCodec; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +@JsonSerialize(using = VolumesFrom.Serializer.class) +@JsonDeserialize(using = VolumesFrom.Deserializer.class) +public class VolumesFrom { + + private VolumeFrom[] volumesFrom; + + public VolumesFrom(VolumeFrom... volumesFrom) { + this.volumesFrom = volumesFrom; + } + + public VolumeFrom[] getVolumesFrom() { + return volumesFrom; + } + + public static class Serializer extends JsonSerializer { + + @Override + public void serialize(VolumesFrom volumesFrom, JsonGenerator jsonGen, + SerializerProvider serProvider) throws IOException, + JsonProcessingException { + + // + jsonGen.writeStartArray(); + for (VolumeFrom bind : volumesFrom.getVolumesFrom()) { + jsonGen.writeString(bind.toString()); + } + jsonGen.writeEndArray(); + // + } + + } + + public static class Deserializer extends JsonDeserializer { + @Override + public VolumesFrom deserialize(JsonParser jsonParser, + DeserializationContext deserializationContext) + throws IOException, JsonProcessingException { + + List volumesFrom = new ArrayList(); + ObjectCodec oc = jsonParser.getCodec(); + JsonNode node = oc.readTree(jsonParser); + for (Iterator it = node.iterator(); it.hasNext();) { + JsonNode field = it.next(); + volumesFrom.add(VolumeFrom.parse(field.asText())); + } + return new VolumesFrom(volumesFrom.toArray(new VolumeFrom[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 ea6d721e7..991ecc8e1 100644 --- a/src/main/java/com/github/dockerjava/core/command/CreateContainerCmdImpl.java +++ b/src/main/java/com/github/dockerjava/core/command/CreateContainerCmdImpl.java @@ -15,7 +15,9 @@ import com.github.dockerjava.api.model.ExposedPorts; import com.github.dockerjava.api.model.HostConfig; import com.github.dockerjava.api.model.Volume; +import com.github.dockerjava.api.model.VolumeFrom; import com.github.dockerjava.api.model.Volumes; +import com.github.dockerjava.api.model.VolumesFrom; /** @@ -303,13 +305,13 @@ public CreateContainerCmdImpl withVolumes(Volume... volumes) { } @Override - public String[] getVolumesFrom() { - return hostConfig.getVolumesFrom(); + public VolumeFrom[] getVolumesFrom() { + return hostConfig.getVolumesFrom() == null ? new VolumeFrom[0] : hostConfig.getVolumesFrom().getVolumesFrom(); } @Override - public CreateContainerCmdImpl withVolumesFrom(String... volumesFrom) { - this.hostConfig.setVolumesFrom(volumesFrom); + public CreateContainerCmdImpl withVolumesFrom(VolumeFrom... volumesFrom) { + this.hostConfig.setVolumesFrom(new VolumesFrom(volumesFrom)); return this; } diff --git a/src/test/java/com/github/dockerjava/api/model/VolumeFrom_SerializingTest.java b/src/test/java/com/github/dockerjava/api/model/VolumeFrom_SerializingTest.java new file mode 100644 index 000000000..37a3e37a6 --- /dev/null +++ b/src/test/java/com/github/dockerjava/api/model/VolumeFrom_SerializingTest.java @@ -0,0 +1,33 @@ +package com.github.dockerjava.api.model; + +import static org.testng.Assert.assertEquals; + +import java.util.Map; + +import org.testng.annotations.Test; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.dockerjava.api.model.Ports.Binding; + +public class VolumeFrom_SerializingTest { + private final ObjectMapper objectMapper = new ObjectMapper(); + private final String json = + "[\"container1:ro\",\"container2:rw\"]"; + @Test + public void deserializing() throws Exception { + VolumesFrom volumesFrom = objectMapper.readValue(json, VolumesFrom.class); + + VolumesFrom expected = new VolumesFrom(new VolumeFrom("container1", AccessMode.ro), new VolumeFrom("container2", AccessMode.rw)); + + assertEquals(volumesFrom.getVolumesFrom(), expected.getVolumesFrom()); + } + + @Test + public void serializing() throws Exception { + VolumesFrom volumesFrom = new VolumesFrom(new VolumeFrom("container1", AccessMode.ro), new VolumeFrom("container2", AccessMode.rw)); + + assertEquals(objectMapper.writeValueAsString(volumesFrom), json); + } + + +} diff --git a/src/test/java/com/github/dockerjava/core/command/CreateContainerCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/CreateContainerCmdImplTest.java index 2b879a9a9..71358ff50 100644 --- a/src/test/java/com/github/dockerjava/core/command/CreateContainerCmdImplTest.java +++ b/src/test/java/com/github/dockerjava/core/command/CreateContainerCmdImplTest.java @@ -32,6 +32,7 @@ import com.github.dockerjava.api.model.Link; import com.github.dockerjava.api.model.Links; import com.github.dockerjava.api.model.Volume; +import com.github.dockerjava.api.model.VolumeFrom; import com.github.dockerjava.client.AbstractDockerClientTest; @Test(groups = "integration") @@ -125,14 +126,14 @@ public void createContainerWithVolumesFrom() throws DockerException { // create a second container with volumes from first container CreateContainerResponse container2 = dockerClient .createContainerCmd("busybox").withCmd("sleep", "9999") - .withVolumesFrom(container1Name).exec(); + .withVolumesFrom(new VolumeFrom(container1Name)).exec(); LOG.info("Created container2 {}", container2.toString()); InspectContainerResponse inspectContainerResponse2 = dockerClient .inspectContainerCmd(container2.getId()).exec(); // No volumes are created, the information is just stored in .HostConfig.VolumesFrom - assertThat(inspectContainerResponse2.getHostConfig().getVolumesFrom(), hasItemInArray(container1Name)); + assertThat(inspectContainerResponse2.getHostConfig().getVolumesFrom().getVolumesFrom(), hasItemInArray(new VolumeFrom(container1Name))); // 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 @@ -144,7 +145,7 @@ public void createContainerWithVolumesFrom() throws DockerException { inspectContainerResponse2 = dockerClient.inspectContainerCmd(container2.getId()).exec(); - assertThat(inspectContainerResponse2.getHostConfig().getVolumesFrom(), hasItemInArray(container1Name)); + assertThat(inspectContainerResponse2.getHostConfig().getVolumesFrom().getVolumesFrom(), hasItemInArray(new VolumeFrom(container1Name))); assertContainerHasVolumes(inspectContainerResponse2, volume1, volume2); }