diff --git a/docker/docker_client.go b/docker/docker_client.go index 023c203ee3..94cbcb1d99 100644 --- a/docker/docker_client.go +++ b/docker/docker_client.go @@ -1097,6 +1097,11 @@ func isManifestUnknownError(err error) bool { if errors.As(err, &e) && e.ErrorCode() == errcode.ErrorCodeUnknown && e.Message == "Not Found" { return true } + // Harbor v2.10.2 + if errors.As(err, &e) && e.ErrorCode() == errcode.ErrorCodeUnknown && strings.Contains(strings.ToLower(e.Message), "not found") { + return true + } + // opencontainers/distribution-spec does not require the errcode.Error payloads to be used, // but specifies that the HTTP status must be 404. var unexpected *unexpectedHTTPResponseError diff --git a/docker/docker_client_test.go b/docker/docker_client_test.go index 4c57fe02dc..a7d16ef7b5 100644 --- a/docker/docker_client_test.go +++ b/docker/docker_client_test.go @@ -392,6 +392,19 @@ func TestIsManifestUnknownError(t *testing.T) { "\r\n" + "Not found\r\n", }, + { + name: "Harbor v2.10.2", + response: "HTTP/1.1 404 Not Found\r\n" + + "Content-Length: 153\r\n" + + "Connection: keep-alive\r\n" + + "Content-Type: application/json; charset=utf-8\r\n" + + "Date: Wed, 08 May 2024 08:14:59 GMT\r\n" + + "Server: nginx\r\n" + + "Set-Cookie: sid=f617c257877837614ada2561513d6827; Path=/; HttpOnly\r\n" + + "X-Request-Id: 1b151fb1-c943-4190-a9ce-5156ed5e3200\r\n" + + "\r\n" + + "{\"errors\":[{\"code\":\"NOT_FOUND\",\"message\":\"artifact test/alpine:sha256-443205b0cfcc78444321d56a2fe273f06e27b2c72b5058f8d7e975997d45b015.sig not found\"}]}\n", + }, } { resp, err := http.ReadResponse(bufio.NewReader(bytes.NewReader([]byte(c.response))), nil) require.NoError(t, err, c.name) @@ -399,6 +412,6 @@ func TestIsManifestUnknownError(t *testing.T) { err = fmt.Errorf("wrapped: %w", registryHTTPResponseToError(resp)) res := isManifestUnknownError(err) - assert.True(t, res, "%#v", err, c.name) + assert.True(t, res, "%s: %#v", c.name, err) } } diff --git a/docker/errors_test.go b/docker/errors_test.go index 2463d275f4..38bc568b2f 100644 --- a/docker/errors_test.go +++ b/docker/errors_test.go @@ -173,6 +173,34 @@ func TestRegistryHTTPResponseToError(t *testing.T) { assert.Equal(t, []byte("Not found\r"), e.Response) }, }, + { // Harbor v2.10.2 uses an unspecified NOT_FOUND error code + name: "Harbor v2.10.2 manifest not found", + response: "HTTP/1.1 404 Not Found\r\n" + + "Content-Length: 153\r\n" + + "Connection: keep-alive\r\n" + + "Content-Type: application/json; charset=utf-8\r\n" + + "Date: Wed, 08 May 2024 08:14:59 GMT\r\n" + + "Server: nginx\r\n" + + "Set-Cookie: sid=f617c257877837614ada2561513d6827; Path=/; HttpOnly\r\n" + + "X-Request-Id: 1b151fb1-c943-4190-a9ce-5156ed5e3200\r\n" + + "\r\n" + + "{\"errors\":[{\"code\":\"NOT_FOUND\",\"message\":\"artifact test/alpine:sha256-443205b0cfcc78444321d56a2fe273f06e27b2c72b5058f8d7e975997d45b015.sig not found\"}]}\n", + errorString: "unknown: artifact test/alpine:sha256-443205b0cfcc78444321d56a2fe273f06e27b2c72b5058f8d7e975997d45b015.sig not found", + errorType: errcode.Error{}, + unwrappedErrorPtr: &unwrappedErrcodeError, + errorCode: &errcode.ErrorCodeUnknown, + fn: func(t *testing.T, err error) { + var e errcode.Error + ok := errors.As(err, &e) + require.True(t, ok) + // isManifestUnknownError is checking for this + assert.Equal(t, errcode.Error{ + Code: errcode.ErrorCodeUnknown, // The NOT_FOUND value is not defined, and turns into Unknown + Message: "artifact test/alpine:sha256-443205b0cfcc78444321d56a2fe273f06e27b2c72b5058f8d7e975997d45b015.sig not found", + Detail: nil, + }, e) + }, + }, } { res, err := http.ReadResponse(bufio.NewReader(bytes.NewReader([]byte(c.response))), nil) require.NoError(t, err, c.name)