diff --git a/tool/automation/framework/app/src/main/kotlin/com/jetbrains/teamcity/Automation.kt b/tool/automation/framework/app/src/main/kotlin/com/jetbrains/teamcity/Automation.kt index 21b79cb11..9258f38e5 100644 --- a/tool/automation/framework/app/src/main/kotlin/com/jetbrains/teamcity/Automation.kt +++ b/tool/automation/framework/app/src/main/kotlin/com/jetbrains/teamcity/Automation.kt @@ -25,29 +25,23 @@ class ValidateImage : Subcommand("validate", "Validate Docker Image with (option * Execute image validation option specified via CLI. */ override fun execute() { - - // 1. Capture current image size - val originalImageName = validationArgs[0] - + val image = validationArgs[0] val username = if (validationArgs.size > 1) validationArgs[1] else null val token = if (validationArgs.size > 2) validationArgs[2] else null - val credentials: DockerhubCredentials = getDockerhubCredentials(username, token) - - - val percentageChangeThreshold = ValidationConstants.ALLOWED_IMAGE_SIZE_INCREASE_THRESHOLD_PERCENT val imagesFailedValidation = DockerImageValidationUtilities.validateImageSize( - originalImageName, - "https://hub.docker.com/v2", - percentageChangeThreshold, - credentials + originalImageFqdn = image, + registryUri = "https://hub.docker.com/v2", + threshold = ValidationConstants.ALLOWED_IMAGE_SIZE_INCREASE_THRESHOLD_PERCENT, + credentials = getDockerhubCredentials(username, token), + ignoreStaging = true ) if (imagesFailedValidation.isNotEmpty()) { imagesFailedValidation.forEach { - println("Validation failed for ${originalImageName}, OS: ${it.os}, OS version: ${it.osVersion}, architecture: ${it.architecture}") + println("Validation failed for ${image}, OS: ${it.os}, OS version: ${it.osVersion}, architecture: ${it.architecture}") } // throw exception in order to handle it within upstream DSL - throw DockerImageValidationException("Validation had failed for $originalImageName") + throw DockerImageValidationException("Validation had failed for $image") } } } diff --git a/tool/automation/framework/app/src/main/kotlin/com/jetbrains/teamcity/common/constants/ValidationConstants.kt b/tool/automation/framework/app/src/main/kotlin/com/jetbrains/teamcity/common/constants/ValidationConstants.kt index a58c5cdfa..49867fb57 100644 --- a/tool/automation/framework/app/src/main/kotlin/com/jetbrains/teamcity/common/constants/ValidationConstants.kt +++ b/tool/automation/framework/app/src/main/kotlin/com/jetbrains/teamcity/common/constants/ValidationConstants.kt @@ -7,4 +7,5 @@ object ValidationConstants { const val ALLOWED_IMAGE_SIZE_INCREASE_THRESHOLD_PERCENT = 5.0f const val PRE_PRODUCTION_IMAGE_PREFIX = "EAP" const val LATEST = "latest" + const val STAGING_POSTFIX = "-staging" } diff --git a/tool/automation/framework/app/src/main/kotlin/com/jetbrains/teamcity/common/network/HttpRequestsUtilities.kt b/tool/automation/framework/app/src/main/kotlin/com/jetbrains/teamcity/common/network/HttpRequestsUtilities.kt index 037120f6b..4d0db33ae 100644 --- a/tool/automation/framework/app/src/main/kotlin/com/jetbrains/teamcity/common/network/HttpRequestsUtilities.kt +++ b/tool/automation/framework/app/src/main/kotlin/com/jetbrains/teamcity/common/network/HttpRequestsUtilities.kt @@ -1,7 +1,5 @@ package com.jetbrains.teamcity.common.network -import kotlinx.serialization.json.JsonObject -import kotlinx.serialization.json.JsonPrimitive import java.io.IOException import java.net.ConnectException import java.net.URI diff --git a/tool/automation/framework/app/src/main/kotlin/com/jetbrains/teamcity/docker/hub/DockerRegistryAccessor.kt b/tool/automation/framework/app/src/main/kotlin/com/jetbrains/teamcity/docker/hub/DockerRegistryAccessor.kt index 69f40c02c..53dcb3da9 100644 --- a/tool/automation/framework/app/src/main/kotlin/com/jetbrains/teamcity/docker/hub/DockerRegistryAccessor.kt +++ b/tool/automation/framework/app/src/main/kotlin/com/jetbrains/teamcity/docker/hub/DockerRegistryAccessor.kt @@ -18,22 +18,26 @@ import java.net.http.HttpResponse * @param uri - Docker Registry URI * @param credentials - (optional) - credentials for the access of Dockerhub REST API */ -class DockerRegistryAccessor(private val uri: String, credentials: DockerhubCredentials?) { - +class DockerRegistryAccessor(private val uri: String, + private val ignoreStaging: Boolean, + credentials: DockerhubCredentials?) { private val httpRequestsUtilities: HttpRequestsUtilities = HttpRequestsUtilities() private val token: String? - private val jsonSerializer: Json + private val jsonSerializer: Json = Json { + // -- remove the necessity to include parsing of unused fields + ignoreUnknownKeys = true + // -- parse JSON fields that don't have an assigned serializer into a String, e.g.: Number + isLenient = true + } + init { - this.jsonSerializer = Json { - // -- remove the necessity to include parsing of unused fields - ignoreUnknownKeys = true - // -- parse JSON fields that don't have an assigned serializer into a String, e.g.: Number - isLenient = true - } this.token = if (credentials != null && credentials.isUsable()) this.getPersonalAccessToken(credentials) else "" } + constructor(uri: String, credentials: DockerhubCredentials?) : this(uri, false, credentials) + + /** * Returns general information about Docker Repository. * @param image image for which the "repository" (all associated images: OS, OS Version, arch.) would be ... @@ -59,9 +63,10 @@ class DockerRegistryAccessor(private val uri: String, credentials: DockerhubCred * @param pageSize maximal amount of images to be included into Dockerhub's response */ fun getInfoAboutImagesInRegistry(image: DockerImage, pageSize: Int): DockerRegistryInfoAboutImages? { + val repo = if (ignoreStaging) image.repo.replace(ValidationConstants.STAGING_POSTFIX, "") else image.repo val registryResponse: HttpResponse = httpRequestsUtilities.getJsonWithAuth( "${this.uri}/repositories" - + "/${image.repo}/tags?page_size=$pageSize", this.token + + "/${repo}/tags?page_size=$pageSize", this.token ) val result = registryResponse.body() ?: "" @@ -105,8 +110,7 @@ class DockerRegistryAccessor(private val uri: String, credentials: DockerhubCred .filter { return@filter isSameDistribution(currentImage.tag, it.name) } // Sort based on tag .sortedWith { lhs, rhs -> imageTagComparator(lhs.name, rhs.name) } - .last() - + .lastOrNull() ?: throw RuntimeException("Previous images weren't found for $currentImage") // -- 1. Filter by OS type previousImageRepository.images = previousImageRepository.images.filter { it.os == targetOs } diff --git a/tool/automation/framework/app/src/main/kotlin/com/jetbrains/teamcity/docker/validation/DockerImageValidationUtilities.kt b/tool/automation/framework/app/src/main/kotlin/com/jetbrains/teamcity/docker/validation/DockerImageValidationUtilities.kt index 536ec529b..4555e859d 100644 --- a/tool/automation/framework/app/src/main/kotlin/com/jetbrains/teamcity/docker/validation/DockerImageValidationUtilities.kt +++ b/tool/automation/framework/app/src/main/kotlin/com/jetbrains/teamcity/docker/validation/DockerImageValidationUtilities.kt @@ -44,15 +44,17 @@ class DockerImageValidationUtilities { * 4. Compare the size of each corresponding image. * @param originalImageFqdn fully-qualified domain name of the original image * @param registryUri URI of Docker Registry where image is placed + * @param ignoreStaging if true, staging images would be compared to production ones * @returns list of associated images that didn't pass the validation. */ fun validateImageSize( originalImageFqdn: String, registryUri: String, threshold: Float, - credentials: DockerhubCredentials? + credentials: DockerhubCredentials?, + ignoreStaging: Boolean ): ArrayList { - val registryAccessor = DockerRegistryAccessor(registryUri, credentials) + val registryAccessor = DockerRegistryAccessor(registryUri, ignoreStaging, credentials) val currentImage = DockerImage(originalImageFqdn) val imagesFailedValidation = ArrayList()