diff --git a/api-client/src/main/kotlin/de/gesellix/docker/remote/api/client/BuildInfoExtensions.kt b/api-client/src/main/kotlin/de/gesellix/docker/remote/api/client/BuildInfoExtensions.kt new file mode 100644 index 00000000..4ca64ac1 --- /dev/null +++ b/api-client/src/main/kotlin/de/gesellix/docker/remote/api/client/BuildInfoExtensions.kt @@ -0,0 +1,29 @@ +package de.gesellix.docker.remote.api.client + +import de.gesellix.docker.remote.api.BuildInfo +import de.gesellix.docker.remote.api.ImageID + +fun List.getImageId(): ImageID? { + val reversedInfos = this.reversed() + val firstAux = reversedInfos.stream() + .filter { (_, _, _, _, _, _, _, aux): BuildInfo -> aux != null } + .findFirst() + if (firstAux.isPresent) { + return firstAux.get().aux + } else { + val idFromStream = reversedInfos.stream() + .filter { (_, stream): BuildInfo -> stream?.contains("Successfully built ")!! } + .findFirst() + return if (idFromStream.isPresent) { + ImageID(idFromStream.get().stream!!.removePrefix("Successfully built ").replaceAfter('\n', "").trim()) + } else { + val tagFromStream = reversedInfos.stream() + .filter { (_, stream): BuildInfo -> stream?.contains("Successfully tagged ")!! } + .findFirst() + tagFromStream.map { (_, stream): BuildInfo -> + ImageID(stream!!.removePrefix("Successfully tagged ").replaceAfter('\n', "").trim()) + } + .orElse(null) + } + } +} diff --git a/api-client/src/main/kotlin/de/gesellix/docker/remote/api/client/ContainerApi.kt b/api-client/src/main/kotlin/de/gesellix/docker/remote/api/client/ContainerApi.kt index 07d08e96..c9ae6b95 100644 --- a/api-client/src/main/kotlin/de/gesellix/docker/remote/api/client/ContainerApi.kt +++ b/api-client/src/main/kotlin/de/gesellix/docker/remote/api/client/ContainerApi.kt @@ -424,7 +424,8 @@ class ContainerApi(dockerClientConfig: DockerClientConfig = defaultClientConfig, path = "/containers/{id}/changes".replace("{" + "id" + "}", id), query = localVariableQuery, headers = localVariableHeaders, - body = localVariableBody + body = localVariableBody, + elementType = ContainerChangeResponseItem::class.java ) } diff --git a/api-client/src/main/kotlin/de/gesellix/docker/remote/api/client/CreateImageInfoExtensions.kt b/api-client/src/main/kotlin/de/gesellix/docker/remote/api/client/CreateImageInfoExtensions.kt new file mode 100644 index 00000000..a862827a --- /dev/null +++ b/api-client/src/main/kotlin/de/gesellix/docker/remote/api/client/CreateImageInfoExtensions.kt @@ -0,0 +1,15 @@ +package de.gesellix.docker.remote.api.client + +import de.gesellix.docker.remote.api.CreateImageInfo + +fun List.getImageId(): String? { + val reversedInfos = this.reversed() + val firstStatus = reversedInfos.stream() + .filter { (_, _, status, _, _): CreateImageInfo -> status != null } + .findFirst() + if (firstStatus.isPresent) { + return firstStatus.get().status + } else { + return null + } +} diff --git a/api-client/src/main/kotlin/de/gesellix/docker/remote/api/client/ExecApi.kt b/api-client/src/main/kotlin/de/gesellix/docker/remote/api/client/ExecApi.kt index aa7d841c..bbdba86c 100644 --- a/api-client/src/main/kotlin/de/gesellix/docker/remote/api/client/ExecApi.kt +++ b/api-client/src/main/kotlin/de/gesellix/docker/remote/api/client/ExecApi.kt @@ -28,6 +28,7 @@ import de.gesellix.docker.remote.api.core.RequestConfig import de.gesellix.docker.remote.api.core.ResponseType import de.gesellix.docker.remote.api.core.ServerError import de.gesellix.docker.remote.api.core.ServerException +import de.gesellix.docker.remote.api.core.StreamCallback import de.gesellix.docker.remote.api.core.Success import de.gesellix.docker.remote.api.core.SuccessStream import kotlinx.coroutines.cancel @@ -234,28 +235,37 @@ class ExecApi(dockerClientConfig: DockerClientConfig = defaultClientConfig, prox * @throws ServerException If the API returns a server error response */ @Throws(UnsupportedOperationException::class, ClientException::class, ServerException::class) - fun execStart(id: String, execStartConfig: ExecStartConfig?) { + @JvmOverloads + fun execStart( + id: String, execStartConfig: ExecStartConfig?, + callback: StreamCallback? = null, timeoutMillis: Long? = null /*= 24.hours.toLongMilliseconds()*/ + ) { val localVariableConfig = execStartRequestConfig(id = id, execStartConfig = execStartConfig) - // TODO do we need to inspect the exec, because it might have been created with tty==false? -// val expectMultiplexedResponse = !(execInspect(id).processConfig?.tty ?: false) - val expectMultiplexedResponse = !(execStartConfig?.tty ?: false) + val expectMultiplexedResponse: Boolean = if (execStartConfig?.tty != null) { + !(execStartConfig.tty ?: false) + } else { + !(execInspect(id).processConfig?.tty ?: false) + } val localVarResponse = requestFrames( localVariableConfig, expectMultiplexedResponse ) - // TODO the caller of #execStart() should decide about timeout and callback - val timeout = Duration.of(1, ChronoUnit.HOURS) - val callback = LoggingCallback() + val timeout = if (timeoutMillis == null) { + Duration.of(10, ChronoUnit.MINUTES) + } else { + Duration.of(timeoutMillis, ChronoUnit.MILLIS) + } + val actualCallback = callback ?: LoggingCallback() when (localVarResponse.responseType) { ResponseType.Success -> { runBlocking { launch { withTimeoutOrNull(timeout.toMillis()) { - callback.onStarting(this@launch::cancel) - ((localVarResponse as SuccessStream<*>).data as Flow).collect { callback.onNext(it) } - callback.onFinished() + actualCallback.onStarting(this@launch::cancel) + ((localVarResponse as SuccessStream<*>).data as Flow).collect { actualCallback.onNext(it) } + actualCallback.onFinished() } } } diff --git a/api-client/src/main/kotlin/de/gesellix/docker/remote/api/client/ImageApi.kt b/api-client/src/main/kotlin/de/gesellix/docker/remote/api/client/ImageApi.kt index d47a28a1..4a29d64a 100644 --- a/api-client/src/main/kotlin/de/gesellix/docker/remote/api/client/ImageApi.kt +++ b/api-client/src/main/kotlin/de/gesellix/docker/remote/api/client/ImageApi.kt @@ -23,7 +23,6 @@ import de.gesellix.docker.remote.api.HistoryResponseItem import de.gesellix.docker.remote.api.IdResponse import de.gesellix.docker.remote.api.Image import de.gesellix.docker.remote.api.ImageDeleteResponseItem -import de.gesellix.docker.remote.api.ImageID import de.gesellix.docker.remote.api.ImagePruneResponse import de.gesellix.docker.remote.api.ImageSearchResponseItem import de.gesellix.docker.remote.api.ImageSummary @@ -270,31 +269,6 @@ class ImageApi(dockerClientConfig: DockerClientConfig = defaultClientConfig, pro } } - fun getImageId(buildInfos: List): ImageID? { - val reversedInfos = buildInfos.reversed() - val firstAux = reversedInfos.stream() - .filter { (_, _, _, _, _, _, _, aux): BuildInfo -> aux != null } - .findFirst() - if (firstAux.isPresent) { - return firstAux.get().aux - } else { - val idFromStream = reversedInfos.stream() - .filter { (_, stream): BuildInfo -> stream?.contains("Successfully built ")!! } - .findFirst() - return if (idFromStream.isPresent) { - ImageID(idFromStream.get().stream!!.removePrefix("Successfully built ").replaceAfter('\n', "").trim()) - } else { - val tagFromStream = reversedInfos.stream() - .filter { (_, stream): BuildInfo -> stream?.contains("Successfully tagged ")!! } - .findFirst() - tagFromStream.map { (_, stream): BuildInfo -> - ImageID(stream!!.removePrefix("Successfully tagged ").replaceAfter('\n', "").trim()) - } - .orElse(null) - } - } - } - /** * To obtain the request config of the operation imageBuild * diff --git a/api-client/src/test/java/de/gesellix/docker/remote/api/client/ImageApiTest.java b/api-client/src/test/java/de/gesellix/docker/remote/api/client/BuildInfoExtensionsTest.java similarity index 81% rename from api-client/src/test/java/de/gesellix/docker/remote/api/client/ImageApiTest.java rename to api-client/src/test/java/de/gesellix/docker/remote/api/client/BuildInfoExtensionsTest.java index 7e3c9f13..c83a2607 100644 --- a/api-client/src/test/java/de/gesellix/docker/remote/api/client/ImageApiTest.java +++ b/api-client/src/test/java/de/gesellix/docker/remote/api/client/BuildInfoExtensionsTest.java @@ -1,9 +1,7 @@ package de.gesellix.docker.remote.api.client; -import de.gesellix.docker.engine.DockerClientConfig; import de.gesellix.docker.remote.api.BuildInfo; import de.gesellix.docker.remote.api.ImageID; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.ArrayList; @@ -11,14 +9,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -class ImageApiTest { - - private ImageApi imageApi; - - @BeforeEach - public void setup() { - imageApi = new ImageApi(new DockerClientConfig()); - } +class BuildInfoExtensionsTest { @Test public void getImageIdFromAux() { @@ -28,7 +19,7 @@ public void getImageIdFromAux() { infos.add(new BuildInfo(null, "Successfully built f9d5f290d048\nfoo bar", null, null, null, null, null, null)); infos.add(new BuildInfo(null, "Successfully tagged image:tag\nbar baz", null, null, null, null, null, null)); - ImageID imageId = imageApi.getImageId(infos); + ImageID imageId = BuildInfoExtensionsKt.getImageId(infos); assertEquals("sha256:expected-id", imageId.getID()); } @@ -40,7 +31,7 @@ public void getImageIdFromStreamWithBuildMessage() { infos.add(new BuildInfo(null, "Successfully built f9d5f290d048\nfoo bar", null, null, null, null, null, null)); infos.add(new BuildInfo(null, "Successfully tagged image:tag\nbar baz", null, null, null, null, null, null)); - ImageID imageId = imageApi.getImageId(infos); + ImageID imageId = BuildInfoExtensionsKt.getImageId(infos); assertEquals("f9d5f290d048", imageId.getID()); } @@ -50,7 +41,7 @@ public void getImageIdFromStreamWithTagMessage() { List infos = new ArrayList<>(); infos.add(new BuildInfo(null, "Successfully tagged image:tag\nbar baz", null, null, null, null, null, null)); - ImageID imageId = imageApi.getImageId(infos); + ImageID imageId = BuildInfoExtensionsKt.getImageId(infos); assertEquals("image:tag", imageId.getID()); } diff --git a/api-client/src/test/java/de/gesellix/docker/remote/api/client/ContainerApiIntegrationTest.java b/api-client/src/test/java/de/gesellix/docker/remote/api/client/ContainerApiIntegrationTest.java index cbb86abc..c11e9944 100644 --- a/api-client/src/test/java/de/gesellix/docker/remote/api/client/ContainerApiIntegrationTest.java +++ b/api-client/src/test/java/de/gesellix/docker/remote/api/client/ContainerApiIntegrationTest.java @@ -318,7 +318,7 @@ public void run() { } @Test - public void containerLogsWithTty() { + public void containerLogsWithTty() throws InterruptedException { imageApi.imageCreate(testImage.getImageName(), null, null, testImage.getImageTag(), null, null, null, null, null); ContainerCreateRequest containerCreateRequest = new ContainerCreateRequest( @@ -342,8 +342,9 @@ public void containerLogsWithTty() { ); containerApi.containerCreate(containerCreateRequest, "container-logs-with-tty-test"); containerApi.containerStart("container-logs-with-tty-test", null); + Thread.sleep(500); - Duration timeout = Duration.of(5, SECONDS); + Duration timeout = Duration.of(10, SECONDS); LogFrameStreamCallback callback = new LogFrameStreamCallback(); new Thread(() -> containerApi.containerLogs( @@ -366,7 +367,9 @@ public void run() { catch (InterruptedException e) { e.printStackTrace(); } - assertSame(callback.frames.stream().findAny().get().getStreamType(), Frame.StreamType.RAW); + Optional anyFrame = callback.frames.stream().findAny(); + assertTrue(anyFrame.isPresent()); + assertSame(anyFrame.get().getStreamType(), Frame.StreamType.RAW); removeContainer(engineApiClient, "container-logs-with-tty-test"); } diff --git a/api-client/src/test/java/de/gesellix/docker/remote/api/client/CreateImageInfoExtensionsTest.java b/api-client/src/test/java/de/gesellix/docker/remote/api/client/CreateImageInfoExtensionsTest.java new file mode 100644 index 00000000..7da356b9 --- /dev/null +++ b/api-client/src/test/java/de/gesellix/docker/remote/api/client/CreateImageInfoExtensionsTest.java @@ -0,0 +1,25 @@ +package de.gesellix.docker.remote.api.client; + +import de.gesellix.docker.remote.api.CreateImageInfo; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class CreateImageInfoExtensionsTest { + + @Test + public void getImageIdFromStatus() { + List infos = new ArrayList<>(); + infos.add(new CreateImageInfo("os-linux", null, "Pulling from gesellix/echo-server", null, null)); + infos.add(new CreateImageInfo(null, null, "Digest: sha256:04c0275878dc243b2f92193467cb33cdb9ee2262be64b627ed476de73e399244", null, null)); + infos.add(new CreateImageInfo(null, null, "Status: Image is up to date for gesellix/echo-server:os-linux", null, null)); + infos.add(new CreateImageInfo(null, null, "sha256:87f5e747ad067f91a7c4adf154deea20cebc3a749be5c2864a0c65cf70ddd8c4", null, null)); + + String imageId = CreateImageInfoExtensionsKt.getImageId(infos); + + assertEquals("sha256:87f5e747ad067f91a7c4adf154deea20cebc3a749be5c2864a0c65cf70ddd8c4", imageId); + } +} diff --git a/api-client/src/test/java/de/gesellix/docker/remote/api/client/ImageApiIntegrationTest.java b/api-client/src/test/java/de/gesellix/docker/remote/api/client/ImageApiIntegrationTest.java index d4f3a20a..4b0edbd7 100644 --- a/api-client/src/test/java/de/gesellix/docker/remote/api/client/ImageApiIntegrationTest.java +++ b/api-client/src/test/java/de/gesellix/docker/remote/api/client/ImageApiIntegrationTest.java @@ -127,7 +127,7 @@ public void onFinished() { log.error("Wait interrupted", e); } - ImageID imageId = imageApi.getImageId(infos); + ImageID imageId = BuildInfoExtensionsKt.getImageId(infos); assertNotNull(imageId); assertNotNull(imageId.getID()); assertTrue(imageId.getID().matches(".+"));