diff --git a/examples/tests/dockerhub/.dockerignore b/examples/tests/dockerhub/.dockerignore new file mode 100644 index 0000000..72e8ffc --- /dev/null +++ b/examples/tests/dockerhub/.dockerignore @@ -0,0 +1 @@ +* diff --git a/examples/tests/dockerhub/Pulumi.yaml b/examples/tests/dockerhub/Pulumi.yaml new file mode 100644 index 0000000..1670a8b --- /dev/null +++ b/examples/tests/dockerhub/Pulumi.yaml @@ -0,0 +1,34 @@ +name: dockerhub +description: Push to DockerHub with caching +runtime: yaml +plugins: + providers: + - name: docker-build + path: ../../../bin +outputs: + ref: ${my-image.ref} +resources: + my-image: + type: docker-build:Image + properties: + tags: + - docker.io/pulumibot/buildkit-e2e + push: true + context: + location: . + dockerfile: + inline: FROM alpine + cacheFrom: + - registry: + ref: docker.io/pulumibot/buildkit-e2e:cache + cacheTo: + - registry: + ref: docker.io/pulumibot/buildkit-e2e:cache + registries: + - username: pulumibot + address: docker.io + password: ${dockerHubPassword} +config: + dockerHubPassword: + type: string + secret: true diff --git a/examples/tests/ecr/Pulumi.yaml b/examples/tests/ecr/Pulumi.yaml index 13636af..5499c2e 100644 --- a/examples/tests/ecr/Pulumi.yaml +++ b/examples/tests/ecr/Pulumi.yaml @@ -1,5 +1,10 @@ name: ecr description: Push to AWS ECR with caching +runtime: yaml +plugins: + providers: + - name: docker-build + path: ../../../bin outputs: ref: ${my-image.ref} resources: @@ -8,10 +13,10 @@ resources: properties: forceDelete: true my-image: - type: dockerbuild:Image + type: docker-build:Image properties: tags: - - ${ecr-repository.repositoryUrl}:tag-name + - ${ecr-repository.repositoryUrl}:latest push: true context: location: . @@ -29,7 +34,6 @@ resources: - username: ${auth-token.userName} password: ${auth-token.password} address: ${ecr-repository.repositoryUrl} -runtime: yaml variables: auth-token: fn::aws:ecr:getAuthorizationToken: diff --git a/examples/yaml_test.go b/examples/yaml_test.go index 3a503fc..0635fc5 100644 --- a/examples/yaml_test.go +++ b/examples/yaml_test.go @@ -40,3 +40,21 @@ func TestECR(t *testing.T) { integration.ProgramTest(t, &test) } + +func TestDockerHub(t *testing.T) { + if os.Getenv("DOCKER_HUB_PASSWORD") == "" { + t.Skip("Missing DockerHub credentials") + } + + cwd, err := os.Getwd() + require.NoError(t, err) + + test := integration.ProgramTestOptions{ + Dir: path.Join(cwd, "tests/dockerhub"), + Secrets: map[string]string{ + "dockerHubPassword": os.Getenv("DOCKER_HUB_PASSWORD"), + }, + } + + integration.ProgramTest(t, &test) +} diff --git a/provider/internal/cache.go b/provider/internal/cache.go index 2c99ad0..471d10d 100644 --- a/provider/internal/cache.go +++ b/provider/internal/cache.go @@ -347,7 +347,8 @@ type CacheWithMode struct { // Annotate sets docstrings and defaults on CacheWithMode. func (c *CacheWithMode) Annotate(a infer.Annotator) { - a.SetDefault(&c.Mode, Min) + m := Min + a.SetDefault(&c.Mode, &m) a.Describe(&c.Mode, dedent(` The cache mode to use. Defaults to "min". `)) @@ -559,7 +560,8 @@ type CacheWithCompression struct { // Annotate sets docstrings and defaults on CacheWithCompression. func (c *CacheWithCompression) Annotate(a infer.Annotator) { - a.SetDefault(&c.Compression, Gzip) + gz := Gzip + a.SetDefault(&c.Compression, &gz) a.SetDefault(&c.CompressionLevel, 0) a.SetDefault(&c.ForceCompression, false) diff --git a/provider/internal/cli.go b/provider/internal/cli.go index 7e62b96..7a1ab95 100644 --- a/provider/internal/cli.go +++ b/provider/internal/cli.go @@ -79,21 +79,27 @@ func wrap(host *host, registries ...Registry) (*cli, error) { auths := map[string]cfgtypes.AuthConfig{} for k, v := range host.auths { - auths[k] = v + auths[k] = cfgtypes.AuthConfig{ + ServerAddress: v.ServerAddress, + Username: v.Username, + Password: v.Password, + } } for _, r := range registries { - // Special handling for legacy DockerHub domains. The OCI-compliant - // registry is registry-1.docker.io but this is stored in config under the - // legacy name. - // https://github.com/docker/cli/issues/3793#issuecomment-1269051403 - key := credentials.ConvertToHostname(r.Address) - if key == "registry-1.docker.io" || key == "index.docker.io" || key == "docker.io" { - key = "https://index.docker.io/v1/" + // HostNewName takes care of DockerHub's special-casing for us. + h := config.HostNewName(credentials.ConvertToHostname(r.Address)) + key := h.CredHost + if key == "" { + key = h.Hostname + } + // Add a scheme if it's missing. + if !strings.Contains(key, "://") { + key = "https://" + key } auths[key] = cfgtypes.AuthConfig{ - ServerAddress: r.Address, + ServerAddress: h.Hostname, Username: r.Username, Password: r.Password, } diff --git a/provider/internal/client.go b/provider/internal/client.go index a807102..c5c679f 100644 --- a/provider/internal/client.go +++ b/provider/internal/client.go @@ -252,8 +252,6 @@ func (c *cli) BuildKitEnabled() (bool, error) { } func (c *cli) ManifestCreate(ctx provider.Context, push bool, target string, refs ...string) error { - // TODO: Create this manifest with regclient or imagetools. - go c.tail(ctx) defer contract.IgnoreClose(c) @@ -261,6 +259,7 @@ func (c *cli) ManifestCreate(ctx provider.Context, push bool, target string, ref // "buildx", "imagetools", "create", + "--progress=plain", "--tag", target, } @@ -273,10 +272,13 @@ func (c *cli) ManifestCreate(ctx provider.Context, push bool, target string, ref cmd := commands.NewRootCmd(os.Args[0], false, c) cmd.SetArgs(args) + + ctx.Log(diag.Debug, fmt.Sprint("creating manifest with args", args)) return cmd.ExecuteContext(ctx) } func (c *cli) ManifestInspect(ctx provider.Context, target string) (string, error) { + ctx.LogStatus(diag.Info, "inspecting manifest") rc := c.rc() ref, err := ref.New(target) @@ -286,7 +288,7 @@ func (c *cli) ManifestInspect(ctx provider.Context, target string) (string, erro m, err := rc.ManifestHead(ctx, ref) if err != nil { - return "", fmt.Errorf("fetching head: %w", err) + return "", fmt.Errorf("fetching %q: %w", ref, err) } return string(m.GetDescriptor().Digest), nil diff --git a/provider/internal/export.go b/provider/internal/export.go index 0b41a2d..260c69a 100644 --- a/provider/internal/export.go +++ b/provider/internal/export.go @@ -393,7 +393,8 @@ type ExportWithCompression struct { // Annotate sets docstrings and defaults on ExportWithCompression. func (e *ExportWithCompression) Annotate(a infer.Annotator) { - a.SetDefault(&e.Compression, Gzip) + gzip := Gzip + a.SetDefault(&e.Compression, &gzip) a.SetDefault(&e.CompressionLevel, 0) a.SetDefault(&e.ForceCompression, false) diff --git a/provider/internal/image.go b/provider/internal/image.go index 0806484..b444654 100644 --- a/provider/internal/image.go +++ b/provider/internal/image.go @@ -287,7 +287,8 @@ func (ia *ImageArgs) Annotate(a infer.Annotator) { "docker-buildx" binary. `)) - a.SetDefault(&ia.Network, Default) + d := Default + a.SetDefault(&ia.Network, &d) } // ImageState is serialized to the program's state file. @@ -350,7 +351,7 @@ func (i *Image) client(pctx provider.Context, state ImageState, args ImageArgs) // We prefer auth from args, the provider, and state in that order. We // build a slice in reverse order because wrap() will overwrite earlier // entries with later ones. - auths := state.Registries + auths := []Registry{} auths = append(auths, cfg.Registries...) auths = append(auths, args.Registries...) diff --git a/provider/internal/index.go b/provider/internal/index.go index 250d57b..4274fb5 100644 --- a/provider/internal/index.go +++ b/provider/internal/index.go @@ -24,6 +24,7 @@ import ( provider "github.com/pulumi/pulumi-go-provider" "github.com/pulumi/pulumi-go-provider/infer" + "github.com/pulumi/pulumi/sdk/v3/go/common/diag" "github.com/pulumi/pulumi/sdk/v3/go/common/resource" ) @@ -138,6 +139,8 @@ func (i *Index) Update( return state, nil } + ctx.Log(diag.Info, fmt.Sprintf("creating index with tag %s and sources %s", input.Tag, input.Sources)) + err = cli.ManifestCreate(ctx, input.Push, input.Tag, input.Sources...) if err != nil { return state, fmt.Errorf("creating: %w", err) @@ -160,6 +163,7 @@ func (i *Index) Read( state.Ref = input.Tag if !input.Push { + ctx.Log(diag.Debug, "skipping read because index was not pushed") return name, input, state, nil // Nothing to read. } @@ -168,6 +172,8 @@ func (i *Index) Read( return name, input, state, err } + ctx.Log(diag.Debug, fmt.Sprintf("reading index with tag %s", input.Tag)) + digest, err := cli.ManifestInspect(ctx, input.Tag) if err != nil && strings.Contains(err.Error(), "No such manifest:") && input.Push { // A remote tag was expected but isn't there -- delete the resource. @@ -175,7 +181,7 @@ func (i *Index) Read( } if err != nil && strings.Contains(err.Error(), "No such manifest:") && !input.Push { // Nothing was pushed, so just use the tag without digest.. - return name, input, state, err + return name, input, state, nil } if err != nil { return name, input, state, err @@ -295,7 +301,7 @@ func (i *Index) Diff( // of any host-level credentials. func (i *Index) client( ctx provider.Context, - state IndexState, + _ IndexState, args IndexArgs, ) (Client, error) { cfg := infer.GetConfig[Config](ctx) @@ -308,9 +314,6 @@ func (i *Index) client( // build a slice in reverse order because wrap() will overwrite earlier // entries with later ones. auths := []Registry{} - if state.Registry != nil { - auths = append(auths, *state.Registry) - } auths = append(auths, cfg.Registries...) if args.Registry != nil { auths = append(auths, *args.Registry) diff --git a/sdk/go/dockerbuild/config/config.go b/sdk/go/dockerbuild/config/config.go index d0baa7e..26bba90 100644 --- a/sdk/go/dockerbuild/config/config.go +++ b/sdk/go/dockerbuild/config/config.go @@ -4,7 +4,6 @@ package config import ( - dockerbuild "github.com/pulumi/pulumi-docker-build/sdk/go/dockerbuild" "github.com/pulumi/pulumi-docker-build/sdk/go/dockerbuild/internal" "github.com/pulumi/pulumi/sdk/v3/go/pulumi" "github.com/pulumi/pulumi/sdk/v3/go/pulumi/config" diff --git a/sdk/go/dockerbuild/x/config/config.go b/sdk/go/dockerbuild/x/config/config.go index d0baa7e..26bba90 100644 --- a/sdk/go/dockerbuild/x/config/config.go +++ b/sdk/go/dockerbuild/x/config/config.go @@ -4,7 +4,6 @@ package config import ( - dockerbuild "github.com/pulumi/pulumi-docker-build/sdk/go/dockerbuild" "github.com/pulumi/pulumi-docker-build/sdk/go/dockerbuild/internal" "github.com/pulumi/pulumi/sdk/v3/go/pulumi" "github.com/pulumi/pulumi/sdk/v3/go/pulumi/config"