More informative errors from RegistryClient (#134)

Also creates an `ErrorResponse` type to model the errors typically
returned by a container registry.

Reference: https://distribution.github.io/distribution/spec/api/#errors

Example error message:
```
Error: HTTP request to https://ghcr.io/token?client_id=containerization-registry-client&service=ghcr.io&scope=repository:user/image:pull failed with response: 403 Forbidden. Reason: {"errors":[{"message":"requested access to the resource is denied","code":"DENIED"}]}
```

Signed-off-by: Aditya Ramani <a_ramani@apple.com>
This commit is contained in:
Aditya Ramani
2025-06-16 11:07:32 -07:00
committed by GitHub
parent 77c54434b8
commit f4177e7d67
4 changed files with 54 additions and 10 deletions
@@ -235,7 +235,8 @@ public final class RegistryClient: ContentClient {
try await request(components: components, method: .GET, headers: headers) { response in
guard response.status == .ok else {
let url = components.url?.absoluteString ?? "unknown"
throw Error.invalidStatus(url: url, response.status)
let reason = await ErrorResponse.fromResponseBody(response.body)?.jsonString
throw Error.invalidStatus(url: url, response.status, reason: reason)
}
var body = try await response.body.collect(upTo: self.bufferSize)
@@ -263,7 +264,8 @@ public final class RegistryClient: ContentClient {
try await request(components: components) { response in
guard response.status == .ok else {
let url = components.url?.absoluteString ?? "unknown"
throw Error.invalidStatus(url: url, response.status)
let reason = await ErrorResponse.fromResponseBody(response.body)?.jsonString
throw Error.invalidStatus(url: url, response.status, reason: reason)
}
}
}