Home / Class/ ImageDockerApiTests Class — spring-boot Architecture

ImageDockerApiTests Class — spring-boot Architecture

Architecture documentation for the ImageDockerApiTests class in DockerApiTests.java from the spring-boot codebase.

Entity Profile

Relationship Graph

Source Code

buildpack/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/DockerApiTests.java lines 219–627

	@Nested
	class ImageDockerApiTests {

		private ImageApi api;

		@Mock
		@SuppressWarnings("NullAway.Init")
		private UpdateListener<PullImageUpdateEvent> pullListener;

		@Mock
		@SuppressWarnings("NullAway.Init")
		private UpdateListener<PushImageUpdateEvent> pushListener;

		@Mock
		@SuppressWarnings("NullAway.Init")
		private UpdateListener<LoadImageUpdateEvent> loadListener;

		@Captor
		@SuppressWarnings("NullAway.Init")
		private ArgumentCaptor<IOConsumer<OutputStream>> writer;

		@BeforeEach
		void setup() {
			this.api = DockerApiTests.this.dockerApi.image();
		}

		@Test
		@SuppressWarnings("NullAway") // Test null check
		void pullWhenReferenceIsNullThrowsException() {
			assertThatIllegalArgumentException().isThrownBy(() -> this.api.pull(null, null, this.pullListener))
				.withMessage("'reference' must not be null");
		}

		@Test
		@SuppressWarnings("NullAway") // Test null check
		void pullWhenListenerIsNullThrowsException() {
			assertThatIllegalArgumentException()
				.isThrownBy(() -> this.api.pull(ImageReference.of("ubuntu"), null, null))
				.withMessage("'listener' must not be null");
		}

		@Test
		void pullPullsImageAndProducesEvents() throws Exception {
			ImageReference reference = ImageReference.of("docker.io/paketobuildpacks/builder:base");
			URI createUri = new URI(IMAGES_URL + "/create?fromImage=docker.io%2Fpaketobuildpacks%2Fbuilder%3Abase");
			URI imageUri = new URI(IMAGES_URL + "/docker.io/paketobuildpacks/builder:base/json");
			given(http().post(eq(createUri), isNull())).willReturn(responseOf("pull-stream.json"));
			given(http().get(imageUri)).willReturn(responseOf("type/image.json"));
			Image image = this.api.pull(reference, null, 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 pullWithRegistryAuthPullsImageAndProducesEvents() throws Exception {
			ImageReference reference = ImageReference.of("docker.io/paketobuildpacks/builder:base");
			URI createUri = new URI(IMAGES_URL + "/create?fromImage=docker.io%2Fpaketobuildpacks%2Fbuilder%3Abase");
			URI imageUri = new URI(IMAGES_URL + "/docker.io/paketobuildpacks/builder:base/json");
			given(http().post(eq(createUri), eq("auth token"))).willReturn(responseOf("pull-stream.json"));
			given(http().get(imageUri)).willReturn(responseOf("type/image.json"));
			Image image = this.api.pull(reference, null, this.pullListener, "auth token");
			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 pullWithPlatformPullsImageAndProducesEvents() throws Exception {
			ImageReference reference = ImageReference.of("gcr.io/paketo-buildpacks/builder:base");
			URI createUri = new URI(
					"/v1.49/images/create?fromImage=gcr.io%2Fpaketo-buildpacks%2Fbuilder%3Abase&platform=linux%2Farm64%2Fv1");
			URI imageUri = new URI("/v1.49/images/gcr.io/paketo-buildpacks/builder:base/json?platform="
					+ ENCODED_LINUX_ARM64_PLATFORM_JSON);
			setVersion("1.49");
			given(http().post(eq(createUri), isNull())).willReturn(responseOf("pull-stream.json"));
			given(http().get(imageUri)).willReturn(responseOf("type/image.json"));
			Image image = this.api.pull(reference, LINUX_ARM64_PLATFORM, 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 pullWithPlatformAndInsufficientApiVersionThrowsException() throws Exception {
			ImageReference reference = ImageReference.of("gcr.io/paketo-buildpacks/builder:base");
			ImagePlatform platform = ImagePlatform.of("linux/arm64/v1");
			setVersion("1.24");
			assertThatIllegalStateException().isThrownBy(() -> this.api.pull(reference, platform, this.pullListener))
				.withMessageContaining("must be at least 1.41")
				.withMessageContaining("current API version is 1.24");
		}

		@Test
		@SuppressWarnings("NullAway") // Test null check
		void pushWhenReferenceIsNullThrowsException() {
			assertThatIllegalArgumentException().isThrownBy(() -> this.api.push(null, this.pushListener, null))
				.withMessage("'reference' must not be null");
		}

		@Test
		@SuppressWarnings("NullAway") // Test null check
		void pushWhenListenerIsNullThrowsException() {
			assertThatIllegalArgumentException()
				.isThrownBy(() -> this.api.push(ImageReference.of("ubuntu"), null, null))
				.withMessage("'listener' must not be null");
		}

		@Test
		void pushPushesImageAndProducesEvents() throws Exception {
			ImageReference reference = ImageReference.of("localhost:5000/ubuntu");
			URI pushUri = new URI(IMAGES_URL + "/localhost:5000/ubuntu/push");
			given(http().post(pushUri, "auth token")).willReturn(responseOf("push-stream.json"));
			this.api.push(reference, this.pushListener, "auth token");
			InOrder ordered = inOrder(this.pushListener);
			ordered.verify(this.pushListener).onStart();
			ordered.verify(this.pushListener, times(44)).onUpdate(any());
			ordered.verify(this.pushListener).onFinish();
		}

		@Test
		void pushWithErrorInStreamThrowsException() throws Exception {
			ImageReference reference = ImageReference.of("localhost:5000/ubuntu");
			URI pushUri = new URI(IMAGES_URL + "/localhost:5000/ubuntu/push");
			given(http().post(pushUri, "auth token")).willReturn(responseOf("push-stream-with-error.json"));
			assertThatIllegalStateException()
				.isThrownBy(() -> this.api.push(reference, this.pushListener, "auth token"))
				.withMessageContaining("test message");
		}

		@Test
		@SuppressWarnings("NullAway") // Test null check
		void loadWhenArchiveIsNullThrowsException() {
			assertThatIllegalArgumentException().isThrownBy(() -> this.api.load(null, UpdateListener.none()))
				.withMessage("'archive' must not be null");
		}

		@Test
		@SuppressWarnings("NullAway") // Test null check
		void loadWhenListenerIsNullThrowsException() {
			ImageArchive archive = mock(ImageArchive.class);
			assertThatIllegalArgumentException().isThrownBy(() -> this.api.load(archive, null))
				.withMessage("'listener' must not be null");
		}

		@Test // gh-23130
		void loadWithEmptyResponseThrowsException() throws Exception {
			Image image = Image.of(getClass().getResourceAsStream("type/image.json"));
			ImageArchive archive = ImageArchive.from(image);
			URI loadUri = new URI(IMAGES_URL + "/load");
			given(http().post(eq(loadUri), eq("application/x-tar"), any())).willReturn(emptyResponse());
			assertThatIllegalStateException().isThrownBy(() -> this.api.load(archive, this.loadListener))
				.withMessageContaining("Invalid response received");
		}

		@Test // gh-31243
		void loadWithErrorResponseThrowsException() throws Exception {
			Image image = Image.of(getClass().getResourceAsStream("type/image.json"));
			ImageArchive archive = ImageArchive.from(image);
			URI loadUri = new URI(IMAGES_URL + "/load");
			given(http().post(eq(loadUri), eq("application/x-tar"), any())).willReturn(responseOf("load-error.json"));
			assertThatIllegalStateException().isThrownBy(() -> this.api.load(archive, this.loadListener))
				.withMessageContaining("Error response received");
		}

		@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(http().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();
			then(http()).should().post(any(), any(), this.writer.capture());
			ByteArrayOutputStream out = new ByteArrayOutputStream();
			this.writer.getValue().accept(out);
			assertThat(out.toByteArray()).hasSizeGreaterThan(21000);
		}

		@Test
		@SuppressWarnings("NullAway") // Test null check
		void removeWhenReferenceIsNullThrowsException() {
			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(http().delete(removeUri)).willReturn(emptyResponse());
			this.api.remove(reference, false);
			then(http()).should().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(http().delete(removeUri)).willReturn(emptyResponse());
			this.api.remove(reference, true);
			then(http()).should().delete(removeUri);
		}

		@Test
		@SuppressWarnings("NullAway") // Test null check
		void inspectWhenReferenceIsNullThrowsException() {
			assertThatIllegalArgumentException().isThrownBy(() -> this.api.inspect(null))
				.withMessage("'reference' must not be null");
		}

		@Test
		void inspectInspectImage() throws Exception {
			ImageReference reference = ImageReference.of("docker.io/paketobuildpacks/builder:base");
			URI imageUri = new URI(IMAGES_URL + "/docker.io/paketobuildpacks/builder:base/json");
			given(http().get(imageUri)).willReturn(responseOf("type/image.json"));
			Image image = this.api.inspect(reference);
			assertThat(image.getArchitecture()).isEqualTo("amd64");
			assertThat(image.getLayers()).hasSize(46);
		}

		@Test
		void inspectWithPlatformWhenSupportedVersionInspectImage() throws Exception {
			ImageReference reference = ImageReference.of("docker.io/paketobuildpacks/builder:base");
			URI imageUri = new URI("/v1.49/images/docker.io/paketobuildpacks/builder:base/json?platform="
					+ ENCODED_LINUX_ARM64_PLATFORM_JSON);
			setVersion("1.49");
			given(http().get(imageUri)).willReturn(responseOf("type/image-platform.json"));
			Image image = this.api.inspect(reference, LINUX_ARM64_PLATFORM);
			assertThat(image.getArchitecture()).isEqualTo("arm64");
			assertThat(image.getLayers()).hasSize(2);
		}

		@Test
		void inspectWithPlatformWhenOldVersionInspectImage() throws Exception {
			ImageReference reference = ImageReference.of("docker.io/paketobuildpacks/builder:base");
			URI imageUri = new URI("/v1.48/images/docker.io/paketobuildpacks/builder:base/json");
			setVersion("1.48");
			given(http().get(imageUri)).willReturn(responseOf("type/image.json"));
			Image image = this.api.inspect(reference, LINUX_ARM64_PLATFORM);
			assertThat(image.getArchitecture()).isEqualTo("amd64");
			assertThat(image.getLayers()).hasSize(46);
		}

		@Test
		void exportLayersExportsLayerTars() throws Exception {
			ImageReference reference = ImageReference.of("docker.io/paketobuildpacks/builder:base");
			URI exportUri = new URI(IMAGES_URL + "/docker.io/paketobuildpacks/builder:base/get");
			given(DockerApiTests.this.http.get(exportUri)).willReturn(responseOf("export.tar"));
			MultiValueMap<String, String> contents = new LinkedMultiValueMap<>();
			this.api.exportLayers(reference, (name, archive) -> {
				ByteArrayOutputStream out = new ByteArrayOutputStream();
				archive.writeTo(out);
				try (TarArchiveInputStream in = new TarArchiveInputStream(
						new ByteArrayInputStream(out.toByteArray()))) {
					TarArchiveEntry entry = in.getNextEntry();
					while (entry != null) {
						contents.add(name, entry.getName());
						entry = in.getNextEntry();
					}
				}
			});
			assertThat(contents).hasSize(3)
				.containsKeys("70bb7a3115f3d5c01099852112c7e05bf593789e510468edb06b6a9a11fa3b73/layer.tar",
						"74a9a50ece13c025cf10e9110d9ddc86c995079c34e2a22a28d1a3d523222c6e/layer.tar",
						"a69532b5b92bb891fbd9fa1a6b3af9087ea7050255f59ba61a796f8555ecd783/layer.tar");
			assertThat(contents.get("70bb7a3115f3d5c01099852112c7e05bf593789e510468edb06b6a9a11fa3b73/layer.tar"))
				.containsExactly("/cnb/order.toml");
			assertThat(contents.get("74a9a50ece13c025cf10e9110d9ddc86c995079c34e2a22a28d1a3d523222c6e/layer.tar"))
				.containsExactly("/cnb/stack.toml");
		}

		@Test
		void exportLayersExportsLayerTarsWithPlatformWhenSupportedVersion() throws Exception {
			setVersion("1.48");
			ImageReference reference = ImageReference.of("docker.io/paketobuildpacks/builder:base");
			URI exportUri = new URI("/v1.48/images/docker.io/paketobuildpacks/builder:base/get?platform="
					+ ENCODED_LINUX_ARM64_PLATFORM_JSON);
			given(DockerApiTests.this.http.get(exportUri)).willReturn(responseOf("export.tar"));
			MultiValueMap<String, String> contents = new LinkedMultiValueMap<>();
			this.api.exportLayers(reference, LINUX_ARM64_PLATFORM, (name, archive) -> {
				ByteArrayOutputStream out = new ByteArrayOutputStream();
				archive.writeTo(out);
				try (TarArchiveInputStream in = new TarArchiveInputStream(
						new ByteArrayInputStream(out.toByteArray()))) {
					TarArchiveEntry entry = in.getNextEntry();
					while (entry != null) {
						contents.add(name, entry.getName());
						entry = in.getNextEntry();
					}
				}
			});
			assertThat(contents).hasSize(3)
				.containsKeys("70bb7a3115f3d5c01099852112c7e05bf593789e510468edb06b6a9a11fa3b73/layer.tar",
						"74a9a50ece13c025cf10e9110d9ddc86c995079c34e2a22a28d1a3d523222c6e/layer.tar",
						"a69532b5b92bb891fbd9fa1a6b3af9087ea7050255f59ba61a796f8555ecd783/layer.tar");
			assertThat(contents.get("70bb7a3115f3d5c01099852112c7e05bf593789e510468edb06b6a9a11fa3b73/layer.tar"))
				.containsExactly("/cnb/order.toml");
			assertThat(contents.get("74a9a50ece13c025cf10e9110d9ddc86c995079c34e2a22a28d1a3d523222c6e/layer.tar"))
				.containsExactly("/cnb/stack.toml");
		}

		@Test
		void exportLayersExportsLayerTarsWithPlatformWhenOldVersionInspectImage() throws Exception {
			setVersion("1.47");
			ImageReference reference = ImageReference.of("docker.io/paketobuildpacks/builder:base");
			URI exportUri = new URI("/v1.47/images/docker.io/paketobuildpacks/builder:base/get");
			given(DockerApiTests.this.http.get(exportUri)).willReturn(responseOf("export.tar"));
			MultiValueMap<String, String> contents = new LinkedMultiValueMap<>();
			this.api.exportLayers(reference, LINUX_ARM64_PLATFORM, (name, archive) -> {
				ByteArrayOutputStream out = new ByteArrayOutputStream();
				archive.writeTo(out);
				try (TarArchiveInputStream in = new TarArchiveInputStream(
						new ByteArrayInputStream(out.toByteArray()))) {
					TarArchiveEntry entry = in.getNextEntry();
					while (entry != null) {
						contents.add(name, entry.getName());
						entry = in.getNextEntry();
					}
				}
			});
			assertThat(contents).hasSize(3)
				.containsKeys("70bb7a3115f3d5c01099852112c7e05bf593789e510468edb06b6a9a11fa3b73/layer.tar",
						"74a9a50ece13c025cf10e9110d9ddc86c995079c34e2a22a28d1a3d523222c6e/layer.tar",
						"a69532b5b92bb891fbd9fa1a6b3af9087ea7050255f59ba61a796f8555ecd783/layer.tar");
			assertThat(contents.get("70bb7a3115f3d5c01099852112c7e05bf593789e510468edb06b6a9a11fa3b73/layer.tar"))
				.containsExactly("/cnb/order.toml");
			assertThat(contents.get("74a9a50ece13c025cf10e9110d9ddc86c995079c34e2a22a28d1a3d523222c6e/layer.tar"))
				.containsExactly("/cnb/stack.toml");
		}

		@Test
		void exportLayersWithSymlinksExportsLayerTars() throws Exception {
			ImageReference reference = ImageReference.of("docker.io/paketobuildpacks/builder:base");
			URI exportUri = new URI(IMAGES_URL + "/docker.io/paketobuildpacks/builder:base/get");
			given(DockerApiTests.this.http.get(exportUri)).willReturn(responseOf("export-symlinks.tar"));
			MultiValueMap<String, String> contents = new LinkedMultiValueMap<>();
			this.api.exportLayers(reference, (name, archive) -> {
				ByteArrayOutputStream out = new ByteArrayOutputStream();
				archive.writeTo(out);
				try (TarArchiveInputStream in = new TarArchiveInputStream(
						new ByteArrayInputStream(out.toByteArray()))) {
					TarArchiveEntry entry = in.getNextEntry();
					while (entry != null) {
						contents.add(name, entry.getName());
						entry = in.getNextEntry();
					}
				}
			});
			assertThat(contents).hasSize(3)
				.containsKeys("6aa3691a73805f608e5fce69fb6bc89aec8362f58a6b4be2682515e9cfa3cc1a.tar",
						"762e198f655bc2580ef3e56b538810fd2b9981bd707f8a44c70344b58f9aee68.tar",
						"d3cc975ad97fdfbb73d9daf157e7f658d6117249fd9c237e3856ad173c87e1d2.tar");
			assertThat(contents.get("d3cc975ad97fdfbb73d9daf157e7f658d6117249fd9c237e3856ad173c87e1d2.tar"))
				.containsExactly("/cnb/order.toml");
			assertThat(contents.get("762e198f655bc2580ef3e56b538810fd2b9981bd707f8a44c70344b58f9aee68.tar"))
				.containsExactly("/cnb/stack.toml");
		}

		@Test
		@SuppressWarnings("NullAway") // Test null check
		void tagWhenReferenceIsNullThrowsException() {
			ImageReference tag = ImageReference.of("localhost:5000/ubuntu");
			assertThatIllegalArgumentException().isThrownBy(() -> this.api.tag(null, tag))
				.withMessage("'sourceReference' must not be null");
		}

		@Test
		@SuppressWarnings("NullAway") // Test null check
		void tagWhenTargetIsNullThrowsException() {
			ImageReference reference = ImageReference.of("localhost:5000/ubuntu");
			assertThatIllegalArgumentException().isThrownBy(() -> this.api.tag(reference, null))
				.withMessage("'targetReference' must not be null");
		}

		@Test
		void tagTagsImage() throws Exception {
			ImageReference sourceReference = ImageReference.of("localhost:5000/ubuntu");
			ImageReference targetReference = ImageReference.of("localhost:5000/ubuntu:tagged");
			URI tagURI = new URI(IMAGES_URL + "/localhost:5000/ubuntu/tag?repo=localhost%3A5000%2Fubuntu&tag=tagged");
			given(http().post(tagURI)).willReturn(emptyResponse());
			this.api.tag(sourceReference, targetReference);
			then(http()).should().post(tagURI);
		}

		@Test
		void tagRenamesImage() throws Exception {
			ImageReference sourceReference = ImageReference.of("localhost:5000/ubuntu");
			ImageReference targetReference = ImageReference.of("localhost:5000/ubuntu-2");
			URI tagURI = new URI(IMAGES_URL + "/localhost:5000/ubuntu/tag?repo=localhost%3A5000%2Fubuntu-2");
			given(http().post(tagURI)).willReturn(emptyResponse());
			this.api.tag(sourceReference, targetReference);
			then(http()).should().post(tagURI);
		}

	}

Analyze Your Own Codebase

Get architecture documentation, dependency graphs, and domain analysis for your codebase in minutes.

Try Supermodel Free