com.netflix.spinnaker.keel.igor.artifact.ArtifactMetadataService.kt Maven / Gradle / Ivy
package com.netflix.spinnaker.keel.igor.artifact
import com.netflix.spinnaker.keel.igor.BuildService
import com.netflix.spinnaker.keel.api.artifacts.ArtifactMetadata
import com.netflix.spinnaker.keel.api.artifacts.BuildMetadata
import com.netflix.spinnaker.keel.api.artifacts.Commit
import com.netflix.spinnaker.keel.api.artifacts.GitMetadata
import com.netflix.spinnaker.keel.api.artifacts.Job
import com.netflix.spinnaker.keel.api.artifacts.PullRequest
import com.netflix.spinnaker.keel.api.artifacts.Repo
import com.netflix.spinnaker.keel.igor.model.Build
import com.netflix.spinnaker.keel.igor.model.CompletionStatus
import io.github.resilience4j.kotlin.retry.executeSuspendFunction
import io.github.resilience4j.retry.Retry
import io.github.resilience4j.retry.RetryConfig
import kotlinx.coroutines.TimeoutCancellationException
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Component
import retrofit2.HttpException
import java.time.Duration
/**
* Provides functionality to convert build metadata, which is coming from internal service, to artifact metadata (via igor).
*/
@Component
class ArtifactMetadataService(
private val buildService: BuildService
) {
companion object {
private const val DEFAULT_MAX_ATTEMPTS = 20
}
/**
* Returns additional metadata about the specified build and commit, if available. This call is configured
* to auto-retry as it's not on a code path where any external retries would happen.
*/
suspend fun getArtifactMetadata(
buildNumber: String,
commitId: String,
maxAttempts: Int = DEFAULT_MAX_ATTEMPTS
): ArtifactMetadata? {
val buildList = getArtifactMetadataWithRetries(commitId, buildNumber)
if (buildList.isNullOrEmpty()) {
log.debug("artifact metadata buildList is null or empty, for build $buildNumber and commit $commitId")
return null
}
log.debug("received artifact metadata $buildList for build $buildNumber and commit $commitId")
return buildList.first().toArtifactMetadata(commitId)
}
private fun Build.toArtifactMetadata(commitId: String) =
ArtifactMetadata(
BuildMetadata(
id = number,
uid = id,
job = Job(
link = url,
name = name
),
startedAt = properties?.get("startedAt") as String?,
completedAt = properties?.get("completedAt") as String?,
number = number.toString(),
status = result.toString()
),
GitMetadata(
commit = let {
if (commitId.length > 7)
commitId.substring(0, 7)
else {
commitId
}
},
commitInfo = Commit(
sha = scm?.first()?.sha1,
link = scm?.first()?.compareUrl,
message = scm?.first()?.message,
),
author = scm?.first()?.committer,
pullRequest = PullRequest(
number = properties?.get("pullRequestNumber") as String?,
url = properties?.get("pullRequestUrl") as String?
),
repo = Repo(
name = properties?.get("repoSlug") as String?,
//TODO[gyardeni]: add link (will come from Igor)
link = ""
),
branch = scm?.first()?.branch,
project = properties?.get("projectKey") as String?,
)
)
//This will not be used for now, until we will figure out what is wrong!
private suspend fun getArtifactMetadataWithRetries(
commitId: String,
buildNumber: String,
maxAttempts: Int = DEFAULT_MAX_ATTEMPTS
): List? {
val retry = Retry.of(
"get artifact metadata",
RetryConfig.custom?>()
// retry 20 times over a total of 90 seconds
.maxAttempts(maxAttempts)
.waitDuration(Duration.ofMillis(4500))
.retryOnResult{ result ->
if (result.isNullOrEmpty()) {
log.debug("Retrying artifact metadata retrieval due to empty response (commit=$commitId, build=$buildNumber)")
}
result.isNullOrEmpty()
}
.retryOnException { t: Throwable ->
// https://github.com/resilience4j/resilience4j/issues/688
val retryFilter = when (t) {
is TimeoutCancellationException -> true
else -> t is HttpException
}
log.debug(if (retryFilter) "Retrying " else "Not retrying " +
"artifact metadata retrieval (commit=$commitId, build=$buildNumber) on exception: ${t::class.java.name}")
retryFilter
}
.build()
)
return retry.executeSuspendFunction {
buildService.getArtifactMetadata(commitId = commitId.trim(), buildNumber = buildNumber.trim(), completionStatus = CompletionStatus.values().joinToString { it.name })
}
}
private val log by lazy { LoggerFactory.getLogger(javaClass) }
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy