Rename spring-boot-cloudnativebuildpack

Rename the `spring-boot-cloudnativebuildpack` module to
`spring-boot-buildpack-platform` and update the the package
name to `org.springframework.boot.buildpack.platform`.

Closes gh-19851
This commit is contained in:
Phillip Webb
2020-01-22 10:33:31 -08:00
parent 288889685d
commit e28338d6cd
156 changed files with 301 additions and 301 deletions

View File

@@ -0,0 +1,117 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.build;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
/**
* Tests for {@link ApiVersion}.
*
* @author Phillip Webb
*/
class ApiVersionTests {
@Test
void parseWhenVersionIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> ApiVersion.parse(null))
.withMessage("Value must not be empty");
}
@Test
void parseWhenVersionIsEmptyThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> ApiVersion.parse(""))
.withMessage("Value must not be empty");
}
@Test
void parseWhenVersionDoesNotMatchPatternThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> ApiVersion.parse("bad"))
.withMessage("Malformed version number 'bad'");
}
@Test
void parseReturnsVersion() {
ApiVersion version = ApiVersion.parse("1.2");
assertThat(version.getMajor()).isEqualTo(1);
assertThat(version.getMinor()).isEqualTo(2);
}
@Test
void assertSupportsWhenSupports() {
ApiVersion.parse("1.2").assertSupports(ApiVersion.parse("1.0"));
}
@Test
void assertSupportsWhenDoesNotSupportThrowsException() {
assertThatIllegalStateException()
.isThrownBy(() -> ApiVersion.parse("1.2").assertSupports(ApiVersion.parse("1.3")))
.withMessage("Version 'v1.3' is not supported by this version ('v1.2')");
}
@Test
void supportWhenSame() {
assertThat(supports("0.0", "0.0")).isTrue();
assertThat(supports("0.1", "0.1")).isTrue();
assertThat(supports("1.0", "1.0")).isTrue();
assertThat(supports("1.1", "1.1")).isTrue();
}
@Test
void supportsWhenDifferentMajor() {
assertThat(supports("0.0", "1.0")).isFalse();
assertThat(supports("1.0", "0.0")).isFalse();
assertThat(supports("1.0", "2.0")).isFalse();
assertThat(supports("2.0", "1.0")).isFalse();
assertThat(supports("1.1", "2.1")).isFalse();
assertThat(supports("2.1", "1.1")).isFalse();
}
@Test
void supportsWhenDifferentMinor() {
assertThat(supports("1.2", "1.1")).isTrue();
assertThat(supports("1.2", "1.3")).isFalse();
}
@Test
void supportWhenMajorZeroAndDifferentMinor() {
assertThat(supports("0.2", "0.1")).isFalse();
assertThat(supports("0.2", "0.3")).isFalse();
}
@Test
void toStringReturnsString() {
assertThat(ApiVersion.parse("1.2").toString()).isEqualTo("v1.2");
}
@Test
void equalsAndHashCode() {
ApiVersion v12a = ApiVersion.parse("1.2");
ApiVersion v12b = ApiVersion.parse("1.2");
ApiVersion v13 = ApiVersion.parse("1.3");
assertThat(v12a.hashCode()).isEqualTo(v12b.hashCode());
assertThat(v12a).isEqualTo(v12a).isEqualTo(v12b).isNotEqualTo(v13);
}
private boolean supports(String v1, String v2) {
return ApiVersion.parse(v1).supports(ApiVersion.parse(v2));
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.build;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link BuildLog}.
*
* @author Phillip Webb
*/
class BuildLogTests {
@Test
void toSystemOutPrintsToSystemOut() {
BuildLog log = BuildLog.toSystemOut();
assertThat(log).isInstanceOf(PrintStreamBuildLog.class);
assertThat(log).extracting("out").isSameAs(System.out);
}
@Test
void toPrintsToOutput() {
BuildLog log = BuildLog.to(System.err);
assertThat(log).isInstanceOf(PrintStreamBuildLog.class);
assertThat(log).extracting("out").isSameAs(System.err);
}
}

View File

@@ -0,0 +1,86 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.build;
import java.util.LinkedHashMap;
import java.util.Map;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
/**
* Tests for {@link BuildOwner}.
*
* @author Phillip Webb
*/
class BuildOwnerTests {
@Test
void fromEnvReturnsOwner() {
Map<String, String> env = new LinkedHashMap<>();
env.put("CNB_USER_ID", "123");
env.put("CNB_GROUP_ID", "456");
BuildOwner owner = BuildOwner.fromEnv(env);
assertThat(owner.getUid()).isEqualTo(123);
assertThat(owner.getGid()).isEqualTo(456);
assertThat(owner.toString()).isEqualTo("123/456");
}
@Test
void fromEnvWhenEnvIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> BuildOwner.fromEnv(null))
.withMessage("Env must not be null");
}
@Test
void fromEnvWhenUserPropertyIsMissingThrowsException() {
Map<String, String> env = new LinkedHashMap<>();
env.put("CNB_GROUP_ID", "456");
assertThatIllegalStateException().isThrownBy(() -> BuildOwner.fromEnv(env))
.withMessage("Missing 'CNB_USER_ID' value from the builder environment");
}
@Test
void fromEnvWhenGroupPropertyIsMissingThrowsException() {
Map<String, String> env = new LinkedHashMap<>();
env.put("CNB_USER_ID", "123");
assertThatIllegalStateException().isThrownBy(() -> BuildOwner.fromEnv(env))
.withMessage("Missing 'CNB_GROUP_ID' value from the builder environment");
}
@Test
void fromEnvWhenUserPropertyIsMalformedThrowsException() {
Map<String, String> env = new LinkedHashMap<>();
env.put("CNB_USER_ID", "nope");
env.put("CNB_GROUP_ID", "456");
assertThatIllegalStateException().isThrownBy(() -> BuildOwner.fromEnv(env))
.withMessage("Malformed 'CNB_USER_ID' value 'nope' in the builder environment");
}
@Test
void fromEnvWhenGroupPropertyIsMalformedThrowsException() {
Map<String, String> env = new LinkedHashMap<>();
env.put("CNB_USER_ID", "123");
env.put("CNB_GROUP_ID", "nope");
assertThatIllegalStateException().isThrownBy(() -> BuildOwner.fromEnv(env))
.withMessage("Malformed 'CNB_GROUP_ID' value 'nope' in the builder environment");
}
}

View File

@@ -0,0 +1,167 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.build;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
import org.springframework.boot.buildpack.platform.io.Owner;
import org.springframework.boot.buildpack.platform.io.TarArchive;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.entry;
/**
* Tests for {@link BuildRequest}.
*
* @author Phillip Webb
*/
public class BuildRequestTests {
@TempDir
File tempDir;
@Test
void forJarFileReturnsRequest() throws IOException {
File jarFile = new File(this.tempDir, "my-app-0.0.1.jar");
writeTestJarFile(jarFile);
BuildRequest request = BuildRequest.forJarFile(jarFile);
assertThat(request.getName().toString()).isEqualTo("docker.io/library/my-app:0.0.1");
assertThat(request.getBuilder().toString()).isEqualTo("docker.io/cloudfoundry/cnb:0.0.43-bionic");
assertThat(request.getApplicationContent(Owner.ROOT)).satisfies(this::hasExpectedJarContent);
assertThat(request.getEnv()).isEmpty();
}
@Test
void forJarFileWithNameReturnsRequest() throws IOException {
File jarFile = new File(this.tempDir, "my-app-0.0.1.jar");
writeTestJarFile(jarFile);
BuildRequest request = BuildRequest.forJarFile(ImageReference.of("test-app"), jarFile);
assertThat(request.getName().toString()).isEqualTo("docker.io/library/test-app:latest");
assertThat(request.getBuilder().toString()).isEqualTo("docker.io/cloudfoundry/cnb:0.0.43-bionic");
assertThat(request.getApplicationContent(Owner.ROOT)).satisfies(this::hasExpectedJarContent);
assertThat(request.getEnv()).isEmpty();
}
@Test
void forJarFileWhenJarFileIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> BuildRequest.forJarFile(null))
.withMessage("JarFile must not be null");
}
@Test
void forJarFileWhenJarFileIsMissingThrowsException() {
assertThatIllegalArgumentException()
.isThrownBy(() -> BuildRequest.forJarFile(new File(this.tempDir, "missing.jar")))
.withMessage("JarFile must exist");
}
@Test
void forJarFileWhenJarFileIsFolderThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> BuildRequest.forJarFile(this.tempDir))
.withMessage("JarFile must be a file");
}
@Test
void withBuilderUpdatesBuilder() throws IOException {
BuildRequest request = BuildRequest.forJarFile(writeTestJarFile("my-app-0.0.1.jar"))
.withBuilder(ImageReference.of("spring/builder"));
assertThat(request.getBuilder().toString()).isEqualTo("docker.io/spring/builder:latest");
}
@Test
void withEnvAddsEnvEntry() throws IOException {
BuildRequest request = BuildRequest.forJarFile(writeTestJarFile("my-app-0.0.1.jar"));
BuildRequest withEnv = request.withEnv("spring", "boot");
assertThat(request.getEnv()).isEmpty();
assertThat(withEnv.getEnv()).containsExactly(entry("spring", "boot"));
}
@Test
void withEnvMapAddsEnvEntries() throws IOException {
BuildRequest request = BuildRequest.forJarFile(writeTestJarFile("my-app-0.0.1.jar"));
Map<String, String> env = new LinkedHashMap<>();
env.put("spring", "boot");
env.put("test", "test");
BuildRequest withEnv = request.withEnv(env);
assertThat(request.getEnv()).isEmpty();
assertThat(withEnv.getEnv()).containsExactly(entry("spring", "boot"), entry("test", "test"));
}
@Test
void withEnvWhenKeyIsNullThrowsException() throws IOException {
BuildRequest request = BuildRequest.forJarFile(writeTestJarFile("my-app-0.0.1.jar"));
assertThatIllegalArgumentException().isThrownBy(() -> request.withEnv(null, "test"))
.withMessage("Name must not be empty");
}
@Test
void withEnvWhenValueIsNullThrowsException() throws IOException {
BuildRequest request = BuildRequest.forJarFile(writeTestJarFile("my-app-0.0.1.jar"));
assertThatIllegalArgumentException().isThrownBy(() -> request.withEnv("test", null))
.withMessage("Value must not be empty");
}
private void hasExpectedJarContent(TarArchive archive) {
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
archive.writeTo(outputStream);
try (TarArchiveInputStream tar = new TarArchiveInputStream(
new ByteArrayInputStream(outputStream.toByteArray()))) {
assertThat(tar.getNextEntry().getName()).isEqualTo("spring/");
assertThat(tar.getNextEntry().getName()).isEqualTo("spring/boot");
assertThat(tar.getNextEntry()).isNull();
}
}
catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
private File writeTestJarFile(String name) throws IOException {
File file = new File(this.tempDir, name);
writeTestJarFile(file);
return file;
}
private void writeTestJarFile(File file) throws IOException {
try (ZipArchiveOutputStream zip = new ZipArchiveOutputStream(file)) {
ZipArchiveEntry dirEntry = new ZipArchiveEntry("spring/");
zip.putArchiveEntry(dirEntry);
zip.closeArchiveEntry();
ZipArchiveEntry fileEntry = new ZipArchiveEntry("spring/boot");
zip.putArchiveEntry(fileEntry);
zip.write("test".getBytes(StandardCharsets.UTF_8));
zip.closeArchiveEntry();
}
}
}

View File

@@ -0,0 +1,97 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.build;
import java.io.IOException;
import org.junit.jupiter.api.Test;
import org.springframework.boot.buildpack.platform.docker.type.Image;
import org.springframework.boot.buildpack.platform.docker.type.ImageConfig;
import org.springframework.boot.buildpack.platform.json.AbstractJsonTests;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link BuilderMetadata}.
*
* @author Phillip Webb
*/
class BuilderMetadataTests extends AbstractJsonTests {
@Test
void fromImageLoadsMetadata() throws IOException {
Image image = Image.of(getContent("image.json"));
BuilderMetadata metadata = BuilderMetadata.fromImage(image);
assertThat(metadata.getStack().getRunImage().getImage()).isEqualTo("cloudfoundry/run:full-cnb");
assertThat(metadata.getStack().getRunImage().getMirrors()).isEmpty();
assertThat(metadata.getLifecycle().getVersion()).isEqualTo("0.5.0");
assertThat(metadata.getLifecycle().getApi().getBuildpack()).isEqualTo("0.2");
assertThat(metadata.getLifecycle().getApi().getPlatform()).isEqualTo("0.1");
assertThat(metadata.getCreatedBy().getName()).isEqualTo("Pack CLI");
assertThat(metadata.getCreatedBy().getVersion())
.isEqualTo("v0.5.0 (git sha: c9cfac75b49609524e1ea33f809c12071406547c)");
}
@Test
void fromImageWhenImageIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> BuilderMetadata.fromImage(null))
.withMessage("Image must not be null");
}
@Test
void fromImageWhenImageConfigIsNullThrowsException() {
Image image = mock(Image.class);
assertThatIllegalArgumentException().isThrownBy(() -> BuilderMetadata.fromImage(image))
.withMessage("ImageConfig must not be null");
}
@Test
void fromImageConfigWhenLabelIsMissingThrowsException() {
Image image = mock(Image.class);
ImageConfig imageConfig = mock(ImageConfig.class);
given(image.getConfig()).willReturn(imageConfig);
assertThatIllegalArgumentException().isThrownBy(() -> BuilderMetadata.fromImage(image))
.withMessage("No 'io.buildpacks.builder.metadata' label found in image config");
}
@Test
void copyWithUpdatedCreatedByReturnsNewMetadata() throws IOException {
Image image = Image.of(getContent("image.json"));
BuilderMetadata metadata = BuilderMetadata.fromImage(image);
BuilderMetadata copy = metadata.copy((update) -> update.withCreatedBy("test123", "test456"));
assertThat(copy).isNotSameAs(metadata);
assertThat(copy.getCreatedBy().getName()).isEqualTo("test123");
assertThat(copy.getCreatedBy().getVersion()).isEqualTo("test456");
}
@Test
void attachToUpdatesMetadata() throws IOException {
Image image = Image.of(getContent("image.json"));
ImageConfig imageConfig = image.getConfig();
BuilderMetadata metadata = BuilderMetadata.fromImage(image);
ImageConfig imageConfigCopy = imageConfig.copy(metadata::attachTo);
String label = imageConfigCopy.getLabels().get("io.buildpacks.builder.metadata");
BuilderMetadata metadataCopy = BuilderMetadata.fromJson(label);
assertThat(metadataCopy.getStack().getRunImage().getImage())
.isEqualTo(metadata.getStack().getRunImage().getImage());
}
}

View File

@@ -0,0 +1,151 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.build;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.stubbing.Answer;
import org.springframework.boot.buildpack.platform.docker.DockerApi;
import org.springframework.boot.buildpack.platform.docker.DockerApi.ContainerApi;
import org.springframework.boot.buildpack.platform.docker.DockerApi.ImageApi;
import org.springframework.boot.buildpack.platform.docker.DockerApi.VolumeApi;
import org.springframework.boot.buildpack.platform.docker.TotalProgressPullListener;
import org.springframework.boot.buildpack.platform.docker.type.Image;
import org.springframework.boot.buildpack.platform.docker.type.ImageArchive;
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
import org.springframework.boot.buildpack.platform.io.TarArchive;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
/**
* Tests for {@link Builder}.
*
* @author Phillip Webb
*/
class BuilderTests {
@Test
void createWhenLogIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> new Builder(null)).withMessage("Log must not be null");
}
@Test
void buildWhenRequestIsNullThrowsException() {
Builder builder = new Builder();
assertThatIllegalArgumentException().isThrownBy(() -> builder.build(null))
.withMessage("Request must not be null");
}
@Test
void buildInvokesBuildpack() throws Exception {
TestPrintStream out = new TestPrintStream();
DockerApi docker = mockDockerApi();
Image builderImage = loadImage("image.json");
Image runImage = loadImage("run-image.json");
given(docker.image().pull(eq(ImageReference.of("docker.io/cloudfoundry/cnb:0.0.43-bionic")), any()))
.willAnswer(withPulledImage(builderImage));
given(docker.image().pull(eq(ImageReference.of("docker.io/cloudfoundry/run:full-cnb")), any()))
.willAnswer(withPulledImage(runImage));
Builder builder = new Builder(BuildLog.to(out), docker);
BuildRequest request = getTestRequest();
builder.build(request);
assertThat(out.toString()).contains("Running detector");
assertThat(out.toString()).contains("Running restorer");
assertThat(out.toString()).contains("Running analyzer");
assertThat(out.toString()).contains("Running builder");
assertThat(out.toString()).contains("Running exporter");
assertThat(out.toString()).contains("Running cacher");
assertThat(out.toString()).contains("Successfully built image 'docker.io/library/my-application:latest'");
ArgumentCaptor<ImageArchive> archive = ArgumentCaptor.forClass(ImageArchive.class);
verify(docker.image()).load(archive.capture(), any());
verify(docker.image()).remove(archive.getValue().getTag(), true);
}
@Test
void buildWhenStackIdDoesNotMatchThrowsException() throws Exception {
TestPrintStream out = new TestPrintStream();
DockerApi docker = mockDockerApi();
Image builderImage = loadImage("image.json");
Image runImage = loadImage("run-image-with-bad-stack.json");
given(docker.image().pull(eq(ImageReference.of("docker.io/cloudfoundry/cnb:0.0.43-bionic")), any()))
.willAnswer(withPulledImage(builderImage));
given(docker.image().pull(eq(ImageReference.of("docker.io/cloudfoundry/run:full-cnb")), any()))
.willAnswer(withPulledImage(runImage));
Builder builder = new Builder(BuildLog.to(out), docker);
BuildRequest request = getTestRequest();
assertThatIllegalStateException().isThrownBy(() -> builder.build(request)).withMessage(
"Run image stack 'org.cloudfoundry.stacks.cfwindowsfs3' does not match builder stack 'org.cloudfoundry.stacks.cflinuxfs3'");
}
private DockerApi mockDockerApi() {
DockerApi docker = mock(DockerApi.class);
ImageApi imageApi = mock(ImageApi.class);
ContainerApi containerApi = mock(ContainerApi.class);
VolumeApi volumeApi = mock(VolumeApi.class);
given(docker.image()).willReturn(imageApi);
given(docker.container()).willReturn(containerApi);
given(docker.volume()).willReturn(volumeApi);
return docker;
}
private BuildRequest getTestRequest() {
TarArchive content = mock(TarArchive.class);
ImageReference name = ImageReference.of("my-application");
BuildRequest request = BuildRequest.of(name, (owner) -> content);
return request;
}
private Image loadImage(String name) throws IOException {
return Image.of(getClass().getResourceAsStream(name));
}
private Answer<Image> withPulledImage(Image image) {
return (invocation) -> {
TotalProgressPullListener listener = invocation.getArgument(1, TotalProgressPullListener.class);
listener.onStart();
listener.onFinish();
return image;
};
}
static class TestPrintStream extends PrintStream {
TestPrintStream() {
super(new ByteArrayOutputStream());
}
@Override
public String toString() {
return this.out.toString();
}
}
}

View File

@@ -0,0 +1,164 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.build;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.time.Clock;
import java.time.Instant;
import java.time.ZoneOffset;
import java.util.Collections;
import java.util.Map;
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.utils.IOUtils;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.springframework.boot.buildpack.platform.docker.type.Image;
import org.springframework.boot.buildpack.platform.docker.type.ImageArchive;
import org.springframework.boot.buildpack.platform.docker.type.ImageConfig;
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
import org.springframework.boot.buildpack.platform.json.AbstractJsonTests;
import org.springframework.util.FileCopyUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link EphemeralBuilder}.
*
* @author Phillip Webb
*/
class EphemeralBuilderTests extends AbstractJsonTests {
@TempDir
File temp;
private final BuildOwner owner = BuildOwner.of(123, 456);
private Image image;
private BuilderMetadata metadata;
private Map<String, String> env;
@BeforeEach
void setup() throws Exception {
this.image = Image.of(getContent("image.json"));
this.metadata = BuilderMetadata.fromImage(this.image);
this.env = Collections.singletonMap("spring", "boot");
}
@Test
void getNameHasRandomName() throws Exception {
EphemeralBuilder b1 = new EphemeralBuilder(this.owner, this.image, this.metadata, this.env);
EphemeralBuilder b2 = new EphemeralBuilder(this.owner, this.image, this.metadata, this.env);
assertThat(b1.getName().toString()).startsWith("pack.local/builder/").endsWith(":latest");
assertThat(b1.getName().toString()).isNotEqualTo(b2.getName().toString());
}
@Test
void getArchiveHasCreatedByConfig() throws Exception {
EphemeralBuilder builder = new EphemeralBuilder(this.owner, this.image, this.metadata, this.env);
ImageConfig config = builder.getArchive().getImageConfig();
BuilderMetadata ephemeralMetadata = BuilderMetadata.fromImageConfig(config);
assertThat(ephemeralMetadata.getCreatedBy().getName()).isEqualTo("Spring Boot");
}
@Test
void getArchiveHasTag() throws Exception {
EphemeralBuilder builder = new EphemeralBuilder(this.owner, this.image, this.metadata, this.env);
ImageReference tag = builder.getArchive().getTag();
assertThat(tag.toString()).startsWith("pack.local/builder/").endsWith(":latest");
}
@Test
void getArchiveHasCreateDate() throws Exception {
Clock clock = Clock.fixed(Instant.now(), ZoneOffset.UTC);
EphemeralBuilder builder = new EphemeralBuilder(clock, this.owner, this.image, this.metadata, this.env);
assertThat(builder.getArchive().getCreateDate()).isEqualTo(Instant.now(clock));
}
@Test
void getArchiveContainsDefaultDirsLayer() throws Exception {
EphemeralBuilder builder = new EphemeralBuilder(this.owner, this.image, this.metadata, this.env);
File folder = unpack(getLayer(builder.getArchive(), 0), "dirs");
assertThat(new File(folder, "workspace")).isDirectory();
assertThat(new File(folder, "layers")).isDirectory();
assertThat(new File(folder, "cnb")).isDirectory();
assertThat(new File(folder, "cnb/buildpacks")).isDirectory();
assertThat(new File(folder, "platform")).isDirectory();
assertThat(new File(folder, "platform/env")).isDirectory();
}
@Test
void getArchiveContainsStackLayer() throws Exception {
EphemeralBuilder builder = new EphemeralBuilder(this.owner, this.image, this.metadata, this.env);
File folder = unpack(getLayer(builder.getArchive(), 1), "stack");
File tomlFile = new File(folder, "cnb/stack.toml");
assertThat(tomlFile).exists();
String toml = FileCopyUtils
.copyToString(new InputStreamReader(new FileInputStream(tomlFile), StandardCharsets.UTF_8));
assertThat(toml).contains("[run-image]").contains("image = ");
}
@Test
void getArchiveContainsEnvLayer() throws Exception {
EphemeralBuilder builder = new EphemeralBuilder(this.owner, this.image, this.metadata, this.env);
File folder = unpack(getLayer(builder.getArchive(), 2), "env");
assertThat(new File(folder, "platform/env/spring")).usingCharset(StandardCharsets.UTF_8).hasContent("boot");
}
private TarArchiveInputStream getLayer(ImageArchive archive, int index) throws Exception {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
archive.writeTo(outputStream);
TarArchiveInputStream tar = new TarArchiveInputStream(new ByteArrayInputStream(outputStream.toByteArray()));
for (int i = 0; i <= index; i++) {
tar.getNextEntry();
}
return new TarArchiveInputStream(tar);
}
private File unpack(TarArchiveInputStream archive, String name) throws Exception {
File folder = new File(this.temp, name);
folder.mkdirs();
ArchiveEntry entry = archive.getNextEntry();
while (entry != null) {
File file = new File(folder, entry.getName());
if (entry.isDirectory()) {
file.mkdirs();
}
else {
file.getParentFile().mkdirs();
try (OutputStream out = new FileOutputStream(file)) {
IOUtils.copy(archive, out);
}
}
entry = archive.getNextEntry();
}
return folder;
}
}

View File

@@ -0,0 +1,225 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.build;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;
import java.util.Map;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.stubbing.Answer;
import org.springframework.boot.buildpack.platform.docker.DockerApi;
import org.springframework.boot.buildpack.platform.docker.DockerApi.ContainerApi;
import org.springframework.boot.buildpack.platform.docker.DockerApi.ImageApi;
import org.springframework.boot.buildpack.platform.docker.DockerApi.VolumeApi;
import org.springframework.boot.buildpack.platform.docker.type.ContainerConfig;
import org.springframework.boot.buildpack.platform.docker.type.ContainerContent;
import org.springframework.boot.buildpack.platform.docker.type.ContainerReference;
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
import org.springframework.boot.buildpack.platform.docker.type.VolumeName;
import org.springframework.boot.buildpack.platform.io.IOConsumer;
import org.springframework.boot.buildpack.platform.io.TarArchive;
import org.springframework.boot.buildpack.platform.json.SharedObjectMapper;
import org.springframework.util.FileCopyUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
/**
* Tests for {@link Lifecycle}.
*
* @author Phillip Webb
*/
class LifecycleTests {
private TestPrintStream out;
private DockerApi docker;
private Lifecycle lifecycle;
private Map<String, ContainerConfig> configs = new LinkedHashMap<>();
private Map<String, ContainerContent> content = new LinkedHashMap<>();
@BeforeEach
void setup() throws Exception {
this.out = new TestPrintStream();
this.docker = mockDockerApi();
BuildRequest request = getTestRequest();
this.lifecycle = createLifecycle(request);
}
private DockerApi mockDockerApi() {
DockerApi docker = mock(DockerApi.class);
ImageApi imageApi = mock(ImageApi.class);
ContainerApi containerApi = mock(ContainerApi.class);
VolumeApi volumeApi = mock(VolumeApi.class);
given(docker.image()).willReturn(imageApi);
given(docker.container()).willReturn(containerApi);
given(docker.volume()).willReturn(volumeApi);
return docker;
}
@Test
void executeExecutesPhases() throws Exception {
given(this.docker.container().create(any())).willAnswer(answerWithGeneratedContainerId());
given(this.docker.container().create(any(), any())).willAnswer(answerWithGeneratedContainerId());
this.lifecycle.execute();
assertPhaseWasRun("detector", withExpectedConfig("lifecycle-detector.json"));
assertPhaseWasRun("restorer", withExpectedConfig("lifecycle-restorer.json"));
assertPhaseWasRun("analyzer", withExpectedConfig("lifecycle-analyzer.json"));
assertPhaseWasRun("builder", withExpectedConfig("lifecycle-builder.json"));
assertPhaseWasRun("exporter", withExpectedConfig("lifecycle-exporter.json"));
assertPhaseWasRun("cacher", withExpectedConfig("lifecycle-cacher.json"));
assertThat(this.out.toString()).contains("Successfully built image 'docker.io/library/my-application:latest'");
}
@Test
void executeOnlyUploadsContentOnce() throws Exception {
given(this.docker.container().create(any())).willAnswer(answerWithGeneratedContainerId());
given(this.docker.container().create(any(), any())).willAnswer(answerWithGeneratedContainerId());
this.lifecycle.execute();
assertThat(this.content).hasSize(1);
}
@Test
void executeWhenAleadyRunThrowsException() throws Exception {
given(this.docker.container().create(any())).willAnswer(answerWithGeneratedContainerId());
given(this.docker.container().create(any(), any())).willAnswer(answerWithGeneratedContainerId());
this.lifecycle.execute();
assertThatIllegalStateException().isThrownBy(this.lifecycle::execute)
.withMessage("Lifecycle has already been executed");
}
@Test
void executeWhenCleanCacheClearsCache() throws Exception {
given(this.docker.container().create(any())).willAnswer(answerWithGeneratedContainerId());
given(this.docker.container().create(any(), any())).willAnswer(answerWithGeneratedContainerId());
BuildRequest request = getTestRequest().withCleanCache(true);
createLifecycle(request).execute();
VolumeName name = VolumeName.of("pack-cache-b35197ac41ea.build");
verify(this.docker.volume()).delete(name, true);
}
@Test
void closeClearsVolumes() throws Exception {
this.lifecycle.close();
verify(this.docker.volume()).delete(VolumeName.of("pack-layers-aaaaaaaaaa"), true);
verify(this.docker.volume()).delete(VolumeName.of("pack-app-aaaaaaaaaa"), true);
}
private BuildRequest getTestRequest() {
TarArchive content = mock(TarArchive.class);
ImageReference name = ImageReference.of("my-application");
BuildRequest request = BuildRequest.of(name, (owner) -> content);
return request;
}
private Lifecycle createLifecycle(BuildRequest request) throws IOException {
EphemeralBuilder builder = mockEphemeralBuilder();
return new TestLifecycle(BuildLog.to(this.out), this.docker, request, ImageReference.of("cloudfoundry/run"),
builder);
}
private EphemeralBuilder mockEphemeralBuilder() throws IOException {
EphemeralBuilder builder = mock(EphemeralBuilder.class);
byte[] metadataContent = FileCopyUtils.copyToByteArray(getClass().getResourceAsStream("builder-metadata.json"));
BuilderMetadata metadata = BuilderMetadata.fromJson(new String(metadataContent, StandardCharsets.UTF_8));
given(builder.getName()).willReturn(ImageReference.of("pack.local/ephemeral-builder"));
given(builder.getBuilderMetadata()).willReturn(metadata);
return builder;
}
private Answer<ContainerReference> answerWithGeneratedContainerId() {
return (invocation) -> {
ContainerConfig config = invocation.getArgument(0, ContainerConfig.class);
ArrayNode command = getCommand(config);
String name = command.get(0).asText().substring(1).replaceAll("/", "-");
this.configs.put(name, config);
if (invocation.getArguments().length > 1) {
this.content.put(name, invocation.getArgument(1, ContainerContent.class));
}
return ContainerReference.of(name);
};
}
private ArrayNode getCommand(ContainerConfig config) throws JsonProcessingException, JsonMappingException {
JsonNode node = SharedObjectMapper.get().readTree(config.toString());
return (ArrayNode) node.at("/Cmd");
}
private void assertPhaseWasRun(String name, IOConsumer<ContainerConfig> configConsumer) throws IOException {
ContainerReference containerReference = ContainerReference.of("lifecycle-" + name);
verify(this.docker.container()).start(containerReference);
verify(this.docker.container()).logs(eq(containerReference), any());
verify(this.docker.container()).remove(containerReference, true);
configConsumer.accept(this.configs.get(containerReference.toString()));
}
private IOConsumer<ContainerConfig> withExpectedConfig(String name) {
return (config) -> {
InputStream in = getClass().getResourceAsStream(name);
String json = FileCopyUtils.copyToString(new InputStreamReader(in, StandardCharsets.UTF_8));
assertThat(config.toString()).isEqualToIgnoringWhitespace(json);
};
}
static class TestLifecycle extends Lifecycle {
TestLifecycle(BuildLog log, DockerApi docker, BuildRequest request, ImageReference runImageReferece,
EphemeralBuilder builder) {
super(log, docker, request, runImageReferece, builder);
}
@Override
protected VolumeName createRandomVolumeName(String prefix) {
return VolumeName.of(prefix + "aaaaaaaaaa");
}
}
static class TestPrintStream extends PrintStream {
TestPrintStream() {
super(new ByteArrayOutputStream());
}
@Override
public String toString() {
return this.out.toString();
}
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.build;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
/**
* Tests for {@link LifecycleVersion}.
*
* @author Phillip Webb
*/
class LifecycleVersionTests {
@Test
void parseWhenValueIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> LifecycleVersion.parse(null))
.withMessage("Value must not be empty");
}
@Test
void parseWhenTooLongThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> LifecycleVersion.parse("v1.2.3.4"))
.withMessage("Malformed version number '1.2.3.4'");
}
@Test
void parseWhenNonNumericThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> LifecycleVersion.parse("v1.2.3a"))
.withMessage("Malformed version number '1.2.3a'");
}
@Test
void compareTo() {
LifecycleVersion v4 = LifecycleVersion.parse("0.0.4");
assertThat(LifecycleVersion.parse("0.0.3").compareTo(v4)).isNegative();
assertThat(LifecycleVersion.parse("0.0.4").compareTo(v4)).isZero();
assertThat(LifecycleVersion.parse("0.0.5").compareTo(v4)).isPositive();
}
@Test
void isEqualOrGreaterThan() {
LifecycleVersion v4 = LifecycleVersion.parse("0.0.4");
assertThat(LifecycleVersion.parse("0.0.3").isEqualOrGreaterThan(v4)).isFalse();
assertThat(LifecycleVersion.parse("0.0.4").isEqualOrGreaterThan(v4)).isTrue();
assertThat(LifecycleVersion.parse("0.0.5").isEqualOrGreaterThan(v4)).isTrue();
}
@Test
void parseReturnsVersion() {
assertThat(LifecycleVersion.parse("1.2.3").toString()).isEqualTo("v1.2.3");
assertThat(LifecycleVersion.parse("1.2").toString()).isEqualTo("v1.2.0");
assertThat(LifecycleVersion.parse("1").toString()).isEqualTo("v1.0.0");
}
}

View File

@@ -0,0 +1,119 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.build;
import org.junit.jupiter.api.Test;
import org.springframework.boot.buildpack.platform.docker.type.ContainerConfig.Update;
import org.springframework.boot.buildpack.platform.docker.type.VolumeName;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
/**
* Tests for {@link Phase}.
*
* @author Phillip Webb
*/
class PhaseTests {
private static final String[] NO_ARGS = {};
@Test
void getNameReturnsName() {
Phase phase = new Phase("test", false);
assertThat(phase.getName()).isEqualTo("test");
}
@Test
void toStringReturnsName() {
Phase phase = new Phase("test", false);
assertThat(phase).hasToString("test");
}
@Test
void applyUpdatesConfiguration() {
Phase phase = new Phase("test", false);
Update update = mock(Update.class);
phase.apply(update);
verify(update).withCommand("/lifecycle/test", NO_ARGS);
verify(update).withLabel("author", "spring-boot");
verifyNoMoreInteractions(update);
}
@Test
void applyWhenWithDaemonAccessUpdatesConfigurationWithRootUserAndDomainSocketBinding() {
Phase phase = new Phase("test", false);
phase.withDaemonAccess();
Update update = mock(Update.class);
phase.apply(update);
verify(update).withUser("root");
verify(update).withBind("/var/run/docker.sock", "/var/run/docker.sock");
verify(update).withCommand("/lifecycle/test", NO_ARGS);
verify(update).withLabel("author", "spring-boot");
verifyNoMoreInteractions(update);
}
@Test
void applyWhenWithLogLevelArgAndVerboseLoggingUpdatesConfigurationWithLogLevel() {
Phase phase = new Phase("test", true);
phase.withLogLevelArg();
Update update = mock(Update.class);
phase.apply(update);
verify(update).withCommand("/lifecycle/test", "-log-level", "debug");
verify(update).withLabel("author", "spring-boot");
verifyNoMoreInteractions(update);
}
@Test
void applyWhenWithLogLevelArgAndNonVerboseLoggingDoesNotUpdateLogLevel() {
Phase phase = new Phase("test", false);
phase.withLogLevelArg();
Update update = mock(Update.class);
phase.apply(update);
verify(update).withCommand("/lifecycle/test");
verify(update).withLabel("author", "spring-boot");
verifyNoMoreInteractions(update);
}
@Test
void applyWhenWithArgsUpdatesConfigurationWithArguments() {
Phase phase = new Phase("test", false);
phase.withArgs("a", "b", "c");
Update update = mock(Update.class);
phase.apply(update);
verify(update).withCommand("/lifecycle/test", "a", "b", "c");
verify(update).withLabel("author", "spring-boot");
verifyNoMoreInteractions(update);
}
@Test
void applyWhenWithBindsUpdatesConfigurationWithBinds() {
Phase phase = new Phase("test", false);
VolumeName volumeName = VolumeName.of("test");
phase.withBinds(volumeName, "/test");
Update update = mock(Update.class);
phase.apply(update);
verify(update).withCommand("/lifecycle/test");
verify(update).withLabel("author", "spring-boot");
verify(update).withBind(volumeName, "/test");
verifyNoMoreInteractions(update);
}
}

