[BAEL-1455] add docker client test cases and update pom (#3375)
This commit is contained in:
parent
cf32e14a9b
commit
db04ce54f0
@ -639,6 +639,33 @@
|
||||
<version>${googleclient.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!--Java Docker API Client-->
|
||||
<dependency>
|
||||
<groupId>com.github.docker-java</groupId>
|
||||
<artifactId>docker-java</artifactId>
|
||||
<version>${docker.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>jcl-over-slf4j</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sun.jersey</groupId>
|
||||
<artifactId>jersey-client</artifactId>
|
||||
<version>1.19.4</version>
|
||||
</dependency>
|
||||
<!--Java Docker API Client-->
|
||||
|
||||
<!-- google api -->
|
||||
<dependency>
|
||||
<groupId>com.google.api-client</groupId>
|
||||
@ -758,5 +785,6 @@
|
||||
<google-api.version>1.23.0</google-api.version>
|
||||
<google-sheets.version>v4-rev493-1.21.0</google-sheets.version>
|
||||
<kafka.version>1.0.0</kafka.version>
|
||||
<docker.version>3.0.14</docker.version>
|
||||
</properties>
|
||||
</project>
|
@ -0,0 +1,165 @@
|
||||
package com.baeldung.dockerapi;
|
||||
|
||||
import com.github.dockerjava.api.DockerClient;
|
||||
import com.github.dockerjava.api.command.CreateContainerResponse;
|
||||
import com.github.dockerjava.api.command.InspectContainerResponse;
|
||||
import com.github.dockerjava.api.model.Container;
|
||||
import com.github.dockerjava.api.model.PortBinding;
|
||||
import com.github.dockerjava.core.DockerClientBuilder;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.core.Is.is;
|
||||
|
||||
public class ContainerLiveTest {
|
||||
|
||||
private static DockerClient dockerClient;
|
||||
|
||||
@BeforeClass
|
||||
public static void setup() {
|
||||
dockerClient = DockerClientBuilder.getInstance().build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenListingRunningContainers_thenReturnNonEmptyList() {
|
||||
|
||||
//when
|
||||
List<Container> containers = dockerClient.listContainersCmd().exec();
|
||||
|
||||
//then
|
||||
assertThat(containers.size(), is(not(0)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenListingExitedContainers_thenReturnNonEmptyList() {
|
||||
|
||||
//when
|
||||
List<Container> containers = dockerClient.listContainersCmd()
|
||||
.withShowSize(true)
|
||||
.withShowAll(true)
|
||||
.withStatusFilter("exited")
|
||||
.exec();
|
||||
|
||||
//then
|
||||
assertThat(containers.size(), is(not(0)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenCreatingContainer_thenMustReturnContainerId() {
|
||||
|
||||
//when
|
||||
CreateContainerResponse container
|
||||
= dockerClient.createContainerCmd("mongo:3.6")
|
||||
.withCmd("--bind_ip_all")
|
||||
.withName("mongo")
|
||||
.withHostName("baeldung")
|
||||
.withEnv("MONGO_LATEST_VERSION=3.6")
|
||||
.withPortBindings(PortBinding.parse("9999:27017"))
|
||||
.exec();
|
||||
|
||||
//then
|
||||
assertThat(container.getId(), is(not(null)));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void whenHavingContainer_thenRunContainer() throws InterruptedException {
|
||||
|
||||
//when
|
||||
CreateContainerResponse container
|
||||
= dockerClient.createContainerCmd("alpine:3.6")
|
||||
.withCmd("sleep", "10000")
|
||||
.exec();
|
||||
|
||||
Thread.sleep(3000);
|
||||
//then
|
||||
dockerClient.startContainerCmd(container.getId())
|
||||
.exec();
|
||||
|
||||
dockerClient.stopContainerCmd(container.getId())
|
||||
.exec();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenRunningContainer_thenStopContainer() throws InterruptedException {
|
||||
|
||||
//when
|
||||
CreateContainerResponse container
|
||||
= dockerClient.createContainerCmd("alpine:3.6")
|
||||
.withCmd("sleep", "10000")
|
||||
.exec();
|
||||
|
||||
Thread.sleep(3000);
|
||||
dockerClient.startContainerCmd(container.getId())
|
||||
.exec();
|
||||
|
||||
//then
|
||||
dockerClient.stopContainerCmd(container.getId())
|
||||
.exec();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenRunningContainer_thenKillContainer() throws InterruptedException {
|
||||
|
||||
//when
|
||||
CreateContainerResponse container
|
||||
= dockerClient.createContainerCmd("alpine:3.6")
|
||||
.withCmd("sleep", "10000")
|
||||
.exec();
|
||||
|
||||
dockerClient.startContainerCmd(container.getId())
|
||||
.exec();
|
||||
|
||||
Thread.sleep(3000);
|
||||
dockerClient.stopContainerCmd(container.getId())
|
||||
.exec();
|
||||
|
||||
//then
|
||||
dockerClient.killContainerCmd(container.getId())
|
||||
.exec();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenHavingContainer_thenInspectContainer() {
|
||||
|
||||
//when
|
||||
CreateContainerResponse container
|
||||
= dockerClient.createContainerCmd("alpine:3.6")
|
||||
.withCmd("sleep", "10000")
|
||||
.exec();
|
||||
|
||||
//then
|
||||
InspectContainerResponse containerResponse
|
||||
= dockerClient.inspectContainerCmd(container.getId())
|
||||
.exec();
|
||||
|
||||
assertThat(containerResponse.getId(), is(container.getId()));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void givenContainer_whenCommittingContainer_thenMustReturnImageId() {
|
||||
|
||||
//given
|
||||
CreateContainerResponse container
|
||||
= dockerClient.createContainerCmd("alpine:3.6")
|
||||
.withCmd("sleep", "10000")
|
||||
.exec();
|
||||
|
||||
//when
|
||||
String imageId = dockerClient.commitCmd(container.getId())
|
||||
.withEnv("SNAPSHOT_YEAR=2018")
|
||||
.withMessage("add git support")
|
||||
.withCmd("sleep", "10000")
|
||||
.withRepository("alpine")
|
||||
.withTag("3.6.v2").exec();
|
||||
|
||||
//then
|
||||
assertThat(imageId, is(not(null)));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
package com.baeldung.dockerapi;
|
||||
|
||||
import com.github.dockerjava.api.DockerClient;
|
||||
import com.github.dockerjava.core.DefaultDockerClientConfig;
|
||||
import com.github.dockerjava.core.DockerClientBuilder;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
public class DockerClientLiveTest {
|
||||
|
||||
@Test
|
||||
public void whenCreatingDockerClient_thenReturnDefaultInstance() {
|
||||
|
||||
//when
|
||||
DefaultDockerClientConfig.Builder config
|
||||
= DefaultDockerClientConfig.createDefaultConfigBuilder();
|
||||
DockerClient dockerClient = DockerClientBuilder.getInstance(config).build();
|
||||
|
||||
//then
|
||||
assertNotNull(dockerClient);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenCreatingDockerClientWithDockerHost_thenReturnInstance() {
|
||||
//when
|
||||
DockerClient dockerClient
|
||||
= DockerClientBuilder.getInstance("tcp://docker.bealdung.com:2375")
|
||||
.build();
|
||||
|
||||
//then
|
||||
assertNotNull(dockerClient);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenCreatingAdvanceDockerClient_thenReturnInstance() {
|
||||
|
||||
//when
|
||||
DefaultDockerClientConfig config
|
||||
= DefaultDockerClientConfig.createDefaultConfigBuilder()
|
||||
.withRegistryEmail("info@bealdung.com")
|
||||
.withRegistryUrl("register.bealdung.io/v2/")
|
||||
.withRegistryPassword("strongpassword")
|
||||
.withRegistryUsername("bealdung")
|
||||
.withDockerCertPath("/home/bealdung/public/.docker/certs")
|
||||
.withDockerConfig("/home/bealdung/public/.docker/")
|
||||
.withDockerTlsVerify("1")
|
||||
.withDockerHost("tcp://docker.beauldung.com:2376")
|
||||
.build();
|
||||
|
||||
DockerClient dockerClient = DockerClientBuilder.getInstance(config).build();
|
||||
|
||||
//then
|
||||
assertNotNull(dockerClient);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenCreatingDockerClientWithProperties_thenReturnInstance() {
|
||||
|
||||
//when
|
||||
Properties properties = new Properties();
|
||||
properties.setProperty("registry.email", "info@bealdung.com");
|
||||
properties.setProperty("registry.url", "register.bealdung.io/v2/");
|
||||
properties.setProperty("registry.password", "strongpassword");
|
||||
properties.setProperty("registry.username", "bealdung");
|
||||
properties.setProperty("DOCKER_CERT_PATH", "/home/bealdung/public/.docker/certs");
|
||||
properties.setProperty("DOCKER_CONFIG", "/home/bealdung/public/.docker/");
|
||||
properties.setProperty("DOCKER_TLS_VERIFY", "1");
|
||||
properties.setProperty("DOCKER_HOST", "tcp://docker.bealdung.com:2376");
|
||||
|
||||
DefaultDockerClientConfig config
|
||||
= DefaultDockerClientConfig.createDefaultConfigBuilder()
|
||||
.withProperties(properties)
|
||||
.build();
|
||||
|
||||
DockerClient dockerClient = DockerClientBuilder.getInstance(config).build();
|
||||
|
||||
//then
|
||||
assertNotNull(dockerClient);
|
||||
}
|
||||
}
|
@ -0,0 +1,155 @@
|
||||
package com.baeldung.dockerapi;
|
||||
|
||||
import com.github.dockerjava.api.DockerClient;
|
||||
import com.github.dockerjava.api.command.InspectImageResponse;
|
||||
import com.github.dockerjava.api.model.Image;
|
||||
import com.github.dockerjava.api.model.SearchItem;
|
||||
import com.github.dockerjava.core.DockerClientBuilder;
|
||||
import com.github.dockerjava.core.command.BuildImageResultCallback;
|
||||
import com.github.dockerjava.core.command.PullImageResultCallback;
|
||||
import com.github.dockerjava.core.command.PushImageResultCallback;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.greaterThan;
|
||||
import static org.hamcrest.Matchers.lessThan;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.core.Is.is;
|
||||
|
||||
public class ImageLiveTest {
|
||||
|
||||
private static DockerClient dockerClient;
|
||||
|
||||
@BeforeClass
|
||||
public static void setup() {
|
||||
dockerClient = DockerClientBuilder.getInstance().build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenListingImages_thenReturnNonEmptyList() {
|
||||
|
||||
//when
|
||||
List<Image> images = dockerClient.listImagesCmd().exec();
|
||||
|
||||
//then
|
||||
assertThat(images.size(), is(not(0)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenListingImagesWithIntermediateImages_thenReturnNonEmptyList() {
|
||||
|
||||
//when
|
||||
List<Image> images = dockerClient.listImagesCmd()
|
||||
.withShowAll(true).exec();
|
||||
|
||||
//then
|
||||
assertThat(images.size(), is(not(0)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenListingDanglingImages_thenReturnNonNullList() {
|
||||
|
||||
//when
|
||||
List<Image> images = dockerClient.listImagesCmd()
|
||||
.withDanglingFilter(true).exec();
|
||||
|
||||
//then
|
||||
assertThat(images, is(not(null)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenBuildingImage_thenMustReturnImageId() {
|
||||
|
||||
//when
|
||||
String imageId = dockerClient.buildImageCmd()
|
||||
.withDockerfile(new File("src/test/resources/dockerapi/Dockerfile"))
|
||||
.withPull(true)
|
||||
.withNoCache(true)
|
||||
.withTag("alpine:git")
|
||||
.exec(new BuildImageResultCallback())
|
||||
.awaitImageId();
|
||||
|
||||
//then
|
||||
assertThat(imageId, is(not(null)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenListOfImages_whenInspectImage_thenMustReturnObject() {
|
||||
|
||||
//given
|
||||
List<Image> images = dockerClient.listImagesCmd().exec();
|
||||
Image image = images.get(0);
|
||||
|
||||
//when
|
||||
InspectImageResponse imageResponse
|
||||
= dockerClient.inspectImageCmd(image.getId()).exec();
|
||||
|
||||
//then
|
||||
assertThat(imageResponse.getId(), is(image.getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenListOfImages_whenTagImage_thenListMustIncrement() {
|
||||
|
||||
//given
|
||||
List<Image> images = dockerClient.listImagesCmd().exec();
|
||||
Image image = images.get(0);
|
||||
|
||||
//when
|
||||
dockerClient.tagImageCmd(image.getId(), "baeldung/alpine", "3.6.v2").exec();
|
||||
|
||||
//then
|
||||
List<Image> imagesNow = dockerClient.listImagesCmd().exec();
|
||||
assertThat(imagesNow.size(), is(greaterThan(images.size())));
|
||||
}
|
||||
|
||||
public void pushingAnImage() throws InterruptedException {
|
||||
|
||||
dockerClient.pushImageCmd("baeldung/alpine")
|
||||
.withTag("3.6.v2")
|
||||
.exec(new PushImageResultCallback())
|
||||
.awaitCompletion(90, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenPullingImage_thenImageListNotEmpty() throws InterruptedException {
|
||||
|
||||
//when
|
||||
dockerClient.pullImageCmd("alpine")
|
||||
.withTag("latest")
|
||||
.exec(new PullImageResultCallback())
|
||||
.awaitCompletion(30, TimeUnit.SECONDS);
|
||||
|
||||
//then
|
||||
List<Image> images = dockerClient.listImagesCmd().exec();
|
||||
assertThat(images.size(), is(not(0)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenRemovingImage_thenImageListDecrease() {
|
||||
|
||||
//when
|
||||
List<Image> images = dockerClient.listImagesCmd().exec();
|
||||
Image image = images.get(0);
|
||||
dockerClient.removeImageCmd(image.getId()).exec();
|
||||
|
||||
//then
|
||||
List<Image> imagesNow = dockerClient.listImagesCmd().exec();
|
||||
assertThat(imagesNow.size(), is(lessThan(images.size())));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenSearchingImage_thenMustReturn25Items() {
|
||||
|
||||
//when
|
||||
List<SearchItem> items = dockerClient.searchImagesCmd("Java").exec();
|
||||
|
||||
//then
|
||||
assertThat(items.size(), is(25));
|
||||
}
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
package com.baeldung.dockerapi;
|
||||
|
||||
import com.github.dockerjava.api.DockerClient;
|
||||
import com.github.dockerjava.api.command.CreateNetworkResponse;
|
||||
import com.github.dockerjava.api.model.Network;
|
||||
import com.github.dockerjava.api.model.Network.Ipam;
|
||||
import com.github.dockerjava.core.DockerClientBuilder;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.greaterThan;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.core.Is.is;
|
||||
|
||||
public class NetworkLiveTest {
|
||||
|
||||
private static DockerClient dockerClient;
|
||||
|
||||
@BeforeClass
|
||||
public static void setup() {
|
||||
dockerClient = DockerClientBuilder.getInstance().build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenListingNetworks_thenSizeMustBeGreaterThanZero() {
|
||||
|
||||
//when
|
||||
List<Network> networks = dockerClient.listNetworksCmd().exec();
|
||||
|
||||
//then
|
||||
assertThat(networks.size(), is(greaterThan(0)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenCreatingNetwork_thenRetrieveResponse() {
|
||||
|
||||
//when
|
||||
CreateNetworkResponse networkResponse
|
||||
= dockerClient.createNetworkCmd()
|
||||
.withName("baeldungDefault")
|
||||
.withDriver("bridge").exec();
|
||||
|
||||
//then
|
||||
assertThat(networkResponse, is(not(null)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenCreatingAdvanceNetwork_thenRetrieveResponse() {
|
||||
|
||||
//when
|
||||
CreateNetworkResponse networkResponse = dockerClient.createNetworkCmd()
|
||||
.withName("baeldungAdvanced")
|
||||
.withIpam(new Ipam()
|
||||
.withConfig(new Ipam.Config()
|
||||
.withSubnet("172.36.0.0/16")
|
||||
.withIpRange("172.36.5.0/24")))
|
||||
.withDriver("bridge").exec();
|
||||
|
||||
//then
|
||||
assertThat(networkResponse, is(not(null)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenInspectingNetwork_thenSizeMustBeGreaterThanZero() {
|
||||
|
||||
//when
|
||||
String networkName = "bridge";
|
||||
Network network
|
||||
= dockerClient.inspectNetworkCmd().withNetworkId(networkName).exec();
|
||||
|
||||
//then
|
||||
assertThat(network.getName(), is(networkName));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenCreatingNetwork_thenRemove() throws InterruptedException {
|
||||
|
||||
//when
|
||||
CreateNetworkResponse networkResponse
|
||||
= dockerClient.createNetworkCmd()
|
||||
.withName("baeldungDefault")
|
||||
.withDriver("bridge").exec();
|
||||
|
||||
//then
|
||||
Thread.sleep(4000);
|
||||
dockerClient.removeNetworkCmd(networkResponse.getId()).exec();
|
||||
}
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
package com.baeldung.dockerapi;
|
||||
|
||||
import com.github.dockerjava.api.DockerClient;
|
||||
import com.github.dockerjava.api.command.CreateVolumeResponse;
|
||||
import com.github.dockerjava.api.command.InspectVolumeResponse;
|
||||
import com.github.dockerjava.api.command.ListVolumesResponse;
|
||||
import com.github.dockerjava.core.DockerClientBuilder;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.greaterThan;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.core.Is.is;
|
||||
|
||||
public class VolumeLiveTest {
|
||||
|
||||
private static DockerClient dockerClient;
|
||||
|
||||
@BeforeClass
|
||||
public static void setup() {
|
||||
dockerClient = DockerClientBuilder.getInstance().build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenListingVolumes_thenSizeMustBeGreaterThanZero() {
|
||||
|
||||
//when
|
||||
ListVolumesResponse volumesResponse = dockerClient.listVolumesCmd().exec();
|
||||
|
||||
//then
|
||||
List<InspectVolumeResponse> volumes = volumesResponse.getVolumes();
|
||||
assertThat(volumes.size(), is(greaterThan(0)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenVolumes_whenInspectingVolume_thenReturnNonNullResponse() {
|
||||
|
||||
//given
|
||||
ListVolumesResponse volumesResponse = dockerClient.listVolumesCmd().exec();
|
||||
List<InspectVolumeResponse> volumes = volumesResponse.getVolumes();
|
||||
InspectVolumeResponse volume = volumes.get(0);
|
||||
|
||||
//when
|
||||
InspectVolumeResponse volumeResponse
|
||||
= dockerClient.inspectVolumeCmd(volume.getName()).exec();
|
||||
|
||||
//then
|
||||
assertThat(volumeResponse, is(not(null)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenCreatingUnnamedVolume_thenGetVolumeId() {
|
||||
|
||||
//when
|
||||
CreateVolumeResponse unnamedVolume = dockerClient.createVolumeCmd().exec();
|
||||
|
||||
//then
|
||||
assertThat(unnamedVolume.getName(), is(not(null)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenCreatingNamedVolume_thenGetVolumeId() {
|
||||
|
||||
//when
|
||||
CreateVolumeResponse namedVolume
|
||||
= dockerClient.createVolumeCmd().withName("myNamedVolume").exec();
|
||||
|
||||
//then
|
||||
assertThat(namedVolume.getName(), is(not(null)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenGettingNamedVolume_thenRemove() throws InterruptedException {
|
||||
|
||||
//when
|
||||
CreateVolumeResponse namedVolume
|
||||
= dockerClient.createVolumeCmd().withName("anotherNamedVolume").exec();
|
||||
|
||||
//then
|
||||
Thread.sleep(4000);
|
||||
dockerClient.removeVolumeCmd(namedVolume.getName()).exec();
|
||||
}
|
||||
}
|
8
libraries/src/test/resources/dockerapi/Dockerfile
Normal file
8
libraries/src/test/resources/dockerapi/Dockerfile
Normal file
@ -0,0 +1,8 @@
|
||||
FROM alpine:3.6
|
||||
|
||||
RUN apk --update add git openssh && \
|
||||
rm -rf /var/lib/apt/lists/* && \
|
||||
rm /var/cache/apk/*
|
||||
|
||||
ENTRYPOINT ["git"]
|
||||
CMD ["--help"]
|
Loading…
x
Reference in New Issue
Block a user