From df2dcca9a8eaefc0e6ba15ccee8a8b1b6e6d98cf Mon Sep 17 00:00:00 2001 From: Bryce Lampe Date: Tue, 12 May 2026 10:46:54 -0700 Subject: [PATCH] Fix 404 handling when deleting a manifest (#850) Upstream was previously using an internal notFound error, and we were relying on fragile string matching to detect it. This broke when we upgraded to Docker v28. https://github.com/docker/buildx/commit/d25e260d2e3b794c2f0e71aea35a44ca6bdea031#diff-a5e122cd2318f2dc156f373804a59d30355b0c308b9e64f48e0713344fcdba33L164 Nowadays containerd exposes a public ErrNotFound which we can use instead. Fixes #849. --- CHANGELOG.md | 8 +++++++- provider/internal/client.go | 7 +++++++ provider/internal/image.go | 2 +- provider/internal/index.go | 8 +++----- provider/internal/index_test.go | 24 ++++++++++++++++++++++++ 5 files changed, 42 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b0cfb3..bab8c42 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ ## Unreleased -## Changed +### Fixed + +- Fixes a regression where a 404 status code during deletion wasn't considered deleted. (https://github.com/pulumi/pulumi-docker-build/issues/849) + +## 0.0.15 (2025-10-17) + +### Changed - Arguments `CacheFromGitHubActions.URL` and `CacheFromGitHubActions.Token` have been removed. If the previous behaviour is desired, set the `ACTIONS_CACHE_URL` and `ACTIONS_RUNTIME_TOKEN` environment variables. (https://github.com/pulumi/pulumi-docker-build/issues/75) diff --git a/provider/internal/client.go b/provider/internal/client.go index f5e7a8b..08e82ff 100644 --- a/provider/internal/client.go +++ b/provider/internal/client.go @@ -24,6 +24,7 @@ import ( "os" "strings" + "github.com/containerd/errdefs" "github.com/distribution/reference" buildx "github.com/docker/buildx/build" "github.com/docker/buildx/builder" @@ -339,6 +340,9 @@ func (c *cli) ManifestInspect(ctx context.Context, target string) (string, error } m, err := rc.ManifestHead(ctx, ref) + if errors.Is(err, errs.ErrNotFound) { + return "", fmt.Errorf("fetching %q: %w", ref, errdefs.ErrNotFound) + } if err != nil { return "", fmt.Errorf("fetching %q: %w", ref, err) } @@ -359,6 +363,9 @@ func (c *cli) ManifestDelete(ctx context.Context, target string) error { provider.GetLogger(ctx).Warning("this registry does not support deletions") return nil } + if errors.Is(err, errs.ErrNotFound) { + return nil + } if err != nil { return err } diff --git a/provider/internal/image.go b/provider/internal/image.go index 982abd2..e6687e4 100644 --- a/provider/internal/image.go +++ b/provider/internal/image.go @@ -28,9 +28,9 @@ import ( _ "github.com/docker/buildx/driver/kubernetes" _ "github.com/docker/buildx/driver/remote" + "github.com/containerd/errdefs" "github.com/distribution/reference" controllerapi "github.com/docker/buildx/controller/pb" - "github.com/docker/docker/errdefs" "github.com/moby/buildkit/exporter/containerimage/exptypes" "github.com/moby/buildkit/session" "github.com/moby/buildkit/session/secrets/secretsprovider" diff --git a/provider/internal/index.go b/provider/internal/index.go index 3cbae69..711c974 100644 --- a/provider/internal/index.go +++ b/provider/internal/index.go @@ -19,11 +19,11 @@ import ( "errors" "fmt" "reflect" - "strings" // For examples/docs. _ "embed" + "github.com/containerd/errdefs" "github.com/regclient/regclient/types/errs" provider "github.com/pulumi/pulumi-go-provider" @@ -223,7 +223,7 @@ func (i *Index) Read( provider.GetLogger(ctx).Debug("reading index with tag " + input.Tag) digest, err := cli.ManifestInspect(ctx, input.Tag) - if errors.Is(err, errs.ErrNotFound) { + if errdefs.IsNotFound(err) { // A remote tag was expected but isn't there -- delete the resource. return infer.ReadResponse[IndexArgs, IndexState]{ID: "", Inputs: input, State: state}, nil } @@ -304,9 +304,7 @@ func (i *Index) Delete( } err = cli.ManifestDelete(ctx, state.Ref) - // TODO: Upstream buildx swallows the error types we'd like to test for - // here. - if err != nil && strings.Contains(err.Error(), "No such manifest:") { + if errdefs.IsNotFound(err) { return infer.DeleteResponse{}, nil } return infer.DeleteResponse{}, err diff --git a/provider/internal/index_test.go b/provider/internal/index_test.go index 009acd6..d05a8c9 100644 --- a/provider/internal/index_test.go +++ b/provider/internal/index_test.go @@ -24,6 +24,7 @@ import ( "go.uber.org/mock/gomock" provider "github.com/pulumi/pulumi-go-provider" + "github.com/pulumi/pulumi-go-provider/infer" "github.com/pulumi/pulumi-go-provider/integration" "github.com/pulumi/pulumi/sdk/v3/go/common/resource" "github.com/pulumi/pulumi/sdk/v3/go/common/util/mapper" @@ -235,3 +236,26 @@ func TestIndexDiff(t *testing.T) { }) } } + +func TestIndexDelete(t *testing.T) { + t.Parallel() + t.Run("manifest already deleted (404)", func(t *testing.T) { + t.Parallel() + ctrl := gomock.NewController(t) + client := NewMockClient(ctrl) + client.EXPECT(). + ManifestDelete(gomock.Any(), "docker.io/pulumi/test:manifest"). + Return(errNotFound{}) + + i := &Index{clientF: mockClientF(client)} + + _, err := i.Delete(t.Context(), infer.DeleteRequest[IndexState]{ + ID: "foo", + State: IndexState{ + IndexArgs: IndexArgs{Tag: "docker.io/pulumi/test:manifest"}, + Ref: "docker.io/pulumi/test:manifest", + }, + }) + assert.NoError(t, err) + }) +}