diff --git a/src/main/kotlin/io/github/sgtsilvio/gradle/oci/OciImagesInputTask.kt b/src/main/kotlin/io/github/sgtsilvio/gradle/oci/OciImagesInputTask.kt index d2b01772..cc10c820 100644 --- a/src/main/kotlin/io/github/sgtsilvio/gradle/oci/OciImagesInputTask.kt +++ b/src/main/kotlin/io/github/sgtsilvio/gradle/oci/OciImagesInputTask.kt @@ -41,7 +41,7 @@ internal class OciImage( ) internal class OciVariant( - val metadata: OciMetadata, + val metadata: OciVariantMetadata, val layers: List, ) @@ -102,7 +102,7 @@ abstract class OciImagesInputTask : DefaultTask() { } private fun OciVariantInput.toVariant(): OciVariant { - val metadata = metadataFile.readText().decodeAsJsonToOciMetadata() + val metadata = metadataFile.readText().decodeAsJsonToOciVariantMetadata() val layerFiles = layerFiles val layers = ArrayList(layerFiles.size) var layerFileIndex = 0 diff --git a/src/main/kotlin/io/github/sgtsilvio/gradle/oci/OciMetadataTask.kt b/src/main/kotlin/io/github/sgtsilvio/gradle/oci/OciVariantMetadataTask.kt similarity index 94% rename from src/main/kotlin/io/github/sgtsilvio/gradle/oci/OciMetadataTask.kt rename to src/main/kotlin/io/github/sgtsilvio/gradle/oci/OciVariantMetadataTask.kt index e4bf5017..020b3601 100644 --- a/src/main/kotlin/io/github/sgtsilvio/gradle/oci/OciMetadataTask.kt +++ b/src/main/kotlin/io/github/sgtsilvio/gradle/oci/OciVariantMetadataTask.kt @@ -13,7 +13,7 @@ import org.gradle.kotlin.dsl.property /** * @author Silvio Giebl */ -abstract class OciMetadataTask : DefaultTask() { +abstract class OciVariantMetadataTask : DefaultTask() { @get:Input val encodedMetadata: Property = project.objects.property() diff --git a/src/main/kotlin/io/github/sgtsilvio/gradle/oci/internal/OciNaming.kt b/src/main/kotlin/io/github/sgtsilvio/gradle/oci/internal/OciNaming.kt index a297c051..11e11e8f 100644 --- a/src/main/kotlin/io/github/sgtsilvio/gradle/oci/internal/OciNaming.kt +++ b/src/main/kotlin/io/github/sgtsilvio/gradle/oci/internal/OciNaming.kt @@ -19,7 +19,7 @@ internal fun createOciVariantName(imageDefName: String, platform: Platform): Str internal fun createOciVariantInternalName(imageDefName: String, platform: Platform): String = imageDefName.mainToEmpty().camelCase().concatCamelCase("ociImageInternal") + createPlatformPostfix(platform) -internal fun createOciMetadataClassifier(imageDefName: String): String = +internal fun createOciVariantMetadataClassifier(imageDefName: String): String = imageDefName.mainToEmpty().kebabCase().concatKebabCase("oci-metadata") internal fun createOciLayerClassifier(imageDefName: String, layerName: String): String = diff --git a/src/main/kotlin/io/github/sgtsilvio/gradle/oci/internal/dsl/OciImageDefinitionImpl.kt b/src/main/kotlin/io/github/sgtsilvio/gradle/oci/internal/dsl/OciImageDefinitionImpl.kt index 1e2d0bc5..5d57e021 100644 --- a/src/main/kotlin/io/github/sgtsilvio/gradle/oci/internal/dsl/OciImageDefinitionImpl.kt +++ b/src/main/kotlin/io/github/sgtsilvio/gradle/oci/internal/dsl/OciImageDefinitionImpl.kt @@ -2,7 +2,7 @@ package io.github.sgtsilvio.gradle.oci.internal.dsl import io.github.sgtsilvio.gradle.oci.OciCopySpec import io.github.sgtsilvio.gradle.oci.OciLayerTask -import io.github.sgtsilvio.gradle.oci.OciMetadataTask +import io.github.sgtsilvio.gradle.oci.OciVariantMetadataTask import io.github.sgtsilvio.gradle.oci.TASK_GROUP_NAME import io.github.sgtsilvio.gradle.oci.attributes.* import io.github.sgtsilvio.gradle.oci.dsl.OciImageDefinition @@ -232,8 +232,9 @@ internal abstract class OciImageDefinitionImpl @Inject constructor( objectFactory.newInstance(imageDefinition.name, Optional.ofNullable(platform)) init { - val metadata = createMetadata(providerFactory) - val metadataTask = taskContainer.createMetadataTask(imageDefinition.name, platform, metadata, projectLayout) + val metadata = createVariantMetadata(providerFactory) + val metadataTask = + taskContainer.createVariantMetadataTask(imageDefinition.name, platform, metadata, projectLayout) configuration.outgoing.addArtifacts(providerFactory.provider { listOf(LazyPublishArtifact(objectFactory).apply { file.set(metadataTask.flatMap { it.file }) @@ -253,31 +254,31 @@ internal abstract class OciImageDefinitionImpl @Inject constructor( }) } - private fun createMetadata(providerFactory: ProviderFactory): Provider = - providerFactory.provider { OciMetadataBuilder() } - .zip(imageDefinition.imageReference, OciMetadataBuilder::imageReference) - .zipAbsentAsNull(config.creationTime, OciMetadataBuilder::creationTime) - .zipAbsentAsNull(config.author, OciMetadataBuilder::author) - .zipAbsentAsNull(config.user, OciMetadataBuilder::user) - .zip(config.ports.orElse(emptySet()), OciMetadataBuilder::ports) - .zip(config.environment.orElse(emptyMap()), OciMetadataBuilder::environment) - .zipAbsentAsNull(config.entryPoint, OciMetadataBuilder::entryPoint) - .zipAbsentAsNull(config.arguments, OciMetadataBuilder::arguments) - .zip(config.volumes.orElse(emptySet()), OciMetadataBuilder::volumes) - .zipAbsentAsNull(config.workingDirectory, OciMetadataBuilder::workingDirectory) - .zipAbsentAsNull(config.stopSignal, OciMetadataBuilder::stopSignal) - .zip(config.configAnnotations.orElse(emptyMap()), OciMetadataBuilder::configAnnotations) + private fun createVariantMetadata(providerFactory: ProviderFactory): Provider = + providerFactory.provider { OciVariantMetadataBuilder() } + .zip(imageDefinition.imageReference, OciVariantMetadataBuilder::imageReference) + .zipAbsentAsNull(config.creationTime, OciVariantMetadataBuilder::creationTime) + .zipAbsentAsNull(config.author, OciVariantMetadataBuilder::author) + .zipAbsentAsNull(config.user, OciVariantMetadataBuilder::user) + .zip(config.ports.orElse(emptySet()), OciVariantMetadataBuilder::ports) + .zip(config.environment.orElse(emptyMap()), OciVariantMetadataBuilder::environment) + .zipAbsentAsNull(config.entryPoint, OciVariantMetadataBuilder::entryPoint) + .zipAbsentAsNull(config.arguments, OciVariantMetadataBuilder::arguments) + .zip(config.volumes.orElse(emptySet()), OciVariantMetadataBuilder::volumes) + .zipAbsentAsNull(config.workingDirectory, OciVariantMetadataBuilder::workingDirectory) + .zipAbsentAsNull(config.stopSignal, OciVariantMetadataBuilder::stopSignal) + .zip(config.configAnnotations.orElse(emptyMap()), OciVariantMetadataBuilder::configAnnotations) .zip( config.configDescriptorAnnotations.orElse(emptyMap()), - OciMetadataBuilder::configDescriptorAnnotations, + OciVariantMetadataBuilder::configDescriptorAnnotations, ) - .zip(config.manifestAnnotations.orElse(emptyMap()), OciMetadataBuilder::manifestAnnotations) + .zip(config.manifestAnnotations.orElse(emptyMap()), OciVariantMetadataBuilder::manifestAnnotations) .zip( config.manifestDescriptorAnnotations.orElse(emptyMap()), - OciMetadataBuilder::manifestDescriptorAnnotations, + OciVariantMetadataBuilder::manifestDescriptorAnnotations, ) - .zip(imageDefinition.indexAnnotations.orElse(emptyMap()), OciMetadataBuilder::indexAnnotations) - .zip(createLayerMetadataList(providerFactory), OciMetadataBuilder::layers) + .zip(imageDefinition.indexAnnotations.orElse(emptyMap()), OciVariantMetadataBuilder::indexAnnotations) + .zip(createLayerMetadataList(providerFactory), OciVariantMetadataBuilder::layers) .map { it.build() } private fun createLayerMetadataList(providerFactory: ProviderFactory): Provider> = @@ -593,17 +594,19 @@ internal abstract class OciImageDefinitionImpl @Inject constructor( } } -private fun TaskContainer.createMetadataTask( +private fun TaskContainer.createVariantMetadataTask( imageDefName: String, platform: Platform?, - metadata: Provider, + variantMetadata: Provider, projectLayout: ProjectLayout, -) = register(createOciMetadataClassifier(imageDefName).camelCase() + createPlatformPostfix(platform)) { +) = register( + createOciVariantMetadataClassifier(imageDefName).camelCase() + createPlatformPostfix(platform) +) { group = TASK_GROUP_NAME description = "Assembles the metadata json file of the '$imageDefName' OCI image" + if (platform == null) "." else " for the platform $platform" - encodedMetadata.set(metadata.map { it.encodeToJsonString() }) + encodedMetadata.set(variantMetadata.map { it.encodeToJsonString() }) destinationDirectory.set(projectLayout.buildDirectory.dir("oci/images/$imageDefName")) - classifier.set(createOciMetadataClassifier(imageDefName) + createPlatformPostfix(platform)) + classifier.set(createOciVariantMetadataClassifier(imageDefName) + createPlatformPostfix(platform)) } private fun TaskContainer.createLayerTask( diff --git a/src/main/kotlin/io/github/sgtsilvio/gradle/oci/internal/registry/OciImageMetadataRegistry.kt b/src/main/kotlin/io/github/sgtsilvio/gradle/oci/internal/registry/OciImageMetadataRegistry.kt index 3b94fea7..0e747cc6 100644 --- a/src/main/kotlin/io/github/sgtsilvio/gradle/oci/internal/registry/OciImageMetadataRegistry.kt +++ b/src/main/kotlin/io/github/sgtsilvio/gradle/oci/internal/registry/OciImageMetadataRegistry.kt @@ -13,7 +13,11 @@ import java.util.* */ internal class OciImageMetadataRegistry(val registryApi: OciRegistryApi) { - class OciImageMetadata(val platformToMetadata: Map, val digest: OciDigest, val size: Int) + class OciImageMetadata( + val platformToVariantMetadata: Map, + val digest: OciDigest, + val size: Int, + ) fun pullImageMetadata( registry: String, @@ -98,7 +102,7 @@ internal class OciImageMetadataRegistry(val registryApi: OciRegistryApi) { ): Mono { val indexJsonObject = jsonObject(String(index.bytes)) val indexAnnotations = indexJsonObject.getStringMapOrEmpty("annotations") - val metadataMonoList = indexJsonObject.get("manifests") { + val platformVariantMetadataMonoList = indexJsonObject.get("manifests") { asArray().toList { val (manifestDescriptorPlatform, manifestDescriptor) = asObject().decodeOciManifestDescriptor() if (manifestDescriptor.mediaType != manifestMediaType) { // TODO support nested index @@ -114,7 +118,7 @@ internal class OciImageMetadataRegistry(val registryApi: OciRegistryApi) { if (manifest.mediaType != manifestMediaType) { throw IllegalStateException("media type in manifest descriptor ($manifestMediaType) and manifest (${manifest.mediaType}) do not match") } - transformManifestToMetadata( + transformManifestToPlatformVariantMetadata( registry, imageReference, manifest, @@ -135,10 +139,10 @@ internal class OciImageMetadataRegistry(val registryApi: OciRegistryApi) { indexJsonObject.requireStringOrNull("mediaType", index.mediaType) indexJsonObject.requireLong("schemaVersion", 2) // the same order as in the manifest is guaranteed by mergeSequential - return Flux.mergeSequential(metadataMonoList) + return Flux.mergeSequential(platformVariantMetadataMonoList) // linked to preserve the platform order - .collect({ LinkedHashMap() }) { map, (platform, metadata) -> - if (map.putIfAbsent(platform, metadata) != null) { + .collect({ LinkedHashMap() }) { map, (platform, variantMetadata) -> + if (map.putIfAbsent(platform, variantMetadata) != null) { throw IllegalStateException("duplicate platform in image index: $platform") } } @@ -152,7 +156,7 @@ internal class OciImageMetadataRegistry(val registryApi: OciRegistryApi) { credentials: Credentials?, configMediaType: String, layerMediaTypePrefix: String, - ): Mono = transformManifestToMetadata( + ): Mono = transformManifestToPlatformVariantMetadata( registry, imageReference, manifest, @@ -163,7 +167,7 @@ internal class OciImageMetadataRegistry(val registryApi: OciRegistryApi) { layerMediaTypePrefix, ).map { OciImageMetadata(mapOf(it), manifest.digest, manifest.bytes.size) } - private fun transformManifestToMetadata( + private fun transformManifestToPlatformVariantMetadata( registry: String, imageReference: OciImageReference, manifest: OciData, @@ -172,7 +176,7 @@ internal class OciImageMetadataRegistry(val registryApi: OciRegistryApi) { credentials: Credentials?, configMediaType: String, layerMediaTypePrefix: String, - ): Mono> { + ): Mono> { val manifestJsonObject = jsonObject(String(manifest.bytes)) val manifestAnnotations = manifestJsonObject.getStringMapOrEmpty("annotations") val configDescriptor = manifestJsonObject.get("config") { asObject().decodeOciDescriptor() } @@ -269,7 +273,7 @@ internal class OciImageMetadataRegistry(val registryApi: OciRegistryApi) { } Pair( Platform(os, architecture, variant, osVersion, osFeatures), - OciMetadata( + OciVariantMetadata( imageReference, creationTime, author, diff --git a/src/main/kotlin/io/github/sgtsilvio/gradle/oci/internal/registry/OciRepositoryHandler.kt b/src/main/kotlin/io/github/sgtsilvio/gradle/oci/internal/registry/OciRepositoryHandler.kt index f3318960..519fb87d 100644 --- a/src/main/kotlin/io/github/sgtsilvio/gradle/oci/internal/registry/OciRepositoryHandler.kt +++ b/src/main/kotlin/io/github/sgtsilvio/gradle/oci/internal/registry/OciRepositoryHandler.kt @@ -98,13 +98,13 @@ internal class OciRepositoryHandler( } val last = segments.last() return when { - last.endsWith(".json") -> getOrHeadMetadata(registryUri, segments, isGet, response) + last.endsWith(".json") -> getOrHeadVariantMetadata(registryUri, segments, isGet, response) last.endsWith("oci-layer") -> getOrHeadLayer(registryUri, segments, isGet, response) else -> response.sendNotFound() } } - private fun getOrHeadMetadata( + private fun getOrHeadVariantMetadata( registryUri: URI, segments: List, isGet: Boolean, @@ -133,17 +133,17 @@ internal class OciRepositoryHandler( } catch (e: IllegalArgumentException) { return response.sendBadRequest() } - val metadataJsonMono = + val variantMetadataJsonMono = getImageMetadata(registryUri, imageReference, digest, size, credentials).handle { imageMetadata, sink -> - val metadata = imageMetadata.platformToMetadata[platform] - if (metadata == null) { + val variantMetadata = imageMetadata.platformToVariantMetadata[platform] + if (variantMetadata == null) { response.status(400) } else { - sink.next(metadata.encodeToJsonString().toByteArray()) + sink.next(variantMetadata.encodeToJsonString().toByteArray()) } } response.header(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_JSON) - return response.sendByteArray(metadataJsonMono, isGet) + return response.sendByteArray(variantMetadataJsonMono, isGet) } private fun getOrHeadLayer( @@ -189,7 +189,7 @@ internal class OciRepositoryHandler( data class OciImageVariantsMetadata( val imageDefName: String, val capabilities: Set, - val platformToMetadata: Map, + val platformToVariantMetadata: Map, val digest: OciDigest, val size: Int, ) @@ -199,7 +199,7 @@ internal class OciRepositoryHandler( OciImageVariantsMetadata( imageDefName, variant.capabilities, - imageMetadata.platformToMetadata, + imageMetadata.platformToVariantMetadata, imageMetadata.digest, imageMetadata.size, ) @@ -218,18 +218,18 @@ internal class OciRepositoryHandler( } val fileNamePrefix = "${componentId.name}-${componentId.version}-" addArray("variants") { - for ((imageDefName, capabilities, platformToMetadata, digest, size) in imageVariantsMetadataList) { + for ((imageDefName, capabilities, platformToVariantMetadata, digest, size) in imageVariantsMetadataList) { addObject { addString("name", createOciVariantName(imageDefName)) addOciVariantAttributes(MULTIPLE_PLATFORMS_ATTRIBUTE_VALUE) addCapabilities("capabilities", capabilities, componentId) addArray("dependencies") { - for (platform in platformToMetadata.keys) { + for (platform in platformToVariantMetadata.keys) { addDependency(componentId, capabilities, platform) } } } - for ((platform, metadata) in platformToMetadata) { + for ((platform, variantMetadata) in platformToVariantMetadata) { addObject { addString("name", createOciVariantName(imageDefName, platform)) addOciVariantAttributes(platform.toString()) @@ -244,19 +244,19 @@ internal class OciRepositoryHandler( addCapabilities("capabilities", capabilities, platform) addArray("files") { addObject { - val metadataJson = metadata.encodeToJsonString().toByteArray() - val metadataName = fileNamePrefix + createOciMetadataClassifier(imageDefName) + createPlatformPostfix(platform) + ".json" - val escapedImageReference = metadata.imageReference.toString().escapePathSegment() - addString("name", metadataName) - addString("url", "$escapedImageReference/$digest/$size/$platform/$metadataName") - addNumber("size", metadataJson.size.toLong()) - addString("sha512", DigestUtils.sha512Hex(metadataJson)) - addString("sha256", DigestUtils.sha256Hex(metadataJson)) - addString("sha1", DigestUtils.sha1Hex(metadataJson)) - addString("md5", DigestUtils.md5Hex(metadataJson)) + val variantMetadataJson = variantMetadata.encodeToJsonString().toByteArray() + val variantMetadataName = fileNamePrefix + createOciVariantMetadataClassifier(imageDefName) + createPlatformPostfix(platform) + ".json" + val escapedImageReference = variantMetadata.imageReference.toString().escapePathSegment() + addString("name", variantMetadataName) + addString("url", "$escapedImageReference/$digest/$size/$platform/$variantMetadataName") + addNumber("size", variantMetadataJson.size.toLong()) + addString("sha512", DigestUtils.sha512Hex(variantMetadataJson)) + addString("sha256", DigestUtils.sha256Hex(variantMetadataJson)) + addString("sha1", DigestUtils.sha1Hex(variantMetadataJson)) + addString("md5", DigestUtils.md5Hex(variantMetadataJson)) } - val escapedImageName = metadata.imageReference.name.escapePathSegment() - for (layerMetadata in metadata.layers) { + val escapedImageName = variantMetadata.imageReference.name.escapePathSegment() + for (layerMetadata in variantMetadata.layers) { layerMetadata.descriptor?.let { layerDescriptor -> addObject { val layerDigest = layerDescriptor.digest diff --git a/src/main/kotlin/io/github/sgtsilvio/gradle/oci/metadata/OciMetadataJsonCodec.kt b/src/main/kotlin/io/github/sgtsilvio/gradle/oci/metadata/OciMetadataJsonCodec.kt index 9d11e5a4..9470d4cd 100644 --- a/src/main/kotlin/io/github/sgtsilvio/gradle/oci/metadata/OciMetadataJsonCodec.kt +++ b/src/main/kotlin/io/github/sgtsilvio/gradle/oci/metadata/OciMetadataJsonCodec.kt @@ -2,9 +2,9 @@ package io.github.sgtsilvio.gradle.oci.metadata import io.github.sgtsilvio.gradle.oci.internal.json.* -internal fun OciMetadata.encodeToJsonString() = jsonObject { encodeOciMetadata(this@encodeToJsonString) } +internal fun OciVariantMetadata.encodeToJsonString() = jsonObject { encodeOciVariantMetadata(this@encodeToJsonString) } -private fun JsonObjectStringBuilder.encodeOciMetadata(metadata: OciMetadata) { +private fun JsonObjectStringBuilder.encodeOciVariantMetadata(metadata: OciVariantMetadata) { addString("imageReference", metadata.imageReference.toString()) addStringIfNotNull("creationTime", metadata.creationTime?.toString()) addStringIfNotNull("author", metadata.author) @@ -44,9 +44,9 @@ private fun JsonObjectStringBuilder.encodeOciLayerDescriptor(descriptor: OciLaye addObjectIfNotEmpty("annotations", descriptor.annotations) } -internal fun String.decodeAsJsonToOciMetadata() = jsonObject(this).decodeOciMetadata() +internal fun String.decodeAsJsonToOciVariantMetadata() = jsonObject(this).decodeOciVariantMetadata() -private fun JsonObject.decodeOciMetadata() = OciMetadata( +private fun JsonObject.decodeOciVariantMetadata() = OciVariantMetadata( get("imageReference") { asString().toOciImageReference() }, getInstantOrNull("creationTime"), getStringOrNull("author"), diff --git a/src/main/kotlin/io/github/sgtsilvio/gradle/oci/metadata/OciMetadata.kt b/src/main/kotlin/io/github/sgtsilvio/gradle/oci/metadata/OciVariantMetadata.kt similarity index 98% rename from src/main/kotlin/io/github/sgtsilvio/gradle/oci/metadata/OciMetadata.kt rename to src/main/kotlin/io/github/sgtsilvio/gradle/oci/metadata/OciVariantMetadata.kt index f79ebc8b..335efb42 100644 --- a/src/main/kotlin/io/github/sgtsilvio/gradle/oci/metadata/OciMetadata.kt +++ b/src/main/kotlin/io/github/sgtsilvio/gradle/oci/metadata/OciVariantMetadata.kt @@ -6,7 +6,7 @@ import java.util.* /** * @author Silvio Giebl */ -class OciMetadata( +class OciVariantMetadata( val imageReference: OciImageReference, val creationTime: Instant?, val author: String?, diff --git a/src/main/kotlin/io/github/sgtsilvio/gradle/oci/metadata/OciMetadataBuilder.kt b/src/main/kotlin/io/github/sgtsilvio/gradle/oci/metadata/OciVariantMetadataBuilder.kt similarity index 98% rename from src/main/kotlin/io/github/sgtsilvio/gradle/oci/metadata/OciMetadataBuilder.kt rename to src/main/kotlin/io/github/sgtsilvio/gradle/oci/metadata/OciVariantMetadataBuilder.kt index eeadce07..6f2990d4 100644 --- a/src/main/kotlin/io/github/sgtsilvio/gradle/oci/metadata/OciMetadataBuilder.kt +++ b/src/main/kotlin/io/github/sgtsilvio/gradle/oci/metadata/OciVariantMetadataBuilder.kt @@ -5,7 +5,7 @@ import java.time.Instant /** * @author Silvio Giebl */ -internal class OciMetadataBuilder { +internal class OciVariantMetadataBuilder { private var imageReference: OciImageReference? = null private var creationTime: SerializableInstant? = null private var author: String? = null @@ -42,7 +42,7 @@ internal class OciMetadataBuilder { fun indexAnnotations(v: Map) = apply { indexAnnotations = v } fun layers(v: List) = apply { layers = v } - fun build() = OciMetadata( + fun build() = OciVariantMetadata( imageReference!!, creationTime?.toInstant(), author,