View File

@@ -0,0 +1,99 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.build;
import java.io.ByteArrayOutputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.function.Consumer;
import org.junit.jupiter.api.Test;
import org.springframework.boot.buildpack.platform.docker.LogUpdateEvent;
import org.springframework.boot.buildpack.platform.docker.TotalProgressEvent;
import org.springframework.boot.buildpack.platform.docker.type.Image;
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
import org.springframework.boot.buildpack.platform.docker.type.VolumeName;
import org.springframework.util.FileCopyUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link PrintStreamBuildLog}.
*
* @author Phillip Webb
*/
class PrintStreamBuildLogTests {
@Test
void printsExpectedOutput() throws Exception {
TestPrintStream out = new TestPrintStream();
PrintStreamBuildLog log = new PrintStreamBuildLog(out);
BuildRequest request = mock(BuildRequest.class);
ImageReference name = ImageReference.of("my-app:latest");
ImageReference builderImageReference = ImageReference.of("cnb/builder");
Image builderImage = mock(Image.class);
given(builderImage.getDigests()).willReturn(Collections.singletonList("00000001"));
ImageReference runImageReference = ImageReference.of("cnb/runner");
Image runImage = mock(Image.class);
given(runImage.getDigests()).willReturn(Collections.singletonList("00000002"));
given(request.getName()).willReturn(name);
log.start(request);
Consumer<TotalProgressEvent> pullBuildImageConsumer = log.pullingBuilder(request, builderImageReference);
pullBuildImageConsumer.accept(new TotalProgressEvent(100));
log.pulledBulder(request, builderImage);
Consumer<TotalProgressEvent> pullRunImageConsumer = log.pullingRunImage(request, runImageReference);
pullRunImageConsumer.accept(new TotalProgressEvent(100));
log.pulledRunImage(request, runImage);
log.executingLifecycle(request, LifecycleVersion.parse("0.5"), VolumeName.of("pack-abc.cache"));
Consumer<LogUpdateEvent> phase1Consumer = log.runningPhase(request, "alphabet");
phase1Consumer.accept(mockLogEvent("one"));
phase1Consumer.accept(mockLogEvent("two"));
phase1Consumer.accept(mockLogEvent("three"));
Consumer<LogUpdateEvent> phase2Consumer = log.runningPhase(request, "basket");
phase2Consumer.accept(mockLogEvent("spring"));
phase2Consumer.accept(mockLogEvent("boot"));
log.executedLifecycle(request);
String expected = FileCopyUtils.copyToString(new InputStreamReader(
getClass().getResourceAsStream("print-stream-build-log.txt"), StandardCharsets.UTF_8));
assertThat(out.toString()).isEqualToIgnoringNewLines(expected);
}
private LogUpdateEvent mockLogEvent(String string) {
LogUpdateEvent event = mock(LogUpdateEvent.class);
given(event.toString()).willReturn(string);
return event;
}
static class TestPrintStream extends PrintStream {
TestPrintStream() {
super(new ByteArrayOutputStream());
}
@Override
public String toString() {
return this.out.toString();
}
}
}

View File

@@ -0,0 +1,85 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.build;
import java.util.Collections;
import org.junit.jupiter.api.Test;
import org.springframework.boot.buildpack.platform.docker.type.Image;
import org.springframework.boot.buildpack.platform.docker.type.ImageConfig;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link StackId}.
*
* @author Phillip Webb
*/
class StackIdTests {
@Test
void fromImageWhenImageIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> StackId.fromImage(null))
.withMessage("Image must not be null");
}
@Test
void fromImageWhenLabelIsMissingThrowsException() {
Image image = mock(Image.class);
ImageConfig imageConfig = mock(ImageConfig.class);
given(image.getConfig()).willReturn(imageConfig);
assertThatIllegalStateException().isThrownBy(() -> StackId.fromImage(image))
.withMessage("Missing 'io.buildpacks.stack.id' stack label");
}
@Test
void fromImageCreatesStackId() {
Image image = mock(Image.class);
ImageConfig imageConfig = mock(ImageConfig.class);
given(image.getConfig()).willReturn(imageConfig);
given(imageConfig.getLabels()).willReturn(Collections.singletonMap("io.buildpacks.stack.id", "test"));
StackId stackId = StackId.fromImage(image);
assertThat(stackId.toString()).isEqualTo("test");
}
@Test
void ofCreatesStackId() {
StackId stackId = StackId.of("test");
assertThat(stackId.toString()).isEqualTo("test");
}
@Test
void equalsAndHashCode() {
StackId s1 = StackId.of("a");
StackId s2 = StackId.of("a");
StackId s3 = StackId.of("b");
assertThat(s1.hashCode()).isEqualTo(s2.hashCode());
assertThat(s1).isEqualTo(s1).isEqualTo(s2).isNotEqualTo(s3);
}
@Test
void toStringReturnsValue() {
StackId stackId = StackId.of("test");
assertThat(stackId.toString()).isEqualTo("test");
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.docker;
import java.io.IOException;
import org.junit.jupiter.api.Test;
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
import org.springframework.boot.testsupport.testcontainers.DisabledIfDockerUnavailable;
/**
* Integration tests for {@link DockerApi}.
*
* @author Phillip Webb
*/
@DisabledIfDockerUnavailable
class DockerApiIntegrationTests {
private final DockerApi docker = new DockerApi();
@Test
void pullImage() throws IOException {
this.docker.image().pull(ImageReference.of("cloudfoundry/cnb:bionic"),
new TotalProgressPullListener(new TotalProgressBar("Pulling: ")));
}
}

View File

@@ -0,0 +1,393 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.docker;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.boot.buildpack.platform.docker.DockerApi.ContainerApi;
import org.springframework.boot.buildpack.platform.docker.DockerApi.ImageApi;
import org.springframework.boot.buildpack.platform.docker.DockerApi.VolumeApi;
import org.springframework.boot.buildpack.platform.docker.Http.Response;
import org.springframework.boot.buildpack.platform.docker.type.ContainerConfig;
import org.springframework.boot.buildpack.platform.docker.type.ContainerContent;
import org.springframework.boot.buildpack.platform.docker.type.ContainerReference;
import org.springframework.boot.buildpack.platform.docker.type.Image;
import org.springframework.boot.buildpack.platform.docker.type.ImageArchive;
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
import org.springframework.boot.buildpack.platform.docker.type.VolumeName;
import org.springframework.boot.buildpack.platform.io.Content;
import org.springframework.boot.buildpack.platform.io.IOConsumer;
import org.springframework.boot.buildpack.platform.io.Owner;
import org.springframework.boot.buildpack.platform.io.TarArchive;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
/**
* Tests for {@link DockerApi}.
*
* @author Phillip Webb
*/
class DockerApiTests {
private static final String API_URL = "docker://localhost/v1.40";
private static final String IMAGES_URL = API_URL + "/images";
private static final String CONTAINERS_URL = API_URL + "/containers";
private static final String VOLUMES_URL = API_URL + "/volumes";
@Mock
private HttpClientHttp httpClient;
private DockerApi dockerApi;
@BeforeEach
void setup() {
MockitoAnnotations.initMocks(this);
this.dockerApi = new DockerApi(this.httpClient);
}
private HttpClientHttp httpClient() {
return this.httpClient;
}
private Response emptyResponse() throws IOException {
return responseOf(null);
}
private Response responseOf(String name) throws IOException {
return new Response() {
@Override
public void close() throws IOException {
}
@Override
public InputStream getContent() throws IOException {
if (name == null) {
return null;
}
return getClass().getResourceAsStream(name);
}
};
}
@Nested
class ImageDockerApiTests {
private ImageApi api;
@Mock
private UpdateListener<PullImageUpdateEvent> pullListener;
@Mock
private UpdateListener<LoadImageUpdateEvent> loadListener;
@Captor
private ArgumentCaptor<IOConsumer<OutputStream>> writer;
@BeforeEach
void setup() {
MockitoAnnotations.initMocks(this);
this.api = DockerApiTests.this.dockerApi.image();
}
@Test
void pullWhenReferenceIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> this.api.pull(null, this.pullListener))
.withMessage("Reference must not be null");
}
@Test
void pullWhenListenerIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> this.api.pull(ImageReference.of("ubuntu"), null))
.withMessage("Listener must not be null");
}
@Test
void pullPullsImageAndProducesEvents() throws Exception {
ImageReference reference = ImageReference.of("cloudfoundry/cnb:bionic");
URI createUri = new URI(IMAGES_URL + "/create?fromImage=docker.io%2Fcloudfoundry%2Fcnb%3Abionic");
String imageHash = "4acb6bfd6c4f0cabaf7f3690e444afe51f1c7de54d51da7e63fac709c56f1c30";
URI imageUri = new URI(IMAGES_URL + "/docker.io/cloudfoundry/cnb@sha256:" + imageHash + "/json");
given(httpClient().post(createUri)).willReturn(responseOf("pull-stream.json"));
given(httpClient().get(imageUri)).willReturn(responseOf("type/image.json"));
Image image = this.api.pull(reference, this.pullListener);
assertThat(image.getLayers()).hasSize(46);
InOrder ordered = inOrder(this.pullListener);
ordered.verify(this.pullListener).onStart();
ordered.verify(this.pullListener, times(595)).onUpdate(any());
ordered.verify(this.pullListener).onFinish();
}
@Test
void loadWhenArchiveIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> this.api.load(null, UpdateListener.none()))
.withMessage("Archive must not be null");
}
@Test
void loadWhenListenerIsNullThrowsException() {
ImageArchive archive = mock(ImageArchive.class);
assertThatIllegalArgumentException().isThrownBy(() -> this.api.load(archive, null))
.withMessage("Listener must not be null");
}
@Test
void loadLoadsImage() throws Exception {
Image image = Image.of(getClass().getResourceAsStream("type/image.json"));
ImageArchive archive = ImageArchive.from(image);
URI loadUri = new URI(IMAGES_URL + "/load");
given(httpClient().post(eq(loadUri), eq("application/x-tar"), any()))
.willReturn(responseOf("load-stream.json"));
this.api.load(archive, this.loadListener);
InOrder ordered = inOrder(this.loadListener);
ordered.verify(this.loadListener).onStart();
ordered.verify(this.loadListener).onUpdate(any());
ordered.verify(this.loadListener).onFinish();
verify(httpClient()).post(any(), any(), this.writer.capture());
ByteArrayOutputStream out = new ByteArrayOutputStream();
this.writer.getValue().accept(out);
assertThat(out.toByteArray()).hasSizeGreaterThan(21000);
}
@Test
void removeWhenReferenceIsNulllThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> this.api.remove(null, true))
.withMessage("Reference must not be null");
}
@Test
void removeRemovesContainer() throws Exception {
ImageReference reference = ImageReference
.of("ubuntu@sha256:6e9f67fa63b0323e9a1e587fd71c561ba48a034504fb804fd26fd8800039835d");
URI removeUri = new URI(IMAGES_URL
+ "/docker.io/library/ubuntu@sha256:6e9f67fa63b0323e9a1e587fd71c561ba48a034504fb804fd26fd8800039835d");
given(httpClient().delete(removeUri)).willReturn(emptyResponse());
this.api.remove(reference, false);
verify(httpClient()).delete(removeUri);
}
@Test
void removeWhenForceIsTrueRemovesContainer() throws Exception {
ImageReference reference = ImageReference
.of("ubuntu@sha256:6e9f67fa63b0323e9a1e587fd71c561ba48a034504fb804fd26fd8800039835d");
URI removeUri = new URI(IMAGES_URL
+ "/docker.io/library/ubuntu@sha256:6e9f67fa63b0323e9a1e587fd71c561ba48a034504fb804fd26fd8800039835d?force=1");
given(httpClient().delete(removeUri)).willReturn(emptyResponse());
this.api.remove(reference, true);
verify(httpClient()).delete(removeUri);
}
}
@Nested
class ContainerDockerApiTests {
private ContainerApi api;
@Captor
private ArgumentCaptor<IOConsumer<OutputStream>> writer;
@Mock
private UpdateListener<LogUpdateEvent> logListener;
@BeforeEach
void setup() {
MockitoAnnotations.initMocks(this);
this.api = DockerApiTests.this.dockerApi.container();
}
@Test
void createWhenConfigIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> this.api.create(null))
.withMessage("Config must not be null");
}
@Test
void createCreatesContainer() throws Exception {
ImageReference imageReference = ImageReference.of("ubuntu:bionic");
ContainerConfig config = ContainerConfig.of(imageReference, (update) -> update.withCommand("/bin/bash"));
URI createUri = new URI(CONTAINERS_URL + "/create");
given(httpClient().post(eq(createUri), eq("application/json"), any()))
.willReturn(responseOf("create-container-response.json"));
ContainerReference containerReference = this.api.create(config);
assertThat(containerReference.toString()).isEqualTo("e90e34656806");
ByteArrayOutputStream out = new ByteArrayOutputStream();
verify(httpClient()).post(any(), any(), this.writer.capture());
this.writer.getValue().accept(out);
assertThat(out.toByteArray()).hasSizeGreaterThan(130);
}
@Test
void createWhenHasContentContainerWithContent() throws Exception {
ImageReference imageReference = ImageReference.of("ubuntu:bionic");
ContainerConfig config = ContainerConfig.of(imageReference, (update) -> update.withCommand("/bin/bash"));
TarArchive archive = TarArchive.of((layout) -> {
layout.folder("/test", Owner.ROOT);
layout.file("/test/file", Owner.ROOT, Content.of("test"));
});
ContainerContent content = ContainerContent.of(archive);
URI createUri = new URI(CONTAINERS_URL + "/create");
given(httpClient().post(eq(createUri), eq("application/json"), any()))
.willReturn(responseOf("create-container-response.json"));
URI uploadUri = new URI(CONTAINERS_URL + "/e90e34656806/archive?path=%2F");
given(httpClient().put(eq(uploadUri), eq("application/x-tar"), any())).willReturn(emptyResponse());
ContainerReference containerReference = this.api.create(config, content);
assertThat(containerReference.toString()).isEqualTo("e90e34656806");
ByteArrayOutputStream out = new ByteArrayOutputStream();
verify(httpClient()).post(any(), any(), this.writer.capture());
this.writer.getValue().accept(out);
assertThat(out.toByteArray()).hasSizeGreaterThan(130);
verify(httpClient()).put(any(), any(), this.writer.capture());
this.writer.getValue().accept(out);
assertThat(out.toByteArray()).hasSizeGreaterThan(2000);
}
@Test
void startWhenReferenceIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> this.api.start(null))
.withMessage("Reference must not be null");
}
@Test
void startStartsContainer() throws Exception {
ContainerReference reference = ContainerReference.of("e90e34656806");
URI startContainerUri = new URI(CONTAINERS_URL + "/e90e34656806/start");
given(httpClient().post(startContainerUri)).willReturn(emptyResponse());
this.api.start(reference);
verify(httpClient()).post(startContainerUri);
}
@Test
void logsWhenReferenceIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> this.api.logs(null, UpdateListener.none()))
.withMessage("Reference must not be null");
}
@Test
void logsWhenListenerIsNullThrowsException() {
assertThatIllegalArgumentException()
.isThrownBy(() -> this.api.logs(ContainerReference.of("e90e34656806"), null))
.withMessage("Listener must not be null");
}
@Test
void logsProducesEvents() throws Exception {
ContainerReference reference = ContainerReference.of("e90e34656806");
URI logsUri = new URI(CONTAINERS_URL + "/e90e34656806/logs?stdout=1&stderr=1&follow=1");
given(httpClient().get(logsUri)).willReturn(responseOf("log-update-event.stream"));
this.api.logs(reference, this.logListener);
InOrder ordered = inOrder(this.logListener);
ordered.verify(this.logListener).onStart();
ordered.verify(this.logListener, times(7)).onUpdate(any());
ordered.verify(this.logListener).onFinish();
}
@Test
void removeWhenReferenceIsNulllThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> this.api.remove(null, true))
.withMessage("Reference must not be null");
}
@Test
void removeRemovesContainer() throws Exception {
ContainerReference reference = ContainerReference.of("e90e34656806");
URI removeUri = new URI(CONTAINERS_URL + "/e90e34656806");
given(httpClient().delete(removeUri)).willReturn(emptyResponse());
this.api.remove(reference, false);
verify(httpClient()).delete(removeUri);
}
@Test
void removeWhenForceIsTrueRemovesContainer() throws Exception {
ContainerReference reference = ContainerReference.of("e90e34656806");
URI removeUri = new URI(CONTAINERS_URL + "/e90e34656806?force=1");
given(httpClient().delete(removeUri)).willReturn(emptyResponse());
this.api.remove(reference, true);
verify(httpClient()).delete(removeUri);
}
}
@Nested
class VolumeDockerApiTests {
private VolumeApi api;
@Captor
private ArgumentCaptor<IOConsumer<OutputStream>> writer;
@Mock
private UpdateListener<LogUpdateEvent> logListener;
@BeforeEach
void setup() {
MockitoAnnotations.initMocks(this);
this.api = DockerApiTests.this.dockerApi.volume();
}
@Test
void deleteWhenNameIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> this.api.delete(null, false))
.withMessage("Name must not be null");
}
@Test
void deleteDeletesContainer() throws Exception {
VolumeName name = VolumeName.of("test");
URI removeUri = new URI(VOLUMES_URL + "/test");
given(httpClient().delete(removeUri)).willReturn(emptyResponse());
this.api.delete(name, false);
verify(httpClient()).delete(removeUri);
}
@Test
void deleteWhenForceIsTrueDeletesContainer() throws Exception {
VolumeName name = VolumeName.of("test");
URI removeUri = new URI(VOLUMES_URL + "/test?force=1");
given(httpClient().delete(removeUri)).willReturn(emptyResponse());
this.api.delete(name, true);
verify(httpClient()).delete(removeUri);
}
}
}

View File

@@ -0,0 +1,92 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.docker;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collections;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
/**
* Tests for {@link DockerException}.
*
* @author Phillip Webb
*/
class DockerExceptionTests {
private static final URI URI;
static {
try {
URI = new URI("docker://localhost");
}
catch (URISyntaxException ex) {
throw new IllegalStateException(ex);
}
}
private static final Errors NO_ERRORS = new Errors(Collections.emptyList());
private static final Errors ERRORS = new Errors(Collections.singletonList(new Errors.Error("code", "message")));
@Test
void createWhenUriIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> new DockerException(null, 404, null, NO_ERRORS))
.withMessage("URI must not be null");
}
@Test
void create() {
DockerException exception = new DockerException(URI, 404, "missing", ERRORS);
assertThat(exception.getMessage()).isEqualTo(
"Docker API call to 'docker://localhost' failed with status code 404 \"missing\" [code: message]");
assertThat(exception.getStatusCode()).isEqualTo(404);
assertThat(exception.getReasonPhrase()).isEqualTo("missing");
assertThat(exception.getErrors()).isSameAs(ERRORS);
}
@Test
void createWhenReasonPhraseIsNull() {
DockerException exception = new DockerException(URI, 404, null, ERRORS);
assertThat(exception.getMessage())
.isEqualTo("Docker API call to 'docker://localhost' failed with status code 404 [code: message]");
assertThat(exception.getStatusCode()).isEqualTo(404);
assertThat(exception.getReasonPhrase()).isNull();
assertThat(exception.getErrors()).isSameAs(ERRORS);
}
@Test
void createWhenErrorsIsNull() {
DockerException exception = new DockerException(URI, 404, "missing", null);
assertThat(exception.getErrors()).isNull();
}
@Test
void createWhenErrorsIsEmpty() {
DockerException exception = new DockerException(URI, 404, "missing", NO_ERRORS);
assertThat(exception.getMessage())
.isEqualTo("Docker API call to 'docker://localhost' failed with status code 404 \"missing\"");
assertThat(exception.getStatusCode()).isEqualTo(404);
assertThat(exception.getReasonPhrase()).isEqualTo("missing");
assertThat(exception.getErrors()).isSameAs(NO_ERRORS);
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.docker;
import java.util.Iterator;
import org.junit.jupiter.api.Test;
import org.springframework.boot.buildpack.platform.docker.Errors.Error;
import org.springframework.boot.buildpack.platform.json.AbstractJsonTests;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link Errors}.
*
* @author Phillip Webb
*/
class ErrorsTests extends AbstractJsonTests {
@Test
void readValueDeserializesJson() throws Exception {
Errors errors = this.getObjectMapper().readValue(getContent("errors.json"), Errors.class);
Iterator<Error> iterator = errors.iterator();
Error error1 = iterator.next();
Error error2 = iterator.next();
assertThat(iterator.hasNext()).isFalse();
assertThat(error1.getCode()).isEqualTo("TEST1");
assertThat(error1.getMessage()).isEqualTo("Test One");
assertThat(error2.getCode()).isEqualTo("TEST2");
assertThat(error2.getMessage()).isEqualTo("Test Two");
}
@Test
void toStringHasErrorDetails() throws Exception {
Errors errors = this.getObjectMapper().readValue(getContent("errors.json"), Errors.class);
assertThat(errors.toString()).isEqualTo("[TEST1: Test One, TEST2: Test Two]");
}
}

View File

@@ -0,0 +1,194 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.docker;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpHeaders;
import org.apache.http.StatusLine;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.CloseableHttpClient;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.boot.buildpack.platform.docker.Http.Response;
import org.springframework.util.StreamUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.verify;
/**
* Tests for {@link HttpClientHttp}.
*
* @author Phillip Webb
*/
class HttpClientHttpTests {
private static final String APPLICATION_JSON = "application/json";
@Mock
private CloseableHttpClient client;
@Mock
private CloseableHttpResponse response;
@Mock
private StatusLine statusLine;
@Mock
private HttpEntity entity;
@Mock
private InputStream content;
@Captor
private ArgumentCaptor<HttpUriRequest> requestCaptor;
private HttpClientHttp http;
private URI uri;
@BeforeEach
void setup() throws Exception {
MockitoAnnotations.initMocks(this);
given(this.client.execute(any())).willReturn(this.response);
given(this.response.getEntity()).willReturn(this.entity);
given(this.response.getStatusLine()).willReturn(this.statusLine);
this.http = new HttpClientHttp(this.client);
this.uri = new URI("docker://localhost/example");
}
@Test
void getShouldExecuteHttpGet() throws Exception {
given(this.entity.getContent()).willReturn(this.content);
given(this.statusLine.getStatusCode()).willReturn(200);
Response response = this.http.get(this.uri);
verify(this.client).execute(this.requestCaptor.capture());
HttpUriRequest request = this.requestCaptor.getValue();
assertThat(request).isInstanceOf(HttpGet.class);
assertThat(request.getURI()).isEqualTo(this.uri);
assertThat(request.getFirstHeader(HttpHeaders.CONTENT_TYPE)).isNull();
assertThat(response.getContent()).isSameAs(this.content);
}
@Test
void postShouldExecuteHttpPost() throws Exception {
given(this.entity.getContent()).willReturn(this.content);
given(this.statusLine.getStatusCode()).willReturn(200);
Response response = this.http.post(this.uri);
verify(this.client).execute(this.requestCaptor.capture());
HttpUriRequest request = this.requestCaptor.getValue();
assertThat(request).isInstanceOf(HttpPost.class);
assertThat(request.getURI()).isEqualTo(this.uri);
assertThat(request.getFirstHeader(HttpHeaders.CONTENT_TYPE)).isNull();
assertThat(response.getContent()).isSameAs(this.content);
}
@Test
void postWithContentShouldExecuteHttpPost() throws Exception {
given(this.entity.getContent()).willReturn(this.content);
given(this.statusLine.getStatusCode()).willReturn(200);
Response response = this.http.post(this.uri, APPLICATION_JSON,
(out) -> StreamUtils.copy("test", StandardCharsets.UTF_8, out));
verify(this.client).execute(this.requestCaptor.capture());
HttpUriRequest request = this.requestCaptor.getValue();
HttpEntity entity = ((HttpEntityEnclosingRequest) request).getEntity();
assertThat(request).isInstanceOf(HttpPost.class);
assertThat(request.getURI()).isEqualTo(this.uri);
assertThat(request.getFirstHeader(HttpHeaders.CONTENT_TYPE).getValue()).isEqualTo(APPLICATION_JSON);
assertThat(entity.isRepeatable()).isFalse();
assertThat(entity.getContentLength()).isEqualTo(-1);
assertThat(entity.isStreaming()).isTrue();
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() -> entity.getContent());
assertThat(writeToString(entity)).isEqualTo("test");
assertThat(response.getContent()).isSameAs(this.content);
}
@Test
void putWithContentShouldExecuteHttpPut() throws Exception {
given(this.entity.getContent()).willReturn(this.content);
given(this.statusLine.getStatusCode()).willReturn(200);
Response response = this.http.put(this.uri, APPLICATION_JSON,
(out) -> StreamUtils.copy("test", StandardCharsets.UTF_8, out));
verify(this.client).execute(this.requestCaptor.capture());
HttpUriRequest request = this.requestCaptor.getValue();
HttpEntity entity = ((HttpEntityEnclosingRequest) request).getEntity();
assertThat(request).isInstanceOf(HttpPut.class);
assertThat(request.getURI()).isEqualTo(this.uri);
assertThat(request.getFirstHeader(HttpHeaders.CONTENT_TYPE).getValue()).isEqualTo(APPLICATION_JSON);
assertThat(entity.isRepeatable()).isFalse();
assertThat(entity.getContentLength()).isEqualTo(-1);
assertThat(entity.isStreaming()).isTrue();
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() -> entity.getContent());
assertThat(writeToString(entity)).isEqualTo("test");
assertThat(response.getContent()).isSameAs(this.content);
}
@Test
void deleteShouldExecuteHttpDelete() throws IOException {
given(this.entity.getContent()).willReturn(this.content);
given(this.statusLine.getStatusCode()).willReturn(200);
Response response = this.http.delete(this.uri);
verify(this.client).execute(this.requestCaptor.capture());
HttpUriRequest request = this.requestCaptor.getValue();
assertThat(request).isInstanceOf(HttpDelete.class);
assertThat(request.getURI()).isEqualTo(this.uri);
assertThat(request.getFirstHeader(HttpHeaders.CONTENT_TYPE)).isNull();
assertThat(response.getContent()).isSameAs(this.content);
}
@Test
void executeWhenResposeIsIn400RangeShouldThrowDockerException() throws ClientProtocolException, IOException {
given(this.entity.getContent()).willReturn(getClass().getResourceAsStream("errors.json"));
given(this.statusLine.getStatusCode()).willReturn(404);
assertThatExceptionOfType(DockerException.class).isThrownBy(() -> this.http.get(this.uri))
.satisfies((ex) -> assertThat(ex.getErrors()).hasSize(2));
}
@Test
void executeWhenResposeIsIn500RangeShouldThrowDockerException() throws ClientProtocolException, IOException {
given(this.statusLine.getStatusCode()).willReturn(500);
assertThatExceptionOfType(DockerException.class).isThrownBy(() -> this.http.get(this.uri))
.satisfies((ex) -> assertThat(ex.getErrors()).isNull());
}
private String writeToString(HttpEntity entity) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
entity.writeTo(out);
return new String(out.toByteArray(), StandardCharsets.UTF_8);
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.docker;
import org.junit.jupiter.api.Test;
import org.springframework.boot.buildpack.platform.docker.ProgressUpdateEvent.ProgressDetail;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link LoadImageUpdateEvent}.
*
* @author Phillip Webb
*/
class LoadImageUpdateEventTests extends ProgressUpdateEventTests {
@Test
void getStreamReturnsStream() {
LoadImageUpdateEvent event = (LoadImageUpdateEvent) createEvent();
assertThat(event.getStream()).isEqualTo("stream");
}
@Override
protected ProgressUpdateEvent createEvent(String status, ProgressDetail progressDetail, String progress) {
return new LoadImageUpdateEvent("stream", status, progressDetail, progress);
}
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.docker;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link LogUpdateEvent}.
*
* @author Phillip Webb
*/
class LogUpdateEventTests {
@Test
void readAllWhenSimpleStreamReturnsEvents() throws Exception {
List<LogUpdateEvent> events = readAll("log-update-event.stream");
assertThat(events).hasSize(7);
assertThat(events.get(0).toString())
.isEqualTo("Analyzing image '307c032c4ceaa6330b6c02af945a1fe56a8c3c27c28268574b217c1d38b093cf'");
assertThat(events.get(1).toString())
.isEqualTo("Writing metadata for uncached layer 'org.cloudfoundry.openjdk:openjdk-jre'");
assertThat(events.get(2).toString())
.isEqualTo("Using cached launch layer 'org.cloudfoundry.jvmapplication:executable-jar'");
}
@Test
void readAllWhenAnsiStreamReturnsEvents() throws Exception {
List<LogUpdateEvent> events = readAll("log-update-event-ansi.stream");
assertThat(events).hasSize(20);
assertThat(events.get(0).toString()).isEqualTo("");
assertThat(events.get(1).toString()).isEqualTo("Cloud Foundry OpenJDK Buildpack v1.0.64");
assertThat(events.get(2).toString()).isEqualTo(" OpenJDK JRE 11.0.5: Reusing cached layer");
}
private List<LogUpdateEvent> readAll(String name) throws IOException {
List<LogUpdateEvent> events = new ArrayList<>();
try (InputStream inputStream = getClass().getResourceAsStream(name)) {
LogUpdateEvent.readAll(inputStream, events::add);
}
return events;
}
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.docker;
import org.junit.jupiter.api.Test;
import org.springframework.boot.buildpack.platform.docker.ProgressUpdateEvent.ProgressDetail;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link ProgressUpdateEvent}.
*
* @author Phillip Webb
*/
abstract class ProgressUpdateEventTests {
@Test
void getStatusReturnsStatus() {
ProgressUpdateEvent event = createEvent();
assertThat(event.getStatus()).isEqualTo("status");
}
@Test
void getProgressDetailsReturnsProgresssDetails() {
ProgressUpdateEvent event = createEvent();
assertThat(event.getProgressDetail().getCurrent()).isEqualTo(1);
assertThat(event.getProgressDetail().getTotal()).isEqualTo(2);
}
@Test
void getProgressReturnsProgress() {
ProgressUpdateEvent event = createEvent();
assertThat(event.getProgress()).isEqualTo("progress");
}
@Test
void progressDetailIsEmptyWhenCurrentIsNullReturnsTrue() {
ProgressDetail detail = new ProgressDetail(null, 2);
assertThat(ProgressDetail.isEmpty(detail)).isTrue();
}
@Test
void progressDetailIsEmptyWhenTotalIsNullReturnsTrue() {
ProgressDetail detail = new ProgressDetail(1, null);
assertThat(ProgressDetail.isEmpty(detail)).isTrue();
}
@Test
void progressDetailIsEmptyWhenTotalAndCurrentAreNotNullReturnsFalse() {
ProgressDetail detail = new ProgressDetail(1, 2);
assertThat(ProgressDetail.isEmpty(detail)).isFalse();
}
protected ProgressUpdateEvent createEvent() {
return createEvent("status", new ProgressDetail(1, 2), "progress");
}
protected abstract ProgressUpdateEvent createEvent(String status, ProgressDetail progressDetail, String progress);
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.docker;
import org.junit.jupiter.api.Test;
import org.springframework.boot.buildpack.platform.docker.ProgressUpdateEvent.ProgressDetail;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link PullImageUpdateEvent}.
*
* @author Phillip Webb
*/
class PullImageUpdateEventTests extends ProgressUpdateEventTests {
@Test
void getIdReturnsId() {
PullImageUpdateEvent event = (PullImageUpdateEvent) createEvent();
assertThat(event.getId()).isEqualTo("id");
}
@Override
protected ProgressUpdateEvent createEvent(String status, ProgressDetail progressDetail, String progress) {
return new PullImageUpdateEvent("id", status, progressDetail, progress);
}
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.docker;
import org.junit.jupiter.api.Test;
import org.springframework.boot.buildpack.platform.json.AbstractJsonTests;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link PullImageUpdateEvent}.
*
* @author Phillip Webb
*/
class PullUpdateEventTests extends AbstractJsonTests {
@Test
void readValueWhenFullDeserializesJson() throws Exception {
PullImageUpdateEvent event = getObjectMapper().readValue(getContent("pull-update-full.json"),
PullImageUpdateEvent.class);
assertThat(event.getId()).isEqualTo("4f4fb700ef54");
assertThat(event.getStatus()).isEqualTo("Extracting");
assertThat(event.getProgressDetail().getCurrent()).isEqualTo(16);
assertThat(event.getProgressDetail().getTotal()).isEqualTo(32);
assertThat(event.getProgress()).isEqualTo("[==================================================>] 32B/32B");
}
@Test
void readValueWhenMinimalDeserializesJson() throws Exception {
PullImageUpdateEvent event = getObjectMapper().readValue(getContent("pull-update-minimal.json"),
PullImageUpdateEvent.class);
assertThat(event.getId()).isNull();
assertThat(event.getStatus()).isEqualTo("Status: Downloaded newer image for cloudfoundry/cnb:bionic");
assertThat(event.getProgressDetail()).isNull();
assertThat(event.getProgress()).isNull();
}
@Test
void readValueWhenEmptyDetailsDeserializesJson() throws Exception {
PullImageUpdateEvent event = getObjectMapper().readValue(getContent("pull-with-empty-details.json"),
PullImageUpdateEvent.class);
assertThat(event.getId()).isEqualTo("d837a2a1365e");
assertThat(event.getStatus()).isEqualTo("Pulling fs layer");
assertThat(event.getProgressDetail()).isNull();
assertThat(event.getProgress()).isNull();
}
}

View File

@@ -0,0 +1,85 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.docker;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link TotalProgressBar}.
*
* @author Phillip Webb
*/
class TotalProgressBarTests {
@Test
void withPrefixAndBookends() {
TestPrintStream out = new TestPrintStream();
TotalProgressBar bar = new TotalProgressBar("prefix:", '#', true, out);
assertThat(out).hasToString("prefix: [ ");
bar.accept(new TotalProgressEvent(10));
assertThat(out.toString()).isEqualTo("prefix: [ #####");
bar.accept(new TotalProgressEvent(50));
assertThat(out.toString()).isEqualTo("prefix: [ #########################");
bar.accept(new TotalProgressEvent(100));
assertThat(out.toString()).isEqualTo("prefix: [ ################################################## ]\n");
}
@Test
void withoutPrefix() {
TestPrintStream out = new TestPrintStream();
TotalProgressBar bar = new TotalProgressBar(null, '#', true, out);
assertThat(out).hasToString("[ ");
bar.accept(new TotalProgressEvent(10));
assertThat(out.toString()).isEqualTo("[ #####");
bar.accept(new TotalProgressEvent(50));
assertThat(out.toString()).isEqualTo("[ #########################");
bar.accept(new TotalProgressEvent(100));
assertThat(out.toString()).isEqualTo("[ ################################################## ]\n");
}
@Test
void withoutBookends() {
TestPrintStream out = new TestPrintStream();
TotalProgressBar bar = new TotalProgressBar("", '.', false, out);
assertThat(out).hasToString("");
bar.accept(new TotalProgressEvent(10));
assertThat(out.toString()).isEqualTo(".....");
bar.accept(new TotalProgressEvent(50));
assertThat(out.toString()).isEqualTo(".........................");
bar.accept(new TotalProgressEvent(100));
assertThat(out.toString()).isEqualTo("..................................................\n");
}
static class TestPrintStream extends PrintStream {
TestPrintStream() {
super(new ByteArrayOutputStream());
}
@Override
public String toString() {
return this.out.toString();
}
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.docker;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
/**
* Tests for {@link TotalProgressEvent}.
*
* @author Phillip Webb
*/
class TotalProgressEventTests {
@Test
void create() {
assertThat(new TotalProgressEvent(0).getPercent()).isEqualTo(0);
assertThat(new TotalProgressEvent(10).getPercent()).isEqualTo(10);
assertThat(new TotalProgressEvent(100).getPercent()).isEqualTo(100);
}
@Test
void createWhenPercentLessThanZeroThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> new TotalProgressEvent(-1))
.withMessage("Percent must be in the range 0 to 100");
}
@Test
void createWhenEventMoreThanOneHundredThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> new TotalProgressEvent(101))
.withMessage("Percent must be in the range 0 to 100");
}
}

