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.

d25e260d2e (diff-a5e122cd2318f2dc156f373804a59d30355b0c308b9e64f48e0713344fcdba33L164)

Nowadays containerd exposes a public ErrNotFound which we can use
instead.

Fixes #849.
This commit is contained in:
Bryce Lampe
2026-05-12 10:46:54 -07:00
committed by GitHub
parent 9048892848
commit df2dcca9a8
5 changed files with 42 additions and 7 deletions

View File

@@ -1,6 +1,12 @@
## Unreleased ## 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) - 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)

View File

@@ -24,6 +24,7 @@ import (
"os" "os"
"strings" "strings"
"github.com/containerd/errdefs"
"github.com/distribution/reference" "github.com/distribution/reference"
buildx "github.com/docker/buildx/build" buildx "github.com/docker/buildx/build"
"github.com/docker/buildx/builder" "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) m, err := rc.ManifestHead(ctx, ref)
if errors.Is(err, errs.ErrNotFound) {
return "", fmt.Errorf("fetching %q: %w", ref, errdefs.ErrNotFound)
}
if err != nil { if err != nil {
return "", fmt.Errorf("fetching %q: %w", ref, err) 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") provider.GetLogger(ctx).Warning("this registry does not support deletions")
return nil return nil
} }
if errors.Is(err, errs.ErrNotFound) {
return nil
}
if err != nil { if err != nil {
return err return err
} }

View File

@@ -28,9 +28,9 @@ import (
_ "github.com/docker/buildx/driver/kubernetes" _ "github.com/docker/buildx/driver/kubernetes"
_ "github.com/docker/buildx/driver/remote" _ "github.com/docker/buildx/driver/remote"
"github.com/containerd/errdefs"
"github.com/distribution/reference" "github.com/distribution/reference"
controllerapi "github.com/docker/buildx/controller/pb" controllerapi "github.com/docker/buildx/controller/pb"
"github.com/docker/docker/errdefs"
"github.com/moby/buildkit/exporter/containerimage/exptypes" "github.com/moby/buildkit/exporter/containerimage/exptypes"
"github.com/moby/buildkit/session" "github.com/moby/buildkit/session"
"github.com/moby/buildkit/session/secrets/secretsprovider" "github.com/moby/buildkit/session/secrets/secretsprovider"

View File

@@ -19,11 +19,11 @@ import (
"errors" "errors"
"fmt" "fmt"
"reflect" "reflect"
"strings"
// For examples/docs. // For examples/docs.
_ "embed" _ "embed"
"github.com/containerd/errdefs"
"github.com/regclient/regclient/types/errs" "github.com/regclient/regclient/types/errs"
provider "github.com/pulumi/pulumi-go-provider" 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) provider.GetLogger(ctx).Debug("reading index with tag " + input.Tag)
digest, err := cli.ManifestInspect(ctx, 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. // A remote tag was expected but isn't there -- delete the resource.
return infer.ReadResponse[IndexArgs, IndexState]{ID: "", Inputs: input, State: state}, nil 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) err = cli.ManifestDelete(ctx, state.Ref)
// TODO: Upstream buildx swallows the error types we'd like to test for if errdefs.IsNotFound(err) {
// here.
if err != nil && strings.Contains(err.Error(), "No such manifest:") {
return infer.DeleteResponse{}, nil return infer.DeleteResponse{}, nil
} }
return infer.DeleteResponse{}, err return infer.DeleteResponse{}, err

View File

@@ -24,6 +24,7 @@ import (
"go.uber.org/mock/gomock" "go.uber.org/mock/gomock"
provider "github.com/pulumi/pulumi-go-provider" 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-go-provider/integration"
"github.com/pulumi/pulumi/sdk/v3/go/common/resource" "github.com/pulumi/pulumi/sdk/v3/go/common/resource"
"github.com/pulumi/pulumi/sdk/v3/go/common/util/mapper" "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)
})
}