From 9feac719e5657b75fdb75234d364f4a1ff92ba7c Mon Sep 17 00:00:00 2001 From: Silvio Giebl Date: Fri, 13 Dec 2024 13:16:51 +0100 Subject: [PATCH] Handle logging output of DockerLayerTask --- .../oci/internal/string/LineOutputStream.kt | 86 +++++++++++++++++++ .../gradle/oci/layer/DockerLayerTask.kt | 13 +++ 2 files changed, 99 insertions(+) create mode 100644 src/main/kotlin/io/github/sgtsilvio/gradle/oci/internal/string/LineOutputStream.kt diff --git a/src/main/kotlin/io/github/sgtsilvio/gradle/oci/internal/string/LineOutputStream.kt b/src/main/kotlin/io/github/sgtsilvio/gradle/oci/internal/string/LineOutputStream.kt new file mode 100644 index 00000000..bd12a2d8 --- /dev/null +++ b/src/main/kotlin/io/github/sgtsilvio/gradle/oci/internal/string/LineOutputStream.kt @@ -0,0 +1,86 @@ +package io.github.sgtsilvio.gradle.oci.internal.string + +import java.io.OutputStream + +private const val CR = '\r'.code.toByte() +private const val LF = '\n'.code.toByte() + +/** + * Interprets an output stream as UTF-8 lines. + * Supports LF, CR LF and CR line endings. + */ +internal class LineOutputStream(private val consumer: (String) -> Unit) : OutputStream() { + + private val buffer = ByteStringBuilder() + private var lastCR = false + + override fun write(b: Int) = write(byteArrayOf(b.toByte())) + + override fun write(b: ByteArray, off: Int, len: Int) { + val end = off + len + if ((len < 0) || (off < 0) || (off > b.size) || (end < 0) || (end > b.size)) { + throw IndexOutOfBoundsException() + } + var start = off + var i = off + while (i < end) { + when (b[i]) { + LF -> { + if (!lastCR) { + consumer(buffer.toString(b, start, i)) + } + start = i + 1 + lastCR = false + } + CR -> { + consumer(buffer.toString(b, start, i)) + start = i + 1 + lastCR = true + } + else -> { + lastCR = false + } + } + i++ + } + buffer.append(b, start, end) + } + + override fun close() { + if (buffer.size > 0) { + consumer(buffer.toString()) + } + } +} + +private class ByteStringBuilder { + + private var buffer = ByteArray(0) + var size = 0 + private set + + fun append(b: ByteArray, fromIndex: Int, toIndex: Int) { + val additionalSize = toIndex - fromIndex + if (additionalSize == 0) { + return + } + val currentSize = size + val newSize = currentSize + additionalSize + if (newSize > buffer.size) { + buffer = buffer.copyOf(newSize) + } + System.arraycopy(b, fromIndex, buffer, currentSize, additionalSize) + size = newSize + } + + fun toString(b: ByteArray, fromIndex: Int, toIndex: Int): String { + return if (size == 0) { + String(b, fromIndex, toIndex - fromIndex) + } else { + append(b, fromIndex, toIndex) + toString() + } + } + + override fun toString() = String(buffer, 0, size).also { size = 0 } +} diff --git a/src/main/kotlin/io/github/sgtsilvio/gradle/oci/layer/DockerLayerTask.kt b/src/main/kotlin/io/github/sgtsilvio/gradle/oci/layer/DockerLayerTask.kt index f57c0f41..b480e73f 100644 --- a/src/main/kotlin/io/github/sgtsilvio/gradle/oci/layer/DockerLayerTask.kt +++ b/src/main/kotlin/io/github/sgtsilvio/gradle/oci/layer/DockerLayerTask.kt @@ -1,6 +1,7 @@ package io.github.sgtsilvio.gradle.oci.layer import io.github.sgtsilvio.gradle.oci.internal.copyspec.DEFAULT_MODIFICATION_TIME +import io.github.sgtsilvio.gradle.oci.internal.string.LineOutputStream import io.github.sgtsilvio.gradle.oci.platform.Platform import org.apache.commons.compress.archivers.tar.TarArchiveInputStream import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream @@ -50,14 +51,18 @@ abstract class DockerLayerTask @Inject constructor(private val execOperations: E execOperations.exec { commandLine("docker", "build", "-", "--platform", platformArgument, "-t", imageReference, "--no-cache") standardInput = ByteArrayInputStream(assembleDockerfile().toByteArray()) + errorOutput = createErrorStream() } val temporaryDirectory = temporaryDir val savedImageTarFile = temporaryDirectory.resolve("image.tar") execOperations.exec { commandLine("docker", "save", imageReference, "-o", savedImageTarFile) + errorOutput = createErrorStream() } execOperations.exec { commandLine("docker", "rmi", imageReference) + standardOutput = LineOutputStream { logger.info(it) } + errorOutput = createErrorStream() } val manifest = TarArchiveInputStream(FileInputStream(savedImageTarFile)).use { savedImageTarInputStream -> if (!savedImageTarInputStream.findEntry("manifest.json")) { @@ -113,6 +118,14 @@ abstract class DockerLayerTask @Inject constructor(private val execOperations: E return if (variant.isEmpty()) s else "$s/$variant" } + private fun createErrorStream() = LineOutputStream { line -> + if (line.startsWith("ERROR")) { + logger.error(line) + } else { + logger.info(line) + } + } + private fun TarArchiveInputStream.findEntry(path: String): Boolean { while (nextEntry != null) { if (currentEntry.name == path) {