View File

@@ -0,0 +1,85 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.docker;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.boot.buildpack.platform.json.AbstractJsonTests;
import org.springframework.boot.buildpack.platform.json.JsonStream;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link TotalProgressPullListener}.
*
* @author Phillip Webb
*/
class TotalProgressPullListenerTests extends AbstractJsonTests {
@Test
void totalProgress() throws Exception {
List<Integer> progress = new ArrayList<>();
TotalProgressPullListener listener = new TotalProgressPullListener((event) -> progress.add(event.getPercent()));
run(listener);
int last = 0;
for (Integer update : progress) {
assertThat(update).isGreaterThanOrEqualTo(last);
last = update;
}
assertThat(last).isEqualTo(100);
}
@Test
@Disabled("For visual inspection")
void totalProgressUpdatesSmoothly() throws Exception {
TestTotalProgressPullListener listener = new TestTotalProgressPullListener(
new TotalProgressBar("Pulling layers:"));
run(listener);
}
private void run(TotalProgressPullListener listener) throws IOException {
JsonStream jsonStream = new JsonStream(getObjectMapper());
listener.onStart();
jsonStream.get(getContent("pull-stream.json"), PullImageUpdateEvent.class, listener::onUpdate);
listener.onFinish();
}
private static class TestTotalProgressPullListener extends TotalProgressPullListener {
TestTotalProgressPullListener(Consumer<TotalProgressEvent> consumer) {
super(consumer);
}
@Override
public void onUpdate(PullImageUpdateEvent event) {
super.onUpdate(event);
try {
Thread.sleep(10);
}
catch (InterruptedException ex) {
}
}
}
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.docker.type;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import org.junit.jupiter.api.Test;
import org.skyscreamer.jsonassert.JSONAssert;
import org.springframework.boot.buildpack.platform.json.AbstractJsonTests;
import org.springframework.util.StreamUtils;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
/**
* Tests for {@link ContainerConfig}.
*
* @author Phillip Webb
*/
class ContainerConfigTests extends AbstractJsonTests {
@Test
void ofWhenImageReferenceIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> ContainerConfig.of(null, (update) -> {
})).withMessage("ImageReference must not be null");
}
@Test
void ofWhenUpdateIsNullThrowsException() {
ImageReference imageReference = ImageReference.of("ubuntu:bionic");
assertThatIllegalArgumentException().isThrownBy(() -> ContainerConfig.of(imageReference, null))
.withMessage("Update must not be null");
}
@Test
void writeToWritesJson() throws Exception {
ImageReference imageReference = ImageReference.of("ubuntu:bionic");
ContainerConfig containerConfig = ContainerConfig.of(imageReference, (update) -> {
update.withUser("root");
update.withCommand("ls", "-l");
update.withArgs("-h");
update.withLabel("spring", "boot");
update.withBind("bind-source", "bind-dest");
});
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
containerConfig.writeTo(outputStream);
String actualJson = new String(outputStream.toByteArray(), StandardCharsets.UTF_8);
String expectedJson = StreamUtils.copyToString(getContent("container-config.json"), StandardCharsets.UTF_8);
JSONAssert.assertEquals(expectedJson, actualJson, false);
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.docker.type;
import org.junit.jupiter.api.Test;
import org.springframework.boot.buildpack.platform.io.TarArchive;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link ContainerContent}.
*
* @author Phillip Webb
*/
class ContainerContentTests {
@Test
void ofWhenArchiveIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> ContainerContent.of(null))
.withMessage("Archive must not be null");
}
@Test
void ofWhenDestinationPathIsNullThrowsException() {
TarArchive archive = mock(TarArchive.class);
assertThatIllegalArgumentException().isThrownBy(() -> ContainerContent.of(archive, null))
.withMessage("DestinationPath must not be empty");
}
@Test
void ofWhenDestinationPathIsEmptyThrowsException() {
TarArchive archive = mock(TarArchive.class);
assertThatIllegalArgumentException().isThrownBy(() -> ContainerContent.of(archive, ""))
.withMessage("DestinationPath must not be empty");
}
@Test
void ofCreatesContainerContent() {
TarArchive archive = mock(TarArchive.class);
ContainerContent content = ContainerContent.of(archive);
assertThat(content.getArchive()).isSameAs(archive);
assertThat(content.getDestinationPath()).isEqualTo("/");
}
@Test
void ofWithDestinationPathCreatesContainerContent() {
TarArchive archive = mock(TarArchive.class);
ContainerContent content = ContainerContent.of(archive, "/test");
assertThat(content.getArchive()).isSameAs(archive);
assertThat(content.getDestinationPath()).isEqualTo("/test");
}
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.docker.type;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
/**
* Tests for {@link ContainerReference}.
*
* @author Phillip Webb
*/
class ContainerReferenceTests {
@Test
void ofCreatesInstance() {
ContainerReference reference = ContainerReference
.of("92691aec176333f7ae890de9aaeeafef11166efcaa3908edf83eb44a5c943781");
assertThat(reference.toString()).isEqualTo("92691aec176333f7ae890de9aaeeafef11166efcaa3908edf83eb44a5c943781");
}
@Test
void ofWhenNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> ContainerReference.of(null))
.withMessage("Value must not be empty");
}
@Test
void ofWhenEmptyThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> ContainerReference.of(""))
.withMessage("Value must not be empty");
}
@Test
void hashCodeAndEquals() {
ContainerReference r1 = ContainerReference
.of("92691aec176333f7ae890de9aaeeafef11166efcaa3908edf83eb44a5c943781");
ContainerReference r2 = ContainerReference
.of("92691aec176333f7ae890de9aaeeafef11166efcaa3908edf83eb44a5c943781");
ContainerReference r3 = ContainerReference
.of("02691aec176333f7ae890de9aaeeafef11166efcaa3908edf83eb44a5c943781");
assertThat(r1.hashCode()).isEqualTo(r2.hashCode());
assertThat(r1).isEqualTo(r1).isEqualTo(r2).isNotEqualTo(r3);
}
}

View File

@@ -0,0 +1,101 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.docker.type;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.junit.jupiter.api.Test;
import org.skyscreamer.jsonassert.JSONAssert;
import org.springframework.boot.buildpack.platform.io.Owner;
import org.springframework.boot.buildpack.platform.json.AbstractJsonTests;
import org.springframework.util.StreamUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link ImageArchive}.
*
* @author Phillip Webb
*/
class ImageArchiveTests extends AbstractJsonTests {
static final Instant CREATE_DATE = OffsetDateTime.of(1906, 12, 9, 11, 30, 0, 0, ZoneOffset.UTC).toInstant();
@Test
void fromImageWritesToValidArchiveTar() throws Exception {
Image image = Image.of(getContent("image.json"));
ImageArchive archive = ImageArchive.from(image, (update) -> {
update.withNewLayer(Layer.of((layout) -> layout.folder("/spring", Owner.ROOT)));
update.withCreateDate(CREATE_DATE);
update.withTag(ImageReference.of("pack.local/builder/6b7874626575656b6162"));
});
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
archive.writeTo(outputStream);
try (TarArchiveInputStream tar = new TarArchiveInputStream(
new ByteArrayInputStream(outputStream.toByteArray()))) {
TarArchiveEntry layerEntry = tar.getNextTarEntry();
byte[] layerContent = read(tar, layerEntry.getSize());
TarArchiveEntry configEntry = tar.getNextTarEntry();
byte[] configContent = read(tar, configEntry.getSize());
TarArchiveEntry manifestEntry = tar.getNextTarEntry();
byte[] manifestContent = read(tar, manifestEntry.getSize());
assertThat(tar.getNextTarEntry()).isNull();
assertExpectedLayer(layerEntry, layerContent);
assertExpectedConfig(configEntry, configContent);
assertExpectedManifest(manifestEntry, manifestContent);
}
}
private void assertExpectedLayer(TarArchiveEntry entry, byte[] content) throws Exception {
assertThat(entry.getName()).isEqualTo("/bb09e17fd1bd2ee47155f1349645fcd9fff31e1247c7ed99cad469f1c16a4216.tar");
try (TarArchiveInputStream tar = new TarArchiveInputStream(new ByteArrayInputStream(content))) {
TarArchiveEntry contentEntry = tar.getNextTarEntry();
assertThat(contentEntry.getName()).isEqualTo("/spring/");
}
}
private void assertExpectedConfig(TarArchiveEntry entry, byte[] content) throws Exception {
assertThat(entry.getName()).isEqualTo("/d1872169d781cff5e1aa22d111f636bef0c57e1c358ca3861e3d33a5bdb1b4a5.json");
String actualJson = new String(content, StandardCharsets.UTF_8);
String expectedJson = StreamUtils.copyToString(getContent("image-archive-config.json"), StandardCharsets.UTF_8);
JSONAssert.assertEquals(expectedJson, actualJson, false);
}
private void assertExpectedManifest(TarArchiveEntry entry, byte[] content) throws Exception {
assertThat(entry.getName()).isEqualTo("/manifest.json");
String actualJson = new String(content, StandardCharsets.UTF_8);
String expectedJson = StreamUtils.copyToString(getContent("image-archive-manifest.json"),
StandardCharsets.UTF_8);
JSONAssert.assertEquals(expectedJson, actualJson, false);
}
private byte[] read(TarArchiveInputStream tar, long size) throws IOException {
byte[] content = new byte[(int) size];
tar.read(content);
return content;
}
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.docker.type;
import java.io.IOException;
import java.util.Map;
import org.junit.jupiter.api.Test;
import org.springframework.boot.buildpack.platform.json.AbstractJsonTests;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
/**
* Tests for {@link ImageConfig}.
*
* @author Phillip Webb
*/
class ImageConfigTests extends AbstractJsonTests {
@Test
void getEnvContainsParsedValues() throws Exception {
ImageConfig imageConfig = getImageConfig();
Map<String, String> env = imageConfig.getEnv();
assertThat(env).contains(entry("PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"),
entry("CNB_USER_ID", "2000"), entry("CNB_GROUP_ID", "2000"),
entry("CNB_STACK_ID", "org.cloudfoundry.stacks.cflinuxfs3"));
}
@Test
void getLabelsReturnsLabels() throws Exception {
ImageConfig imageConfig = getImageConfig();
Map<String, String> lables = imageConfig.getLabels();
assertThat(lables).hasSize(4).contains(entry("io.buildpacks.stack.id", "org.cloudfoundry.stacks.cflinuxfs3"));
}
@Test
void updateWithLabelUpdatesLabels() throws Exception {
ImageConfig imageConfig = getImageConfig();
ImageConfig updatedImageConfig = imageConfig
.copy((update) -> update.withLabel("io.buildpacks.stack.id", "test"));
assertThat(imageConfig.getLabels()).hasSize(4)
.contains(entry("io.buildpacks.stack.id", "org.cloudfoundry.stacks.cflinuxfs3"));
assertThat(updatedImageConfig.getLabels()).hasSize(4).contains(entry("io.buildpacks.stack.id", "test"));
}
private ImageConfig getImageConfig() throws IOException {
return new ImageConfig(getObjectMapper().readTree(getContent("image-config.json")));
}
}

View File

@@ -0,0 +1,109 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.docker.type;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
/**
* Tests for {@link ImageName}.
*
* @author Phillip Webb
*/
class ImageNameTests {
@Test
void ofWhenNameOnlyCreatesImageName() {
ImageName imageName = ImageName.of("ubuntu");
assertThat(imageName.toString()).isEqualTo("docker.io/library/ubuntu");
assertThat(imageName.getDomain()).isEqualTo("docker.io");
assertThat(imageName.getName()).isEqualTo("library/ubuntu");
}
@Test
void ofWhenSlashedNameCreatesImageName() {
ImageName imageName = ImageName.of("canonical/ubuntu");
assertThat(imageName.toString()).isEqualTo("docker.io/canonical/ubuntu");
assertThat(imageName.getDomain()).isEqualTo("docker.io");
assertThat(imageName.getName()).isEqualTo("canonical/ubuntu");
}
@Test
void ofWhenLocalhostNameCreatesImageName() {
ImageName imageName = ImageName.of("localhost/canonical/ubuntu");
assertThat(imageName.toString()).isEqualTo("localhost/canonical/ubuntu");
assertThat(imageName.getDomain()).isEqualTo("localhost");
assertThat(imageName.getName()).isEqualTo("canonical/ubuntu");
}
@Test
void ofWhenDomainAndNameCreatesImageName() {
ImageName imageName = ImageName.of("repo.spring.io/canonical/ubuntu");
assertThat(imageName.toString()).isEqualTo("repo.spring.io/canonical/ubuntu");
assertThat(imageName.getDomain()).isEqualTo("repo.spring.io");
assertThat(imageName.getName()).isEqualTo("canonical/ubuntu");
}
@Test
void ofWhenDomainNameAndPortCreatesImageName() {
ImageName imageName = ImageName.of("repo.spring.io:8080/canonical/ubuntu");
assertThat(imageName.toString()).isEqualTo("repo.spring.io:8080/canonical/ubuntu");
assertThat(imageName.getDomain()).isEqualTo("repo.spring.io:8080");
assertThat(imageName.getName()).isEqualTo("canonical/ubuntu");
}
@Test
void ofWhenSimpleNameAndPortCreatesImageName() {
ImageName imageName = ImageName.of("repo:8080/canonical/ubuntu");
assertThat(imageName.toString()).isEqualTo("repo:8080/canonical/ubuntu");
assertThat(imageName.getDomain()).isEqualTo("repo:8080");
assertThat(imageName.getName()).isEqualTo("canonical/ubuntu");
}
@Test
void ofWhenLegacyDomainUsesNewDomain() {
ImageName imageName = ImageName.of("index.docker.io/ubuntu");
assertThat(imageName.toString()).isEqualTo("docker.io/library/ubuntu");
assertThat(imageName.getDomain()).isEqualTo("docker.io");
assertThat(imageName.getName()).isEqualTo("library/ubuntu");
}
@Test
void ofWhenNameIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> ImageName.of(null))
.withMessage("Value must not be empty");
}
@Test
void ofWhenNameIsEmptyThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> ImageName.of("")).withMessage("Value must not be empty");
}
@Test
void hashCodeAndEquals() {
ImageName n1 = ImageName.of("ubuntu");
ImageName n2 = ImageName.of("library/ubuntu");
ImageName n3 = ImageName.of("docker.io/library/ubuntu");
ImageName n4 = ImageName.of("index.docker.io/library/ubuntu");
ImageName n5 = ImageName.of("alpine");
assertThat(n1.hashCode()).isEqualTo(n2.hashCode()).isEqualTo(n3.hashCode()).isEqualTo(n4.hashCode());
assertThat(n1).isEqualTo(n1).isEqualTo(n2).isEqualTo(n3).isEqualTo(n4).isNotEqualTo(n5);
}
}

View File

@@ -0,0 +1,235 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.docker.type;
import java.io.File;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
/**
* Tests for {@link ImageReference}.
*
* @author Phillip Webb
*/
class ImageReferenceTests {
@Test
void ofSimpleName() {
ImageReference reference = ImageReference.of("ubuntu");
assertThat(reference.getDomain()).isEqualTo("docker.io");
assertThat(reference.getName()).isEqualTo("library/ubuntu");
assertThat(reference.getTag()).isNull();
assertThat(reference.getDigest()).isNull();
assertThat(reference.toString()).isEqualTo("docker.io/library/ubuntu");
}
@Test
void ofLibrarySlashName() {
ImageReference reference = ImageReference.of("library/ubuntu");
assertThat(reference.getDomain()).isEqualTo("docker.io");
assertThat(reference.getName()).isEqualTo("library/ubuntu");
assertThat(reference.getTag()).isNull();
assertThat(reference.getDigest()).isNull();
assertThat(reference.toString()).isEqualTo("docker.io/library/ubuntu");
}
@Test
void ofSlashName() {
ImageReference reference = ImageReference.of("adoptopenjdk/openjdk11");
assertThat(reference.getDomain()).isEqualTo("docker.io");
assertThat(reference.getName()).isEqualTo("adoptopenjdk/openjdk11");
assertThat(reference.getTag()).isNull();
assertThat(reference.getDigest()).isNull();
assertThat(reference.toString()).isEqualTo("docker.io/adoptopenjdk/openjdk11");
}
@Test
void ofCustomDomain() {
ImageReference reference = ImageReference.of("repo.example.com/java/jdk");
assertThat(reference.getDomain()).isEqualTo("repo.example.com");
assertThat(reference.getName()).isEqualTo("java/jdk");
assertThat(reference.getTag()).isNull();
assertThat(reference.getDigest()).isNull();
assertThat(reference.toString()).isEqualTo("repo.example.com/java/jdk");
}
@Test
void ofCustomDomainAndPort() {
ImageReference reference = ImageReference.of("repo.example.com:8080/java/jdk");
assertThat(reference.getDomain()).isEqualTo("repo.example.com:8080");
assertThat(reference.getName()).isEqualTo("java/jdk");
assertThat(reference.getTag()).isNull();
assertThat(reference.getDigest()).isNull();
assertThat(reference.toString()).isEqualTo("repo.example.com:8080/java/jdk");
}
@Test
void ofLegacyDomain() {
ImageReference reference = ImageReference.of("index.docker.io/ubuntu");
assertThat(reference.getDomain()).isEqualTo("docker.io");
assertThat(reference.getName()).isEqualTo("library/ubuntu");
assertThat(reference.getTag()).isNull();
assertThat(reference.getDigest()).isNull();
assertThat(reference.toString()).isEqualTo("docker.io/library/ubuntu");
}
@Test
void ofNameAndTag() {
ImageReference reference = ImageReference.of("ubuntu:bionic");
assertThat(reference.getDomain()).isEqualTo("docker.io");
assertThat(reference.getName()).isEqualTo("library/ubuntu");
assertThat(reference.getTag()).isEqualTo("bionic");
assertThat(reference.getDigest()).isNull();
assertThat(reference.toString()).isEqualTo("docker.io/library/ubuntu:bionic");
}
@Test
void ofNameAndDigest() {
ImageReference reference = ImageReference
.of("ubuntu@sha256:6e9f67fa63b0323e9a1e587fd71c561ba48a034504fb804fd26fd8800039835d");
assertThat(reference.getDomain()).isEqualTo("docker.io");
assertThat(reference.getName()).isEqualTo("library/ubuntu");
assertThat(reference.getTag()).isNull();
assertThat(reference.getDigest())
.isEqualTo("sha256:6e9f67fa63b0323e9a1e587fd71c561ba48a034504fb804fd26fd8800039835d");
assertThat(reference.toString()).isEqualTo(
"docker.io/library/ubuntu@sha256:6e9f67fa63b0323e9a1e587fd71c561ba48a034504fb804fd26fd8800039835d");
}
@Test
void ofNameAndTagAndDigest() {
ImageReference reference = ImageReference
.of("ubuntu:bionic@sha256:6e9f67fa63b0323e9a1e587fd71c561ba48a034504fb804fd26fd8800039835d");
assertThat(reference.getDomain()).isEqualTo("docker.io");
assertThat(reference.getName()).isEqualTo("library/ubuntu");
assertThat(reference.getTag()).isEqualTo("bionic");
assertThat(reference.getDigest())
.isEqualTo("sha256:6e9f67fa63b0323e9a1e587fd71c561ba48a034504fb804fd26fd8800039835d");
assertThat(reference.toString()).isEqualTo(
"docker.io/library/ubuntu:bionic@sha256:6e9f67fa63b0323e9a1e587fd71c561ba48a034504fb804fd26fd8800039835d");
}
@Test
void ofCustomDomainAndPortWithTag() {
ImageReference reference = ImageReference.of(
"example.com:8080/canonical/ubuntu:bionic@sha256:6e9f67fa63b0323e9a1e587fd71c561ba48a034504fb804fd26fd8800039835d");
assertThat(reference.getDomain()).isEqualTo("example.com:8080");
assertThat(reference.getName()).isEqualTo("canonical/ubuntu");
assertThat(reference.getTag()).isEqualTo("bionic");
assertThat(reference.getDigest())
.isEqualTo("sha256:6e9f67fa63b0323e9a1e587fd71c561ba48a034504fb804fd26fd8800039835d");
assertThat(reference.toString()).isEqualTo(
"example.com:8080/canonical/ubuntu:bionic@sha256:6e9f67fa63b0323e9a1e587fd71c561ba48a034504fb804fd26fd8800039835d");
}
@Test
void ofImageName() {
ImageReference reference = ImageReference.of(ImageName.of("ubuntu"));
assertThat(reference.toString()).isEqualTo("docker.io/library/ubuntu");
}
@Test
void ofImageNameAndTag() {
ImageReference reference = ImageReference.of(ImageName.of("ubuntu"), "bionic");
assertThat(reference.toString()).isEqualTo("docker.io/library/ubuntu:bionic");
}
@Test
void ofImageNameTagAndDigest() {
ImageReference reference = ImageReference.of(ImageName.of("ubuntu"), "bionic",
"sha256:6e9f67fa63b0323e9a1e587fd71c561ba48a034504fb804fd26fd8800039835d");
assertThat(reference.toString()).isEqualTo(
"docker.io/library/ubuntu:bionic@sha256:6e9f67fa63b0323e9a1e587fd71c561ba48a034504fb804fd26fd8800039835d");
}
@Test
void forJarFile() {
assertForJarFile("spring-boot.2.0.0.BUILD-SNAPSHOT.jar", "library/spring-boot", "2.0.0.BUILD-SNAPSHOT");
assertForJarFile("spring-boot.2.0.0.M1.jar", "library/spring-boot", "2.0.0.M1");
assertForJarFile("spring-boot.2.0.0.RC1.jar", "library/spring-boot", "2.0.0.RC1");
assertForJarFile("spring-boot.2.0.0.RELEASE.jar", "library/spring-boot", "2.0.0.RELEASE");
assertForJarFile("sample-0.0.1-SNAPSHOT.jar", "library/sample", "0.0.1-SNAPSHOT");
assertForJarFile("sample-0.0.1.jar", "library/sample", "0.0.1");
}
private void assertForJarFile(String jarFile, String expectedName, String expectedTag) {
ImageReference reference = ImageReference.forJarFile(new File(jarFile));
assertThat(reference.getName()).isEqualTo(expectedName);
assertThat(reference.getTag()).isEqualTo(expectedTag);
}
@Test
void randomGeneratesRandomName() {
String prefix = "pack.local/builder/";
ImageReference random = ImageReference.random(prefix);
assertThat(random.toString()).startsWith(prefix).hasSize(prefix.length() + 10);
ImageReference another = ImageReference.random(prefix);
int attempts = 0;
while (another.equals(random)) {
assertThat(attempts).as("Duplicate results").isLessThan(10);
another = ImageReference.random(prefix);
attempts++;
}
}
@Test
void randomWithLengthGeneratesRandomName() {
String prefix = "pack.local/builder/";
ImageReference random = ImageReference.random(prefix, 20);
assertThat(random.toString()).startsWith(prefix).hasSize(prefix.length() + 20);
}
@Test
void randomWherePrefixIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> ImageReference.random(null))
.withMessage("Prefix must not be null");
}
@Test
void inTaggedFormWhenHasDigestThrowsException() {
ImageReference reference = ImageReference
.of("ubuntu@sha256:6e9f67fa63b0323e9a1e587fd71c561ba48a034504fb804fd26fd8800039835d");
assertThatIllegalStateException().isThrownBy(() -> reference.inTaggedForm()).withMessage(
"Image reference 'docker.io/library/ubuntu@sha256:6e9f67fa63b0323e9a1e587fd71c561ba48a034504fb804fd26fd8800039835d' cannot contain a digest");
}
@Test
void inTaggedFormWhenHasNoTagUsesLatest() {
ImageReference reference = ImageReference.of("ubuntu");
assertThat(reference.inTaggedForm().toString()).isEqualTo("docker.io/library/ubuntu:latest");
}
@Test
void inTaggedFormWhenHasTagUsesTag() {
ImageReference reference = ImageReference.of("ubuntu:bionic");
assertThat(reference.inTaggedForm().toString()).isEqualTo("docker.io/library/ubuntu:bionic");
}
@Test
void equalsAndHashCode() {
ImageReference r1 = ImageReference.of("ubuntu:bionic");
ImageReference r2 = ImageReference.of("docker.io/library/ubuntu:bionic");
ImageReference r3 = ImageReference.of("docker.io/library/ubuntu:latest");
assertThat(r1.hashCode()).isEqualTo(r2.hashCode());
assertThat(r1).isEqualTo(r1).isEqualTo(r2).isNotEqualTo(r3);
}
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.docker.type;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import org.junit.jupiter.api.Test;
import org.springframework.boot.buildpack.platform.json.AbstractJsonTests;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
/**
* Tests for {@link Image}.
*
* @author Phillip Webb
*/
class ImageTests extends AbstractJsonTests {
@Test
void getConfigEnvContainsParsedValues() throws Exception {
Image image = getImage();
Map<String, String> env = image.getConfig().getEnv();
assertThat(env).contains(entry("PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"),
entry("CNB_USER_ID", "2000"), entry("CNB_GROUP_ID", "2000"),
entry("CNB_STACK_ID", "org.cloudfoundry.stacks.cflinuxfs3"));
}
@Test
void getConfigLabelsReturnsLabels() throws Exception {
Image image = getImage();
Map<String, String> lables = image.getConfig().getLabels();
assertThat(lables).contains(entry("io.buildpacks.stack.id", "org.cloudfoundry.stacks.cflinuxfs3"));
}
@Test
void getLayersReturnsImageLayers() throws Exception {
Image image = getImage();
List<LayerId> layers = image.getLayers();
assertThat(layers).hasSize(46);
assertThat(layers.get(0).toString())
.isEqualTo("sha256:733a8e5ce32984099ef675fce04730f6e2a6dcfdf5bd292fea01a8f936265342");
assertThat(layers.get(45).toString())
.isEqualTo("sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef");
}
@Test
void getOsReturnsOs() throws Exception {
Image image = getImage();
assertThat(image.getOs()).isEqualTo("linux");
}
private Image getImage() throws IOException {
return Image.of(getContent("image.json"));
}
}

View File

@@ -0,0 +1,82 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.docker.type;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
/**
* Test for {@link LayerId}.
*
* @author Phillip Webb
*/
class LayerIdTests {
@Test
void ofReturnsLayerId() {
LayerId id = LayerId.of("sha256:9a183e56c86d376b408bdf922746d0a657f62b0e18c7c8f82a496b87710c576f");
assertThat(id.getAlgorithm()).isEqualTo("sha256");
assertThat(id.getHash()).isEqualTo("9a183e56c86d376b408bdf922746d0a657f62b0e18c7c8f82a496b87710c576f");
assertThat(id.toString()).isEqualTo("sha256:9a183e56c86d376b408bdf922746d0a657f62b0e18c7c8f82a496b87710c576f");
}
@Test
void hashCodeAndEquals() {
LayerId id1 = LayerId.of("sha256:9a183e56c86d376b408bdf922746d0a657f62b0e18c7c8f82a496b87710c576f");
LayerId id2 = LayerId.of("sha256:9a183e56c86d376b408bdf922746d0a657f62b0e18c7c8f82a496b87710c576f");
LayerId id3 = LayerId.of("sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
assertThat(id1.hashCode()).isEqualTo(id2.hashCode());
assertThat(id1).isEqualTo(id1).isEqualTo(id2).isNotEqualTo(id3);
}
@Test
void ofWhenValueIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> LayerId.of((String) null))
.withMessage("Value must not be empty");
}
@Test
void ofWhenValueIsEmptyThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> LayerId.of(" ")).withMessage("Value must not be empty");
}
@Test
void ofSha256Digest() throws Exception {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
digest.update("test".getBytes(StandardCharsets.UTF_8));
LayerId id = LayerId.ofSha256Digest(digest.digest());
assertThat(id.toString()).isEqualTo("sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08");
}
@Test
void ofSha256DigestWhenNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> LayerId.ofSha256Digest((byte[]) null))
.withMessage("Digest must not be null");
}
@Test
void ofSha256DigestWhenWrongLengthThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> LayerId.ofSha256Digest(new byte[31]))
.withMessage("Digest must be exactly 32 bytes");
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.docker.type;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.junit.jupiter.api.Test;
import org.springframework.boot.buildpack.platform.io.Content;
import org.springframework.boot.buildpack.platform.io.IOConsumer;
import org.springframework.boot.buildpack.platform.io.Layout;
import org.springframework.boot.buildpack.platform.io.Owner;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
/**
* Tests for {@link Layer}.
*
* @author Phillip Webb
*/
class LayerTests {
@Test
void ofWhenLayoutIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> Layer.of((IOConsumer<Layout>) null))
.withMessage("Layout must not be null");
}
@Test
void fromTarArchiveWhenTarArchiveIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> Layer.fromTarArchive(null))
.withMessage("TarArchive must not be null");
}
@Test
void ofCreatesLayer() throws Exception {
Layer layer = Layer.of((layout) -> {
layout.folder("/folder", Owner.ROOT);
layout.file("/folder/file", Owner.ROOT, Content.of("test"));
});
assertThat(layer.getId().toString())
.isEqualTo("sha256:8b8a3cea2ba716da6bbb0a3bf7472f235fa08c71a27cec5fbf2de1cf1baa513f");
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
layer.writeTo(outputStream);
try (TarArchiveInputStream tarStream = new TarArchiveInputStream(
new ByteArrayInputStream(outputStream.toByteArray()))) {
assertThat(tarStream.getNextTarEntry().getName()).isEqualTo("/folder/");
assertThat(tarStream.getNextTarEntry().getName()).isEqualTo("/folder/file");
assertThat(tarStream.getNextTarEntry()).isNull();
}
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.docker.type;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
/**
* Tests for {@link RandomString}.
*
* @author Phillip Webb
*/
class RandomStringTests {
@Test
void generateWhenPrefixIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> RandomString.generate(null, 10))
.withMessage("Prefix must not be null");
}
@Test
void generateGeneratesRandomString() {
String s1 = RandomString.generate("abc-", 10);
String s2 = RandomString.generate("abc-", 10);
String s3 = RandomString.generate("abc-", 20);
assertThat(s1).hasSize(14).startsWith("abc-").isNotEqualTo(s2);
assertThat(s3).hasSize(24).startsWith("abc-");
}
}

View File

@@ -0,0 +1,114 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.docker.type;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
/**
* Tests for {@link VolumeName}.
*
* @author Phillip Webb
*/
class VolumeNameTests {
@Test
void randomWhenPrefixIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> VolumeName.random(null))
.withMessage("Prefix must not be null");
}
@Test
void randomGeneratesRandomString() {
VolumeName v1 = VolumeName.random("abc-");
VolumeName v2 = VolumeName.random("abc-");
assertThat(v1.toString()).startsWith("abc-").hasSize(14);
assertThat(v2.toString()).startsWith("abc-").hasSize(14);
assertThat(v1).isNotEqualTo(v2);
assertThat(v1.toString()).isNotEqualTo(v2.toString());
}
@Test
void randomStringWithLengthGeneratesRandomString() {
VolumeName v1 = VolumeName.random("abc-", 20);
VolumeName v2 = VolumeName.random("abc-", 20);
assertThat(v1.toString()).startsWith("abc-").hasSize(24);
assertThat(v2.toString()).startsWith("abc-").hasSize(24);
assertThat(v1).isNotEqualTo(v2);
assertThat(v1.toString()).isNotEqualTo(v2.toString());
}
@Test
void basedOnWhenSourceIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> VolumeName.basedOn(null, "prefix", "suffix", 6))
.withMessage("Source must not be null");
}
@Test
void basedOnWhenNameExtractorIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> VolumeName.basedOn("test", null, "prefix", "suffix", 6))
.withMessage("NameExtractor must not be null");
}
@Test
void basedOnWhenPrefixIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> VolumeName.basedOn("test", null, "suffix", 6))
.withMessage("Prefix must not be null");
}
@Test
void basedOnWhenSuffixIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> VolumeName.basedOn("test", "prefix", null, 6))
.withMessage("Suffix must not be null");
}
@Test
void basedOnGeneratesHashBasedName() {
VolumeName name = VolumeName.basedOn("index.docker.io/library/myapp:latest", "pack-cache-", ".build", 6);
assertThat(name.toString()).isEqualTo("pack-cache-40a311b545d7.build");
}
@Test
void basedOnWhenSizeIsTooBigThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> VolumeName.basedOn("name", "prefix", "suffix", 33))
.withMessage("DigestLength must be less than or equal to 32");
}
@Test
void ofWhenValueIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> VolumeName.of(null))
.withMessage("Value must not be null");
}
@Test
void ofGeneratesValue() {
VolumeName name = VolumeName.of("test");
assertThat(name.toString()).isEqualTo("test");
}
@Test
void equalsAndHashCode() {
VolumeName n1 = VolumeName.of("test1");
VolumeName n2 = VolumeName.of("test1");
VolumeName n3 = VolumeName.of("test2");
assertThat(n1.hashCode()).isEqualTo(n2.hashCode());
assertThat(n1).isEqualTo(n1).isEqualTo(n2).isNotEqualTo(n3);
}
}

View File

@@ -0,0 +1,82 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.io;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
/**
* Tests for {@link Content}.
*
* @author Phillip Webb
*/
class ContentTests {
@Test
void ofWhenStreamIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> Content.of(1, (IOSupplier<InputStream>) null))
.withMessage("Supplier must not be null");
}
@Test
void ofWhenStreamReturnsWritable() throws Exception {
byte[] bytes = { 1, 2, 3, 4 };
ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
Content writable = Content.of(4, () -> inputStream);
assertThat(writeToAndGetBytes(writable)).isEqualTo(bytes);
}
@Test
void ofWhenStringIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> Content.of((String) null))
.withMessage("String must not be null");
}
@Test
void ofWhenStringReturnsWritable() throws Exception {
Content writable = Content.of("spring");
assertThat(writeToAndGetBytes(writable)).isEqualTo("spring".getBytes(StandardCharsets.UTF_8));
}
@Test
void ofWhenBytesIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> Content.of((byte[]) null))
.withMessage("Bytes must not be null");
}
@Test
void ofWhenBytesReturnsWritable() throws Exception {
byte[] bytes = { 1, 2, 3, 4 };
Content writable = Content.of(bytes);
assertThat(writeToAndGetBytes(writable)).isEqualTo(bytes);
}
private byte[] writeToAndGetBytes(Content writable) throws IOException {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
writable.writeTo(outputStream);
return outputStream.toByteArray();
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.io;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link DefaultOwner}.
*
* @author Phillip Webb
*/
class DefaultOwnerTests {
@Test
void getUidReturnsUid() {
DefaultOwner owner = new DefaultOwner(123, 456);
assertThat(owner.getUid()).isEqualTo(123);
}
@Test
void getGidReturnsGid() {
DefaultOwner owner = new DefaultOwner(123, 456);
assertThat(owner.getGid()).isEqualTo(456);
}
@Test
void toStringReturnsString() {
DefaultOwner owner = new DefaultOwner(123, 456);
assertThat(owner).hasToString("123/456");
}
}

