From a58de048efe6c781ffa8b2c2f7c1a127cf06ae25 Mon Sep 17 00:00:00 2001 From: Leonid Rudenko Date: Wed, 6 Jul 2016 13:42:54 +0300 Subject: [PATCH] Implement POST /images/load endpoint --- .../github/dockerjava/api/DockerClient.java | 13 +++ .../api/command/DockerCmdExecFactory.java | 2 + .../dockerjava/api/command/LoadImageCmd.java | 21 +++++ .../dockerjava/core/DockerClientImpl.java | 7 ++ .../dockerjava/core/RemoteApiVersion.java | 5 ++ .../core/command/LoadImageCmdImpl.java | 39 +++++++++ .../jaxrs/DockerCmdExecFactoryImpl.java | 6 ++ .../dockerjava/jaxrs/LoadImageCmdExec.java | 31 +++++++ .../netty/DockerCmdExecFactoryImpl.java | 7 ++ .../netty/exec/LoadImageCmdExec.java | 29 +++++++ .../core/TestDockerCmdExecFactory.java | 12 +++ .../core/command/LoadImageCmdImplTest.java | 76 ++++++++++++++++++ .../netty/exec/LoadImageCmdExecTest.java | 76 ++++++++++++++++++ .../dockerjava/utils/TestResources.java | 14 ++++ src/test/resources/api/images/load/image.tar | Bin 0 -> 11264 bytes 15 files changed, 338 insertions(+) create mode 100644 src/main/java/com/github/dockerjava/api/command/LoadImageCmd.java create mode 100644 src/main/java/com/github/dockerjava/core/command/LoadImageCmdImpl.java create mode 100644 src/main/java/com/github/dockerjava/jaxrs/LoadImageCmdExec.java create mode 100644 src/main/java/com/github/dockerjava/netty/exec/LoadImageCmdExec.java create mode 100644 src/test/java/com/github/dockerjava/core/command/LoadImageCmdImplTest.java create mode 100644 src/test/java/com/github/dockerjava/netty/exec/LoadImageCmdExecTest.java create mode 100644 src/test/java/com/github/dockerjava/utils/TestResources.java create mode 100644 src/test/resources/api/images/load/image.tar diff --git a/src/main/java/com/github/dockerjava/api/DockerClient.java b/src/main/java/com/github/dockerjava/api/DockerClient.java index 4c0e2ed56..491c11ac8 100644 --- a/src/main/java/com/github/dockerjava/api/DockerClient.java +++ b/src/main/java/com/github/dockerjava/api/DockerClient.java @@ -35,6 +35,7 @@ import com.github.dockerjava.api.command.ListImagesCmd; import com.github.dockerjava.api.command.ListNetworksCmd; import com.github.dockerjava.api.command.ListVolumesCmd; +import com.github.dockerjava.api.command.LoadImageCmd; import com.github.dockerjava.api.command.LogContainerCmd; import com.github.dockerjava.api.command.PauseContainerCmd; import com.github.dockerjava.api.command.PingCmd; @@ -90,6 +91,18 @@ public interface DockerClient extends Closeable { CreateImageCmd createImageCmd(@Nonnull String repository, @Nonnull InputStream imageStream); + /** + * Loads a tarball with a set of images and tags into a Docker repository. + * + * Corresponds to POST /images/load API endpoint. + * + * @param imageStream + * stream of the tarball file + * @return created command + * @since {@link RemoteApiVersion#VERSION_1_7} + */ + LoadImageCmd loadImageCmd(@Nonnull InputStream imageStream); + SearchImagesCmd searchImagesCmd(@Nonnull String term); RemoveImageCmd removeImageCmd(@Nonnull String imageId); diff --git a/src/main/java/com/github/dockerjava/api/command/DockerCmdExecFactory.java b/src/main/java/com/github/dockerjava/api/command/DockerCmdExecFactory.java index 0de3afe38..671c0d535 100644 --- a/src/main/java/com/github/dockerjava/api/command/DockerCmdExecFactory.java +++ b/src/main/java/com/github/dockerjava/api/command/DockerCmdExecFactory.java @@ -28,6 +28,8 @@ public interface DockerCmdExecFactory extends Closeable { CreateImageCmd.Exec createCreateImageCmdExec(); + LoadImageCmd.Exec createLoadImageCmdExec(); + SearchImagesCmd.Exec createSearchImagesCmdExec(); RemoveImageCmd.Exec createRemoveImageCmdExec(); diff --git a/src/main/java/com/github/dockerjava/api/command/LoadImageCmd.java b/src/main/java/com/github/dockerjava/api/command/LoadImageCmd.java new file mode 100644 index 000000000..184b7ef33 --- /dev/null +++ b/src/main/java/com/github/dockerjava/api/command/LoadImageCmd.java @@ -0,0 +1,21 @@ +package com.github.dockerjava.api.command; + +import java.io.InputStream; + +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; + +public interface LoadImageCmd extends SyncDockerCmd { + + @CheckForNull + InputStream getImageStream(); + + /** + * @param imageStream + * the InputStream of the tar file + */ + LoadImageCmd withImageStream(@Nonnull InputStream imageStream); + + interface Exec extends DockerCmdSyncExec { + } +} diff --git a/src/main/java/com/github/dockerjava/core/DockerClientImpl.java b/src/main/java/com/github/dockerjava/core/DockerClientImpl.java index e11ca4f30..2d0b5caab 100644 --- a/src/main/java/com/github/dockerjava/core/DockerClientImpl.java +++ b/src/main/java/com/github/dockerjava/core/DockerClientImpl.java @@ -37,6 +37,7 @@ import com.github.dockerjava.api.command.ListImagesCmd; import com.github.dockerjava.api.command.ListNetworksCmd; import com.github.dockerjava.api.command.ListVolumesCmd; +import com.github.dockerjava.api.command.LoadImageCmd; import com.github.dockerjava.api.command.LogContainerCmd; import com.github.dockerjava.api.command.PauseContainerCmd; import com.github.dockerjava.api.command.PingCmd; @@ -89,6 +90,7 @@ import com.github.dockerjava.core.command.ListImagesCmdImpl; import com.github.dockerjava.core.command.ListNetworksCmdImpl; import com.github.dockerjava.core.command.ListVolumesCmdImpl; +import com.github.dockerjava.core.command.LoadImageCmdImpl; import com.github.dockerjava.core.command.LogContainerCmdImpl; import com.github.dockerjava.core.command.PauseContainerCmdImpl; import com.github.dockerjava.core.command.PingCmdImpl; @@ -249,6 +251,11 @@ public CreateImageCmd createImageCmd(String repository, InputStream imageStream) return new CreateImageCmdImpl(getDockerCmdExecFactory().createCreateImageCmdExec(), repository, imageStream); } + @Override + public LoadImageCmd loadImageCmd(@Nonnull InputStream imageStream) { + return new LoadImageCmdImpl(getDockerCmdExecFactory().createLoadImageCmdExec(), imageStream); + } + @Override public SearchImagesCmd searchImagesCmd(String term) { return new SearchImagesCmdImpl(getDockerCmdExecFactory().createSearchImagesCmdExec(), term); diff --git a/src/main/java/com/github/dockerjava/core/RemoteApiVersion.java b/src/main/java/com/github/dockerjava/core/RemoteApiVersion.java index 992125414..954ed9971 100644 --- a/src/main/java/com/github/dockerjava/core/RemoteApiVersion.java +++ b/src/main/java/com/github/dockerjava/core/RemoteApiVersion.java @@ -22,6 +22,11 @@ public class RemoteApiVersion implements Serializable { private static final Pattern VERSION_REGEX = Pattern.compile("v?(\\d+)\\.(\\d+)"); + /** + * Online documentation is not available anymore. + */ + public static final RemoteApiVersion VERSION_1_7 = RemoteApiVersion.create(1, 7); + /** * @see Docker API 1.16 */ diff --git a/src/main/java/com/github/dockerjava/core/command/LoadImageCmdImpl.java b/src/main/java/com/github/dockerjava/core/command/LoadImageCmdImpl.java new file mode 100644 index 000000000..496ea3176 --- /dev/null +++ b/src/main/java/com/github/dockerjava/core/command/LoadImageCmdImpl.java @@ -0,0 +1,39 @@ +package com.github.dockerjava.core.command; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.InputStream; + +import com.github.dockerjava.api.command.LoadImageCmd; + +import javax.annotation.Nonnull; + +public class LoadImageCmdImpl extends AbstrDockerCmd implements LoadImageCmd { + + private InputStream imageStream; + + /** + * @param imageStream + * the InputStream of the tar file + */ + public LoadImageCmdImpl(LoadImageCmd.Exec exec, InputStream imageStream) { + super(exec); + withImageStream(imageStream); + } + + @Override + public InputStream getImageStream() { + return imageStream; + } + + /** + * @param imageStream + * the InputStream of the tar file + */ + @Override + public LoadImageCmdImpl withImageStream(@Nonnull InputStream imageStream) { + checkNotNull(imageStream, "imageStream was not specified"); + this.imageStream = imageStream; + return this; + } +} diff --git a/src/main/java/com/github/dockerjava/jaxrs/DockerCmdExecFactoryImpl.java b/src/main/java/com/github/dockerjava/jaxrs/DockerCmdExecFactoryImpl.java index 4800f22ae..7e573a123 100644 --- a/src/main/java/com/github/dockerjava/jaxrs/DockerCmdExecFactoryImpl.java +++ b/src/main/java/com/github/dockerjava/jaxrs/DockerCmdExecFactoryImpl.java @@ -63,6 +63,7 @@ import com.github.dockerjava.api.command.ListImagesCmd; import com.github.dockerjava.api.command.ListNetworksCmd; import com.github.dockerjava.api.command.ListVolumesCmd; +import com.github.dockerjava.api.command.LoadImageCmd; import com.github.dockerjava.api.command.LogContainerCmd; import com.github.dockerjava.api.command.PauseContainerCmd; import com.github.dockerjava.api.command.PingCmd; @@ -322,6 +323,11 @@ public CreateImageCmd.Exec createCreateImageCmdExec() { return new CreateImageCmdExec(getBaseResource(), getDockerClientConfig()); } + @Override + public LoadImageCmd.Exec createLoadImageCmdExec() { + return new LoadImageCmdExec(getBaseResource(), getDockerClientConfig()); + } + @Override public SearchImagesCmd.Exec createSearchImagesCmdExec() { return new SearchImagesCmdExec(getBaseResource(), getDockerClientConfig()); diff --git a/src/main/java/com/github/dockerjava/jaxrs/LoadImageCmdExec.java b/src/main/java/com/github/dockerjava/jaxrs/LoadImageCmdExec.java new file mode 100644 index 000000000..23d6c493a --- /dev/null +++ b/src/main/java/com/github/dockerjava/jaxrs/LoadImageCmdExec.java @@ -0,0 +1,31 @@ +package com.github.dockerjava.jaxrs; + +import static javax.ws.rs.client.Entity.entity; + +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.LoadImageCmd; +import com.github.dockerjava.core.DockerClientConfig; + +public class LoadImageCmdExec extends AbstrSyncDockerCmdExec implements LoadImageCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(LoadImageCmdExec.class); + + public LoadImageCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { + super(baseResource, dockerClientConfig); + } + + @Override + protected Void execute(LoadImageCmd command) { + WebTarget webTarget = getBaseResource().path("/images/load"); + + LOGGER.trace("POST: {}", webTarget); + webTarget.request().post(entity(command.getImageStream(), MediaType.APPLICATION_OCTET_STREAM)); + + return null; + } +} diff --git a/src/main/java/com/github/dockerjava/netty/DockerCmdExecFactoryImpl.java b/src/main/java/com/github/dockerjava/netty/DockerCmdExecFactoryImpl.java index 1f55c61a8..dff69a228 100644 --- a/src/main/java/com/github/dockerjava/netty/DockerCmdExecFactoryImpl.java +++ b/src/main/java/com/github/dockerjava/netty/DockerCmdExecFactoryImpl.java @@ -29,6 +29,7 @@ import com.github.dockerjava.api.command.ListImagesCmd; import com.github.dockerjava.api.command.ListNetworksCmd; import com.github.dockerjava.api.command.ListVolumesCmd; +import com.github.dockerjava.api.command.LoadImageCmd; import com.github.dockerjava.api.command.LogContainerCmd; import com.github.dockerjava.api.command.PauseContainerCmd; import com.github.dockerjava.api.command.PingCmd; @@ -82,6 +83,7 @@ import com.github.dockerjava.netty.exec.ListImagesCmdExec; import com.github.dockerjava.netty.exec.ListNetworksCmdExec; import com.github.dockerjava.netty.exec.ListVolumesCmdExec; +import com.github.dockerjava.netty.exec.LoadImageCmdExec; import com.github.dockerjava.netty.exec.LogContainerCmdExec; import com.github.dockerjava.netty.exec.PauseContainerCmdExec; import com.github.dockerjava.netty.exec.PingCmdExec; @@ -366,6 +368,11 @@ public CreateImageCmd.Exec createCreateImageCmdExec() { return new CreateImageCmdExec(getBaseResource(), getDockerClientConfig()); } + @Override + public LoadImageCmd.Exec createLoadImageCmdExec() { + return new LoadImageCmdExec(getBaseResource(), getDockerClientConfig()); + } + @Override public SearchImagesCmd.Exec createSearchImagesCmdExec() { return new SearchImagesCmdExec(getBaseResource(), getDockerClientConfig()); diff --git a/src/main/java/com/github/dockerjava/netty/exec/LoadImageCmdExec.java b/src/main/java/com/github/dockerjava/netty/exec/LoadImageCmdExec.java new file mode 100644 index 000000000..23581119f --- /dev/null +++ b/src/main/java/com/github/dockerjava/netty/exec/LoadImageCmdExec.java @@ -0,0 +1,29 @@ +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.command.LoadImageCmd; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.netty.WebTarget; + +public class LoadImageCmdExec extends AbstrSyncDockerCmdExec implements + LoadImageCmd.Exec { + + private static final Logger LOGGER = LoggerFactory.getLogger(LoadImageCmdExec.class); + + public LoadImageCmdExec(WebTarget baseResource, DockerClientConfig dockerClientConfig) { + super(baseResource, dockerClientConfig); + } + + @Override + protected Void execute(LoadImageCmd command) { + WebTarget webResource = getBaseResource().path("/images/load"); + + LOGGER.trace("POST: {}", webResource); + return webResource.request() + .post(new TypeReference() { + }, command.getImageStream()); + } +} diff --git a/src/test/java/com/github/dockerjava/core/TestDockerCmdExecFactory.java b/src/test/java/com/github/dockerjava/core/TestDockerCmdExecFactory.java index 48ea1e164..46d6d4dcc 100644 --- a/src/test/java/com/github/dockerjava/core/TestDockerCmdExecFactory.java +++ b/src/test/java/com/github/dockerjava/core/TestDockerCmdExecFactory.java @@ -34,6 +34,7 @@ import com.github.dockerjava.api.command.ListImagesCmd; import com.github.dockerjava.api.command.ListNetworksCmd; import com.github.dockerjava.api.command.ListVolumesCmd; +import com.github.dockerjava.api.command.LoadImageCmd; import com.github.dockerjava.api.command.LogContainerCmd; import com.github.dockerjava.api.command.PauseContainerCmd; import com.github.dockerjava.api.command.PingCmd; @@ -131,6 +132,17 @@ public CreateImageResponse exec(CreateImageCmd command) { }; } + @Override + public LoadImageCmd.Exec createLoadImageCmdExec() { + return new LoadImageCmd.Exec() { + @Override + public Void exec(LoadImageCmd command) { + delegate.createLoadImageCmdExec().exec(command); + return null; + } + }; + } + @Override public RemoveImageCmd.Exec createRemoveImageCmdExec() { return new RemoveImageCmd.Exec() { diff --git a/src/test/java/com/github/dockerjava/core/command/LoadImageCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/LoadImageCmdImplTest.java new file mode 100644 index 000000000..7cecf7334 --- /dev/null +++ b/src/test/java/com/github/dockerjava/core/command/LoadImageCmdImplTest.java @@ -0,0 +1,76 @@ +package com.github.dockerjava.core.command; + +import com.github.dockerjava.api.model.Image; +import com.github.dockerjava.client.AbstractDockerClientTest; + +import com.github.dockerjava.utils.TestResources; +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import java.io.InputStream; +import java.lang.reflect.Method; +import java.nio.file.Files; +import java.util.List; + +import static java.util.Arrays.asList; +import static java.util.Collections.singletonList; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsNull.notNullValue; +import static org.hamcrest.core.IsEqual.equalTo; + +@Test(groups = "integration") +public class LoadImageCmdImplTest extends AbstractDockerClientTest { + + private String expectedImageId; + + @BeforeTest + public void beforeTest() throws Exception { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + expectedImageId = "sha256:56031f66eb0cef2e2e5cb2d1dabafaa0ebcd0a18a507d313b5bdb8c0472c5eba"; + if (findImageWithId(expectedImageId, dockerClient.listImagesCmd().exec()) != null) { + dockerClient.removeImageCmd(expectedImageId).exec(); + } + } + + @AfterMethod + public void afterMethod(ITestResult result) { + dockerClient.removeImageCmd(expectedImageId).exec(); + super.afterMethod(result); + } + + @Test + public void loadImageFromTar() throws Exception { + try (InputStream uploadStream = Files.newInputStream(TestResources.getApiImagesLoadTestTarball())) { + dockerClient.loadImageCmd(uploadStream).exec(); + } + + final Image image = findImageWithId(expectedImageId, dockerClient.listImagesCmd().exec()); + + assertThat("Can't find expected image after loading from a tar archive!", image, notNullValue()); + assertThat("Image after loading from a tar archive has wrong tags!", + asList(image.getRepoTags()), equalTo(singletonList("docker-java/load:1.0"))); + } + + private Image findImageWithId(final String id, final List images) { + for (Image image : images) { + if (id.equals(image.getId())) { + return image; + } + } + return null; + } +} diff --git a/src/test/java/com/github/dockerjava/netty/exec/LoadImageCmdExecTest.java b/src/test/java/com/github/dockerjava/netty/exec/LoadImageCmdExecTest.java new file mode 100644 index 000000000..d978f5662 --- /dev/null +++ b/src/test/java/com/github/dockerjava/netty/exec/LoadImageCmdExecTest.java @@ -0,0 +1,76 @@ +package com.github.dockerjava.netty.exec; + +import com.github.dockerjava.api.model.Image; + +import com.github.dockerjava.netty.AbstractNettyDockerClientTest; +import com.github.dockerjava.utils.TestResources; +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import java.io.InputStream; +import java.lang.reflect.Method; +import java.nio.file.Files; +import java.util.List; + +import static java.util.Arrays.asList; +import static java.util.Collections.singletonList; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsNull.notNullValue; +import static org.hamcrest.core.IsEqual.equalTo; + +@Test(groups = "integration") +public class LoadImageCmdExecTest extends AbstractNettyDockerClientTest { + + private String expectedImageId; + + @BeforeTest + public void beforeTest() throws Exception { + super.beforeTest(); + } + + @AfterTest + public void afterTest() { + super.afterTest(); + } + + @BeforeMethod + public void beforeMethod(Method method) { + super.beforeMethod(method); + expectedImageId = "sha256:56031f66eb0cef2e2e5cb2d1dabafaa0ebcd0a18a507d313b5bdb8c0472c5eba"; + if (findImageWithId(expectedImageId, dockerClient.listImagesCmd().exec()) != null) { + dockerClient.removeImageCmd(expectedImageId).exec(); + } + } + + @AfterMethod + public void afterMethod(ITestResult result) { + dockerClient.removeImageCmd(expectedImageId).exec(); + super.afterMethod(result); + } + + @Test + public void loadImageFromTar() throws Exception { + try (InputStream uploadStream = Files.newInputStream(TestResources.getApiImagesLoadTestTarball())) { + dockerClient.loadImageCmd(uploadStream).exec(); + } + + final Image image = findImageWithId(expectedImageId, dockerClient.listImagesCmd().exec()); + + assertThat("Can't find expected image after loading from a tar archive!", image, notNullValue()); + assertThat("Image after loading from a tar archive has wrong tags!", + asList(image.getRepoTags()), equalTo(singletonList("docker-java/load:1.0"))); + } + + private Image findImageWithId(final String id, final List images) { + for (Image image : images) { + if (id.equals(image.getId())) { + return image; + } + } + return null; + } +} diff --git a/src/test/java/com/github/dockerjava/utils/TestResources.java b/src/test/java/com/github/dockerjava/utils/TestResources.java new file mode 100644 index 000000000..35ece680f --- /dev/null +++ b/src/test/java/com/github/dockerjava/utils/TestResources.java @@ -0,0 +1,14 @@ +package com.github.dockerjava.utils; + +import java.nio.file.Path; +import java.nio.file.Paths; + +public class TestResources { + + private TestResources() { + } + + public static Path getApiImagesLoadTestTarball() { + return Paths.get("src/test/resources/api/images/load/image.tar"); + } +} diff --git a/src/test/resources/api/images/load/image.tar b/src/test/resources/api/images/load/image.tar new file mode 100644 index 0000000000000000000000000000000000000000..6f2f08f9e945811249c0027eec3a0de531ead521 GIT binary patch literal 11264 zcmeHNU2oeq6!r6dg~7dSfMfHUl7KyQX@C|e=Az9ybU_eM{7BTwQXt7e1H=D)SGJQl zZc`_<5_AalV2kACC3%i6DISUw%9s-|cByxSTE>L%Owu^wR>{~?N(lB&lGIZk5$z(u zm`oMJ80$#kQE&T&F~>@X9#mJE6eGJe5nab(LwazFe zau2ILEDs4!h)gIO#)?WQW5)h%P0OOuS>Y-$_Hi0x?&FvlDJ&;mOD!~IX+#+Dm@DpL ztWzs36A|a|mLRZnOgax4g3xt$C*as;rzn}BS7qH4dg3-(0PC0K1lnGeLHh4{(GR$L z(KOney>Dz*pv3FEc58X%wD~&IRWO8=u*JQ{(FCa z{PtttXSqv+RVtq}P++db5s*Ov94w|m0d@BaiQsj&Xv3227} zi|M>Wf8TPog@2%*-tbQ;?Ef)gBJlq$EW6IrzjW;}_zMUG1Ofs9f&T*nLSoovOX*T< zoTrXDVNz;|)v5Mc!?A#|Sd&N#tSloe6{$@l1N(AngiH0X1O12NP5p-`9M=CQd3J?Ynf@b+ ziE~gB*Hoqfs>-tQAUn-ab9vrsQ45iw1~JNJ-k)R^%8pQdrm2vLW1M;d$Jf#*2WQj~ z7cm7T*~Lz!B-TVyr5#Cyj%}<(Y?x&auat%)u&@9O40>-di@NZa=R zjFPppVE8_O!1^EbpC5($LG@sA*k2u*pibTeAxPi3^YT!iE`oG9Kka?e7a9&Yv`yf^ zxjj92)5?Ymi|!Oih~(oSXFQ1hUIkp$87vf;s~=YL{?^;#pYZE%g4(ZW+W40u#Q*&P m-obLVI&Hg&(018vgS3M~;rTp-;||<}ssVw3KtSLz5%>?llNJ&H literal 0 HcmV?d00001