Skip to content

Commit

Permalink
made errors more expressive
Browse files Browse the repository at this point in the history
  • Loading branch information
cmdjulian committed Sep 15, 2023
1 parent 87e95b9 commit 9ea8ffb
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 30 deletions.
Original file line number Diff line number Diff line change
@@ -1,43 +1,86 @@
package de.cmdjulian.kirc.exception

sealed class RegistryClientException(override val message: String, throwable: Throwable? = null) :
RuntimeException(message, throwable) {
import de.cmdjulian.kirc.image.Reference
import de.cmdjulian.kirc.image.Repository
import java.net.URL

sealed class ClientException(message: String, val error: ErrorResponse?, cause: Throwable) :
RegistryClientException(message, cause) {
sealed class RegistryClientException(
val url: URL,
val repository: Repository?,
val reference: Reference?,
override val message: String,
throwable: Throwable? = null,
) : RuntimeException(message, throwable) {

class AuthenticationException(error: ErrorResponse?, cause: Throwable) :
ClientException("authentication required", error, cause) {
sealed class ClientException(
url: URL,
repository: Repository?,
reference: Reference?,
message: String,
val error: ErrorResponse?,
cause: Throwable,
) : RegistryClientException(url, repository, reference, message, cause) {

class AuthenticationException(
url: URL,
repository: Repository?,
reference: Reference?,
error: ErrorResponse?,
cause: Throwable,
) : ClientException(url, repository, reference, "authentication required", error, cause) {
override fun toString(): String = "DistributionClientException.AuthenticationError -> $message"
}

class AuthorizationException(error: ErrorResponse?, cause: Throwable) :
ClientException("authorization required", error, cause) {
class AuthorizationException(
url: URL,
repository: Repository?,
reference: Reference?,
error: ErrorResponse?,
cause: Throwable,
) : ClientException(url, repository, reference, "authorization required", error, cause) {
override fun toString(): String = "DistributionClientException.AuthorizationError -> $message"
}

class NotFoundException(error: ErrorResponse?, cause: Throwable) : ClientException("not found", error, cause) {
class NotFoundException(
url: URL,
repository: Repository?,
reference: Reference?,
error: ErrorResponse?,
cause: Throwable,
) : ClientException(url, repository, reference, "not found", error, cause) {
override fun toString(): String = "DistributionClientException.NotFoundException -> $message"
}

class MethodNotAllowed(
url: URL,
repository: Repository?,
reference: Reference?,
error: ErrorResponse?,
cause: Throwable,
) : ClientException("method not allowed", error, cause) {
) : ClientException(url, repository, reference, "method not allowed", error, cause) {
override fun toString() = "ClientException.MethodNotAllowed (is registry delete enabled?) -> $message"
}

class UnexpectedErrorException(error: ErrorResponse?, cause: Throwable) :
ClientException("unknown error returned by server", error, cause) {
class UnexpectedErrorException(
url: URL,
repository: Repository?,
reference: Reference?,
error: ErrorResponse?,
cause: Throwable,
) : ClientException(url, repository, reference, "unknown error returned by server", error, cause) {
override fun toString(): String = "DistributionClientException.UnexpectedErrorException -> $message"
}
}

class NetworkErrorException(t: Throwable) : RegistryClientException("Network error on registry connect", t) {
class NetworkErrorException(url: URL, repository: Repository?, reference: Reference?, t: Throwable) :
RegistryClientException(url, repository, reference, "Network error on registry connect", t) {

override fun toString(): String = "DistributionClientException.NetworkError -> $message"
}

class UnknownErrorException(t: Throwable) : RegistryClientException("An unknown error occurred", t) {
class UnknownErrorException(url: URL, repository: Repository?, reference: Reference?, t: Throwable) :
RegistryClientException(url, repository, reference, "An unknown error occurred", t) {

override fun toString(): String = "DistributionClientException.UnknownError -> $message"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,33 +47,35 @@ internal class SuspendingContainerImageRegistryClientImpl(private val api: Conta
override suspend fun tags(repository: Repository, limit: Int?, last: Int?): List<Tag> =
api.tags(repository, limit, last)
.map(TagList::tags)
.getOrElse { throw it.toRegistryClientError() }
.getOrElse { throw it.toRegistryClientError(repository, null) }

override suspend fun exists(repository: Repository, reference: Reference): Boolean =
api.digest(repository, reference)
.map { true }
.getOrElse { if (it.response.statusCode == 404) false else throw it.toRegistryClientError() }
.getOrElse {
if (it.response.statusCode == 404) false else throw it.toRegistryClientError(repository, reference)
}

override suspend fun manifest(repository: Repository, reference: Reference): Manifest =
api.manifests(repository, reference)
.getOrElse { throw it.toRegistryClientError() }
.getOrElse { throw it.toRegistryClientError(repository, reference) }

override suspend fun manifestDelete(repository: Repository, reference: Reference): Digest =
api.deleteManifest(repository, reference)
.getOrElse { throw it.toRegistryClientError() }
.getOrElse { throw it.toRegistryClientError(repository, reference) }

override suspend fun manifestDigest(repository: Repository, reference: Reference): Digest =
api.digest(repository, reference)
.getOrElse { throw it.toRegistryClientError() }
.getOrElse { throw it.toRegistryClientError(repository, reference) }

override suspend fun blob(repository: Repository, digest: Digest): ByteArray =
api.blob(repository, digest)
.getOrElse { throw it.toRegistryClientError() }
.getOrElse { throw it.toRegistryClientError(repository) }

override suspend fun config(repository: Repository, reference: Reference): ImageConfig =
api.manifest(repository, reference)
.map { config(repository, it) }
.getOrElse { throw it.toRegistryClientError() }
.getOrElse { throw it.toRegistryClientError(repository, reference) }

override suspend fun config(repository: Repository, manifest: ManifestSingle): ImageConfig =
api.blob(repository, manifest.config.digest)
Expand All @@ -83,7 +85,7 @@ internal class SuspendingContainerImageRegistryClientImpl(private val api: Conta
is OciManifestV1 -> jacksonDeserializer<OciImageConfigV1>().deserialize(config)
}
}
.getOrElse { throw it.toRegistryClientError() }
.getOrElse { throw it.toRegistryClientError(repository) }

override suspend fun toImageClient(repository: Repository, reference: Reference): SuspendingContainerImageClient =
when (reference) {
Expand Down Expand Up @@ -114,14 +116,27 @@ internal class SuspendingContainerImageRegistryClientImpl(private val api: Conta
}
}

private fun FuelError.toRegistryClientError(): RegistryClientException = when (response.statusCode) {
-1 -> NetworkErrorException(this)
401 -> AuthenticationException(tryOrNull { JsonMapper.readValue(response.data) }, this)
403 -> AuthorizationException(tryOrNull { JsonMapper.readValue(response.data) }, this)
404 -> NotFoundException(tryOrNull { JsonMapper.readValue(response.data) }, this)
405 -> MethodNotAllowed(tryOrNull { JsonMapper.readValue(response.data) }, this)
in 406..499 -> UnexpectedErrorException(tryOrNull { JsonMapper.readValue(response.data) }, this)
else -> UnknownErrorException(this)
private fun FuelError.toRegistryClientError(
repository: Repository? = null,
reference: Reference? = null,
): RegistryClientException {
val url = this.response.url
val data = response.data
return when (response.statusCode) {
-1 -> NetworkErrorException(url, repository, reference, this)

401 -> AuthenticationException(url, repository, reference, tryOrNull { JsonMapper.readValue(data) }, this)

403 -> AuthorizationException(url, repository, reference, tryOrNull { JsonMapper.readValue(data) }, this)

404 -> NotFoundException(url, repository, reference, tryOrNull { JsonMapper.readValue(data) }, this)

405 -> MethodNotAllowed(url, repository, reference, tryOrNull { JsonMapper.readValue(data) }, this)
in 406..499 ->
UnexpectedErrorException(url, repository, reference, tryOrNull { JsonMapper.readValue(data) }, this)

else -> UnknownErrorException(url, repository, reference, this)
}
}

private inline fun <T> tryOrNull(block: () -> T): T? = runCatching(block).getOrNull()

0 comments on commit 9ea8ffb

Please sign in to comment.