View File

@@ -0,0 +1,99 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.io;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
/**
* Tests for {@link InspectedContent}.
*
* @author Phillip Webb
*/
class InspectedContentTests {
@Test
void ofWhenInputStreamThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> InspectedContent.of((InputStream) null))
.withMessage("InputStream must not be null");
}
@Test
void ofWhenContentIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> InspectedContent.of((Content) null))
.withMessage("Content must not be null");
}
@Test
void ofWhenConsumerIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> InspectedContent.of((IOConsumer<OutputStream>) null))
.withMessage("Writer must not be null");
}
@Test
void ofFromContent() throws Exception {
InspectedContent content = InspectedContent.of(Content.of("test"));
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
content.writeTo(outputStream);
assertThat(outputStream.toByteArray()).containsExactly("test".getBytes(StandardCharsets.UTF_8));
}
@Test
void ofSmallContent() throws Exception {
InputStream inputStream = new ByteArrayInputStream(new byte[] { 0, 1, 2 });
InspectedContent content = InspectedContent.of(inputStream);
assertThat(content.size()).isEqualTo(3);
assertThat(readBytes(content)).containsExactly(0, 1, 2);
}
@Test
void ofLargeContent() throws Exception {
byte[] bytes = new byte[InspectedContent.MEMORY_LIMIT + 3];
System.arraycopy(new byte[] { 0, 1, 2 }, 0, bytes, 0, 3);
InputStream inputStream = new ByteArrayInputStream(bytes);
InspectedContent content = InspectedContent.of(inputStream);
assertThat(content.size()).isEqualTo(bytes.length);
assertThat(readBytes(content)).isEqualTo(bytes);
}
@Test
void ofWithInspector() throws Exception {
InputStream inputStream = new ByteArrayInputStream("test".getBytes(StandardCharsets.UTF_8));
MessageDigest digest = MessageDigest.getInstance("SHA-256");
InspectedContent.of(inputStream, digest::update);
assertThat(digest.digest()).inHexadecimal().contains(0x9f, 0x86, 0xd0, 0x81, 0x88, 0x4c, 0x7d, 0x65, 0x9a, 0x2f,
0xea, 0xa0, 0xc5, 0x5a, 0xd0, 0x15, 0xa3, 0xbf, 0x4f, 0x1b, 0x2b, 0x0b, 0x82, 0x2c, 0xd1, 0x5d, 0x6c,
0x15, 0xb0, 0xf0, 0x0a, 0x08);
}
private byte[] readBytes(InspectedContent content) throws IOException {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
content.writeTo(outputStream);
return outputStream.toByteArray();
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.io;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link Owner}.
*
* @author Phillip Webb
*/
class OwnerTests {
@Test
void ofReturnsNewOwner() {
Owner owner = Owner.of(123, 456);
assertThat(owner.getUid()).isEqualTo(123);
assertThat(owner.getGid()).isEqualTo(456);
}
}

View File

@@ -0,0 +1,93 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.io;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link TarArchive}.
*
* @author Phillip Webb
*/
class TarArchiveTests {
@TempDir
File tempDir;
@Test
void ofWritesTarContent() throws Exception {
Owner owner = Owner.of(123, 456);
TarArchive tarArchive = TarArchive.of((content) -> {
content.folder("/workspace", owner);
content.folder("/layers", owner);
content.folder("/cnb", Owner.ROOT);
content.folder("/cnb/buildpacks", Owner.ROOT);
content.folder("/platform", Owner.ROOT);
content.folder("/platform/env", Owner.ROOT);
});
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
tarArchive.writeTo(outputStream);
try (TarArchiveInputStream tarStream = new TarArchiveInputStream(
new ByteArrayInputStream(outputStream.toByteArray()))) {
List<TarArchiveEntry> entries = new ArrayList<>();
TarArchiveEntry entry = tarStream.getNextTarEntry();
while (entry != null) {
entries.add(entry);
entry = tarStream.getNextTarEntry();
}
assertThat(entries).hasSize(6);
assertThat(entries.get(0).getName()).isEqualTo("/workspace/");
assertThat(entries.get(0).getLongUserId()).isEqualTo(123);
assertThat(entries.get(0).getLongGroupId()).isEqualTo(456);
assertThat(entries.get(2).getName()).isEqualTo("/cnb/");
assertThat(entries.get(2).getLongUserId()).isEqualTo(0);
assertThat(entries.get(2).getLongGroupId()).isEqualTo(0);
}
}
@Test
void fromZipFileReturnsZipFileAdapter() throws Exception {
Owner owner = Owner.of(123, 456);
File file = new File(this.tempDir, "test.zip");
writeTestZip(file);
TarArchive tarArchive = TarArchive.fromZip(file, owner);
assertThat(tarArchive).isInstanceOf(ZipFileTarArchive.class);
}
private void writeTestZip(File file) throws IOException {
try (ZipArchiveOutputStream zip = new ZipArchiveOutputStream(file)) {
ZipArchiveEntry dirEntry = new ZipArchiveEntry("spring/");
zip.putArchiveEntry(dirEntry);
zip.closeArchiveEntry();
}
}
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.io;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link TarLayoutWriter}.
*
* @author Phillip Webb
*/
class TarLayoutWriterTests {
@Test
void writesTarArchive() throws Exception {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try (TarLayoutWriter writer = new TarLayoutWriter(outputStream)) {
writer.folder("/foo", Owner.ROOT);
writer.file("/foo/bar.txt", Owner.of(1, 1), Content.of("test"));
}
try (TarArchiveInputStream tarInputStream = new TarArchiveInputStream(
new ByteArrayInputStream(outputStream.toByteArray()))) {
TarArchiveEntry folderEntry = tarInputStream.getNextTarEntry();
TarArchiveEntry fileEntry = tarInputStream.getNextTarEntry();
byte[] fileContent = new byte[(int) fileEntry.getSize()];
tarInputStream.read(fileContent);
assertThat(tarInputStream.getNextEntry()).isNull();
assertThat(folderEntry.getName()).isEqualTo("/foo/");
assertThat(folderEntry.getMode()).isEqualTo(0755);
assertThat(folderEntry.getLongUserId()).isEqualTo(0);
assertThat(folderEntry.getLongGroupId()).isEqualTo(0);
assertThat(folderEntry.getModTime()).isEqualTo(new Date(TarLayoutWriter.NORMALIZED_MOD_TIME));
assertThat(fileEntry.getName()).isEqualTo("/foo/bar.txt");
assertThat(fileEntry.getMode()).isEqualTo(0644);
assertThat(fileEntry.getLongUserId()).isEqualTo(1);
assertThat(fileEntry.getLongGroupId()).isEqualTo(1);
assertThat(fileEntry.getModTime()).isEqualTo(new Date(TarLayoutWriter.NORMALIZED_MOD_TIME));
assertThat(fileContent).isEqualTo("test".getBytes(StandardCharsets.UTF_8));
}
}
}

View File

@@ -0,0 +1,97 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.io;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.springframework.util.StreamUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
/**
* Tests for {@link ZipFileTarArchive}.
*
* @author Phillip Webb
*/
class ZipFileTarArchiveTests {
@TempDir
File tempDir;
@Test
void createWhenZipIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> new ZipFileTarArchive(null, Owner.ROOT))
.withMessage("Zip must not be null");
}
@Test
void createWhenOwnerIsNullThrowsException() throws Exception {
File file = new File(this.tempDir, "test.zip");
writeTestZip(file);
assertThatIllegalArgumentException().isThrownBy(() -> new ZipFileTarArchive(file, null))
.withMessage("Owner must not be null");
}
@Test
void writeToAdaptsContent() throws Exception {
Owner owner = Owner.of(123, 456);
File file = new File(this.tempDir, "test.zip");
writeTestZip(file);
TarArchive tarArchive = TarArchive.fromZip(file, owner);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
tarArchive.writeTo(outputStream);
try (TarArchiveInputStream tarStream = new TarArchiveInputStream(
new ByteArrayInputStream(outputStream.toByteArray()))) {
TarArchiveEntry dirEntry = tarStream.getNextTarEntry();
assertThat(dirEntry.getName()).isEqualTo("spring/");
assertThat(dirEntry.getLongUserId()).isEqualTo(123);
assertThat(dirEntry.getLongGroupId()).isEqualTo(456);
TarArchiveEntry fileEntry = tarStream.getNextTarEntry();
assertThat(fileEntry.getName()).isEqualTo("spring/boot");
assertThat(fileEntry.getLongUserId()).isEqualTo(123);
assertThat(fileEntry.getLongGroupId()).isEqualTo(456);
assertThat(fileEntry.getSize()).isEqualTo(4);
String fileContent = StreamUtils.copyToString(tarStream, StandardCharsets.UTF_8);
assertThat(fileContent).isEqualTo("test");
}
}
private void writeTestZip(File file) throws IOException {
try (ZipArchiveOutputStream zip = new ZipArchiveOutputStream(file)) {
ZipArchiveEntry dirEntry = new ZipArchiveEntry("spring/");
zip.putArchiveEntry(dirEntry);
zip.closeArchiveEntry();
ZipArchiveEntry fileEntry = new ZipArchiveEntry("spring/boot");
zip.putArchiveEntry(fileEntry);
zip.write("test".getBytes(StandardCharsets.UTF_8));
zip.closeArchiveEntry();
}
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.json;
import java.io.InputStream;
import com.fasterxml.jackson.databind.ObjectMapper;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Abstract base class for JSON based tests.
*
* @author Phillip Webb
*/
public abstract class AbstractJsonTests {
protected final ObjectMapper getObjectMapper() {
return SharedObjectMapper.get();
}
protected final InputStream getContent(String name) {
InputStream result = getClass().getResourceAsStream(name);
assertThat(result).as("JSON source " + name).isNotNull();
return result;
}
}

View File

@@ -0,0 +1,83 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.json;
import java.util.ArrayList;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link JsonStream}.
*
* @author Phillip Webb
*/
class JsonStreamTests extends AbstractJsonTests {
private JsonStream jsonStream;
JsonStreamTests() {
this.jsonStream = new JsonStream(getObjectMapper());
}
@Test
void getWhenReadingObjectNodeReturnsNodes() throws Exception {
List<ObjectNode> result = new ArrayList<>();
this.jsonStream.get(getContent("stream.json"), result::add);
assertThat(result).hasSize(595);
assertThat(result.get(594).toString()).contains("Status: Downloaded newer image for cloudfoundry/cnb:bionic");
}
@Test
void getWhenReadTypesReturnsTypes() throws Exception {
List<TestEvent> result = new ArrayList<>();
this.jsonStream.get(getContent("stream.json"), TestEvent.class, result::add);
assertThat(result).hasSize(595);
assertThat(result.get(1).getId()).isEqualTo("5667fdb72017");
assertThat(result.get(594).getStatus()).isEqualTo("Status: Downloaded newer image for cloudfoundry/cnb:bionic");
}
/**
* Event for type deserialization tests.
*/
static class TestEvent {
private final String id;
private final String status;
@JsonCreator
TestEvent(String id, String status) {
this.id = id;
this.status = status;
}
String getId() {
return this.id;
}
String getStatus() {
return this.status;
}
}
}

View File

@@ -0,0 +1,122 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.json;
import java.io.IOException;
import java.io.InputStream;
import java.lang.invoke.MethodHandles;
import com.fasterxml.jackson.databind.JsonNode;
import org.junit.jupiter.api.Test;
import org.springframework.boot.buildpack.platform.json.MappedObjectTests.TestMappedObject.Person;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link MappedObject}.
*
* @author Phillip Webb
*/
class MappedObjectTests extends AbstractJsonTests {
private final TestMappedObject mapped;
MappedObjectTests() throws IOException {
this.mapped = TestMappedObject.of(getContent("test-mapped-object.json"));
}
@Test
void ofReadsJson() throws Exception {
assertThat(this.mapped.getNode()).isNotNull();
}
@Test
void valueAtWhenStringReturnsValue() {
assertThat(this.mapped.valueAt("/string", String.class)).isEqualTo("stringvalue");
}
@Test
void valueAtWhenStringArrayReturnsValue() {
assertThat(this.mapped.valueAt("/stringarray", String[].class)).containsExactly("a", "b");
}
@Test
void valueAtWhenMissingReturnsNull() {
assertThat(this.mapped.valueAt("/missing", String.class)).isNull();
}
@Test
void valueAtWhenInterfaceReturnsProxy() {
Person person = this.mapped.valueAt("/person", Person.class);
assertThat(person.getName().getFirst()).isEqualTo("spring");
assertThat(person.getName().getLast()).isEqualTo("boot");
}
@Test
void valueAtWhenInterfaceAndMissingReturnsProxy() {
Person person = this.mapped.valueAt("/missing", Person.class);
assertThat(person.getName().getFirst()).isNull();
assertThat(person.getName().getLast()).isNull();
}
@Test
void valueAtWhenActualPropertyStartsWithUppercaseReturnsValue() {
assertThat(this.mapped.valueAt("/startsWithUppercase", String.class)).isEqualTo("value");
}
@Test
void valueAtWhenDefaultMethodReturnsValue() {
Person person = this.mapped.valueAt("/person", Person.class);
assertThat(person.getName().getFullName()).isEqualTo("dr spring boot");
}
/**
* {@link MappedObject} for testing.
*/
static class TestMappedObject extends MappedObject {
TestMappedObject(JsonNode node) {
super(node, MethodHandles.lookup());
}
static TestMappedObject of(InputStream content) throws IOException {
return of(content, TestMappedObject::new);
}
interface Person {
Name getName();
interface Name {
String getFirst();
String getLast();
default String getFullName() {
String title = valueAt(this, "/title", String.class);
return title + " " + getFirst() + " " + getLast();
}
}
}
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.json;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link SharedObjectMapper}.
*
* @author Phillip Webb
*/
class SharedObjectMapperTests {
@Test
void getReturnsConfiguredObjectMapper() {
ObjectMapper mapper = SharedObjectMapper.get();
assertThat(mapper).isNotNull();
assertThat(mapper.getRegisteredModuleIds()).contains(new ParameterNamesModule().getTypeId());
assertThat(SerializationFeature.INDENT_OUTPUT
.enabledIn(mapper.getSerializationConfig().getSerializationFeatures())).isTrue();
assertThat(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
.enabledIn(mapper.getDeserializationConfig().getDeserializationFeatures())).isFalse();
assertThat(mapper.getSerializationConfig().getPropertyNamingStrategy())
.isEqualTo(PropertyNamingStrategy.LOWER_CAMEL_CASE);
assertThat(mapper.getDeserializationConfig().getPropertyNamingStrategy())
.isEqualTo(PropertyNamingStrategy.LOWER_CAMEL_CASE);
}
}

View File

@@ -0,0 +1,95 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.socket;
import org.junit.jupiter.api.Test;
import org.springframework.boot.buildpack.platform.socket.FileDescriptor.Handle;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Test for {@link FileDescriptor}.
*
* @author Phillip Webb
*/
class FileDescriptorTests {
private int sourceHandle = 123;
private int closedHandle = 0;
@Test
void acquireReturnsHandle() throws Exception {
FileDescriptor descriptor = new FileDescriptor(this.sourceHandle, this::close);
try (Handle handle = descriptor.acquire()) {
assertThat(handle.intValue()).isEqualTo(this.sourceHandle);
assertThat(handle.isClosed()).isFalse();
}
}
@Test
void acquireWhenClosedReturnsClosedHandle() throws Exception {
FileDescriptor descriptor = new FileDescriptor(this.sourceHandle, this::close);
descriptor.close();
try (Handle handle = descriptor.acquire()) {
assertThat(handle.intValue()).isEqualTo(-1);
assertThat(handle.isClosed()).isTrue();
}
}
@Test
void acquireWhenPendingCloseReturnsClosedHandle() throws Exception {
FileDescriptor descriptor = new FileDescriptor(this.sourceHandle, this::close);
try (Handle handle1 = descriptor.acquire()) {
descriptor.close();
try (Handle handle2 = descriptor.acquire()) {
assertThat(handle2.intValue()).isEqualTo(-1);
assertThat(handle2.isClosed()).isTrue();
}
}
}
@Test
void finalizeTriggersClose() {
FileDescriptor descriptor = new FileDescriptor(this.sourceHandle, this::close);
descriptor.close();
assertThat(this.closedHandle).isEqualTo(this.sourceHandle);
}
@Test
void closeWhenHandleAcquiredClosesOnRelease() throws Exception {
FileDescriptor descriptor = new FileDescriptor(this.sourceHandle, this::close);
try (Handle handle = descriptor.acquire()) {
descriptor.close();
assertThat(this.closedHandle).isEqualTo(0);
}
assertThat(this.closedHandle).isEqualTo(this.sourceHandle);
}
@Test
void closeWhenHandleNotAcquiredClosesImmediately() {
FileDescriptor descriptor = new FileDescriptor(this.sourceHandle, this::close);
descriptor.close();
assertThat(this.closedHandle).isEqualTo(this.sourceHandle);
}
private void close(int handle) {
this.closedHandle = handle;
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* 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
*
* https://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.springframework.boot.buildpack.platform.toml;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link Toml}.
*
* @author Phillip Webb
*/
class TomlTests {
@Test
void createsTomlMarkup() {
Toml toml = new Toml();
toml.table("run-image");
toml.string("image", "cnb/test");
toml.array("mirrors", "a", "b", "c");
String expected = "";
expected += "[run-image]\n";
expected += "image = \"cnb/test\"\n";
expected += "mirrors = [\"a\", \"b\", \"c\"]\n";
assertThat(toml.toString()).isEqualTo(expected);
}
}

View File

@@ -0,0 +1,222 @@
{
"description": "Ubuntu bionic base image with buildpacks for Java, NodeJS and Golang",
"buildpacks": [
{
"id": "org.cloudfoundry.debug",
"version": "v1.0.106",
"latest": true
},
{
"id": "org.cloudfoundry.go",
"version": "v0.0.2",
"latest": true
},
{
"id": "org.cloudfoundry.springautoreconfiguration",
"version": "v1.0.113",
"latest": true
},
{
"id": "org.cloudfoundry.buildsystem",
"version": "v1.0.133",
"latest": true
},
{
"id": "org.cloudfoundry.procfile",
"version": "v1.0.41",
"latest": true
},
{
"id": "org.cloudfoundry.nodejs",
"version": "v0.0.5",
"latest": true
},
{
"id": "org.cloudfoundry.distzip",
"version": "v1.0.101",
"latest": true
},
{
"id": "org.cloudfoundry.jdbc",
"version": "v1.0.107",
"latest": true
},
{
"id": "org.cloudfoundry.azureapplicationinsights",
"version": "v1.0.105",
"latest": true
},
{
"id": "org.cloudfoundry.springboot",
"version": "v1.0.112",
"latest": true
},
{
"id": "org.cloudfoundry.openjdk",
"version": "v1.0.58",
"latest": true
},
{
"id": "org.cloudfoundry.tomcat",
"version": "v1.1.24",
"latest": true
},
{
"id": "org.cloudfoundry.googlestackdriver",
"version": "v1.0.54",
"latest": true
},
{
"id": "org.cloudfoundry.jmx",
"version": "v1.0.107",
"latest": true
},
{
"id": "org.cloudfoundry.archiveexpanding",
"version": "v1.0.99",
"latest": true
},
{
"id": "org.cloudfoundry.jvmapplication",
"version": "v1.0.82",
"latest": true
},
{
"id": "org.cloudfoundry.dep",
"version": "0.0.64",
"latest": true
},
{
"id": "org.cloudfoundry.go-compiler",
"version": "0.0.55",
"latest": true
},
{
"id": "org.cloudfoundry.go-mod",
"version": "0.0.58",
"latest": true
},
{
"id": "org.cloudfoundry.node-engine",
"version": "0.0.102",
"latest": true
},
{
"id": "org.cloudfoundry.npm",
"version": "0.0.63",
"latest": true
},
{
"id": "org.cloudfoundry.yarn",
"version": "0.0.69",
"latest": true
}
],
"groups": [
{
"buildpacks": [
{
"id": "org.cloudfoundry.archiveexpanding",
"version": "v1.0.99",
"optional": true
},
{
"id": "org.cloudfoundry.openjdk",
"version": "v1.0.58"
},
{
"id": "org.cloudfoundry.buildsystem",
"version": "v1.0.133",
"optional": true
},
{
"id": "org.cloudfoundry.jvmapplication",
"version": "v1.0.82"
},
{
"id": "org.cloudfoundry.tomcat",
"version": "v1.1.24",
"optional": true
},
{
"id": "org.cloudfoundry.springboot",
"version": "v1.0.112",
"optional": true
},
{
"id": "org.cloudfoundry.distzip",
"version": "v1.0.101",
"optional": true
},
{
"id": "org.cloudfoundry.procfile",
"version": "v1.0.41",
"optional": true
},
{
"id": "org.cloudfoundry.azureapplicationinsights",
"version": "v1.0.105",
"optional": true
},
{
"id": "org.cloudfoundry.debug",
"version": "v1.0.106",
"optional": true
},
{
"id": "org.cloudfoundry.googlestackdriver",
"version": "v1.0.54",
"optional": true
},
{
"id": "org.cloudfoundry.jdbc",
"version": "v1.0.107",
"optional": true
},
{
"id": "org.cloudfoundry.jmx",
"version": "v1.0.107",
"optional": true
},
{
"id": "org.cloudfoundry.springautoreconfiguration",
"version": "v1.0.113",
"optional": true
}
]
},
{
"buildpacks": [
{
"id": "org.cloudfoundry.nodejs",
"version": "v0.0.5"
}
]
},
{
"buildpacks": [
{
"id": "org.cloudfoundry.go",
"version": "v0.0.2"
}
]
}
],
"stack": {
"runImage": {
"image": "cloudfoundry/run:base-cnb",
"mirrors": null
}
},
"lifecycle": {
"version": "0.5.0",
"api": {
"buildpack": "0.2",
"platform": "0.1"
}
},
"createdBy": {
"name": "Pack CLI",
"version": "dev-2019-11-19-22:34:59"
}
}

View File

@@ -0,0 +1,11 @@
{
"User" : "root",
"Image" : "pack.local/ephemeral-builder",
"Cmd" : [ "/lifecycle/analyzer", "-daemon", "-layers", "/layers", "docker.io/library/my-application:latest" ],
"Labels" : {
"author" : "spring-boot"
},
"HostConfig" : {
"Binds" : [ "/var/run/docker.sock:/var/run/docker.sock", "pack-layers-aaaaaaaaaa:/layers", "pack-app-aaaaaaaaaa:/workspace" ]
}
}

View File

@@ -0,0 +1,10 @@
{
"Image" : "pack.local/ephemeral-builder",
"Cmd" : [ "/lifecycle/builder", "-layers", "/layers", "-app", "/workspace", "-platform", "/platform" ],
"Labels" : {
"author" : "spring-boot"
},
"HostConfig" : {
"Binds" : [ "pack-layers-aaaaaaaaaa:/layers", "pack-app-aaaaaaaaaa:/workspace" ]
}
}

View File

@@ -0,0 +1,11 @@
{
"User" : "root",
"Image" : "pack.local/ephemeral-builder",
"Cmd" : [ "/lifecycle/cacher", "-path", "/cache", "-layers", "/layers" ],
"Labels" : {
"author" : "spring-boot"
},
"HostConfig" : {
"Binds" : [ "/var/run/docker.sock:/var/run/docker.sock", "pack-layers-aaaaaaaaaa:/layers", "pack-app-aaaaaaaaaa:/workspace", "pack-cache-b35197ac41ea.build:/cache" ]
}
}

View File

@@ -0,0 +1,10 @@
{
"Image" : "pack.local/ephemeral-builder",
"Cmd" : [ "/lifecycle/detector", "-app", "/workspace", "-platform", "/platform" ],
"Labels" : {
"author" : "spring-boot"
},
"HostConfig" : {
"Binds" : [ "pack-layers-aaaaaaaaaa:/layers", "pack-app-aaaaaaaaaa:/workspace" ]
}
}

View File

@@ -0,0 +1,11 @@
{
"User" : "root",
"Image" : "pack.local/ephemeral-builder",
"Cmd" : [ "/lifecycle/exporter", "-image", "docker.io/cloudfoundry/run", "-layers", "/layers", "-app", "/workspace", "-daemon", "-launch-cache", "/launch-cache", "docker.io/library/my-application:latest" ],
"Labels" : {
"author" : "spring-boot"
},
"HostConfig" : {
"Binds" : [ "/var/run/docker.sock:/var/run/docker.sock", "pack-layers-aaaaaaaaaa:/layers", "pack-app-aaaaaaaaaa:/workspace", "pack-cache-b35197ac41ea.launch:/launch-cache" ]
}
}

View File

@@ -0,0 +1,11 @@
{
"User" : "root",
"Image" : "pack.local/ephemeral-builder",
"Cmd" : [ "/lifecycle/restorer", "-path", "/cache", "-layers", "/layers" ],
"Labels" : {
"author" : "spring-boot"
},
"HostConfig" : {
"Binds" : [ "/var/run/docker.sock:/var/run/docker.sock", "pack-layers-aaaaaaaaaa:/layers", "pack-app-aaaaaaaaaa:/workspace", "pack-cache-b35197ac41ea.build:/cache" ]
}
}

View File

@@ -0,0 +1,19 @@
Building image 'docker.io/library/my-app:latest'
> Pulling builder image 'docker.io/cnb/builder' ..................................................
> Pulled builder image '00000001'
> Pulling run image 'docker.io/cnb/runner' ..................................................
> Pulled run image '00000002'
> Executing lifecycle version v0.5.0
> Using build cache volume 'pack-abc.cache'
> Running alphabet
[alphabet] one
[alphabet] two
[alphabet] three
> Running basket
[basket] spring
[basket] boot
Successfully built image 'docker.io/library/my-app:latest'

View File

@@ -0,0 +1,14 @@
{
"errors": [
{
"code": "TEST1",
"message": "Test One",
"detail": 123
},
{
"code": "TEST2",
"message": "Test Two",
"detail": "fail"
}
]
}

View File

@@ -0,0 +1 @@
{"stream":"Loaded image: pack.local/builder/auqfjjbaod:latest\n"}

View File

@@ -0,0 +1,598 @@
{
"status": "Pulling from cloudfoundry/cnb",
"id": "bionic"
}
{"status":"Pulling fs layer","progressDetail":{},"id":"5667fdb72017"}
{"status":"Pulling fs layer","progressDetail":{},"id":"d83811f270d5"}
{"status":"Pulling fs layer","progressDetail":{},"id":"ee671aafb583"}
{"status":"Pulling fs layer","progressDetail":{},"id":"7fc152dfb3a6"}
{"status":"Pulling fs layer","progressDetail":{},"id":"4ab897fa6fbf"}
{"status":"Pulling fs layer","progressDetail":{},"id":"d837a2a1365e"}
{"status":"Pulling fs layer","progressDetail":{},"id":"988ae18fe41a"}
{"status":"Pulling fs layer","progressDetail":{},"id":"eeb8ef83b565"}
{"status":"Pulling fs layer","progressDetail":{},"id":"357fefdf9bc9"}
{"status":"Pulling fs layer","progressDetail":{},"id":"45b746196f82"}
{"status":"Pulling fs layer","progressDetail":{},"id":"fbf4ce20f8c2"}
{"status":"Pulling fs layer","progressDetail":{},"id":"90aca3c647fe"}
{"status":"Pulling fs layer","progressDetail":{},"id":"1dd62f37c84c"}
{"status":"Pulling fs layer","progressDetail":{},"id":"3192b2fa42db"}
{"status":"Pulling fs layer","progressDetail":{},"id":"ae190b8f66a7"}
{"status":"Pulling fs layer","progressDetail":{},"id":"97bb6e138460"}
{"status":"Waiting","progressDetail":{},"id":"eeb8ef83b565"}
{"status":"Pulling fs layer","progressDetail":{},"id":"2edb982d5170"}
{"status":"Waiting","progressDetail":{},"id":"357fefdf9bc9"}
{"status":"Pulling fs layer","progressDetail":{},"id":"7ddc8e6d6da9"}
{"status":"Pulling fs layer","progressDetail":{},"id":"0df6fd234b59"}
{"status":"Waiting","progressDetail":{},"id":"45b746196f82"}
{"status":"Pulling fs layer","progressDetail":{},"id":"8fc1ba8efe21"}
{"status":"Pulling fs layer","progressDetail":{},"id":"1f6f45e783b5"}
{"status":"Pulling fs layer","progressDetail":{},"id":"43ea61082f68"}
{"status":"Waiting","progressDetail":{},"id":"7fc152dfb3a6"}
{"status":"Pulling fs layer","progressDetail":{},"id":"b8cf53bbc6ba"}
{"status":"Waiting","progressDetail":{},"id":"fbf4ce20f8c2"}
{"status":"Pulling fs layer","progressDetail":{},"id":"25efb07e4521"}
{"status":"Waiting","progressDetail":{},"id":"90aca3c647fe"}
{"status":"Pulling fs layer","progressDetail":{},"id":"1c3245356213"}
{"status":"Waiting","progressDetail":{},"id":"1dd62f37c84c"}
{"status":"Pulling fs layer","progressDetail":{},"id":"61ebb123c1eb"}
{"status":"Pulling fs layer","progressDetail":{},"id":"0964b769d2c9"}
{"status":"Waiting","progressDetail":{},"id":"3192b2fa42db"}
{"status":"Pulling fs layer","progressDetail":{},"id":"87f7843f43cd"}
{"status":"Pulling fs layer","progressDetail":{},"id":"a89dbf94d794"}
{"status":"Waiting","progressDetail":{},"id":"ae190b8f66a7"}
{"status":"Pulling fs layer","progressDetail":{},"id":"f0d43ddca77f"}
{"status":"Pulling fs layer","progressDetail":{},"id":"7c674f0cb40c"}
{"status":"Waiting","progressDetail":{},"id":"97bb6e138460"}
{"status":"Pulling fs layer","progressDetail":{},"id":"b48a885b52bc"}
{"status":"Pulling fs layer","progressDetail":{},"id":"272cdf839cbb"}
{"status":"Pulling fs layer","progressDetail":{},"id":"50d054c97f4f"}
{"status":"Pulling fs layer","progressDetail":{},"id":"4c6bbd90b64d"}
{"status":"Waiting","progressDetail":{},"id":"2edb982d5170"}
{"status":"Pulling fs layer","progressDetail":{},"id":"4f4fb700ef54"}
{"status":"Waiting","progressDetail":{},"id":"7ddc8e6d6da9"}
{"status":"Waiting","progressDetail":{},"id":"b8cf53bbc6ba"}
{"status":"Waiting","progressDetail":{},"id":"25efb07e4521"}
{"status":"Waiting","progressDetail":{},"id":"0df6fd234b59"}
{"status":"Waiting","progressDetail":{},"id":"1c3245356213"}
{"status":"Waiting","progressDetail":{},"id":"61ebb123c1eb"}
{"status":"Waiting","progressDetail":{},"id":"8fc1ba8efe21"}
{"status":"Waiting","progressDetail":{},"id":"0964b769d2c9"}
{"status":"Waiting","progressDetail":{},"id":"87f7843f43cd"}
{"status":"Waiting","progressDetail":{},"id":"1f6f45e783b5"}
{"status":"Waiting","progressDetail":{},"id":"a89dbf94d794"}
{"status":"Waiting","progressDetail":{},"id":"43ea61082f68"}
{"status":"Waiting","progressDetail":{},"id":"f0d43ddca77f"}
{"status":"Waiting","progressDetail":{},"id":"7c674f0cb40c"}
{"status":"Waiting","progressDetail":{},"id":"b48a885b52bc"}
{"status":"Waiting","progressDetail":{},"id":"272cdf839cbb"}
{"status":"Waiting","progressDetail":{},"id":"50d054c97f4f"}
{"status":"Waiting","progressDetail":{},"id":"4c6bbd90b64d"}
{"status":"Waiting","progressDetail":{},"id":"4f4fb700ef54"}
{"status":"Waiting","progressDetail":{},"id":"4ab897fa6fbf"}
{"status":"Waiting","progressDetail":{},"id":"d837a2a1365e"}
{"status":"Waiting","progressDetail":{},"id":"988ae18fe41a"}
{"status":"Downloading","progressDetail":{"current":487,"total":850},"progress":"[============================\u003e ] 487B/850B","id":"ee671aafb583"}
{"status":"Downloading","progressDetail":{"current":485,"total":35355},"progress":"[\u003e ] 485B/35.35kB","id":"d83811f270d5"}
{"status":"Downloading","progressDetail":{"current":35355,"total":35355},"progress":"[==================================================\u003e] 35.35kB/35.35kB","id":"d83811f270d5"}
{"status":"Verifying Checksum","progressDetail":{},"id":"d83811f270d5"}
{"status":"Download complete","progressDetail":{},"id":"d83811f270d5"}
{"status":"Downloading","progressDetail":{"current":277600,"total":26683298},"progress":"[\u003e ] 277.6kB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":850,"total":850},"progress":"[==================================================\u003e] 850B/850B","id":"ee671aafb583"}
{"status":"Verifying Checksum","progressDetail":{},"id":"ee671aafb583"}
{"status":"Download complete","progressDetail":{},"id":"ee671aafb583"}
{"status":"Downloading","progressDetail":{"current":2218692,"total":26683298},"progress":"[====\u003e ] 2.219MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":4160196,"total":26683298},"progress":"[=======\u003e ] 4.16MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":6109892,"total":26683298},"progress":"[===========\u003e ] 6.11MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":7772868,"total":26683298},"progress":"[==============\u003e ] 7.773MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":9444036,"total":26683298},"progress":"[=================\u003e ] 9.444MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":163,"total":163},"progress":"[==================================================\u003e] 163B/163B","id":"7fc152dfb3a6"}
{"status":"Verifying Checksum","progressDetail":{},"id":"7fc152dfb3a6"}
{"status":"Download complete","progressDetail":{},"id":"7fc152dfb3a6"}
{"status":"Downloading","progressDetail":{"current":10832580,"total":26683298},"progress":"[====================\u003e ] 10.83MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":531179,"total":88111129},"progress":"[\u003e ] 531.2kB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":11668164,"total":26683298},"progress":"[=====================\u003e ] 11.67MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":1604331,"total":88111129},"progress":"[\u003e ] 1.604MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":12495556,"total":26683298},"progress":"[=======================\u003e ] 12.5MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":3209963,"total":88111129},"progress":"[=\u003e ] 3.21MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":13331140,"total":26683298},"progress":"[========================\u003e ] 13.33MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":4283115,"total":88111129},"progress":"[==\u003e ] 4.283MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":14166724,"total":26683298},"progress":"[==========================\u003e ] 14.17MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":5888747,"total":88111129},"progress":"[===\u003e ] 5.889MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":15280836,"total":26683298},"progress":"[============================\u003e ] 15.28MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":14318,"total":1391657},"progress":"[\u003e ] 14.32kB/1.392MB","id":"d837a2a1365e"}
{"status":"Downloading","progressDetail":{"current":6961899,"total":88111129},"progress":"[===\u003e ] 6.962MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":16116420,"total":26683298},"progress":"[==============================\u003e ] 16.12MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":936688,"total":1391657},"progress":"[=================================\u003e ] 936.7kB/1.392MB","id":"d837a2a1365e"}
{"status":"Verifying Checksum","progressDetail":{},"id":"d837a2a1365e"}
{"status":"Download complete","progressDetail":{},"id":"d837a2a1365e"}
{"status":"Downloading","progressDetail":{"current":8022763,"total":88111129},"progress":"[====\u003e ] 8.023MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":16931524,"total":26683298},"progress":"[===============================\u003e ] 16.93MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":18045636,"total":26683298},"progress":"[=================================\u003e ] 18.05MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":9632491,"total":88111129},"progress":"[=====\u003e ] 9.632MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":10709739,"total":88111129},"progress":"[======\u003e ] 10.71MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":19143364,"total":26683298},"progress":"[===================================\u003e ] 19.14MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":11778795,"total":88111129},"progress":"[======\u003e ] 11.78MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":20249284,"total":26683298},"progress":"[=====================================\u003e ] 20.25MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":12851947,"total":88111129},"progress":"[=======\u003e ] 12.85MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":21072580,"total":26683298},"progress":"[=======================================\u003e ] 21.07MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":14133,"total":1328346},"progress":"[\u003e ] 14.13kB/1.328MB","id":"988ae18fe41a"}
{"status":"Downloading","progressDetail":{"current":13933291,"total":88111129},"progress":"[=======\u003e ] 13.93MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":21908164,"total":26683298},"progress":"[=========================================\u003e ] 21.91MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":973511,"total":1328346},"progress":"[====================================\u003e ] 973.5kB/1.328MB","id":"988ae18fe41a"}
{"status":"Verifying Checksum","progressDetail":{},"id":"988ae18fe41a"}
{"status":"Download complete","progressDetail":{},"id":"988ae18fe41a"}
{"status":"Downloading","progressDetail":{"current":15014635,"total":88111129},"progress":"[========\u003e ] 15.01MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":22747844,"total":26683298},"progress":"[==========================================\u003e ] 22.75MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":16075499,"total":88111129},"progress":"[=========\u003e ] 16.08MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":23575236,"total":26683298},"progress":"[============================================\u003e ] 23.58MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":24414916,"total":26683298},"progress":"[=============================================\u003e ] 24.41MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":17132267,"total":88111129},"progress":"[=========\u003e ] 17.13MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":25250500,"total":26683298},"progress":"[===============================================\u003e ] 25.25MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":18213611,"total":88111129},"progress":"[==========\u003e ] 18.21MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":26073796,"total":26683298},"progress":"[================================================\u003e ] 26.07MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":19286763,"total":88111129},"progress":"[==========\u003e ] 19.29MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":490,"total":4478},"progress":"[=====\u003e ] 490B/4.478kB","id":"eeb8ef83b565"}
{"status":"Downloading","progressDetail":{"current":4478,"total":4478},"progress":"[==================================================\u003e] 4.478kB/4.478kB","id":"eeb8ef83b565"}
{"status":"Verifying Checksum","progressDetail":{},"id":"eeb8ef83b565"}
{"status":"Download complete","progressDetail":{},"id":"eeb8ef83b565"}
{"status":"Verifying Checksum","progressDetail":{},"id":"5667fdb72017"}
{"status":"Download complete","progressDetail":{},"id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":20892395,"total":88111129},"progress":"[===========\u003e ] 20.89MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Extracting","progressDetail":{"current":294912,"total":26683298},"progress":"[\u003e ] 294.9kB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":23050987,"total":88111129},"progress":"[=============\u003e ] 23.05MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Extracting","progressDetail":{"current":2654208,"total":26683298},"progress":"[====\u003e ] 2.654MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":25205483,"total":88111129},"progress":"[==============\u003e ] 25.21MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Extracting","progressDetail":{"current":6193152,"total":26683298},"progress":"[===========\u003e ] 6.193MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":27355883,"total":88111129},"progress":"[===============\u003e ] 27.36MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Extracting","progressDetail":{"current":8552448,"total":26683298},"progress":"[================\u003e ] 8.552MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":197,"total":197},"progress":"[==================================================\u003e] 197B/197B","id":"357fefdf9bc9"}
{"status":"Verifying Checksum","progressDetail":{},"id":"357fefdf9bc9"}
{"status":"Download complete","progressDetail":{},"id":"357fefdf9bc9"}
{"status":"Extracting","progressDetail":{"current":11796480,"total":26683298},"progress":"[======================\u003e ] 11.8MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":29510379,"total":88111129},"progress":"[================\u003e ] 29.51MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":277600,"total":27504647},"progress":"[\u003e ] 277.6kB/27.5MB","id":"45b746196f82"}
{"status":"Extracting","progressDetail":{"current":15040512,"total":26683298},"progress":"[============================\u003e ] 15.04MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":1391300,"total":27504647},"progress":"[==\u003e ] 1.391MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":31132395,"total":88111129},"progress":"[=================\u003e ] 31.13MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Extracting","progressDetail":{"current":17989632,"total":26683298},"progress":"[=================================\u003e ] 17.99MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":32754411,"total":88111129},"progress":"[==================\u003e ] 32.75MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":2230980,"total":27504647},"progress":"[====\u003e ] 2.231MB/27.5MB","id":"45b746196f82"}
{"status":"Extracting","progressDetail":{"current":22118400,"total":26683298},"progress":"[=========================================\u003e ] 22.12MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":33835755,"total":88111129},"progress":"[===================\u003e ] 33.84MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":3078852,"total":27504647},"progress":"[=====\u003e ] 3.079MB/27.5MB","id":"45b746196f82"}
{"status":"Extracting","progressDetail":{"current":24477696,"total":26683298},"progress":"[=============================================\u003e ] 24.48MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":52419,"total":5205016},"progress":"[\u003e ] 52.42kB/5.205MB","id":"fbf4ce20f8c2"}
{"status":"Downloading","progressDetail":{"current":34917099,"total":88111129},"progress":"[===================\u003e ] 34.92MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":3922628,"total":27504647},"progress":"[=======\u003e ] 3.923MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":912096,"total":5205016},"progress":"[========\u003e ] 912.1kB/5.205MB","id":"fbf4ce20f8c2"}
{"status":"Extracting","progressDetail":{"current":26247168,"total":26683298},"progress":"[=================================================\u003e ] 26.25MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":4487876,"total":27504647},"progress":"[========\u003e ] 4.488MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":35986155,"total":88111129},"progress":"[====================\u003e ] 35.99MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Extracting","progressDetail":{"current":26683298,"total":26683298},"progress":"[==================================================\u003e] 26.68MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":1805024,"total":5205016},"progress":"[=================\u003e ] 1.805MB/5.205MB","id":"fbf4ce20f8c2"}
{"status":"Downloading","progressDetail":{"current":5044932,"total":27504647},"progress":"[=========\u003e ] 5.045MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":36522731,"total":88111129},"progress":"[====================\u003e ] 36.52MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":2550496,"total":5205016},"progress":"[========================\u003e ] 2.55MB/5.205MB","id":"fbf4ce20f8c2"}
{"status":"Downloading","progressDetail":{"current":5601988,"total":27504647},"progress":"[==========\u003e ] 5.602MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":3381984,"total":5205016},"progress":"[================================\u003e ] 3.382MB/5.205MB","id":"fbf4ce20f8c2"}
{"status":"Downloading","progressDetail":{"current":37063403,"total":88111129},"progress":"[=====================\u003e ] 37.06MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":6159044,"total":27504647},"progress":"[===========\u003e ] 6.159MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":4152032,"total":5205016},"progress":"[=======================================\u003e ] 4.152MB/5.205MB","id":"fbf4ce20f8c2"}
{"status":"Downloading","progressDetail":{"current":37604075,"total":88111129},"progress":"[=====================\u003e ] 37.6MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Pull complete","progressDetail":{},"id":"5667fdb72017"}
{"status":"Extracting","progressDetail":{"current":32768,"total":35355},"progress":"[==============================================\u003e ] 32.77kB/35.35kB","id":"d83811f270d5"}
{"status":"Extracting","progressDetail":{"current":35355,"total":35355},"progress":"[==================================================\u003e] 35.35kB/35.35kB","id":"d83811f270d5"}
{"status":"Downloading","progressDetail":{"current":5004000,"total":5205016},"progress":"[================================================\u003e ] 5.004MB/5.205MB","id":"fbf4ce20f8c2"}
{"status":"Downloading","progressDetail":{"current":6716100,"total":27504647},"progress":"[============\u003e ] 6.716MB/27.5MB","id":"45b746196f82"}
{"status":"Verifying Checksum","progressDetail":{},"id":"fbf4ce20f8c2"}
{"status":"Download complete","progressDetail":{},"id":"fbf4ce20f8c2"}
{"status":"Downloading","progressDetail":{"current":38144747,"total":88111129},"progress":"[=====================\u003e ] 38.14MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Pull complete","progressDetail":{},"id":"d83811f270d5"}
{"status":"Extracting","progressDetail":{"current":850,"total":850},"progress":"[==================================================\u003e] 850B/850B","id":"ee671aafb583"}
{"status":"Extracting","progressDetail":{"current":850,"total":850},"progress":"[==================================================\u003e] 850B/850B","id":"ee671aafb583"}
{"status":"Downloading","progressDetail":{"current":7293636,"total":27504647},"progress":"[=============\u003e ] 7.294MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":39213803,"total":88111129},"progress":"[======================\u003e ] 39.21MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":8129220,"total":27504647},"progress":"[==============\u003e ] 8.129MB/27.5MB","id":"45b746196f82"}
{"status":"Pull complete","progressDetail":{},"id":"ee671aafb583"}
{"status":"Extracting","progressDetail":{"current":163,"total":163},"progress":"[==================================================\u003e] 163B/163B","id":"7fc152dfb3a6"}
{"status":"Extracting","progressDetail":{"current":163,"total":163},"progress":"[==================================================\u003e] 163B/163B","id":"7fc152dfb3a6"}
{"status":"Downloading","progressDetail":{"current":40295147,"total":88111129},"progress":"[======================\u003e ] 40.3MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":8964804,"total":27504647},"progress":"[================\u003e ] 8.965MB/27.5MB","id":"45b746196f82"}
{"status":"Pull complete","progressDetail":{},"id":"7fc152dfb3a6"}
{"status":"Downloading","progressDetail":{"current":9800388,"total":27504647},"progress":"[=================\u003e ] 9.8MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":41368299,"total":88111129},"progress":"[=======================\u003e ] 41.37MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":49680,"total":4964709},"progress":"[\u003e ] 49.68kB/4.965MB","id":"90aca3c647fe"}
{"status":"Downloading","progressDetail":{"current":10635972,"total":27504647},"progress":"[===================\u003e ] 10.64MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":908013,"total":4964709},"progress":"[=========\u003e ] 908kB/4.965MB","id":"90aca3c647fe"}
{"status":"Downloading","progressDetail":{"current":41908971,"total":88111129},"progress":"[=======================\u003e ] 41.91MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":11193028,"total":27504647},"progress":"[====================\u003e ] 11.19MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":2038509,"total":4964709},"progress":"[====================\u003e ] 2.039MB/4.965MB","id":"90aca3c647fe"}
{"status":"Downloading","progressDetail":{"current":42449643,"total":88111129},"progress":"[========================\u003e ] 42.45MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":11750084,"total":27504647},"progress":"[=====================\u003e ] 11.75MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":3316461,"total":4964709},"progress":"[=================================\u003e ] 3.316MB/4.965MB","id":"90aca3c647fe"}
{"status":"Downloading","progressDetail":{"current":4791021,"total":4964709},"progress":"[================================================\u003e ] 4.791MB/4.965MB","id":"90aca3c647fe"}
{"status":"Verifying Checksum","progressDetail":{},"id":"90aca3c647fe"}
{"status":"Download complete","progressDetail":{},"id":"90aca3c647fe"}
{"status":"Downloading","progressDetail":{"current":12315332,"total":27504647},"progress":"[======================\u003e ] 12.32MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":42990315,"total":88111129},"progress":"[========================\u003e ] 42.99MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":13155012,"total":27504647},"progress":"[=======================\u003e ] 13.16MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":43530987,"total":88111129},"progress":"[========================\u003e ] 43.53MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":13990596,"total":27504647},"progress":"[=========================\u003e ] 13.99MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":44063467,"total":88111129},"progress":"[=========================\u003e ] 44.06MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":15112900,"total":27504647},"progress":"[===========================\u003e ] 15.11MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":45132523,"total":88111129},"progress":"[=========================\u003e ] 45.13MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":16235204,"total":27504647},"progress":"[=============================\u003e ] 16.24MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":52418,"total":5149051},"progress":"[\u003e ] 52.42kB/5.149MB","id":"1dd62f37c84c"}
{"status":"Downloading","progressDetail":{"current":1195147,"total":5149051},"progress":"[===========\u003e ] 1.195MB/5.149MB","id":"1dd62f37c84c"}
{"status":"Downloading","progressDetail":{"current":16792260,"total":27504647},"progress":"[==============================\u003e ] 16.79MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":45673195,"total":88111129},"progress":"[=========================\u003e ] 45.67MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":2702475,"total":5149051},"progress":"[==========================\u003e ] 2.702MB/5.149MB","id":"1dd62f37c84c"}
{"status":"Downloading","progressDetail":{"current":4078320,"total":5149051},"progress":"[=======================================\u003e ] 4.078MB/5.149MB","id":"1dd62f37c84c"}
{"status":"Downloading","progressDetail":{"current":17349316,"total":27504647},"progress":"[===============================\u003e ] 17.35MB/27.5MB","id":"45b746196f82"}
{"status":"Verifying Checksum","progressDetail":{},"id":"1dd62f37c84c"}
{"status":"Download complete","progressDetail":{},"id":"1dd62f37c84c"}
{"status":"Downloading","progressDetail":{"current":46213867,"total":88111129},"progress":"[==========================\u003e ] 46.21MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":17918660,"total":27504647},"progress":"[================================\u003e ] 17.92MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":19040964,"total":27504647},"progress":"[==================================\u003e ] 19.04MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":47295211,"total":88111129},"progress":"[==========================\u003e ] 47.3MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":20183748,"total":27504647},"progress":"[====================================\u003e ] 20.18MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":48368363,"total":88111129},"progress":"[===========================\u003e ] 48.37MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":21301956,"total":27504647},"progress":"[======================================\u003e ] 21.3MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":22432452,"total":27504647},"progress":"[========================================\u003e ] 22.43MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":38884,"total":3855277},"progress":"[\u003e ] 38.88kB/3.855MB","id":"3192b2fa42db"}
{"status":"Downloading","progressDetail":{"current":49445611,"total":88111129},"progress":"[============================\u003e ] 49.45MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":977632,"total":3855277},"progress":"[============\u003e ] 977.6kB/3.855MB","id":"3192b2fa42db"}
{"status":"Downloading","progressDetail":{"current":23268036,"total":27504647},"progress":"[==========================================\u003e ] 23.27MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":49986283,"total":88111129},"progress":"[============================\u003e ] 49.99MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":1895136,"total":3855277},"progress":"[========================\u003e ] 1.895MB/3.855MB","id":"3192b2fa42db"}
{"status":"Downloading","progressDetail":{"current":23833284,"total":27504647},"progress":"[===========================================\u003e ] 23.83MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":2939616,"total":3855277},"progress":"[======================================\u003e ] 2.94MB/3.855MB","id":"3192b2fa42db"}
{"status":"Downloading","progressDetail":{"current":24390340,"total":27504647},"progress":"[============================================\u003e ] 24.39MB/27.5MB","id":"45b746196f82"}
{"status":"Download complete","progressDetail":{},"id":"3192b2fa42db"}
{"status":"Downloading","progressDetail":{"current":50518763,"total":88111129},"progress":"[============================\u003e ] 50.52MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":24947396,"total":27504647},"progress":"[=============================================\u003e ] 24.95MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":51059435,"total":88111129},"progress":"[============================\u003e ] 51.06MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":25803460,"total":27504647},"progress":"[==============================================\u003e ] 25.8MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":26942148,"total":27504647},"progress":"[================================================\u003e ] 26.94MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":52140779,"total":88111129},"progress":"[=============================\u003e ] 52.14MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":27504647,"total":27504647},"progress":"[==================================================\u003e] 27.5MB/27.5MB","id":"45b746196f82"}
{"status":"Verifying Checksum","progressDetail":{},"id":"45b746196f82"}
{"status":"Download complete","progressDetail":{},"id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":53222123,"total":88111129},"progress":"[==============================\u003e ] 53.22MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":51194,"total":4983195},"progress":"[\u003e ] 51.19kB/4.983MB","id":"ae190b8f66a7"}
{"status":"Downloading","progressDetail":{"current":54299371,"total":88111129},"progress":"[==============================\u003e ] 54.3MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":1268464,"total":4983195},"progress":"[============\u003e ] 1.268MB/4.983MB","id":"ae190b8f66a7"}
{"status":"Downloading","progressDetail":{"current":54827755,"total":88111129},"progress":"[===============================\u003e ] 54.83MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":2767600,"total":4983195},"progress":"[===========================\u003e ] 2.768MB/4.983MB","id":"ae190b8f66a7"}
{"status":"Downloading","progressDetail":{"current":4528880,"total":4983195},"progress":"[=============================================\u003e ] 4.529MB/4.983MB","id":"ae190b8f66a7"}
{"status":"Downloading","progressDetail":{"current":55368427,"total":88111129},"progress":"[===============================\u003e ] 55.37MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Verifying Checksum","progressDetail":{},"id":"ae190b8f66a7"}
{"status":"Download complete","progressDetail":{},"id":"ae190b8f66a7"}
{"status":"Downloading","progressDetail":{"current":63614,"total":6103207},"progress":"[\u003e ] 63.61kB/6.103MB","id":"97bb6e138460"}
{"status":"Downloading","progressDetail":{"current":56449771,"total":88111129},"progress":"[================================\u003e ] 56.45MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":1530606,"total":6103207},"progress":"[============\u003e ] 1.531MB/6.103MB","id":"97bb6e138460"}
{"status":"Downloading","progressDetail":{"current":3193582,"total":6103207},"progress":"[==========================\u003e ] 3.194MB/6.103MB","id":"97bb6e138460"}
{"status":"Downloading","progressDetail":{"current":56990443,"total":88111129},"progress":"[================================\u003e ] 56.99MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":4786926,"total":6103207},"progress":"[=======================================\u003e ] 4.787MB/6.103MB","id":"97bb6e138460"}
{"status":"Downloading","progressDetail":{"current":57531115,"total":88111129},"progress":"[================================\u003e ] 57.53MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Download complete","progressDetail":{},"id":"97bb6e138460"}
{"status":"Downloading","progressDetail":{"current":489,"total":787},"progress":"[===============================\u003e ] 489B/787B","id":"2edb982d5170"}
{"status":"Downloading","progressDetail":{"current":58612459,"total":88111129},"progress":"[=================================\u003e ] 58.61MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":787,"total":787},"progress":"[==================================================\u003e] 787B/787B","id":"2edb982d5170"}
{"status":"Verifying Checksum","progressDetail":{},"id":"2edb982d5170"}
{"status":"Download complete","progressDetail":{},"id":"2edb982d5170"}
{"status":"Downloading","progressDetail":{"current":60213995,"total":88111129},"progress":"[==================================\u003e ] 60.21MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":61827819,"total":88111129},"progress":"[===================================\u003e ] 61.83MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":63449835,"total":88111129},"progress":"[====================================\u003e ] 63.45MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":65071851,"total":88111129},"progress":"[====================================\u003e ] 65.07MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":49803,"total":4894860},"progress":"[\u003e ] 49.8kB/4.895MB","id":"7ddc8e6d6da9"}
{"status":"Downloading","progressDetail":{"current":49681,"total":4953791},"progress":"[\u003e ] 49.68kB/4.954MB","id":"0df6fd234b59"}
{"status":"Downloading","progressDetail":{"current":912099,"total":4894860},"progress":"[=========\u003e ] 912.1kB/4.895MB","id":"7ddc8e6d6da9"}
{"status":"Downloading","progressDetail":{"current":66145003,"total":88111129},"progress":"[=====================================\u003e ] 66.15MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":748270,"total":4953791},"progress":"[=======\u003e ] 748.3kB/4.954MB","id":"0df6fd234b59"}
{"status":"Downloading","progressDetail":{"current":1702627,"total":4894860},"progress":"[=================\u003e ] 1.703MB/4.895MB","id":"7ddc8e6d6da9"}
{"status":"Downloading","progressDetail":{"current":67205867,"total":88111129},"progress":"[======================================\u003e ] 67.21MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":1678062,"total":4953791},"progress":"[================\u003e ] 1.678MB/4.954MB","id":"0df6fd234b59"}
{"status":"Downloading","progressDetail":{"current":2194147,"total":4894860},"progress":"[======================\u003e ] 2.194MB/4.895MB","id":"7ddc8e6d6da9"}
{"status":"Downloading","progressDetail":{"current":67746539,"total":88111129},"progress":"[======================================\u003e ] 67.75MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":2648814,"total":4953791},"progress":"[==========================\u003e ] 2.649MB/4.954MB","id":"0df6fd234b59"}
{"status":"Downloading","progressDetail":{"current":2743011,"total":4894860},"progress":"[============================\u003e ] 2.743MB/4.895MB","id":"7ddc8e6d6da9"}
{"status":"Downloading","progressDetail":{"current":68287211,"total":88111129},"progress":"[======================================\u003e ] 68.29MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":3697390,"total":4953791},"progress":"[=====================================\u003e ] 3.697MB/4.954MB","id":"0df6fd234b59"}
{"status":"Downloading","progressDetail":{"current":3381987,"total":4894860},"progress":"[==================================\u003e ] 3.382MB/4.895MB","id":"7ddc8e6d6da9"}
{"status":"Downloading","progressDetail":{"current":4774638,"total":4953791},"progress":"[================================================\u003e ] 4.775MB/4.954MB","id":"0df6fd234b59"}
{"status":"Downloading","progressDetail":{"current":68827883,"total":88111129},"progress":"[=======================================\u003e ] 68.83MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":4953791,"total":4953791},"progress":"[==================================================\u003e] 4.954MB/4.954MB","id":"0df6fd234b59"}
{"status":"Verifying Checksum","progressDetail":{},"id":"0df6fd234b59"}
{"status":"Download complete","progressDetail":{},"id":"0df6fd234b59"}
{"status":"Downloading","progressDetail":{"current":4004579,"total":4894860},"progress":"[========================================\u003e ] 4.005MB/4.895MB","id":"7ddc8e6d6da9"}
{"status":"Downloading","progressDetail":{"current":4893411,"total":4894860},"progress":"[=================================================\u003e ] 4.893MB/4.895MB","id":"7ddc8e6d6da9"}
{"status":"Verifying Checksum","progressDetail":{},"id":"7ddc8e6d6da9"}
{"status":"Download complete","progressDetail":{},"id":"7ddc8e6d6da9"}
{"status":"Downloading","progressDetail":{"current":69909227,"total":88111129},"progress":"[=======================================\u003e ] 69.91MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":71527147,"total":88111129},"progress":"[========================================\u003e ] 71.53MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":73149163,"total":88111129},"progress":"[=========================================\u003e ] 73.15MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":74771179,"total":88111129},"progress":"[==========================================\u003e ] 74.77MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":63573,"total":6137526},"progress":"[\u003e ] 63.57kB/6.138MB","id":"8fc1ba8efe21"}
{"status":"Downloading","progressDetail":{"current":75311851,"total":88111129},"progress":"[==========================================\u003e ] 75.31MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":1317559,"total":6137526},"progress":"[==========\u003e ] 1.318MB/6.138MB","id":"8fc1ba8efe21"}
{"status":"Downloading","progressDetail":{"current":2710199,"total":6137526},"progress":"[======================\u003e ] 2.71MB/6.138MB","id":"8fc1ba8efe21"}
{"status":"Downloading","progressDetail":{"current":38729,"total":3854415},"progress":"[\u003e ] 38.73kB/3.854MB","id":"1f6f45e783b5"}
{"status":"Downloading","progressDetail":{"current":76368619,"total":88111129},"progress":"[===========================================\u003e ] 76.37MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":3783351,"total":6137526},"progress":"[==============================\u003e ] 3.783MB/6.138MB","id":"8fc1ba8efe21"}
{"status":"Downloading","progressDetail":{"current":658157,"total":3854415},"progress":"[========\u003e ] 658.2kB/3.854MB","id":"1f6f45e783b5"}
{"status":"Downloading","progressDetail":{"current":4520631,"total":6137526},"progress":"[====================================\u003e ] 4.521MB/6.138MB","id":"8fc1ba8efe21"}
{"status":"Downloading","progressDetail":{"current":1350381,"total":3854415},"progress":"[=================\u003e ] 1.35MB/3.854MB","id":"1f6f45e783b5"}
{"status":"Downloading","progressDetail":{"current":5364407,"total":6137526},"progress":"[===========================================\u003e ] 5.364MB/6.138MB","id":"8fc1ba8efe21"}
{"status":"Downloading","progressDetail":{"current":77445867,"total":88111129},"progress":"[===========================================\u003e ] 77.45MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":2153197,"total":3854415},"progress":"[===========================\u003e ] 2.153MB/3.854MB","id":"1f6f45e783b5"}
{"status":"Verifying Checksum","progressDetail":{},"id":"8fc1ba8efe21"}
{"status":"Download complete","progressDetail":{},"id":"8fc1ba8efe21"}
{"status":"Downloading","progressDetail":{"current":77986539,"total":88111129},"progress":"[============================================\u003e ] 77.99MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":3021549,"total":3854415},"progress":"[=======================================\u003e ] 3.022MB/3.854MB","id":"1f6f45e783b5"}
{"status":"Verifying Checksum","progressDetail":{},"id":"1f6f45e783b5"}
{"status":"Download complete","progressDetail":{},"id":"1f6f45e783b5"}
{"status":"Downloading","progressDetail":{"current":79067883,"total":88111129},"progress":"[============================================\u003e ] 79.07MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":80149227,"total":88111129},"progress":"[=============================================\u003e ] 80.15MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":81767147,"total":88111129},"progress":"[==============================================\u003e ] 81.77MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":52419,"total":5222290},"progress":"[\u003e ] 52.42kB/5.222MB","id":"43ea61082f68"}
{"status":"Downloading","progressDetail":{"current":1055455,"total":5222290},"progress":"[==========\u003e ] 1.055MB/5.222MB","id":"43ea61082f68"}
{"status":"Downloading","progressDetail":{"current":83372779,"total":88111129},"progress":"[===============================================\u003e ] 83.37MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":2333407,"total":5222290},"progress":"[======================\u003e ] 2.333MB/5.222MB","id":"43ea61082f68"}
{"status":"Downloading","progressDetail":{"current":35991,"total":3564359},"progress":"[\u003e ] 35.99kB/3.564MB","id":"b8cf53bbc6ba"}
{"status":"Downloading","progressDetail":{"current":84454123,"total":88111129},"progress":"[===============================================\u003e ] 84.45MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":3300063,"total":5222290},"progress":"[===============================\u003e ] 3.3MB/5.222MB","id":"43ea61082f68"}
{"status":"Downloading","progressDetail":{"current":752366,"total":3564359},"progress":"[==========\u003e ] 752.4kB/3.564MB","id":"b8cf53bbc6ba"}
{"status":"Downloading","progressDetail":{"current":3979999,"total":5222290},"progress":"[======================================\u003e ] 3.98MB/5.222MB","id":"43ea61082f68"}
{"status":"Downloading","progressDetail":{"current":1743598,"total":3564359},"progress":"[========================\u003e ] 1.744MB/3.564MB","id":"b8cf53bbc6ba"}
{"status":"Downloading","progressDetail":{"current":85527275,"total":88111129},"progress":"[================================================\u003e ] 85.53MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":4537055,"total":5222290},"progress":"[===========================================\u003e ] 4.537MB/5.222MB","id":"43ea61082f68"}
{"status":"Downloading","progressDetail":{"current":2833134,"total":3564359},"progress":"[=======================================\u003e ] 2.833MB/3.564MB","id":"b8cf53bbc6ba"}
{"status":"Downloading","progressDetail":{"current":5077727,"total":5222290},"progress":"[================================================\u003e ] 5.078MB/5.222MB","id":"43ea61082f68"}
{"status":"Verifying Checksum","progressDetail":{},"id":"b8cf53bbc6ba"}
{"status":"Download complete","progressDetail":{},"id":"b8cf53bbc6ba"}
{"status":"Verifying Checksum","progressDetail":{},"id":"43ea61082f68"}
{"status":"Download complete","progressDetail":{},"id":"43ea61082f68"}
{"status":"Downloading","progressDetail":{"current":86067947,"total":88111129},"progress":"[================================================\u003e ] 86.07MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":87132907,"total":88111129},"progress":"[=================================================\u003e ] 87.13MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Verifying Checksum","progressDetail":{},"id":"4ab897fa6fbf"}
{"status":"Download complete","progressDetail":{},"id":"4ab897fa6fbf"}
{"status":"Extracting","progressDetail":{"current":557056,"total":88111129},"progress":"[\u003e ] 557.1kB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":52418,"total":5120108},"progress":"[\u003e ] 52.42kB/5.12MB","id":"1c3245356213"}
{"status":"Downloading","progressDetail":{"current":489,"total":790},"progress":"[==============================\u003e ] 489B/790B","id":"25efb07e4521"}
{"status":"Extracting","progressDetail":{"current":5013504,"total":88111129},"progress":"[==\u003e ] 5.014MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":790,"total":790},"progress":"[==================================================\u003e] 790B/790B","id":"25efb07e4521"}
{"status":"Verifying Checksum","progressDetail":{},"id":"25efb07e4521"}
{"status":"Download complete","progressDetail":{},"id":"25efb07e4521"}
{"status":"Downloading","progressDetail":{"current":1764079,"total":5120108},"progress":"[=================\u003e ] 1.764MB/5.12MB","id":"1c3245356213"}
{"status":"Extracting","progressDetail":{"current":8355840,"total":88111129},"progress":"[====\u003e ] 8.356MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":3635951,"total":5120108},"progress":"[===================================\u003e ] 3.636MB/5.12MB","id":"1c3245356213"}
{"status":"Verifying Checksum","progressDetail":{},"id":"1c3245356213"}
{"status":"Download complete","progressDetail":{},"id":"1c3245356213"}
{"status":"Extracting","progressDetail":{"current":11141120,"total":88111129},"progress":"[======\u003e ] 11.14MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":52419,"total":5117023},"progress":"[\u003e ] 52.42kB/5.117MB","id":"61ebb123c1eb"}
{"status":"Extracting","progressDetail":{"current":13369344,"total":88111129},"progress":"[=======\u003e ] 13.37MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":1596142,"total":5117023},"progress":"[===============\u003e ] 1.596MB/5.117MB","id":"61ebb123c1eb"}
{"status":"Extracting","progressDetail":{"current":13926400,"total":88111129},"progress":"[=======\u003e ] 13.93MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":3242734,"total":5117023},"progress":"[===============================\u003e ] 3.243MB/5.117MB","id":"61ebb123c1eb"}
{"status":"Downloading","progressDetail":{"current":55157,"total":5384215},"progress":"[\u003e ] 55.16kB/5.384MB","id":"0964b769d2c9"}
{"status":"Downloading","progressDetail":{"current":4635374,"total":5117023},"progress":"[=============================================\u003e ] 4.635MB/5.117MB","id":"61ebb123c1eb"}
{"status":"Extracting","progressDetail":{"current":15040512,"total":88111129},"progress":"[========\u003e ] 15.04MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Verifying Checksum","progressDetail":{},"id":"61ebb123c1eb"}
{"status":"Download complete","progressDetail":{},"id":"61ebb123c1eb"}
{"status":"Downloading","progressDetail":{"current":989937,"total":5384215},"progress":"[=========\u003e ] 989.9kB/5.384MB","id":"0964b769d2c9"}
{"status":"Extracting","progressDetail":{"current":15597568,"total":88111129},"progress":"[========\u003e ] 15.6MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":2558705,"total":5384215},"progress":"[=======================\u003e ] 2.559MB/5.384MB","id":"0964b769d2c9"}
{"status":"Extracting","progressDetail":{"current":18382848,"total":88111129},"progress":"[==========\u003e ] 18.38MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":4311793,"total":5384215},"progress":"[========================================\u003e ] 4.312MB/5.384MB","id":"0964b769d2c9"}
{"status":"Downloading","progressDetail":{"current":53788,"total":5252487},"progress":"[\u003e ] 53.79kB/5.252MB","id":"87f7843f43cd"}
{"status":"Extracting","progressDetail":{"current":22839296,"total":88111129},"progress":"[============\u003e ] 22.84MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":5212913,"total":5384215},"progress":"[================================================\u003e ] 5.213MB/5.384MB","id":"0964b769d2c9"}
{"status":"Downloading","progressDetail":{"current":846577,"total":5252487},"progress":"[========\u003e ] 846.6kB/5.252MB","id":"87f7843f43cd"}
{"status":"Verifying Checksum","progressDetail":{},"id":"0964b769d2c9"}
{"status":"Download complete","progressDetail":{},"id":"0964b769d2c9"}
{"status":"Extracting","progressDetail":{"current":26181632,"total":88111129},"progress":"[==============\u003e ] 26.18MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":2628337,"total":5252487},"progress":"[=========================\u003e ] 2.628MB/5.252MB","id":"87f7843f43cd"}
{"status":"Extracting","progressDetail":{"current":30638080,"total":88111129},"progress":"[=================\u003e ] 30.64MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":4340465,"total":5252487},"progress":"[=========================================\u003e ] 4.34MB/5.252MB","id":"87f7843f43cd"}
{"status":"Download complete","progressDetail":{},"id":"87f7843f43cd"}
{"status":"Extracting","progressDetail":{"current":33423360,"total":88111129},"progress":"[==================\u003e ] 33.42MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":51204,"total":5015856},"progress":"[\u003e ] 51.2kB/5.016MB","id":"a89dbf94d794"}
{"status":"Extracting","progressDetail":{"current":36208640,"total":88111129},"progress":"[====================\u003e ] 36.21MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":1624816,"total":5015856},"progress":"[================\u003e ] 1.625MB/5.016MB","id":"a89dbf94d794"}
{"status":"Extracting","progressDetail":{"current":38436864,"total":88111129},"progress":"[=====================\u003e ] 38.44MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":3373808,"total":5015856},"progress":"[=================================\u003e ] 3.374MB/5.016MB","id":"a89dbf94d794"}
{"status":"Extracting","progressDetail":{"current":40665088,"total":88111129},"progress":"[=======================\u003e ] 40.67MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":53910,"total":5310566},"progress":"[\u003e ] 53.91kB/5.311MB","id":"f0d43ddca77f"}
{"status":"Downloading","progressDetail":{"current":4905712,"total":5015856},"progress":"[================================================\u003e ] 4.906MB/5.016MB","id":"a89dbf94d794"}
{"status":"Verifying Checksum","progressDetail":{},"id":"a89dbf94d794"}
{"status":"Download complete","progressDetail":{},"id":"a89dbf94d794"}
{"status":"Downloading","progressDetail":{"current":1313521,"total":5310566},"progress":"[============\u003e ] 1.314MB/5.311MB","id":"f0d43ddca77f"}
{"status":"Extracting","progressDetail":{"current":44007424,"total":88111129},"progress":"[========================\u003e ] 44.01MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Extracting","progressDetail":{"current":46792704,"total":88111129},"progress":"[==========================\u003e ] 46.79MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":3082993,"total":5310566},"progress":"[=============================\u003e ] 3.083MB/5.311MB","id":"f0d43ddca77f"}
{"status":"Downloading","progressDetail":{"current":49836,"total":4915049},"progress":"[\u003e ] 49.84kB/4.915MB","id":"7c674f0cb40c"}
{"status":"Downloading","progressDetail":{"current":4373233,"total":5310566},"progress":"[=========================================\u003e ] 4.373MB/5.311MB","id":"f0d43ddca77f"}
{"status":"Extracting","progressDetail":{"current":48463872,"total":88111129},"progress":"[===========================\u003e ] 48.46MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":711407,"total":4915049},"progress":"[=======\u003e ] 711.4kB/4.915MB","id":"7c674f0cb40c"}
{"status":"Verifying Checksum","progressDetail":{},"id":"f0d43ddca77f"}
{"status":"Download complete","progressDetail":{},"id":"f0d43ddca77f"}
{"status":"Downloading","progressDetail":{"current":1710831,"total":4915049},"progress":"[=================\u003e ] 1.711MB/4.915MB","id":"7c674f0cb40c"}
{"status":"Extracting","progressDetail":{"current":52363264,"total":88111129},"progress":"[=============================\u003e ] 52.36MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":3504879,"total":4915049},"progress":"[===================================\u003e ] 3.505MB/4.915MB","id":"7c674f0cb40c"}
{"status":"Extracting","progressDetail":{"current":55705600,"total":88111129},"progress":"[===============================\u003e ] 55.71MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":4905711,"total":4915049},"progress":"[=================================================\u003e ] 4.906MB/4.915MB","id":"7c674f0cb40c"}
{"status":"Verifying Checksum","progressDetail":{},"id":"7c674f0cb40c"}
{"status":"Download complete","progressDetail":{},"id":"7c674f0cb40c"}
{"status":"Extracting","progressDetail":{"current":58490880,"total":88111129},"progress":"[=================================\u003e ] 58.49MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":52419,"total":5119213},"progress":"[\u003e ] 52.42kB/5.119MB","id":"b48a885b52bc"}
{"status":"Extracting","progressDetail":{"current":61276160,"total":88111129},"progress":"[==================================\u003e ] 61.28MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":1333999,"total":5119213},"progress":"[=============\u003e ] 1.334MB/5.119MB","id":"b48a885b52bc"}
{"status":"Downloading","progressDetail":{"current":2657007,"total":5119213},"progress":"[=========================\u003e ] 2.657MB/5.119MB","id":"b48a885b52bc"}
{"status":"Extracting","progressDetail":{"current":64061440,"total":88111129},"progress":"[====================================\u003e ] 64.06MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Download complete","progressDetail":{},"id":"272cdf839cbb"}
{"status":"Downloading","progressDetail":{"current":4344559,"total":5119213},"progress":"[==========================================\u003e ] 4.345MB/5.119MB","id":"b48a885b52bc"}
{"status":"Extracting","progressDetail":{"current":66289664,"total":88111129},"progress":"[=====================================\u003e ] 66.29MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Verifying Checksum","progressDetail":{},"id":"b48a885b52bc"}
{"status":"Download complete","progressDetail":{},"id":"b48a885b52bc"}
{"status":"Extracting","progressDetail":{"current":70746112,"total":88111129},"progress":"[========================================\u003e ] 70.75MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Extracting","progressDetail":{"current":73531392,"total":88111129},"progress":"[=========================================\u003e ] 73.53MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Extracting","progressDetail":{"current":77430784,"total":88111129},"progress":"[===========================================\u003e ] 77.43MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Download complete","progressDetail":{},"id":"50d054c97f4f"}
{"status":"Downloading","progressDetail":{"current":488,"total":1069},"progress":"[======================\u003e ] 488B/1.069kB","id":"4c6bbd90b64d"}
{"status":"Extracting","progressDetail":{"current":80216064,"total":88111129},"progress":"[=============================================\u003e ] 80.22MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":1069,"total":1069},"progress":"[==================================================\u003e] 1.069kB/1.069kB","id":"4c6bbd90b64d"}
{"status":"Verifying Checksum","progressDetail":{},"id":"4c6bbd90b64d"}
{"status":"Download complete","progressDetail":{},"id":"4c6bbd90b64d"}
{"status":"Downloading","progressDetail":{"current":32,"total":32},"progress":"[==================================================\u003e] 32B/32B","id":"4f4fb700ef54"}
{"status":"Verifying Checksum","progressDetail":{},"id":"4f4fb700ef54"}
{"status":"Download complete","progressDetail":{},"id":"4f4fb700ef54"}
{"status":"Extracting","progressDetail":{"current":81887232,"total":88111129},"progress":"[==============================================\u003e ] 81.89MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Extracting","progressDetail":{"current":83558400,"total":88111129},"progress":"[===============================================\u003e ] 83.56MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Extracting","progressDetail":{"current":85229568,"total":88111129},"progress":"[================================================\u003e ] 85.23MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Extracting","progressDetail":{"current":86900736,"total":88111129},"progress":"[=================================================\u003e ] 86.9MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Extracting","progressDetail":{"current":88014848,"total":88111129},"progress":"[=================================================\u003e ] 88.01MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Extracting","progressDetail":{"current":88111129,"total":88111129},"progress":"[==================================================\u003e] 88.11MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Pull complete","progressDetail":{},"id":"4ab897fa6fbf"}
{"status":"Extracting","progressDetail":{"current":32768,"total":1391657},"progress":"[=\u003e ] 32.77kB/1.392MB","id":"d837a2a1365e"}
{"status":"Extracting","progressDetail":{"current":327680,"total":1391657},"progress":"[===========\u003e ] 327.7kB/1.392MB","id":"d837a2a1365e"}
{"status":"Extracting","progressDetail":{"current":1391657,"total":1391657},"progress":"[==================================================\u003e] 1.392MB/1.392MB","id":"d837a2a1365e"}
{"status":"Extracting","progressDetail":{"current":1391657,"total":1391657},"progress":"[==================================================\u003e] 1.392MB/1.392MB","id":"d837a2a1365e"}
{"status":"Pull complete","progressDetail":{},"id":"d837a2a1365e"}
{"status":"Extracting","progressDetail":{"current":32768,"total":1328346},"progress":"[=\u003e ] 32.77kB/1.328MB","id":"988ae18fe41a"}
{"status":"Extracting","progressDetail":{"current":753664,"total":1328346},"progress":"[============================\u003e ] 753.7kB/1.328MB","id":"988ae18fe41a"}
{"status":"Extracting","progressDetail":{"current":1328346,"total":1328346},"progress":"[==================================================\u003e] 1.328MB/1.328MB","id":"988ae18fe41a"}
{"status":"Extracting","progressDetail":{"current":1328346,"total":1328346},"progress":"[==================================================\u003e] 1.328MB/1.328MB","id":"988ae18fe41a"}
{"status":"Pull complete","progressDetail":{},"id":"988ae18fe41a"}
{"status":"Extracting","progressDetail":{"current":4478,"total":4478},"progress":"[==================================================\u003e] 4.478kB/4.478kB","id":"eeb8ef83b565"}
{"status":"Extracting","progressDetail":{"current":4478,"total":4478},"progress":"[==================================================\u003e] 4.478kB/4.478kB","id":"eeb8ef83b565"}
{"status":"Pull complete","progressDetail":{},"id":"eeb8ef83b565"}
{"status":"Extracting","progressDetail":{"current":197,"total":197},"progress":"[==================================================\u003e] 197B/197B","id":"357fefdf9bc9"}
{"status":"Extracting","progressDetail":{"current":197,"total":197},"progress":"[==================================================\u003e] 197B/197B","id":"357fefdf9bc9"}
{"status":"Pull complete","progressDetail":{},"id":"357fefdf9bc9"}
{"status":"Extracting","progressDetail":{"current":294912,"total":27504647},"progress":"[\u003e ] 294.9kB/27.5MB","id":"45b746196f82"}
{"status":"Extracting","progressDetail":{"current":589824,"total":27504647},"progress":"[=\u003e ] 589.8kB/27.5MB","id":"45b746196f82"}
{"status":"Extracting","progressDetail":{"current":5013504,"total":27504647},"progress":"[=========\u003e ] 5.014MB/27.5MB","id":"45b746196f82"}
{"status":"Extracting","progressDetail":{"current":9142272,"total":27504647},"progress":"[================\u003e ] 9.142MB/27.5MB","id":"45b746196f82"}
{"status":"Extracting","progressDetail":{"current":13565952,"total":27504647},"progress":"[========================\u003e ] 13.57MB/27.5MB","id":"45b746196f82"}
{"status":"Extracting","progressDetail":{"current":16515072,"total":27504647},"progress":"[==============================\u003e ] 16.52MB/27.5MB","id":"45b746196f82"}
{"status":"Extracting","progressDetail":{"current":18579456,"total":27504647},"progress":"[=================================\u003e ] 18.58MB/27.5MB","id":"45b746196f82"}
{"status":"Extracting","progressDetail":{"current":21528576,"total":27504647},"progress":"[=======================================\u003e ] 21.53MB/27.5MB","id":"45b746196f82"}
{"status":"Extracting","progressDetail":{"current":25657344,"total":27504647},"progress":"[==============================================\u003e ] 25.66MB/27.5MB","id":"45b746196f82"}
{"status":"Extracting","progressDetail":{"current":27504647,"total":27504647},"progress":"[==================================================\u003e] 27.5MB/27.5MB","id":"45b746196f82"}
{"status":"Pull complete","progressDetail":{},"id":"45b746196f82"}
{"status":"Extracting","progressDetail":{"current":65536,"total":5205016},"progress":"[\u003e ] 65.54kB/5.205MB","id":"fbf4ce20f8c2"}
{"status":"Extracting","progressDetail":{"current":1048576,"total":5205016},"progress":"[==========\u003e ] 1.049MB/5.205MB","id":"fbf4ce20f8c2"}
{"status":"Extracting","progressDetail":{"current":5205016,"total":5205016},"progress":"[==================================================\u003e] 5.205MB/5.205MB","id":"fbf4ce20f8c2"}
{"status":"Pull complete","progressDetail":{},"id":"fbf4ce20f8c2"}
{"status":"Extracting","progressDetail":{"current":65536,"total":4964709},"progress":"[\u003e ] 65.54kB/4.965MB","id":"90aca3c647fe"}
{"status":"Extracting","progressDetail":{"current":1245184,"total":4964709},"progress":"[============\u003e ] 1.245MB/4.965MB","id":"90aca3c647fe"}
{"status":"Extracting","progressDetail":{"current":4964709,"total":4964709},"progress":"[==================================================\u003e] 4.965MB/4.965MB","id":"90aca3c647fe"}
{"status":"Pull complete","progressDetail":{},"id":"90aca3c647fe"}
{"status":"Extracting","progressDetail":{"current":65536,"total":5149051},"progress":"[\u003e ] 65.54kB/5.149MB","id":"1dd62f37c84c"}
{"status":"Extracting","progressDetail":{"current":393216,"total":5149051},"progress":"[===\u003e ] 393.2kB/5.149MB","id":"1dd62f37c84c"}
{"status":"Extracting","progressDetail":{"current":5149051,"total":5149051},"progress":"[==================================================\u003e] 5.149MB/5.149MB","id":"1dd62f37c84c"}
{"status":"Pull complete","progressDetail":{},"id":"1dd62f37c84c"}
{"status":"Extracting","progressDetail":{"current":65536,"total":3855277},"progress":"[\u003e ] 65.54kB/3.855MB","id":"3192b2fa42db"}
{"status":"Extracting","progressDetail":{"current":851968,"total":3855277},"progress":"[===========\u003e ] 852kB/3.855MB","id":"3192b2fa42db"}
{"status":"Extracting","progressDetail":{"current":3855277,"total":3855277},"progress":"[==================================================\u003e] 3.855MB/3.855MB","id":"3192b2fa42db"}
{"status":"Extracting","progressDetail":{"current":3855277,"total":3855277},"progress":"[==================================================\u003e] 3.855MB/3.855MB","id":"3192b2fa42db"}
{"status":"Pull complete","progressDetail":{},"id":"3192b2fa42db"}
{"status":"Extracting","progressDetail":{"current":65536,"total":4983195},"progress":"[\u003e ] 65.54kB/4.983MB","id":"ae190b8f66a7"}
{"status":"Extracting","progressDetail":{"current":327680,"total":4983195},"progress":"[===\u003e ] 327.7kB/4.983MB","id":"ae190b8f66a7"}
{"status":"Extracting","progressDetail":{"current":4980736,"total":4983195},"progress":"[=================================================\u003e ] 4.981MB/4.983MB","id":"ae190b8f66a7"}
{"status":"Extracting","progressDetail":{"current":4983195,"total":4983195},"progress":"[==================================================\u003e] 4.983MB/4.983MB","id":"ae190b8f66a7"}
{"status":"Pull complete","progressDetail":{},"id":"ae190b8f66a7"}
{"status":"Extracting","progressDetail":{"current":65536,"total":6103207},"progress":"[\u003e ] 65.54kB/6.103MB","id":"97bb6e138460"}
{"status":"Extracting","progressDetail":{"current":327680,"total":6103207},"progress":"[==\u003e ] 327.7kB/6.103MB","id":"97bb6e138460"}
{"status":"Extracting","progressDetail":{"current":3670016,"total":6103207},"progress":"[==============================\u003e ] 3.67MB/6.103MB","id":"97bb6e138460"}
{"status":"Extracting","progressDetail":{"current":6103207,"total":6103207},"progress":"[==================================================\u003e] 6.103MB/6.103MB","id":"97bb6e138460"}
{"status":"Pull complete","progressDetail":{},"id":"97bb6e138460"}
{"status":"Extracting","progressDetail":{"current":787,"total":787},"progress":"[==================================================\u003e] 787B/787B","id":"2edb982d5170"}
{"status":"Extracting","progressDetail":{"current":787,"total":787},"progress":"[==================================================\u003e] 787B/787B","id":"2edb982d5170"}
{"status":"Pull complete","progressDetail":{},"id":"2edb982d5170"}
{"status":"Extracting","progressDetail":{"current":65536,"total":4894860},"progress":"[\u003e ] 65.54kB/4.895MB","id":"7ddc8e6d6da9"}
{"status":"Extracting","progressDetail":{"current":327680,"total":4894860},"progress":"[===\u003e ] 327.7kB/4.895MB","id":"7ddc8e6d6da9"}
{"status":"Extracting","progressDetail":{"current":3735552,"total":4894860},"progress":"[======================================\u003e ] 3.736MB/4.895MB","id":"7ddc8e6d6da9"}
{"status":"Extracting","progressDetail":{"current":4894860,"total":4894860},"progress":"[==================================================\u003e] 4.895MB/4.895MB","id":"7ddc8e6d6da9"}
{"status":"Pull complete","progressDetail":{},"id":"7ddc8e6d6da9"}
{"status":"Extracting","progressDetail":{"current":65536,"total":4953791},"progress":"[\u003e ] 65.54kB/4.954MB","id":"0df6fd234b59"}
{"status":"Extracting","progressDetail":{"current":327680,"total":4953791},"progress":"[===\u003e ] 327.7kB/4.954MB","id":"0df6fd234b59"}
{"status":"Extracting","progressDetail":{"current":4325376,"total":4953791},"progress":"[===========================================\u003e ] 4.325MB/4.954MB","id":"0df6fd234b59"}
{"status":"Extracting","progressDetail":{"current":4953791,"total":4953791},"progress":"[==================================================\u003e] 4.954MB/4.954MB","id":"0df6fd234b59"}
{"status":"Pull complete","progressDetail":{},"id":"0df6fd234b59"}
{"status":"Extracting","progressDetail":{"current":65536,"total":6137526},"progress":"[\u003e ] 65.54kB/6.138MB","id":"8fc1ba8efe21"}
{"status":"Extracting","progressDetail":{"current":327680,"total":6137526},"progress":"[==\u003e ] 327.7kB/6.138MB","id":"8fc1ba8efe21"}
{"status":"Extracting","progressDetail":{"current":3801088,"total":6137526},"progress":"[==============================\u003e ] 3.801MB/6.138MB","id":"8fc1ba8efe21"}
{"status":"Extracting","progressDetail":{"current":6137526,"total":6137526},"progress":"[==================================================\u003e] 6.138MB/6.138MB","id":"8fc1ba8efe21"}
{"status":"Pull complete","progressDetail":{},"id":"8fc1ba8efe21"}
{"status":"Extracting","progressDetail":{"current":65536,"total":3854415},"progress":"[\u003e ] 65.54kB/3.854MB","id":"1f6f45e783b5"}
{"status":"Extracting","progressDetail":{"current":851968,"total":3854415},"progress":"[===========\u003e ] 852kB/3.854MB","id":"1f6f45e783b5"}
{"status":"Extracting","progressDetail":{"current":3854415,"total":3854415},"progress":"[==================================================\u003e] 3.854MB/3.854MB","id":"1f6f45e783b5"}
{"status":"Extracting","progressDetail":{"current":3854415,"total":3854415},"progress":"[==================================================\u003e] 3.854MB/3.854MB","id":"1f6f45e783b5"}
{"status":"Pull complete","progressDetail":{},"id":"1f6f45e783b5"}
{"status":"Extracting","progressDetail":{"current":65536,"total":5222290},"progress":"[\u003e ] 65.54kB/5.222MB","id":"43ea61082f68"}
{"status":"Extracting","progressDetail":{"current":458752,"total":5222290},"progress":"[====\u003e ] 458.8kB/5.222MB","id":"43ea61082f68"}
{"status":"Extracting","progressDetail":{"current":4849664,"total":5222290},"progress":"[==============================================\u003e ] 4.85MB/5.222MB","id":"43ea61082f68"}
{"status":"Extracting","progressDetail":{"current":5222290,"total":5222290},"progress":"[==================================================\u003e] 5.222MB/5.222MB","id":"43ea61082f68"}
{"status":"Pull complete","progressDetail":{},"id":"43ea61082f68"}
{"status":"Extracting","progressDetail":{"current":65536,"total":3564359},"progress":"[\u003e ] 65.54kB/3.564MB","id":"b8cf53bbc6ba"}
{"status":"Extracting","progressDetail":{"current":327680,"total":3564359},"progress":"[====\u003e ] 327.7kB/3.564MB","id":"b8cf53bbc6ba"}
{"status":"Extracting","progressDetail":{"current":3564359,"total":3564359},"progress":"[==================================================\u003e] 3.564MB/3.564MB","id":"b8cf53bbc6ba"}
{"status":"Pull complete","progressDetail":{},"id":"b8cf53bbc6ba"}
{"status":"Extracting","progressDetail":{"current":790,"total":790},"progress":"[==================================================\u003e] 790B/790B","id":"25efb07e4521"}
{"status":"Extracting","progressDetail":{"current":790,"total":790},"progress":"[==================================================\u003e] 790B/790B","id":"25efb07e4521"}
{"status":"Pull complete","progressDetail":{},"id":"25efb07e4521"}
{"status":"Extracting","progressDetail":{"current":65536,"total":5120108},"progress":"[\u003e ] 65.54kB/5.12MB","id":"1c3245356213"}
{"status":"Extracting","progressDetail":{"current":327680,"total":5120108},"progress":"[===\u003e ] 327.7kB/5.12MB","id":"1c3245356213"}
{"status":"Extracting","progressDetail":{"current":5111808,"total":5120108},"progress":"[=================================================\u003e ] 5.112MB/5.12MB","id":"1c3245356213"}
{"status":"Extracting","progressDetail":{"current":5120108,"total":5120108},"progress":"[==================================================\u003e] 5.12MB/5.12MB","id":"1c3245356213"}
{"status":"Pull complete","progressDetail":{},"id":"1c3245356213"}
{"status":"Extracting","progressDetail":{"current":65536,"total":5117023},"progress":"[\u003e ] 65.54kB/5.117MB","id":"61ebb123c1eb"}
{"status":"Extracting","progressDetail":{"current":655360,"total":5117023},"progress":"[======\u003e ] 655.4kB/5.117MB","id":"61ebb123c1eb"}
{"status":"Extracting","progressDetail":{"current":4259840,"total":5117023},"progress":"[=========================================\u003e ] 4.26MB/5.117MB","id":"61ebb123c1eb"}
{"status":"Extracting","progressDetail":{"current":5117023,"total":5117023},"progress":"[==================================================\u003e] 5.117MB/5.117MB","id":"61ebb123c1eb"}
{"status":"Pull complete","progressDetail":{},"id":"61ebb123c1eb"}
{"status":"Extracting","progressDetail":{"current":65536,"total":5384215},"progress":"[\u003e ] 65.54kB/5.384MB","id":"0964b769d2c9"}
{"status":"Extracting","progressDetail":{"current":327680,"total":5384215},"progress":"[===\u003e ] 327.7kB/5.384MB","id":"0964b769d2c9"}
{"status":"Extracting","progressDetail":{"current":5177344,"total":5384215},"progress":"[================================================\u003e ] 5.177MB/5.384MB","id":"0964b769d2c9"}
{"status":"Extracting","progressDetail":{"current":5384215,"total":5384215},"progress":"[==================================================\u003e] 5.384MB/5.384MB","id":"0964b769d2c9"}
{"status":"Pull complete","progressDetail":{},"id":"0964b769d2c9"}
{"status":"Extracting","progressDetail":{"current":65536,"total":5252487},"progress":"[\u003e ] 65.54kB/5.252MB","id":"87f7843f43cd"}
{"status":"Extracting","progressDetail":{"current":655360,"total":5252487},"progress":"[======\u003e ] 655.4kB/5.252MB","id":"87f7843f43cd"}
{"status":"Extracting","progressDetail":{"current":5252487,"total":5252487},"progress":"[==================================================\u003e] 5.252MB/5.252MB","id":"87f7843f43cd"}
{"status":"Pull complete","progressDetail":{},"id":"87f7843f43cd"}
{"status":"Extracting","progressDetail":{"current":65536,"total":5015856},"progress":"[\u003e ] 65.54kB/5.016MB","id":"a89dbf94d794"}
{"status":"Extracting","progressDetail":{"current":327680,"total":5015856},"progress":"[===\u003e ] 327.7kB/5.016MB","id":"a89dbf94d794"}
{"status":"Extracting","progressDetail":{"current":3997696,"total":5015856},"progress":"[=======================================\u003e ] 3.998MB/5.016MB","id":"a89dbf94d794"}
{"status":"Extracting","progressDetail":{"current":5015856,"total":5015856},"progress":"[==================================================\u003e] 5.016MB/5.016MB","id":"a89dbf94d794"}
{"status":"Pull complete","progressDetail":{},"id":"a89dbf94d794"}
{"status":"Extracting","progressDetail":{"current":65536,"total":5310566},"progress":"[\u003e ] 65.54kB/5.311MB","id":"f0d43ddca77f"}
{"status":"Extracting","progressDetail":{"current":393216,"total":5310566},"progress":"[===\u003e ] 393.2kB/5.311MB","id":"f0d43ddca77f"}
{"status":"Extracting","progressDetail":{"current":3407872,"total":5310566},"progress":"[================================\u003e ] 3.408MB/5.311MB","id":"f0d43ddca77f"}
{"status":"Extracting","progressDetail":{"current":5310566,"total":5310566},"progress":"[==================================================\u003e] 5.311MB/5.311MB","id":"f0d43ddca77f"}
{"status":"Pull complete","progressDetail":{},"id":"f0d43ddca77f"}
{"status":"Extracting","progressDetail":{"current":65536,"total":4915049},"progress":"[\u003e ] 65.54kB/4.915MB","id":"7c674f0cb40c"}
{"status":"Extracting","progressDetail":{"current":786432,"total":4915049},"progress":"[========\u003e ] 786.4kB/4.915MB","id":"7c674f0cb40c"}
{"status":"Extracting","progressDetail":{"current":4915049,"total":4915049},"progress":"[==================================================\u003e] 4.915MB/4.915MB","id":"7c674f0cb40c"}
{"status":"Extracting","progressDetail":{"current":4915049,"total":4915049},"progress":"[==================================================\u003e] 4.915MB/4.915MB","id":"7c674f0cb40c"}
{"status":"Pull complete","progressDetail":{},"id":"7c674f0cb40c"}
{"status":"Extracting","progressDetail":{"current":65536,"total":5119213},"progress":"[\u003e ] 65.54kB/5.119MB","id":"b48a885b52bc"}
{"status":"Extracting","progressDetail":{"current":327680,"total":5119213},"progress":"[===\u003e ] 327.7kB/5.119MB","id":"b48a885b52bc"}
{"status":"Extracting","progressDetail":{"current":4390912,"total":5119213},"progress":"[==========================================\u003e ] 4.391MB/5.119MB","id":"b48a885b52bc"}
{"status":"Extracting","progressDetail":{"current":5119213,"total":5119213},"progress":"[==================================================\u003e] 5.119MB/5.119MB","id":"b48a885b52bc"}
{"status":"Pull complete","progressDetail":{},"id":"b48a885b52bc"}
{"status":"Extracting","progressDetail":{"current":395,"total":395},"progress":"[==================================================\u003e] 395B/395B","id":"272cdf839cbb"}
{"status":"Extracting","progressDetail":{"current":395,"total":395},"progress":"[==================================================\u003e] 395B/395B","id":"272cdf839cbb"}
{"status":"Pull complete","progressDetail":{},"id":"272cdf839cbb"}
{"status":"Extracting","progressDetail":{"current":155,"total":155},"progress":"[==================================================\u003e] 155B/155B","id":"50d054c97f4f"}
{"status":"Extracting","progressDetail":{"current":155,"total":155},"progress":"[==================================================\u003e] 155B/155B","id":"50d054c97f4f"}
{"status":"Pull complete","progressDetail":{},"id":"50d054c97f4f"}
{"status":"Extracting","progressDetail":{"current":1069,"total":1069},"progress":"[==================================================\u003e] 1.069kB/1.069kB","id":"4c6bbd90b64d"}
{"status":"Extracting","progressDetail":{"current":1069,"total":1069},"progress":"[==================================================\u003e] 1.069kB/1.069kB","id":"4c6bbd90b64d"}
{"status":"Pull complete","progressDetail":{},"id":"4c6bbd90b64d"}
{"status":"Extracting","progressDetail":{"current":32,"total":32},"progress":"[==================================================\u003e] 32B/32B","id":"4f4fb700ef54"}
{"status":"Extracting","progressDetail":{"current":32,"total":32},"progress":"[==================================================\u003e] 32B/32B","id":"4f4fb700ef54"}
{"status":"Pull complete","progressDetail":{},"id":"4f4fb700ef54"}
{"status":"Digest: sha256:4acb6bfd6c4f0cabaf7f3690e444afe51f1c7de54d51da7e63fac709c56f1c30"}
{"status":"Status: Downloaded newer image for cloudfoundry/cnb:bionic"}

View File

@@ -0,0 +1,9 @@
{
"status": "Extracting",
"progressDetail": {
"current": 16,
"total": 32
},
"progress": "[==================================================\u003e] 32B/32B",
"id": "4f4fb700ef54"
}

View File

@@ -0,0 +1,3 @@
{
"status": "Status: Downloaded newer image for cloudfoundry/cnb:bionic"
}

View File

@@ -0,0 +1,6 @@
{
"status": "Pulling fs layer",
"progressDetail": {
},
"id": "d837a2a1365e"
}

View File

@@ -0,0 +1,17 @@
{
"User": "root",
"Image": "docker.io/library/ubuntu:bionic",
"Cmd": [
"ls",
"-l",
"-h"
],
"Labels": {
"spring": "boot"
},
"HostConfig": {
"Binds": [
"bind-source:bind-dest"
]
}
}

View File

@@ -0,0 +1,224 @@
{
"config": {
"Hostname": "",
"Domainname": "",
"User": "vcap",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"CNB_USER_ID=2000",
"CNB_GROUP_ID=2000",
"CNB_STACK_ID=org.cloudfoundry.stacks.cflinuxfs3"
],
"Cmd": null,
"Image": "sha256:523c8ade6e06f814469b2cf04c8045a74becee17088955f2657958476d3fba1f",
"Volumes": null,
"WorkingDir": "/layers",
"Entrypoint": null,
"OnBuild": null,
"Labels": {
"io.buildpacks.stack.id": "org.cloudfoundry.stacks.cflinuxfs3"
}
},
"created": "1906-12-09T11:30:00Z",
"history": [
{
},
{
},
{
},
{
},
{
},
{
},
{
},
{
},
{
},
{
},
{
},
{
},
{
},
{
},
{
},
{
},
{
},
{
},
{
},
{
},
{
},
{
},
{
},
{
},
{
},
{
},
{
},
{
},
{
},
{
},
{
},
{
},
{
},
{
},
{
},
{
},
{
},
{
},
{
},
{
},
{
},
{
},
{
},
{
},
{
},
{
},
{
}
],
"os": "linux",
"rootfs": {
"diff_ids": [
"sha256:733a8e5ce32984099ef675fce04730f6e2a6dcfdf5bd292fea01a8f936265342",
"sha256:7755b972f0b4f49de73ef5114fb3ba9c69d80f217e80da99f56f0d0a5dcb3d70",
"sha256:8f0b2d09ab4b38530a1630403967d11a601e56e02e79d3f56370d34fd071fe38",
"sha256:8debe4b6b4290dbbfecea9edea61c22fb455e69e3cbc7d63b17f8e1ab8ea669b",
"sha256:0c6ddab305e5452850f3c09fe15310dff8dc7221702d736dc7705882c1df9658",
"sha256:a9527973bb5d7ccdf88b5be8eb81e024094be1709df659af3127865463c1c188",
"sha256:480cd420e43c6895240c87c88969b87417549c02393cde1b6f71a3a3d5a2a620",
"sha256:391d950d763a33d8ae0373f218aa59907599f51e42cd864129591887e1291034",
"sha256:5b3ec0a6ed9e3de93bb082151f56b1cde5d7e31f2809039a1b5b55a5052fe873",
"sha256:ef935546e2c99da3e8962f2eb3cd6813e9e9a8b19bc8d15b56d1cac37f0342d5",
"sha256:6d644992d62bd09a2bbf490b7fe3aa1e35e6d0d2479583c2decec7092f193310",
"sha256:59d817c36a25078c8ec1f6de0d8336aec598037f89708ed13dbf661557a25084",
"sha256:6636ce01d12372e56a89ec77ea8d9ed510f8c701df1220750add4613764c05a4",
"sha256:96c7f369c29bbf11b971e4dbb6473e8991b666f9de046a414317634eb0a25d2a",
"sha256:3544ba1fa82d1e89619ed04c2485fab3445b1603959d224792d1183dd658033d",
"sha256:12d99406b52b526af152628cd72ba6eacf5d18484dc79cfdacd4b38a21620a2b",
"sha256:3b3cda9eceb0fca56c274e3be93daf53f59501e6b3628fabbaea8ea416eb757a",
"sha256:27ad0fc48c381eb77f69b4e80edccb4d8a2399f5cebd5a8c5a3e1c32313343a6",
"sha256:52845fb94361dad36cc4136e49b92c79ca59c16c579e2f51df0c58ba355c4367",
"sha256:7bf3a57229276fb913155b077d00a18ec6cba92c7f062728ca1c3bc3503c0b55",
"sha256:e5df92d3db931488225ca9f7290de0334225d4bd7c48fc2dcd380d0921bb6680",
"sha256:290ac64fbae3288821551371c8dda38fcf5dfa063a54cb270dcc395a090f5173",
"sha256:996aee90e29ed78d80a5a0c0e50d60a732a18fddae06f87b68bef183beddd2c4",
"sha256:30f6a316d4da01d694d8c17aa84b37f468cccc7184248e255486eb3095ebb87c",
"sha256:c694476a7241ba4e4a0663606d4d6eec7ed8624252c010fbef2713968e8f9436",
"sha256:ab9aaff2160873663388faea6d987cd8f2b5935137b81c64fde145bf2a330d54",
"sha256:e1f3ab860045b96235cbc1b89a3e73add955a303eb42905b570b6012b73b9184",
"sha256:0b260d90d097379d4351132b45110d013b98f4a335795baeb95788fcebcb7f3c",
"sha256:f0f5ecd72b4e0a38d3ad73b5756d8f209955932e9615715502a61dffe56f401a",
"sha256:b4cd790490e41c808e8d65f9ac8f2e58c79bc1a9919a713c4519e77b26dc2053",
"sha256:16b88c0e7f950c32c7496117d1efad90a8557a2badcb267d99a19676b1f0b76a",
"sha256:49d36ba00b17fb605f374ca7877ae129678de925d10fd1955f07c2b6f74dd1c9",
"sha256:b31d189a88ca43fee6077c25bcb623582d569193ed6ac11b4e5623558911e3de",
"sha256:3ecfd2822cf64c609c9c8489e2accfbc0b1de0f2a3637ff1b5d30768fb34b40c",
"sha256:a7f09c3e09b29c5503962a068f29e8726cb91d1dbce2fab688aee0a98189b2be",
"sha256:3d12e651068a0ff19afdd568b5d14ee5292f849542b31d6c9b099a09344e1f4d",
"sha256:f01e41975a9335f5983021b081bc700e46b85efb262670223c4db61eea0a3ebd",
"sha256:2b1b655bb8752f631e786c4c55670315d8569acccfe26402942977c216f2803a",
"sha256:0943c634f5c24311ebdeca6fef5682a4a374c89a831700d188bff7f987470004",
"sha256:9a183e56c86d376b408bdf922746d0a657f62b0e18c7c8f82a496b87710c576f",
"sha256:d919f3c2f534ddbb0b6057f82bca36051ce80a2a9cd3016c320ae276884311f5",
"sha256:108a3eb288f8094aab6ffd822c593902e48e85c8a37b7da2bd21b15f785d92c5",
"sha256:f8b5dcfa1d082af23bb2b2c08526131921329d48d1614d9f2f163a997176087a",
"sha256:ee13e75c33e0af49fbf6c3aaa5bbd102fc468c2d554c4f94763d35a33964dfe4",
"sha256:2571abab1776d4c2e427fba10d61531afff2ab0789f89ef46ce925b6a5d98e0f",
"sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef",
"sha256:bb09e17fd1bd2ee47155f1349645fcd9fff31e1247c7ed99cad469f1c16a4216"
]
}
}

View File

@@ -0,0 +1,57 @@
[
{
"Config": "/d1872169d781cff5e1aa22d111f636bef0c57e1c358ca3861e3d33a5bdb1b4a5.json",
"Layers": [
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"bb09e17fd1bd2ee47155f1349645fcd9fff31e1247c7ed99cad469f1c16a4216.tar"
],
"RepoTags": [
"pack.local/builder/6b7874626575656b6162:latest"
]
}
]

View File

@@ -0,0 +1,51 @@
[
{
"Config": "fdc5f384ea0818dd99462e53bf2088a0fa42ad4de5878fdf078935192604da6d.json",
"Layers": [
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"/e39b9186d3d35693645f81db5ec6ced177c4da2d26f71a55de7834fc3b161a60.tar",
"/791b31c608b369f0d6e23aaf55dd6bae76ffd92292afd3eb4dd35f8a389636fb.tar",
"/66d1ab676a2ecb3852104177d2fd9499d90bbbd97984bccb62180502e15a7086.tar",
"/b5787d8d30d02769ebbe6b1ac32d37764feef3cd5cdc68aeffd72bb27d1886e5.tar"
],
"RepoTags": [
"pack.local/builder/6b7874626575656b6162:latest"
]
}
]

View File

@@ -0,0 +1,598 @@
{
"status": "Pulling from cloudfoundry/cnb",
"id": "bionic"
}
{"status":"Pulling fs layer","progressDetail":{},"id":"5667fdb72017"}
{"status":"Pulling fs layer","progressDetail":{},"id":"d83811f270d5"}
{"status":"Pulling fs layer","progressDetail":{},"id":"ee671aafb583"}
{"status":"Pulling fs layer","progressDetail":{},"id":"7fc152dfb3a6"}
{"status":"Pulling fs layer","progressDetail":{},"id":"4ab897fa6fbf"}
{"status":"Pulling fs layer","progressDetail":{},"id":"d837a2a1365e"}
{"status":"Pulling fs layer","progressDetail":{},"id":"988ae18fe41a"}
{"status":"Pulling fs layer","progressDetail":{},"id":"eeb8ef83b565"}
{"status":"Pulling fs layer","progressDetail":{},"id":"357fefdf9bc9"}
{"status":"Pulling fs layer","progressDetail":{},"id":"45b746196f82"}
{"status":"Pulling fs layer","progressDetail":{},"id":"fbf4ce20f8c2"}
{"status":"Pulling fs layer","progressDetail":{},"id":"90aca3c647fe"}
{"status":"Pulling fs layer","progressDetail":{},"id":"1dd62f37c84c"}
{"status":"Pulling fs layer","progressDetail":{},"id":"3192b2fa42db"}
{"status":"Pulling fs layer","progressDetail":{},"id":"ae190b8f66a7"}
{"status":"Pulling fs layer","progressDetail":{},"id":"97bb6e138460"}
{"status":"Waiting","progressDetail":{},"id":"eeb8ef83b565"}
{"status":"Pulling fs layer","progressDetail":{},"id":"2edb982d5170"}
{"status":"Waiting","progressDetail":{},"id":"357fefdf9bc9"}
{"status":"Pulling fs layer","progressDetail":{},"id":"7ddc8e6d6da9"}
{"status":"Pulling fs layer","progressDetail":{},"id":"0df6fd234b59"}
{"status":"Waiting","progressDetail":{},"id":"45b746196f82"}
{"status":"Pulling fs layer","progressDetail":{},"id":"8fc1ba8efe21"}
{"status":"Pulling fs layer","progressDetail":{},"id":"1f6f45e783b5"}
{"status":"Pulling fs layer","progressDetail":{},"id":"43ea61082f68"}
{"status":"Waiting","progressDetail":{},"id":"7fc152dfb3a6"}
{"status":"Pulling fs layer","progressDetail":{},"id":"b8cf53bbc6ba"}
{"status":"Waiting","progressDetail":{},"id":"fbf4ce20f8c2"}
{"status":"Pulling fs layer","progressDetail":{},"id":"25efb07e4521"}
{"status":"Waiting","progressDetail":{},"id":"90aca3c647fe"}
{"status":"Pulling fs layer","progressDetail":{},"id":"1c3245356213"}
{"status":"Waiting","progressDetail":{},"id":"1dd62f37c84c"}
{"status":"Pulling fs layer","progressDetail":{},"id":"61ebb123c1eb"}
{"status":"Pulling fs layer","progressDetail":{},"id":"0964b769d2c9"}
{"status":"Waiting","progressDetail":{},"id":"3192b2fa42db"}
{"status":"Pulling fs layer","progressDetail":{},"id":"87f7843f43cd"}
{"status":"Pulling fs layer","progressDetail":{},"id":"a89dbf94d794"}
{"status":"Waiting","progressDetail":{},"id":"ae190b8f66a7"}
{"status":"Pulling fs layer","progressDetail":{},"id":"f0d43ddca77f"}
{"status":"Pulling fs layer","progressDetail":{},"id":"7c674f0cb40c"}
{"status":"Waiting","progressDetail":{},"id":"97bb6e138460"}
{"status":"Pulling fs layer","progressDetail":{},"id":"b48a885b52bc"}
{"status":"Pulling fs layer","progressDetail":{},"id":"272cdf839cbb"}
{"status":"Pulling fs layer","progressDetail":{},"id":"50d054c97f4f"}
{"status":"Pulling fs layer","progressDetail":{},"id":"4c6bbd90b64d"}
{"status":"Waiting","progressDetail":{},"id":"2edb982d5170"}
{"status":"Pulling fs layer","progressDetail":{},"id":"4f4fb700ef54"}
{"status":"Waiting","progressDetail":{},"id":"7ddc8e6d6da9"}
{"status":"Waiting","progressDetail":{},"id":"b8cf53bbc6ba"}
{"status":"Waiting","progressDetail":{},"id":"25efb07e4521"}
{"status":"Waiting","progressDetail":{},"id":"0df6fd234b59"}
{"status":"Waiting","progressDetail":{},"id":"1c3245356213"}
{"status":"Waiting","progressDetail":{},"id":"61ebb123c1eb"}
{"status":"Waiting","progressDetail":{},"id":"8fc1ba8efe21"}
{"status":"Waiting","progressDetail":{},"id":"0964b769d2c9"}
{"status":"Waiting","progressDetail":{},"id":"87f7843f43cd"}
{"status":"Waiting","progressDetail":{},"id":"1f6f45e783b5"}
{"status":"Waiting","progressDetail":{},"id":"a89dbf94d794"}
{"status":"Waiting","progressDetail":{},"id":"43ea61082f68"}
{"status":"Waiting","progressDetail":{},"id":"f0d43ddca77f"}
{"status":"Waiting","progressDetail":{},"id":"7c674f0cb40c"}
{"status":"Waiting","progressDetail":{},"id":"b48a885b52bc"}
{"status":"Waiting","progressDetail":{},"id":"272cdf839cbb"}
{"status":"Waiting","progressDetail":{},"id":"50d054c97f4f"}
{"status":"Waiting","progressDetail":{},"id":"4c6bbd90b64d"}
{"status":"Waiting","progressDetail":{},"id":"4f4fb700ef54"}
{"status":"Waiting","progressDetail":{},"id":"4ab897fa6fbf"}
{"status":"Waiting","progressDetail":{},"id":"d837a2a1365e"}
{"status":"Waiting","progressDetail":{},"id":"988ae18fe41a"}
{"status":"Downloading","progressDetail":{"current":487,"total":850},"progress":"[============================\u003e ] 487B/850B","id":"ee671aafb583"}
{"status":"Downloading","progressDetail":{"current":485,"total":35355},"progress":"[\u003e ] 485B/35.35kB","id":"d83811f270d5"}
{"status":"Downloading","progressDetail":{"current":35355,"total":35355},"progress":"[==================================================\u003e] 35.35kB/35.35kB","id":"d83811f270d5"}
{"status":"Verifying Checksum","progressDetail":{},"id":"d83811f270d5"}
{"status":"Download complete","progressDetail":{},"id":"d83811f270d5"}
{"status":"Downloading","progressDetail":{"current":277600,"total":26683298},"progress":"[\u003e ] 277.6kB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":850,"total":850},"progress":"[==================================================\u003e] 850B/850B","id":"ee671aafb583"}
{"status":"Verifying Checksum","progressDetail":{},"id":"ee671aafb583"}
{"status":"Download complete","progressDetail":{},"id":"ee671aafb583"}
{"status":"Downloading","progressDetail":{"current":2218692,"total":26683298},"progress":"[====\u003e ] 2.219MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":4160196,"total":26683298},"progress":"[=======\u003e ] 4.16MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":6109892,"total":26683298},"progress":"[===========\u003e ] 6.11MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":7772868,"total":26683298},"progress":"[==============\u003e ] 7.773MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":9444036,"total":26683298},"progress":"[=================\u003e ] 9.444MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":163,"total":163},"progress":"[==================================================\u003e] 163B/163B","id":"7fc152dfb3a6"}
{"status":"Verifying Checksum","progressDetail":{},"id":"7fc152dfb3a6"}
{"status":"Download complete","progressDetail":{},"id":"7fc152dfb3a6"}
{"status":"Downloading","progressDetail":{"current":10832580,"total":26683298},"progress":"[====================\u003e ] 10.83MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":531179,"total":88111129},"progress":"[\u003e ] 531.2kB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":11668164,"total":26683298},"progress":"[=====================\u003e ] 11.67MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":1604331,"total":88111129},"progress":"[\u003e ] 1.604MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":12495556,"total":26683298},"progress":"[=======================\u003e ] 12.5MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":3209963,"total":88111129},"progress":"[=\u003e ] 3.21MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":13331140,"total":26683298},"progress":"[========================\u003e ] 13.33MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":4283115,"total":88111129},"progress":"[==\u003e ] 4.283MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":14166724,"total":26683298},"progress":"[==========================\u003e ] 14.17MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":5888747,"total":88111129},"progress":"[===\u003e ] 5.889MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":15280836,"total":26683298},"progress":"[============================\u003e ] 15.28MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":14318,"total":1391657},"progress":"[\u003e ] 14.32kB/1.392MB","id":"d837a2a1365e"}
{"status":"Downloading","progressDetail":{"current":6961899,"total":88111129},"progress":"[===\u003e ] 6.962MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":16116420,"total":26683298},"progress":"[==============================\u003e ] 16.12MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":936688,"total":1391657},"progress":"[=================================\u003e ] 936.7kB/1.392MB","id":"d837a2a1365e"}
{"status":"Verifying Checksum","progressDetail":{},"id":"d837a2a1365e"}
{"status":"Download complete","progressDetail":{},"id":"d837a2a1365e"}
{"status":"Downloading","progressDetail":{"current":8022763,"total":88111129},"progress":"[====\u003e ] 8.023MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":16931524,"total":26683298},"progress":"[===============================\u003e ] 16.93MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":18045636,"total":26683298},"progress":"[=================================\u003e ] 18.05MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":9632491,"total":88111129},"progress":"[=====\u003e ] 9.632MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":10709739,"total":88111129},"progress":"[======\u003e ] 10.71MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":19143364,"total":26683298},"progress":"[===================================\u003e ] 19.14MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":11778795,"total":88111129},"progress":"[======\u003e ] 11.78MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":20249284,"total":26683298},"progress":"[=====================================\u003e ] 20.25MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":12851947,"total":88111129},"progress":"[=======\u003e ] 12.85MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":21072580,"total":26683298},"progress":"[=======================================\u003e ] 21.07MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":14133,"total":1328346},"progress":"[\u003e ] 14.13kB/1.328MB","id":"988ae18fe41a"}
{"status":"Downloading","progressDetail":{"current":13933291,"total":88111129},"progress":"[=======\u003e ] 13.93MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":21908164,"total":26683298},"progress":"[=========================================\u003e ] 21.91MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":973511,"total":1328346},"progress":"[====================================\u003e ] 973.5kB/1.328MB","id":"988ae18fe41a"}
{"status":"Verifying Checksum","progressDetail":{},"id":"988ae18fe41a"}
{"status":"Download complete","progressDetail":{},"id":"988ae18fe41a"}
{"status":"Downloading","progressDetail":{"current":15014635,"total":88111129},"progress":"[========\u003e ] 15.01MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":22747844,"total":26683298},"progress":"[==========================================\u003e ] 22.75MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":16075499,"total":88111129},"progress":"[=========\u003e ] 16.08MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":23575236,"total":26683298},"progress":"[============================================\u003e ] 23.58MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":24414916,"total":26683298},"progress":"[=============================================\u003e ] 24.41MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":17132267,"total":88111129},"progress":"[=========\u003e ] 17.13MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":25250500,"total":26683298},"progress":"[===============================================\u003e ] 25.25MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":18213611,"total":88111129},"progress":"[==========\u003e ] 18.21MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":26073796,"total":26683298},"progress":"[================================================\u003e ] 26.07MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":19286763,"total":88111129},"progress":"[==========\u003e ] 19.29MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":490,"total":4478},"progress":"[=====\u003e ] 490B/4.478kB","id":"eeb8ef83b565"}
{"status":"Downloading","progressDetail":{"current":4478,"total":4478},"progress":"[==================================================\u003e] 4.478kB/4.478kB","id":"eeb8ef83b565"}
{"status":"Verifying Checksum","progressDetail":{},"id":"eeb8ef83b565"}
{"status":"Download complete","progressDetail":{},"id":"eeb8ef83b565"}
{"status":"Verifying Checksum","progressDetail":{},"id":"5667fdb72017"}
{"status":"Download complete","progressDetail":{},"id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":20892395,"total":88111129},"progress":"[===========\u003e ] 20.89MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Extracting","progressDetail":{"current":294912,"total":26683298},"progress":"[\u003e ] 294.9kB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":23050987,"total":88111129},"progress":"[=============\u003e ] 23.05MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Extracting","progressDetail":{"current":2654208,"total":26683298},"progress":"[====\u003e ] 2.654MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":25205483,"total":88111129},"progress":"[==============\u003e ] 25.21MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Extracting","progressDetail":{"current":6193152,"total":26683298},"progress":"[===========\u003e ] 6.193MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":27355883,"total":88111129},"progress":"[===============\u003e ] 27.36MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Extracting","progressDetail":{"current":8552448,"total":26683298},"progress":"[================\u003e ] 8.552MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":197,"total":197},"progress":"[==================================================\u003e] 197B/197B","id":"357fefdf9bc9"}
{"status":"Verifying Checksum","progressDetail":{},"id":"357fefdf9bc9"}
{"status":"Download complete","progressDetail":{},"id":"357fefdf9bc9"}
{"status":"Extracting","progressDetail":{"current":11796480,"total":26683298},"progress":"[======================\u003e ] 11.8MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":29510379,"total":88111129},"progress":"[================\u003e ] 29.51MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":277600,"total":27504647},"progress":"[\u003e ] 277.6kB/27.5MB","id":"45b746196f82"}
{"status":"Extracting","progressDetail":{"current":15040512,"total":26683298},"progress":"[============================\u003e ] 15.04MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":1391300,"total":27504647},"progress":"[==\u003e ] 1.391MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":31132395,"total":88111129},"progress":"[=================\u003e ] 31.13MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Extracting","progressDetail":{"current":17989632,"total":26683298},"progress":"[=================================\u003e ] 17.99MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":32754411,"total":88111129},"progress":"[==================\u003e ] 32.75MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":2230980,"total":27504647},"progress":"[====\u003e ] 2.231MB/27.5MB","id":"45b746196f82"}
{"status":"Extracting","progressDetail":{"current":22118400,"total":26683298},"progress":"[=========================================\u003e ] 22.12MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":33835755,"total":88111129},"progress":"[===================\u003e ] 33.84MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":3078852,"total":27504647},"progress":"[=====\u003e ] 3.079MB/27.5MB","id":"45b746196f82"}
{"status":"Extracting","progressDetail":{"current":24477696,"total":26683298},"progress":"[=============================================\u003e ] 24.48MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":52419,"total":5205016},"progress":"[\u003e ] 52.42kB/5.205MB","id":"fbf4ce20f8c2"}
{"status":"Downloading","progressDetail":{"current":34917099,"total":88111129},"progress":"[===================\u003e ] 34.92MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":3922628,"total":27504647},"progress":"[=======\u003e ] 3.923MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":912096,"total":5205016},"progress":"[========\u003e ] 912.1kB/5.205MB","id":"fbf4ce20f8c2"}
{"status":"Extracting","progressDetail":{"current":26247168,"total":26683298},"progress":"[=================================================\u003e ] 26.25MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":4487876,"total":27504647},"progress":"[========\u003e ] 4.488MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":35986155,"total":88111129},"progress":"[====================\u003e ] 35.99MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Extracting","progressDetail":{"current":26683298,"total":26683298},"progress":"[==================================================\u003e] 26.68MB/26.68MB","id":"5667fdb72017"}
{"status":"Downloading","progressDetail":{"current":1805024,"total":5205016},"progress":"[=================\u003e ] 1.805MB/5.205MB","id":"fbf4ce20f8c2"}
{"status":"Downloading","progressDetail":{"current":5044932,"total":27504647},"progress":"[=========\u003e ] 5.045MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":36522731,"total":88111129},"progress":"[====================\u003e ] 36.52MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":2550496,"total":5205016},"progress":"[========================\u003e ] 2.55MB/5.205MB","id":"fbf4ce20f8c2"}
{"status":"Downloading","progressDetail":{"current":5601988,"total":27504647},"progress":"[==========\u003e ] 5.602MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":3381984,"total":5205016},"progress":"[================================\u003e ] 3.382MB/5.205MB","id":"fbf4ce20f8c2"}
{"status":"Downloading","progressDetail":{"current":37063403,"total":88111129},"progress":"[=====================\u003e ] 37.06MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":6159044,"total":27504647},"progress":"[===========\u003e ] 6.159MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":4152032,"total":5205016},"progress":"[=======================================\u003e ] 4.152MB/5.205MB","id":"fbf4ce20f8c2"}
{"status":"Downloading","progressDetail":{"current":37604075,"total":88111129},"progress":"[=====================\u003e ] 37.6MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Pull complete","progressDetail":{},"id":"5667fdb72017"}
{"status":"Extracting","progressDetail":{"current":32768,"total":35355},"progress":"[==============================================\u003e ] 32.77kB/35.35kB","id":"d83811f270d5"}
{"status":"Extracting","progressDetail":{"current":35355,"total":35355},"progress":"[==================================================\u003e] 35.35kB/35.35kB","id":"d83811f270d5"}
{"status":"Downloading","progressDetail":{"current":5004000,"total":5205016},"progress":"[================================================\u003e ] 5.004MB/5.205MB","id":"fbf4ce20f8c2"}
{"status":"Downloading","progressDetail":{"current":6716100,"total":27504647},"progress":"[============\u003e ] 6.716MB/27.5MB","id":"45b746196f82"}
{"status":"Verifying Checksum","progressDetail":{},"id":"fbf4ce20f8c2"}
{"status":"Download complete","progressDetail":{},"id":"fbf4ce20f8c2"}
{"status":"Downloading","progressDetail":{"current":38144747,"total":88111129},"progress":"[=====================\u003e ] 38.14MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Pull complete","progressDetail":{},"id":"d83811f270d5"}
{"status":"Extracting","progressDetail":{"current":850,"total":850},"progress":"[==================================================\u003e] 850B/850B","id":"ee671aafb583"}
{"status":"Extracting","progressDetail":{"current":850,"total":850},"progress":"[==================================================\u003e] 850B/850B","id":"ee671aafb583"}
{"status":"Downloading","progressDetail":{"current":7293636,"total":27504647},"progress":"[=============\u003e ] 7.294MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":39213803,"total":88111129},"progress":"[======================\u003e ] 39.21MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":8129220,"total":27504647},"progress":"[==============\u003e ] 8.129MB/27.5MB","id":"45b746196f82"}
{"status":"Pull complete","progressDetail":{},"id":"ee671aafb583"}
{"status":"Extracting","progressDetail":{"current":163,"total":163},"progress":"[==================================================\u003e] 163B/163B","id":"7fc152dfb3a6"}
{"status":"Extracting","progressDetail":{"current":163,"total":163},"progress":"[==================================================\u003e] 163B/163B","id":"7fc152dfb3a6"}
{"status":"Downloading","progressDetail":{"current":40295147,"total":88111129},"progress":"[======================\u003e ] 40.3MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":8964804,"total":27504647},"progress":"[================\u003e ] 8.965MB/27.5MB","id":"45b746196f82"}
{"status":"Pull complete","progressDetail":{},"id":"7fc152dfb3a6"}
{"status":"Downloading","progressDetail":{"current":9800388,"total":27504647},"progress":"[=================\u003e ] 9.8MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":41368299,"total":88111129},"progress":"[=======================\u003e ] 41.37MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":49680,"total":4964709},"progress":"[\u003e ] 49.68kB/4.965MB","id":"90aca3c647fe"}
{"status":"Downloading","progressDetail":{"current":10635972,"total":27504647},"progress":"[===================\u003e ] 10.64MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":908013,"total":4964709},"progress":"[=========\u003e ] 908kB/4.965MB","id":"90aca3c647fe"}
{"status":"Downloading","progressDetail":{"current":41908971,"total":88111129},"progress":"[=======================\u003e ] 41.91MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":11193028,"total":27504647},"progress":"[====================\u003e ] 11.19MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":2038509,"total":4964709},"progress":"[====================\u003e ] 2.039MB/4.965MB","id":"90aca3c647fe"}
{"status":"Downloading","progressDetail":{"current":42449643,"total":88111129},"progress":"[========================\u003e ] 42.45MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":11750084,"total":27504647},"progress":"[=====================\u003e ] 11.75MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":3316461,"total":4964709},"progress":"[=================================\u003e ] 3.316MB/4.965MB","id":"90aca3c647fe"}
{"status":"Downloading","progressDetail":{"current":4791021,"total":4964709},"progress":"[================================================\u003e ] 4.791MB/4.965MB","id":"90aca3c647fe"}
{"status":"Verifying Checksum","progressDetail":{},"id":"90aca3c647fe"}
{"status":"Download complete","progressDetail":{},"id":"90aca3c647fe"}
{"status":"Downloading","progressDetail":{"current":12315332,"total":27504647},"progress":"[======================\u003e ] 12.32MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":42990315,"total":88111129},"progress":"[========================\u003e ] 42.99MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":13155012,"total":27504647},"progress":"[=======================\u003e ] 13.16MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":43530987,"total":88111129},"progress":"[========================\u003e ] 43.53MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":13990596,"total":27504647},"progress":"[=========================\u003e ] 13.99MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":44063467,"total":88111129},"progress":"[=========================\u003e ] 44.06MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":15112900,"total":27504647},"progress":"[===========================\u003e ] 15.11MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":45132523,"total":88111129},"progress":"[=========================\u003e ] 45.13MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":16235204,"total":27504647},"progress":"[=============================\u003e ] 16.24MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":52418,"total":5149051},"progress":"[\u003e ] 52.42kB/5.149MB","id":"1dd62f37c84c"}
{"status":"Downloading","progressDetail":{"current":1195147,"total":5149051},"progress":"[===========\u003e ] 1.195MB/5.149MB","id":"1dd62f37c84c"}
{"status":"Downloading","progressDetail":{"current":16792260,"total":27504647},"progress":"[==============================\u003e ] 16.79MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":45673195,"total":88111129},"progress":"[=========================\u003e ] 45.67MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":2702475,"total":5149051},"progress":"[==========================\u003e ] 2.702MB/5.149MB","id":"1dd62f37c84c"}
{"status":"Downloading","progressDetail":{"current":4078320,"total":5149051},"progress":"[=======================================\u003e ] 4.078MB/5.149MB","id":"1dd62f37c84c"}
{"status":"Downloading","progressDetail":{"current":17349316,"total":27504647},"progress":"[===============================\u003e ] 17.35MB/27.5MB","id":"45b746196f82"}
{"status":"Verifying Checksum","progressDetail":{},"id":"1dd62f37c84c"}
{"status":"Download complete","progressDetail":{},"id":"1dd62f37c84c"}
{"status":"Downloading","progressDetail":{"current":46213867,"total":88111129},"progress":"[==========================\u003e ] 46.21MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":17918660,"total":27504647},"progress":"[================================\u003e ] 17.92MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":19040964,"total":27504647},"progress":"[==================================\u003e ] 19.04MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":47295211,"total":88111129},"progress":"[==========================\u003e ] 47.3MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":20183748,"total":27504647},"progress":"[====================================\u003e ] 20.18MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":48368363,"total":88111129},"progress":"[===========================\u003e ] 48.37MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":21301956,"total":27504647},"progress":"[======================================\u003e ] 21.3MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":22432452,"total":27504647},"progress":"[========================================\u003e ] 22.43MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":38884,"total":3855277},"progress":"[\u003e ] 38.88kB/3.855MB","id":"3192b2fa42db"}
{"status":"Downloading","progressDetail":{"current":49445611,"total":88111129},"progress":"[============================\u003e ] 49.45MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":977632,"total":3855277},"progress":"[============\u003e ] 977.6kB/3.855MB","id":"3192b2fa42db"}
{"status":"Downloading","progressDetail":{"current":23268036,"total":27504647},"progress":"[==========================================\u003e ] 23.27MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":49986283,"total":88111129},"progress":"[============================\u003e ] 49.99MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":1895136,"total":3855277},"progress":"[========================\u003e ] 1.895MB/3.855MB","id":"3192b2fa42db"}
{"status":"Downloading","progressDetail":{"current":23833284,"total":27504647},"progress":"[===========================================\u003e ] 23.83MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":2939616,"total":3855277},"progress":"[======================================\u003e ] 2.94MB/3.855MB","id":"3192b2fa42db"}
{"status":"Downloading","progressDetail":{"current":24390340,"total":27504647},"progress":"[============================================\u003e ] 24.39MB/27.5MB","id":"45b746196f82"}
{"status":"Download complete","progressDetail":{},"id":"3192b2fa42db"}
{"status":"Downloading","progressDetail":{"current":50518763,"total":88111129},"progress":"[============================\u003e ] 50.52MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":24947396,"total":27504647},"progress":"[=============================================\u003e ] 24.95MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":51059435,"total":88111129},"progress":"[============================\u003e ] 51.06MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":25803460,"total":27504647},"progress":"[==============================================\u003e ] 25.8MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":26942148,"total":27504647},"progress":"[================================================\u003e ] 26.94MB/27.5MB","id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":52140779,"total":88111129},"progress":"[=============================\u003e ] 52.14MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":27504647,"total":27504647},"progress":"[==================================================\u003e] 27.5MB/27.5MB","id":"45b746196f82"}
{"status":"Verifying Checksum","progressDetail":{},"id":"45b746196f82"}
{"status":"Download complete","progressDetail":{},"id":"45b746196f82"}
{"status":"Downloading","progressDetail":{"current":53222123,"total":88111129},"progress":"[==============================\u003e ] 53.22MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":51194,"total":4983195},"progress":"[\u003e ] 51.19kB/4.983MB","id":"ae190b8f66a7"}
{"status":"Downloading","progressDetail":{"current":54299371,"total":88111129},"progress":"[==============================\u003e ] 54.3MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":1268464,"total":4983195},"progress":"[============\u003e ] 1.268MB/4.983MB","id":"ae190b8f66a7"}
{"status":"Downloading","progressDetail":{"current":54827755,"total":88111129},"progress":"[===============================\u003e ] 54.83MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":2767600,"total":4983195},"progress":"[===========================\u003e ] 2.768MB/4.983MB","id":"ae190b8f66a7"}
{"status":"Downloading","progressDetail":{"current":4528880,"total":4983195},"progress":"[=============================================\u003e ] 4.529MB/4.983MB","id":"ae190b8f66a7"}
{"status":"Downloading","progressDetail":{"current":55368427,"total":88111129},"progress":"[===============================\u003e ] 55.37MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Verifying Checksum","progressDetail":{},"id":"ae190b8f66a7"}
{"status":"Download complete","progressDetail":{},"id":"ae190b8f66a7"}
{"status":"Downloading","progressDetail":{"current":63614,"total":6103207},"progress":"[\u003e ] 63.61kB/6.103MB","id":"97bb6e138460"}
{"status":"Downloading","progressDetail":{"current":56449771,"total":88111129},"progress":"[================================\u003e ] 56.45MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":1530606,"total":6103207},"progress":"[============\u003e ] 1.531MB/6.103MB","id":"97bb6e138460"}
{"status":"Downloading","progressDetail":{"current":3193582,"total":6103207},"progress":"[==========================\u003e ] 3.194MB/6.103MB","id":"97bb6e138460"}
{"status":"Downloading","progressDetail":{"current":56990443,"total":88111129},"progress":"[================================\u003e ] 56.99MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":4786926,"total":6103207},"progress":"[=======================================\u003e ] 4.787MB/6.103MB","id":"97bb6e138460"}
{"status":"Downloading","progressDetail":{"current":57531115,"total":88111129},"progress":"[================================\u003e ] 57.53MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Download complete","progressDetail":{},"id":"97bb6e138460"}
{"status":"Downloading","progressDetail":{"current":489,"total":787},"progress":"[===============================\u003e ] 489B/787B","id":"2edb982d5170"}
{"status":"Downloading","progressDetail":{"current":58612459,"total":88111129},"progress":"[=================================\u003e ] 58.61MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":787,"total":787},"progress":"[==================================================\u003e] 787B/787B","id":"2edb982d5170"}
{"status":"Verifying Checksum","progressDetail":{},"id":"2edb982d5170"}
{"status":"Download complete","progressDetail":{},"id":"2edb982d5170"}
{"status":"Downloading","progressDetail":{"current":60213995,"total":88111129},"progress":"[==================================\u003e ] 60.21MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":61827819,"total":88111129},"progress":"[===================================\u003e ] 61.83MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":63449835,"total":88111129},"progress":"[====================================\u003e ] 63.45MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":65071851,"total":88111129},"progress":"[====================================\u003e ] 65.07MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":49803,"total":4894860},"progress":"[\u003e ] 49.8kB/4.895MB","id":"7ddc8e6d6da9"}
{"status":"Downloading","progressDetail":{"current":49681,"total":4953791},"progress":"[\u003e ] 49.68kB/4.954MB","id":"0df6fd234b59"}
{"status":"Downloading","progressDetail":{"current":912099,"total":4894860},"progress":"[=========\u003e ] 912.1kB/4.895MB","id":"7ddc8e6d6da9"}
{"status":"Downloading","progressDetail":{"current":66145003,"total":88111129},"progress":"[=====================================\u003e ] 66.15MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":748270,"total":4953791},"progress":"[=======\u003e ] 748.3kB/4.954MB","id":"0df6fd234b59"}
{"status":"Downloading","progressDetail":{"current":1702627,"total":4894860},"progress":"[=================\u003e ] 1.703MB/4.895MB","id":"7ddc8e6d6da9"}
{"status":"Downloading","progressDetail":{"current":67205867,"total":88111129},"progress":"[======================================\u003e ] 67.21MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":1678062,"total":4953791},"progress":"[================\u003e ] 1.678MB/4.954MB","id":"0df6fd234b59"}
{"status":"Downloading","progressDetail":{"current":2194147,"total":4894860},"progress":"[======================\u003e ] 2.194MB/4.895MB","id":"7ddc8e6d6da9"}
{"status":"Downloading","progressDetail":{"current":67746539,"total":88111129},"progress":"[======================================\u003e ] 67.75MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":2648814,"total":4953791},"progress":"[==========================\u003e ] 2.649MB/4.954MB","id":"0df6fd234b59"}
{"status":"Downloading","progressDetail":{"current":2743011,"total":4894860},"progress":"[============================\u003e ] 2.743MB/4.895MB","id":"7ddc8e6d6da9"}
{"status":"Downloading","progressDetail":{"current":68287211,"total":88111129},"progress":"[======================================\u003e ] 68.29MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":3697390,"total":4953791},"progress":"[=====================================\u003e ] 3.697MB/4.954MB","id":"0df6fd234b59"}
{"status":"Downloading","progressDetail":{"current":3381987,"total":4894860},"progress":"[==================================\u003e ] 3.382MB/4.895MB","id":"7ddc8e6d6da9"}
{"status":"Downloading","progressDetail":{"current":4774638,"total":4953791},"progress":"[================================================\u003e ] 4.775MB/4.954MB","id":"0df6fd234b59"}
{"status":"Downloading","progressDetail":{"current":68827883,"total":88111129},"progress":"[=======================================\u003e ] 68.83MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":4953791,"total":4953791},"progress":"[==================================================\u003e] 4.954MB/4.954MB","id":"0df6fd234b59"}
{"status":"Verifying Checksum","progressDetail":{},"id":"0df6fd234b59"}
{"status":"Download complete","progressDetail":{},"id":"0df6fd234b59"}
{"status":"Downloading","progressDetail":{"current":4004579,"total":4894860},"progress":"[========================================\u003e ] 4.005MB/4.895MB","id":"7ddc8e6d6da9"}
{"status":"Downloading","progressDetail":{"current":4893411,"total":4894860},"progress":"[=================================================\u003e ] 4.893MB/4.895MB","id":"7ddc8e6d6da9"}
{"status":"Verifying Checksum","progressDetail":{},"id":"7ddc8e6d6da9"}
{"status":"Download complete","progressDetail":{},"id":"7ddc8e6d6da9"}
{"status":"Downloading","progressDetail":{"current":69909227,"total":88111129},"progress":"[=======================================\u003e ] 69.91MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":71527147,"total":88111129},"progress":"[========================================\u003e ] 71.53MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":73149163,"total":88111129},"progress":"[=========================================\u003e ] 73.15MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":74771179,"total":88111129},"progress":"[==========================================\u003e ] 74.77MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":63573,"total":6137526},"progress":"[\u003e ] 63.57kB/6.138MB","id":"8fc1ba8efe21"}
{"status":"Downloading","progressDetail":{"current":75311851,"total":88111129},"progress":"[==========================================\u003e ] 75.31MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":1317559,"total":6137526},"progress":"[==========\u003e ] 1.318MB/6.138MB","id":"8fc1ba8efe21"}
{"status":"Downloading","progressDetail":{"current":2710199,"total":6137526},"progress":"[======================\u003e ] 2.71MB/6.138MB","id":"8fc1ba8efe21"}
{"status":"Downloading","progressDetail":{"current":38729,"total":3854415},"progress":"[\u003e ] 38.73kB/3.854MB","id":"1f6f45e783b5"}
{"status":"Downloading","progressDetail":{"current":76368619,"total":88111129},"progress":"[===========================================\u003e ] 76.37MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":3783351,"total":6137526},"progress":"[==============================\u003e ] 3.783MB/6.138MB","id":"8fc1ba8efe21"}
{"status":"Downloading","progressDetail":{"current":658157,"total":3854415},"progress":"[========\u003e ] 658.2kB/3.854MB","id":"1f6f45e783b5"}
{"status":"Downloading","progressDetail":{"current":4520631,"total":6137526},"progress":"[====================================\u003e ] 4.521MB/6.138MB","id":"8fc1ba8efe21"}
{"status":"Downloading","progressDetail":{"current":1350381,"total":3854415},"progress":"[=================\u003e ] 1.35MB/3.854MB","id":"1f6f45e783b5"}
{"status":"Downloading","progressDetail":{"current":5364407,"total":6137526},"progress":"[===========================================\u003e ] 5.364MB/6.138MB","id":"8fc1ba8efe21"}
{"status":"Downloading","progressDetail":{"current":77445867,"total":88111129},"progress":"[===========================================\u003e ] 77.45MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":2153197,"total":3854415},"progress":"[===========================\u003e ] 2.153MB/3.854MB","id":"1f6f45e783b5"}
{"status":"Verifying Checksum","progressDetail":{},"id":"8fc1ba8efe21"}
{"status":"Download complete","progressDetail":{},"id":"8fc1ba8efe21"}
{"status":"Downloading","progressDetail":{"current":77986539,"total":88111129},"progress":"[============================================\u003e ] 77.99MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":3021549,"total":3854415},"progress":"[=======================================\u003e ] 3.022MB/3.854MB","id":"1f6f45e783b5"}
{"status":"Verifying Checksum","progressDetail":{},"id":"1f6f45e783b5"}
{"status":"Download complete","progressDetail":{},"id":"1f6f45e783b5"}
{"status":"Downloading","progressDetail":{"current":79067883,"total":88111129},"progress":"[============================================\u003e ] 79.07MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":80149227,"total":88111129},"progress":"[=============================================\u003e ] 80.15MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":81767147,"total":88111129},"progress":"[==============================================\u003e ] 81.77MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":52419,"total":5222290},"progress":"[\u003e ] 52.42kB/5.222MB","id":"43ea61082f68"}
{"status":"Downloading","progressDetail":{"current":1055455,"total":5222290},"progress":"[==========\u003e ] 1.055MB/5.222MB","id":"43ea61082f68"}
{"status":"Downloading","progressDetail":{"current":83372779,"total":88111129},"progress":"[===============================================\u003e ] 83.37MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":2333407,"total":5222290},"progress":"[======================\u003e ] 2.333MB/5.222MB","id":"43ea61082f68"}
{"status":"Downloading","progressDetail":{"current":35991,"total":3564359},"progress":"[\u003e ] 35.99kB/3.564MB","id":"b8cf53bbc6ba"}
{"status":"Downloading","progressDetail":{"current":84454123,"total":88111129},"progress":"[===============================================\u003e ] 84.45MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":3300063,"total":5222290},"progress":"[===============================\u003e ] 3.3MB/5.222MB","id":"43ea61082f68"}
{"status":"Downloading","progressDetail":{"current":752366,"total":3564359},"progress":"[==========\u003e ] 752.4kB/3.564MB","id":"b8cf53bbc6ba"}
{"status":"Downloading","progressDetail":{"current":3979999,"total":5222290},"progress":"[======================================\u003e ] 3.98MB/5.222MB","id":"43ea61082f68"}
{"status":"Downloading","progressDetail":{"current":1743598,"total":3564359},"progress":"[========================\u003e ] 1.744MB/3.564MB","id":"b8cf53bbc6ba"}
{"status":"Downloading","progressDetail":{"current":85527275,"total":88111129},"progress":"[================================================\u003e ] 85.53MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":4537055,"total":5222290},"progress":"[===========================================\u003e ] 4.537MB/5.222MB","id":"43ea61082f68"}
{"status":"Downloading","progressDetail":{"current":2833134,"total":3564359},"progress":"[=======================================\u003e ] 2.833MB/3.564MB","id":"b8cf53bbc6ba"}
{"status":"Downloading","progressDetail":{"current":5077727,"total":5222290},"progress":"[================================================\u003e ] 5.078MB/5.222MB","id":"43ea61082f68"}
{"status":"Verifying Checksum","progressDetail":{},"id":"b8cf53bbc6ba"}
{"status":"Download complete","progressDetail":{},"id":"b8cf53bbc6ba"}
{"status":"Verifying Checksum","progressDetail":{},"id":"43ea61082f68"}
{"status":"Download complete","progressDetail":{},"id":"43ea61082f68"}
{"status":"Downloading","progressDetail":{"current":86067947,"total":88111129},"progress":"[================================================\u003e ] 86.07MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":87132907,"total":88111129},"progress":"[=================================================\u003e ] 87.13MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Verifying Checksum","progressDetail":{},"id":"4ab897fa6fbf"}
{"status":"Download complete","progressDetail":{},"id":"4ab897fa6fbf"}
{"status":"Extracting","progressDetail":{"current":557056,"total":88111129},"progress":"[\u003e ] 557.1kB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":52418,"total":5120108},"progress":"[\u003e ] 52.42kB/5.12MB","id":"1c3245356213"}
{"status":"Downloading","progressDetail":{"current":489,"total":790},"progress":"[==============================\u003e ] 489B/790B","id":"25efb07e4521"}
{"status":"Extracting","progressDetail":{"current":5013504,"total":88111129},"progress":"[==\u003e ] 5.014MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":790,"total":790},"progress":"[==================================================\u003e] 790B/790B","id":"25efb07e4521"}
{"status":"Verifying Checksum","progressDetail":{},"id":"25efb07e4521"}
{"status":"Download complete","progressDetail":{},"id":"25efb07e4521"}
{"status":"Downloading","progressDetail":{"current":1764079,"total":5120108},"progress":"[=================\u003e ] 1.764MB/5.12MB","id":"1c3245356213"}
{"status":"Extracting","progressDetail":{"current":8355840,"total":88111129},"progress":"[====\u003e ] 8.356MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":3635951,"total":5120108},"progress":"[===================================\u003e ] 3.636MB/5.12MB","id":"1c3245356213"}
{"status":"Verifying Checksum","progressDetail":{},"id":"1c3245356213"}
{"status":"Download complete","progressDetail":{},"id":"1c3245356213"}
{"status":"Extracting","progressDetail":{"current":11141120,"total":88111129},"progress":"[======\u003e ] 11.14MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":52419,"total":5117023},"progress":"[\u003e ] 52.42kB/5.117MB","id":"61ebb123c1eb"}
{"status":"Extracting","progressDetail":{"current":13369344,"total":88111129},"progress":"[=======\u003e ] 13.37MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":1596142,"total":5117023},"progress":"[===============\u003e ] 1.596MB/5.117MB","id":"61ebb123c1eb"}
{"status":"Extracting","progressDetail":{"current":13926400,"total":88111129},"progress":"[=======\u003e ] 13.93MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":3242734,"total":5117023},"progress":"[===============================\u003e ] 3.243MB/5.117MB","id":"61ebb123c1eb"}
{"status":"Downloading","progressDetail":{"current":55157,"total":5384215},"progress":"[\u003e ] 55.16kB/5.384MB","id":"0964b769d2c9"}
{"status":"Downloading","progressDetail":{"current":4635374,"total":5117023},"progress":"[=============================================\u003e ] 4.635MB/5.117MB","id":"61ebb123c1eb"}
{"status":"Extracting","progressDetail":{"current":15040512,"total":88111129},"progress":"[========\u003e ] 15.04MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Verifying Checksum","progressDetail":{},"id":"61ebb123c1eb"}
{"status":"Download complete","progressDetail":{},"id":"61ebb123c1eb"}
{"status":"Downloading","progressDetail":{"current":989937,"total":5384215},"progress":"[=========\u003e ] 989.9kB/5.384MB","id":"0964b769d2c9"}
{"status":"Extracting","progressDetail":{"current":15597568,"total":88111129},"progress":"[========\u003e ] 15.6MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":2558705,"total":5384215},"progress":"[=======================\u003e ] 2.559MB/5.384MB","id":"0964b769d2c9"}
{"status":"Extracting","progressDetail":{"current":18382848,"total":88111129},"progress":"[==========\u003e ] 18.38MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":4311793,"total":5384215},"progress":"[========================================\u003e ] 4.312MB/5.384MB","id":"0964b769d2c9"}
{"status":"Downloading","progressDetail":{"current":53788,"total":5252487},"progress":"[\u003e ] 53.79kB/5.252MB","id":"87f7843f43cd"}
{"status":"Extracting","progressDetail":{"current":22839296,"total":88111129},"progress":"[============\u003e ] 22.84MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":5212913,"total":5384215},"progress":"[================================================\u003e ] 5.213MB/5.384MB","id":"0964b769d2c9"}
{"status":"Downloading","progressDetail":{"current":846577,"total":5252487},"progress":"[========\u003e ] 846.6kB/5.252MB","id":"87f7843f43cd"}
{"status":"Verifying Checksum","progressDetail":{},"id":"0964b769d2c9"}
{"status":"Download complete","progressDetail":{},"id":"0964b769d2c9"}
{"status":"Extracting","progressDetail":{"current":26181632,"total":88111129},"progress":"[==============\u003e ] 26.18MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":2628337,"total":5252487},"progress":"[=========================\u003e ] 2.628MB/5.252MB","id":"87f7843f43cd"}
{"status":"Extracting","progressDetail":{"current":30638080,"total":88111129},"progress":"[=================\u003e ] 30.64MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":4340465,"total":5252487},"progress":"[=========================================\u003e ] 4.34MB/5.252MB","id":"87f7843f43cd"}
{"status":"Download complete","progressDetail":{},"id":"87f7843f43cd"}
{"status":"Extracting","progressDetail":{"current":33423360,"total":88111129},"progress":"[==================\u003e ] 33.42MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":51204,"total":5015856},"progress":"[\u003e ] 51.2kB/5.016MB","id":"a89dbf94d794"}
{"status":"Extracting","progressDetail":{"current":36208640,"total":88111129},"progress":"[====================\u003e ] 36.21MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":1624816,"total":5015856},"progress":"[================\u003e ] 1.625MB/5.016MB","id":"a89dbf94d794"}
{"status":"Extracting","progressDetail":{"current":38436864,"total":88111129},"progress":"[=====================\u003e ] 38.44MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":3373808,"total":5015856},"progress":"[=================================\u003e ] 3.374MB/5.016MB","id":"a89dbf94d794"}
{"status":"Extracting","progressDetail":{"current":40665088,"total":88111129},"progress":"[=======================\u003e ] 40.67MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":53910,"total":5310566},"progress":"[\u003e ] 53.91kB/5.311MB","id":"f0d43ddca77f"}
{"status":"Downloading","progressDetail":{"current":4905712,"total":5015856},"progress":"[================================================\u003e ] 4.906MB/5.016MB","id":"a89dbf94d794"}
{"status":"Verifying Checksum","progressDetail":{},"id":"a89dbf94d794"}
{"status":"Download complete","progressDetail":{},"id":"a89dbf94d794"}
{"status":"Downloading","progressDetail":{"current":1313521,"total":5310566},"progress":"[============\u003e ] 1.314MB/5.311MB","id":"f0d43ddca77f"}
{"status":"Extracting","progressDetail":{"current":44007424,"total":88111129},"progress":"[========================\u003e ] 44.01MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Extracting","progressDetail":{"current":46792704,"total":88111129},"progress":"[==========================\u003e ] 46.79MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":3082993,"total":5310566},"progress":"[=============================\u003e ] 3.083MB/5.311MB","id":"f0d43ddca77f"}
{"status":"Downloading","progressDetail":{"current":49836,"total":4915049},"progress":"[\u003e ] 49.84kB/4.915MB","id":"7c674f0cb40c"}
{"status":"Downloading","progressDetail":{"current":4373233,"total":5310566},"progress":"[=========================================\u003e ] 4.373MB/5.311MB","id":"f0d43ddca77f"}
{"status":"Extracting","progressDetail":{"current":48463872,"total":88111129},"progress":"[===========================\u003e ] 48.46MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":711407,"total":4915049},"progress":"[=======\u003e ] 711.4kB/4.915MB","id":"7c674f0cb40c"}
{"status":"Verifying Checksum","progressDetail":{},"id":"f0d43ddca77f"}
{"status":"Download complete","progressDetail":{},"id":"f0d43ddca77f"}
{"status":"Downloading","progressDetail":{"current":1710831,"total":4915049},"progress":"[=================\u003e ] 1.711MB/4.915MB","id":"7c674f0cb40c"}
{"status":"Extracting","progressDetail":{"current":52363264,"total":88111129},"progress":"[=============================\u003e ] 52.36MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":3504879,"total":4915049},"progress":"[===================================\u003e ] 3.505MB/4.915MB","id":"7c674f0cb40c"}
{"status":"Extracting","progressDetail":{"current":55705600,"total":88111129},"progress":"[===============================\u003e ] 55.71MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":4905711,"total":4915049},"progress":"[=================================================\u003e ] 4.906MB/4.915MB","id":"7c674f0cb40c"}
{"status":"Verifying Checksum","progressDetail":{},"id":"7c674f0cb40c"}
{"status":"Download complete","progressDetail":{},"id":"7c674f0cb40c"}
{"status":"Extracting","progressDetail":{"current":58490880,"total":88111129},"progress":"[=================================\u003e ] 58.49MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":52419,"total":5119213},"progress":"[\u003e ] 52.42kB/5.119MB","id":"b48a885b52bc"}
{"status":"Extracting","progressDetail":{"current":61276160,"total":88111129},"progress":"[==================================\u003e ] 61.28MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":1333999,"total":5119213},"progress":"[=============\u003e ] 1.334MB/5.119MB","id":"b48a885b52bc"}
{"status":"Downloading","progressDetail":{"current":2657007,"total":5119213},"progress":"[=========================\u003e ] 2.657MB/5.119MB","id":"b48a885b52bc"}
{"status":"Extracting","progressDetail":{"current":64061440,"total":88111129},"progress":"[====================================\u003e ] 64.06MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Download complete","progressDetail":{},"id":"272cdf839cbb"}
{"status":"Downloading","progressDetail":{"current":4344559,"total":5119213},"progress":"[==========================================\u003e ] 4.345MB/5.119MB","id":"b48a885b52bc"}
{"status":"Extracting","progressDetail":{"current":66289664,"total":88111129},"progress":"[=====================================\u003e ] 66.29MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Verifying Checksum","progressDetail":{},"id":"b48a885b52bc"}
{"status":"Download complete","progressDetail":{},"id":"b48a885b52bc"}
{"status":"Extracting","progressDetail":{"current":70746112,"total":88111129},"progress":"[========================================\u003e ] 70.75MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Extracting","progressDetail":{"current":73531392,"total":88111129},"progress":"[=========================================\u003e ] 73.53MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Extracting","progressDetail":{"current":77430784,"total":88111129},"progress":"[===========================================\u003e ] 77.43MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Download complete","progressDetail":{},"id":"50d054c97f4f"}
{"status":"Downloading","progressDetail":{"current":488,"total":1069},"progress":"[======================\u003e ] 488B/1.069kB","id":"4c6bbd90b64d"}
{"status":"Extracting","progressDetail":{"current":80216064,"total":88111129},"progress":"[=============================================\u003e ] 80.22MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Downloading","progressDetail":{"current":1069,"total":1069},"progress":"[==================================================\u003e] 1.069kB/1.069kB","id":"4c6bbd90b64d"}
{"status":"Verifying Checksum","progressDetail":{},"id":"4c6bbd90b64d"}
{"status":"Download complete","progressDetail":{},"id":"4c6bbd90b64d"}
{"status":"Downloading","progressDetail":{"current":32,"total":32},"progress":"[==================================================\u003e] 32B/32B","id":"4f4fb700ef54"}
{"status":"Verifying Checksum","progressDetail":{},"id":"4f4fb700ef54"}
{"status":"Download complete","progressDetail":{},"id":"4f4fb700ef54"}
{"status":"Extracting","progressDetail":{"current":81887232,"total":88111129},"progress":"[==============================================\u003e ] 81.89MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Extracting","progressDetail":{"current":83558400,"total":88111129},"progress":"[===============================================\u003e ] 83.56MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Extracting","progressDetail":{"current":85229568,"total":88111129},"progress":"[================================================\u003e ] 85.23MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Extracting","progressDetail":{"current":86900736,"total":88111129},"progress":"[=================================================\u003e ] 86.9MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Extracting","progressDetail":{"current":88014848,"total":88111129},"progress":"[=================================================\u003e ] 88.01MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Extracting","progressDetail":{"current":88111129,"total":88111129},"progress":"[==================================================\u003e] 88.11MB/88.11MB","id":"4ab897fa6fbf"}
{"status":"Pull complete","progressDetail":{},"id":"4ab897fa6fbf"}
{"status":"Extracting","progressDetail":{"current":32768,"total":1391657},"progress":"[=\u003e ] 32.77kB/1.392MB","id":"d837a2a1365e"}
{"status":"Extracting","progressDetail":{"current":327680,"total":1391657},"progress":"[===========\u003e ] 327.7kB/1.392MB","id":"d837a2a1365e"}
{"status":"Extracting","progressDetail":{"current":1391657,"total":1391657},"progress":"[==================================================\u003e] 1.392MB/1.392MB","id":"d837a2a1365e"}
{"status":"Extracting","progressDetail":{"current":1391657,"total":1391657},"progress":"[==================================================\u003e] 1.392MB/1.392MB","id":"d837a2a1365e"}
{"status":"Pull complete","progressDetail":{},"id":"d837a2a1365e"}
{"status":"Extracting","progressDetail":{"current":32768,"total":1328346},"progress":"[=\u003e ] 32.77kB/1.328MB","id":"988ae18fe41a"}
{"status":"Extracting","progressDetail":{"current":753664,"total":1328346},"progress":"[============================\u003e ] 753.7kB/1.328MB","id":"988ae18fe41a"}
{"status":"Extracting","progressDetail":{"current":1328346,"total":1328346},"progress":"[==================================================\u003e] 1.328MB/1.328MB","id":"988ae18fe41a"}
{"status":"Extracting","progressDetail":{"current":1328346,"total":1328346},"progress":"[==================================================\u003e] 1.328MB/1.328MB","id":"988ae18fe41a"}
{"status":"Pull complete","progressDetail":{},"id":"988ae18fe41a"}
{"status":"Extracting","progressDetail":{"current":4478,"total":4478},"progress":"[==================================================\u003e] 4.478kB/4.478kB","id":"eeb8ef83b565"}
{"status":"Extracting","progressDetail":{"current":4478,"total":4478},"progress":"[==================================================\u003e] 4.478kB/4.478kB","id":"eeb8ef83b565"}
{"status":"Pull complete","progressDetail":{},"id":"eeb8ef83b565"}
{"status":"Extracting","progressDetail":{"current":197,"total":197},"progress":"[==================================================\u003e] 197B/197B","id":"357fefdf9bc9"}
{"status":"Extracting","progressDetail":{"current":197,"total":197},"progress":"[==================================================\u003e] 197B/197B","id":"357fefdf9bc9"}
{"status":"Pull complete","progressDetail":{},"id":"357fefdf9bc9"}
{"status":"Extracting","progressDetail":{"current":294912,"total":27504647},"progress":"[\u003e ] 294.9kB/27.5MB","id":"45b746196f82"}
{"status":"Extracting","progressDetail":{"current":589824,"total":27504647},"progress":"[=\u003e ] 589.8kB/27.5MB","id":"45b746196f82"}
{"status":"Extracting","progressDetail":{"current":5013504,"total":27504647},"progress":"[=========\u003e ] 5.014MB/27.5MB","id":"45b746196f82"}
{"status":"Extracting","progressDetail":{"current":9142272,"total":27504647},"progress":"[================\u003e ] 9.142MB/27.5MB","id":"45b746196f82"}
{"status":"Extracting","progressDetail":{"current":13565952,"total":27504647},"progress":"[========================\u003e ] 13.57MB/27.5MB","id":"45b746196f82"}
{"status":"Extracting","progressDetail":{"current":16515072,"total":27504647},"progress":"[==============================\u003e ] 16.52MB/27.5MB","id":"45b746196f82"}
{"status":"Extracting","progressDetail":{"current":18579456,"total":27504647},"progress":"[=================================\u003e ] 18.58MB/27.5MB","id":"45b746196f82"}
{"status":"Extracting","progressDetail":{"current":21528576,"total":27504647},"progress":"[=======================================\u003e ] 21.53MB/27.5MB","id":"45b746196f82"}
{"status":"Extracting","progressDetail":{"current":25657344,"total":27504647},"progress":"[==============================================\u003e ] 25.66MB/27.5MB","id":"45b746196f82"}
{"status":"Extracting","progressDetail":{"current":27504647,"total":27504647},"progress":"[==================================================\u003e] 27.5MB/27.5MB","id":"45b746196f82"}
{"status":"Pull complete","progressDetail":{},"id":"45b746196f82"}
{"status":"Extracting","progressDetail":{"current":65536,"total":5205016},"progress":"[\u003e ] 65.54kB/5.205MB","id":"fbf4ce20f8c2"}
{"status":"Extracting","progressDetail":{"current":1048576,"total":5205016},"progress":"[==========\u003e ] 1.049MB/5.205MB","id":"fbf4ce20f8c2"}
{"status":"Extracting","progressDetail":{"current":5205016,"total":5205016},"progress":"[==================================================\u003e] 5.205MB/5.205MB","id":"fbf4ce20f8c2"}
{"status":"Pull complete","progressDetail":{},"id":"fbf4ce20f8c2"}
{"status":"Extracting","progressDetail":{"current":65536,"total":4964709},"progress":"[\u003e ] 65.54kB/4.965MB","id":"90aca3c647fe"}
{"status":"Extracting","progressDetail":{"current":1245184,"total":4964709},"progress":"[============\u003e ] 1.245MB/4.965MB","id":"90aca3c647fe"}
{"status":"Extracting","progressDetail":{"current":4964709,"total":4964709},"progress":"[==================================================\u003e] 4.965MB/4.965MB","id":"90aca3c647fe"}
{"status":"Pull complete","progressDetail":{},"id":"90aca3c647fe"}
{"status":"Extracting","progressDetail":{"current":65536,"total":5149051},"progress":"[\u003e ] 65.54kB/5.149MB","id":"1dd62f37c84c"}
{"status":"Extracting","progressDetail":{"current":393216,"total":5149051},"progress":"[===\u003e ] 393.2kB/5.149MB","id":"1dd62f37c84c"}
{"status":"Extracting","progressDetail":{"current":5149051,"total":5149051},"progress":"[==================================================\u003e] 5.149MB/5.149MB","id":"1dd62f37c84c"}
{"status":"Pull complete","progressDetail":{},"id":"1dd62f37c84c"}
{"status":"Extracting","progressDetail":{"current":65536,"total":3855277},"progress":"[\u003e ] 65.54kB/3.855MB","id":"3192b2fa42db"}
{"status":"Extracting","progressDetail":{"current":851968,"total":3855277},"progress":"[===========\u003e ] 852kB/3.855MB","id":"3192b2fa42db"}
{"status":"Extracting","progressDetail":{"current":3855277,"total":3855277},"progress":"[==================================================\u003e] 3.855MB/3.855MB","id":"3192b2fa42db"}
{"status":"Extracting","progressDetail":{"current":3855277,"total":3855277},"progress":"[==================================================\u003e] 3.855MB/3.855MB","id":"3192b2fa42db"}
{"status":"Pull complete","progressDetail":{},"id":"3192b2fa42db"}
{"status":"Extracting","progressDetail":{"current":65536,"total":4983195},"progress":"[\u003e ] 65.54kB/4.983MB","id":"ae190b8f66a7"}
{"status":"Extracting","progressDetail":{"current":327680,"total":4983195},"progress":"[===\u003e ] 327.7kB/4.983MB","id":"ae190b8f66a7"}
{"status":"Extracting","progressDetail":{"current":4980736,"total":4983195},"progress":"[=================================================\u003e ] 4.981MB/4.983MB","id":"ae190b8f66a7"}
{"status":"Extracting","progressDetail":{"current":4983195,"total":4983195},"progress":"[==================================================\u003e] 4.983MB/4.983MB","id":"ae190b8f66a7"}
{"status":"Pull complete","progressDetail":{},"id":"ae190b8f66a7"}
{"status":"Extracting","progressDetail":{"current":65536,"total":6103207},"progress":"[\u003e ] 65.54kB/6.103MB","id":"97bb6e138460"}
{"status":"Extracting","progressDetail":{"current":327680,"total":6103207},"progress":"[==\u003e ] 327.7kB/6.103MB","id":"97bb6e138460"}
{"status":"Extracting","progressDetail":{"current":3670016,"total":6103207},"progress":"[==============================\u003e ] 3.67MB/6.103MB","id":"97bb6e138460"}
{"status":"Extracting","progressDetail":{"current":6103207,"total":6103207},"progress":"[==================================================\u003e] 6.103MB/6.103MB","id":"97bb6e138460"}
{"status":"Pull complete","progressDetail":{},"id":"97bb6e138460"}
{"status":"Extracting","progressDetail":{"current":787,"total":787},"progress":"[==================================================\u003e] 787B/787B","id":"2edb982d5170"}
{"status":"Extracting","progressDetail":{"current":787,"total":787},"progress":"[==================================================\u003e] 787B/787B","id":"2edb982d5170"}
{"status":"Pull complete","progressDetail":{},"id":"2edb982d5170"}
{"status":"Extracting","progressDetail":{"current":65536,"total":4894860},"progress":"[\u003e ] 65.54kB/4.895MB","id":"7ddc8e6d6da9"}
{"status":"Extracting","progressDetail":{"current":327680,"total":4894860},"progress":"[===\u003e ] 327.7kB/4.895MB","id":"7ddc8e6d6da9"}
{"status":"Extracting","progressDetail":{"current":3735552,"total":4894860},"progress":"[======================================\u003e ] 3.736MB/4.895MB","id":"7ddc8e6d6da9"}
{"status":"Extracting","progressDetail":{"current":4894860,"total":4894860},"progress":"[==================================================\u003e] 4.895MB/4.895MB","id":"7ddc8e6d6da9"}
{"status":"Pull complete","progressDetail":{},"id":"7ddc8e6d6da9"}
{"status":"Extracting","progressDetail":{"current":65536,"total":4953791},"progress":"[\u003e ] 65.54kB/4.954MB","id":"0df6fd234b59"}
{"status":"Extracting","progressDetail":{"current":327680,"total":4953791},"progress":"[===\u003e ] 327.7kB/4.954MB","id":"0df6fd234b59"}
{"status":"Extracting","progressDetail":{"current":4325376,"total":4953791},"progress":"[===========================================\u003e ] 4.325MB/4.954MB","id":"0df6fd234b59"}
{"status":"Extracting","progressDetail":{"current":4953791,"total":4953791},"progress":"[==================================================\u003e] 4.954MB/4.954MB","id":"0df6fd234b59"}
{"status":"Pull complete","progressDetail":{},"id":"0df6fd234b59"}
{"status":"Extracting","progressDetail":{"current":65536,"total":6137526},"progress":"[\u003e ] 65.54kB/6.138MB","id":"8fc1ba8efe21"}
{"status":"Extracting","progressDetail":{"current":327680,"total":6137526},"progress":"[==\u003e ] 327.7kB/6.138MB","id":"8fc1ba8efe21"}
{"status":"Extracting","progressDetail":{"current":3801088,"total":6137526},"progress":"[==============================\u003e ] 3.801MB/6.138MB","id":"8fc1ba8efe21"}
{"status":"Extracting","progressDetail":{"current":6137526,"total":6137526},"progress":"[==================================================\u003e] 6.138MB/6.138MB","id":"8fc1ba8efe21"}
{"status":"Pull complete","progressDetail":{},"id":"8fc1ba8efe21"}
{"status":"Extracting","progressDetail":{"current":65536,"total":3854415},"progress":"[\u003e ] 65.54kB/3.854MB","id":"1f6f45e783b5"}
{"status":"Extracting","progressDetail":{"current":851968,"total":3854415},"progress":"[===========\u003e ] 852kB/3.854MB","id":"1f6f45e783b5"}
{"status":"Extracting","progressDetail":{"current":3854415,"total":3854415},"progress":"[==================================================\u003e] 3.854MB/3.854MB","id":"1f6f45e783b5"}
{"status":"Extracting","progressDetail":{"current":3854415,"total":3854415},"progress":"[==================================================\u003e] 3.854MB/3.854MB","id":"1f6f45e783b5"}
{"status":"Pull complete","progressDetail":{},"id":"1f6f45e783b5"}
{"status":"Extracting","progressDetail":{"current":65536,"total":5222290},"progress":"[\u003e ] 65.54kB/5.222MB","id":"43ea61082f68"}
{"status":"Extracting","progressDetail":{"current":458752,"total":5222290},"progress":"[====\u003e ] 458.8kB/5.222MB","id":"43ea61082f68"}
{"status":"Extracting","progressDetail":{"current":4849664,"total":5222290},"progress":"[==============================================\u003e ] 4.85MB/5.222MB","id":"43ea61082f68"}
{"status":"Extracting","progressDetail":{"current":5222290,"total":5222290},"progress":"[==================================================\u003e] 5.222MB/5.222MB","id":"43ea61082f68"}
{"status":"Pull complete","progressDetail":{},"id":"43ea61082f68"}
{"status":"Extracting","progressDetail":{"current":65536,"total":3564359},"progress":"[\u003e ] 65.54kB/3.564MB","id":"b8cf53bbc6ba"}
{"status":"Extracting","progressDetail":{"current":327680,"total":3564359},"progress":"[====\u003e ] 327.7kB/3.564MB","id":"b8cf53bbc6ba"}
{"status":"Extracting","progressDetail":{"current":3564359,"total":3564359},"progress":"[==================================================\u003e] 3.564MB/3.564MB","id":"b8cf53bbc6ba"}
{"status":"Pull complete","progressDetail":{},"id":"b8cf53bbc6ba"}
{"status":"Extracting","progressDetail":{"current":790,"total":790},"progress":"[==================================================\u003e] 790B/790B","id":"25efb07e4521"}
{"status":"Extracting","progressDetail":{"current":790,"total":790},"progress":"[==================================================\u003e] 790B/790B","id":"25efb07e4521"}
{"status":"Pull complete","progressDetail":{},"id":"25efb07e4521"}
{"status":"Extracting","progressDetail":{"current":65536,"total":5120108},"progress":"[\u003e ] 65.54kB/5.12MB","id":"1c3245356213"}
{"status":"Extracting","progressDetail":{"current":327680,"total":5120108},"progress":"[===\u003e ] 327.7kB/5.12MB","id":"1c3245356213"}
{"status":"Extracting","progressDetail":{"current":5111808,"total":5120108},"progress":"[=================================================\u003e ] 5.112MB/5.12MB","id":"1c3245356213"}
{"status":"Extracting","progressDetail":{"current":5120108,"total":5120108},"progress":"[==================================================\u003e] 5.12MB/5.12MB","id":"1c3245356213"}
{"status":"Pull complete","progressDetail":{},"id":"1c3245356213"}
{"status":"Extracting","progressDetail":{"current":65536,"total":5117023},"progress":"[\u003e ] 65.54kB/5.117MB","id":"61ebb123c1eb"}
{"status":"Extracting","progressDetail":{"current":655360,"total":5117023},"progress":"[======\u003e ] 655.4kB/5.117MB","id":"61ebb123c1eb"}
{"status":"Extracting","progressDetail":{"current":4259840,"total":5117023},"progress":"[=========================================\u003e ] 4.26MB/5.117MB","id":"61ebb123c1eb"}
{"status":"Extracting","progressDetail":{"current":5117023,"total":5117023},"progress":"[==================================================\u003e] 5.117MB/5.117MB","id":"61ebb123c1eb"}
{"status":"Pull complete","progressDetail":{},"id":"61ebb123c1eb"}
{"status":"Extracting","progressDetail":{"current":65536,"total":5384215},"progress":"[\u003e ] 65.54kB/5.384MB","id":"0964b769d2c9"}
{"status":"Extracting","progressDetail":{"current":327680,"total":5384215},"progress":"[===\u003e ] 327.7kB/5.384MB","id":"0964b769d2c9"}
{"status":"Extracting","progressDetail":{"current":5177344,"total":5384215},"progress":"[================================================\u003e ] 5.177MB/5.384MB","id":"0964b769d2c9"}
{"status":"Extracting","progressDetail":{"current":5384215,"total":5384215},"progress":"[==================================================\u003e] 5.384MB/5.384MB","id":"0964b769d2c9"}
{"status":"Pull complete","progressDetail":{},"id":"0964b769d2c9"}
{"status":"Extracting","progressDetail":{"current":65536,"total":5252487},"progress":"[\u003e ] 65.54kB/5.252MB","id":"87f7843f43cd"}
{"status":"Extracting","progressDetail":{"current":655360,"total":5252487},"progress":"[======\u003e ] 655.4kB/5.252MB","id":"87f7843f43cd"}
{"status":"Extracting","progressDetail":{"current":5252487,"total":5252487},"progress":"[==================================================\u003e] 5.252MB/5.252MB","id":"87f7843f43cd"}
{"status":"Pull complete","progressDetail":{},"id":"87f7843f43cd"}
{"status":"Extracting","progressDetail":{"current":65536,"total":5015856},"progress":"[\u003e ] 65.54kB/5.016MB","id":"a89dbf94d794"}
{"status":"Extracting","progressDetail":{"current":327680,"total":5015856},"progress":"[===\u003e ] 327.7kB/5.016MB","id":"a89dbf94d794"}
{"status":"Extracting","progressDetail":{"current":3997696,"total":5015856},"progress":"[=======================================\u003e ] 3.998MB/5.016MB","id":"a89dbf94d794"}
{"status":"Extracting","progressDetail":{"current":5015856,"total":5015856},"progress":"[==================================================\u003e] 5.016MB/5.016MB","id":"a89dbf94d794"}
{"status":"Pull complete","progressDetail":{},"id":"a89dbf94d794"}
{"status":"Extracting","progressDetail":{"current":65536,"total":5310566},"progress":"[\u003e ] 65.54kB/5.311MB","id":"f0d43ddca77f"}
{"status":"Extracting","progressDetail":{"current":393216,"total":5310566},"progress":"[===\u003e ] 393.2kB/5.311MB","id":"f0d43ddca77f"}
{"status":"Extracting","progressDetail":{"current":3407872,"total":5310566},"progress":"[================================\u003e ] 3.408MB/5.311MB","id":"f0d43ddca77f"}
{"status":"Extracting","progressDetail":{"current":5310566,"total":5310566},"progress":"[==================================================\u003e] 5.311MB/5.311MB","id":"f0d43ddca77f"}
{"status":"Pull complete","progressDetail":{},"id":"f0d43ddca77f"}
{"status":"Extracting","progressDetail":{"current":65536,"total":4915049},"progress":"[\u003e ] 65.54kB/4.915MB","id":"7c674f0cb40c"}
{"status":"Extracting","progressDetail":{"current":786432,"total":4915049},"progress":"[========\u003e ] 786.4kB/4.915MB","id":"7c674f0cb40c"}
{"status":"Extracting","progressDetail":{"current":4915049,"total":4915049},"progress":"[==================================================\u003e] 4.915MB/4.915MB","id":"7c674f0cb40c"}
{"status":"Extracting","progressDetail":{"current":4915049,"total":4915049},"progress":"[==================================================\u003e] 4.915MB/4.915MB","id":"7c674f0cb40c"}
{"status":"Pull complete","progressDetail":{},"id":"7c674f0cb40c"}
{"status":"Extracting","progressDetail":{"current":65536,"total":5119213},"progress":"[\u003e ] 65.54kB/5.119MB","id":"b48a885b52bc"}
{"status":"Extracting","progressDetail":{"current":327680,"total":5119213},"progress":"[===\u003e ] 327.7kB/5.119MB","id":"b48a885b52bc"}
{"status":"Extracting","progressDetail":{"current":4390912,"total":5119213},"progress":"[==========================================\u003e ] 4.391MB/5.119MB","id":"b48a885b52bc"}
{"status":"Extracting","progressDetail":{"current":5119213,"total":5119213},"progress":"[==================================================\u003e] 5.119MB/5.119MB","id":"b48a885b52bc"}
{"status":"Pull complete","progressDetail":{},"id":"b48a885b52bc"}
{"status":"Extracting","progressDetail":{"current":395,"total":395},"progress":"[==================================================\u003e] 395B/395B","id":"272cdf839cbb"}
{"status":"Extracting","progressDetail":{"current":395,"total":395},"progress":"[==================================================\u003e] 395B/395B","id":"272cdf839cbb"}
{"status":"Pull complete","progressDetail":{},"id":"272cdf839cbb"}
{"status":"Extracting","progressDetail":{"current":155,"total":155},"progress":"[==================================================\u003e] 155B/155B","id":"50d054c97f4f"}
{"status":"Extracting","progressDetail":{"current":155,"total":155},"progress":"[==================================================\u003e] 155B/155B","id":"50d054c97f4f"}
{"status":"Pull complete","progressDetail":{},"id":"50d054c97f4f"}
{"status":"Extracting","progressDetail":{"current":1069,"total":1069},"progress":"[==================================================\u003e] 1.069kB/1.069kB","id":"4c6bbd90b64d"}
{"status":"Extracting","progressDetail":{"current":1069,"total":1069},"progress":"[==================================================\u003e] 1.069kB/1.069kB","id":"4c6bbd90b64d"}
{"status":"Pull complete","progressDetail":{},"id":"4c6bbd90b64d"}
{"status":"Extracting","progressDetail":{"current":32,"total":32},"progress":"[==================================================\u003e] 32B/32B","id":"4f4fb700ef54"}
{"status":"Extracting","progressDetail":{"current":32,"total":32},"progress":"[==================================================\u003e] 32B/32B","id":"4f4fb700ef54"}
{"status":"Pull complete","progressDetail":{},"id":"4f4fb700ef54"}
{"status":"Digest: sha256:4acb6bfd6c4f0cabaf7f3690e444afe51f1c7de54d51da7e63fac709c56f1c30"}
{"status":"Status: Downloaded newer image for cloudfoundry/cnb:bionic"}

View File

@@ -0,0 +1,15 @@
{
"string": "stringvalue",
"stringarray": [
"a",
"b"
],
"StartsWithUppercase": "value",
"person": {
"name": {
"title": "dr",
"first": "spring",
"last": "boot"
}
}
}