From 6fe24f1c96c04e86ff56c6ba1a39d67a14642aaa Mon Sep 17 00:00:00 2001 From: Bryce Lampe Date: Mon, 29 Jan 2024 11:05:44 -0800 Subject: [PATCH] Handle unknown inputs and buildOnPreview --- .../cmd/pulumi-resource-docker/schema.json | 8 + provider/internal/image.go | 147 ++++++++++----- provider/internal/image_test.go | 171 ++++++++++++++++-- provider/internal/preview.go | 67 +++++++ sdk/dotnet/Buildx/Image.cs | 16 ++ sdk/go/docker/buildx/image.go | 15 ++ .../java/com/pulumi/docker/buildx/Image.java | 17 ++ .../com/pulumi/docker/buildx/ImageArgs.java | 44 +++++ sdk/nodejs/buildx/image.ts | 14 ++ sdk/python/pulumi_docker/buildx/image.py | 37 ++++ 10 files changed, 474 insertions(+), 62 deletions(-) create mode 100644 provider/internal/preview.go diff --git a/provider/cmd/pulumi-resource-docker/schema.json b/provider/cmd/pulumi-resource-docker/schema.json index 8431f1f1..efba49e0 100644 --- a/provider/cmd/pulumi-resource-docker/schema.json +++ b/provider/cmd/pulumi-resource-docker/schema.json @@ -2105,6 +2105,10 @@ }, "description": "\nAn optional map of named build-time argument variables to set during\nthe Docker build. This flag allows you to pass build-time variables that\ncan be accessed like environment variables inside the RUN\ninstruction." }, + "buildOnPreview": { + "type": "boolean", + "description": "\nWhen true, attempt to build the image during previews. Outputs are not\npushed to registries, however caches are still populated.\n" + }, "builder": { "type": "string", "description": "\nBuild with a specific builder instance" @@ -2186,6 +2190,10 @@ }, "description": "\nAn optional map of named build-time argument variables to set during\nthe Docker build. This flag allows you to pass build-time variables that\ncan be accessed like environment variables inside the RUN\ninstruction." }, + "buildOnPreview": { + "type": "boolean", + "description": "\nWhen true, attempt to build the image during previews. Outputs are not\npushed to registries, however caches are still populated.\n" + }, "builder": { "type": "string", "description": "\nBuild with a specific builder instance" diff --git a/provider/internal/image.go b/provider/internal/image.go index 232a2b77..ad98ddef 100644 --- a/provider/internal/image.go +++ b/provider/internal/image.go @@ -51,17 +51,18 @@ func (i *Image) Annotate(a infer.Annotator) { // ImageArgs instantiates a new Image. type ImageArgs struct { - BuildArgs map[string]string `pulumi:"buildArgs,optional"` - Builder string `pulumi:"builder,optional"` - CacheFrom []string `pulumi:"cacheFrom,optional"` - CacheTo []string `pulumi:"cacheTo,optional"` - Context string `pulumi:"context,optional"` - Exports []string `pulumi:"exports,optional"` - File string `pulumi:"file,optional"` - Platforms []string `pulumi:"platforms,optional"` - Pull bool `pulumi:"pull,optional"` - Registries []properties.RegistryAuth `pulumi:"registries,optional"` - Tags []string `pulumi:"tags"` + BuildArgs map[string]string `pulumi:"buildArgs,optional"` + Builder string `pulumi:"builder,optional"` + BuildOnPreview bool `pulumi:"buildOnPreview,optional"` + CacheFrom []string `pulumi:"cacheFrom,optional"` + CacheTo []string `pulumi:"cacheTo,optional"` + Context string `pulumi:"context,optional"` + Exports []string `pulumi:"exports,optional"` + File string `pulumi:"file,optional"` + Platforms []string `pulumi:"platforms,optional"` + Pull bool `pulumi:"pull,optional"` + Registries []properties.RegistryAuth `pulumi:"registries,optional"` + Tags []string `pulumi:"tags"` } // Annotate describes inputs to the Image resource. @@ -75,6 +76,10 @@ func (ia *ImageArgs) Annotate(a infer.Annotator) { a.Describe(&ia.Builder, dedent.String(` Build with a specific builder instance`, )) + a.Describe(&ia.BuildOnPreview, dedent.String(` + When true, attempt to build the image during previews. Outputs are not + pushed to registries, however caches are still populated. + `)) a.Describe(&ia.CacheFrom, dedent.String(` External cache sources (e.g., "user/app:cache", "type=local,src=path/to/dir")`, )) @@ -135,7 +140,11 @@ func (*Image) Check( if err != nil || len(failures) != 0 { return args, failures, err } - if _, berr := args.toBuildOptions(); berr != nil { + + // :( + preview := news.ContainsUnknowns() + + if _, berr := args.toBuildOptions(preview); berr != nil { errs := berr.(interface{ Unwrap() []error }).Unwrap() for _, e := range errs { if cf, ok := e.(checkFailure); ok { @@ -173,62 +182,98 @@ func newCheckFailure(property string, err error) checkFailure { return checkFailure{provider.CheckFailure{Property: property, Reason: err.Error()}} } -func (ia *ImageArgs) toBuildOptions() (controllerapi.BuildOptions, error) { +func (ia *ImageArgs) withoutUnknowns(preview bool) ImageArgs { + sk := stringKeeper{preview} + filtered := ImageArgs{ + BuildArgs: mapKeeper{preview}.keep(ia.BuildArgs), + Builder: ia.Builder, + BuildOnPreview: ia.BuildOnPreview, + CacheFrom: filter(sk, ia.CacheFrom...), + CacheTo: filter(sk, ia.CacheTo...), + Context: ia.Context, + Exports: filter(sk, ia.Exports...), + File: ia.File, // + Platforms: filter(sk, ia.Platforms...), + Pull: ia.Pull, + Registries: filter(registryKeeper{preview}, ia.Registries...), + Tags: filter(sk, ia.Tags...), + } + + return filtered +} + +func (ia *ImageArgs) buildable() bool { + // We can build the given inputs if filtered out unknowns is a no-op. + filtered := ia.withoutUnknowns(true) + return reflect.DeepEqual(ia, &filtered) +} + +func (ia *ImageArgs) toBuildOptions(preview bool) (controllerapi.BuildOptions, error) { var multierr error - exports, err := buildflags.ParseExports(ia.Exports) + + if len(ia.Tags) == 0 { + multierr = errors.Join(multierr, newCheckFailure("tags", errors.New("at least one tag is required"))) + } + + // TODO(https://github.com/pulumi/pulumi-docker/issues/860): Empty build context + if ia.Context != "" && !preview { + if ia.File == "" { + ia.File = filepath.Join(ia.Context, "Dockerfile") + } + if _, err := os.Stat(ia.File); err != nil { + multierr = errors.Join(multierr, newCheckFailure("context", fmt.Errorf("%q: %w", ia.File, err))) + } + } + + // Discard any unknown inputs if this is a preview -- we don't want them to + // cause validation errors. + filtered := ia.withoutUnknowns(preview) + + exports, err := buildflags.ParseExports(filtered.Exports) if err != nil { multierr = errors.Join(multierr, newCheckFailure("exports", err)) } + if preview { + // Don't perform registry pushes during previews. + for _, e := range exports { + if e.Type == "image" && e.Attrs["push"] == "true" { + e.Attrs["push"] = "false" + } + } + } - _, err = platformutil.Parse(ia.Platforms) + _, err = platformutil.Parse(filtered.Platforms) if err != nil { multierr = errors.Join(multierr, newCheckFailure("platforms", err)) } - cacheFrom, err := buildflags.ParseCacheEntry(ia.CacheFrom) + cacheFrom, err := buildflags.ParseCacheEntry(filtered.CacheFrom) if err != nil { multierr = errors.Join(multierr, newCheckFailure("cacheFrom", err)) } - cacheTo, err := buildflags.ParseCacheEntry(ia.CacheTo) + cacheTo, err := buildflags.ParseCacheEntry(filtered.CacheTo) if err != nil { multierr = errors.Join(multierr, newCheckFailure("cacheTo", err)) } - // TODO(https://github.com/pulumi/pulumi-docker/issues/860): Empty build context - if ia.Context != "" { - if ia.File == "" { - ia.File = filepath.Join(ia.Context, "Dockerfile") - } - if _, err := os.Stat(ia.File); err != nil { - multierr = errors.Join(multierr, newCheckFailure("context", err)) - } - } - - if len(ia.Tags) == 0 { - multierr = errors.Join(multierr, newCheckFailure("tags", errors.New("at least one tag is required"))) - } - for _, t := range ia.Tags { - if t == "" { - // TODO(https://github.com/pulumi/pulumi-go-provider/pull/155): This is likely unresolved. - continue - } + for _, t := range filtered.Tags { if _, err := reference.Parse(t); err != nil { multierr = errors.Join(multierr, newCheckFailure("tags", err)) } } opts := controllerapi.BuildOptions{ - BuildArgs: ia.BuildArgs, - Builder: ia.Builder, + BuildArgs: filtered.BuildArgs, + Builder: filtered.Builder, CacheFrom: cacheFrom, CacheTo: cacheTo, - ContextPath: ia.Context, - DockerfileName: ia.File, + ContextPath: filtered.Context, + DockerfileName: filtered.File, Exports: exports, - Platforms: ia.Platforms, - Pull: ia.Pull, - Tags: ia.Tags, + Platforms: filtered.Platforms, + Pull: filtered.Pull, + Tags: filtered.Tags, } return opts, multierr @@ -254,7 +299,7 @@ func (i *Image) Update( return state, fmt.Errorf("buildkit is not supported on this host") } - opts, err := input.toBuildOptions() + opts, err := input.toBuildOptions(preview) if err != nil { return state, fmt.Errorf("validating input: %w", err) } @@ -265,7 +310,11 @@ func (i *Image) Update( } state.ContextHash = hash - if preview { + if preview && !input.BuildOnPreview { + return state, nil + } + if preview && !input.buildable() { + ctx.Log(diag.Warning, "Skipping preview build because some inputs are unknown.") return state, nil } @@ -318,7 +367,7 @@ func (*Image) Read( ImageState, // normalized state error, ) { - opts, err := input.toBuildOptions() + opts, err := input.toBuildOptions(false) if err != nil { return id, input, state, err } @@ -331,6 +380,7 @@ func (*Image) Read( } } + expectManifest := false manifests := []properties.Manifest{} for _, export := range opts.Exports { switch export.GetType() { @@ -340,6 +390,7 @@ func (*Image) Read( continue } for _, tag := range input.Tags { + expectManifest = true infos, err := cfg.client.Inspect(ctx, tag) if err != nil { continue @@ -372,6 +423,12 @@ func (*Image) Read( } } + // If we couldn't find the tags we expected then return an empty ID to + // delete the resource. + if expectManifest && len(manifests) == 0 { + return "", input, state, nil + } + state.id = id state.Manifests = manifests diff --git a/provider/internal/image_test.go b/provider/internal/image_test.go index f0cbfa1b..74701d53 100644 --- a/provider/internal/image_test.go +++ b/provider/internal/image_test.go @@ -36,6 +36,9 @@ func TestLifecycle(t *testing.T) { return mock.NewMockClient(ctrl) } + ref, err := reference.ParseNamed("docker.io/pulumibot/myapp") + require.NoError(t, err) + tests := []struct { name string @@ -58,7 +61,12 @@ func TestLifecycle(t *testing.T) { }, ), c.EXPECT().Inspect(gomock.Any(), "docker.io/blampe/buildkit-e2e").Return( - []manifesttypes.ImageManifest{}, nil, + []manifesttypes.ImageManifest{ + { + Ref: &manifesttypes.SerializableNamed{Named: ref}, + Descriptor: v1.Descriptor{Platform: &v1.Platform{}}, + }, + }, nil, ), c.EXPECT().Inspect(gomock.Any(), "docker.io/blampe/buildkit-e2e:main"), c.EXPECT().Delete(gomock.Any(), "SHA256:digest").Return( @@ -501,21 +509,150 @@ func TestDiff(t *testing.T) { } } -func TestBuildOptionParsing(t *testing.T) { - args := ImageArgs{ - Tags: []string{"a/bad:tag:format"}, - Exports: []string{"badexport,-"}, - Context: "does/not/exist", - Platforms: []string{","}, - CacheFrom: []string{"=badcachefrom"}, - CacheTo: []string{"=badcacheto"}, - } +func TestBuildOptions(t *testing.T) { + t.Run("invalid inputs", func(t *testing.T) { + args := ImageArgs{ + Tags: []string{"a/bad:tag:format"}, + Exports: []string{"badexport,-"}, + Context: "does/not/exist", + Platforms: []string{","}, + CacheFrom: []string{"=badcachefrom"}, + CacheTo: []string{"=badcacheto"}, + } + + _, err := args.toBuildOptions(false) + assert.ErrorContains(t, err, "invalid value badexport") + assert.ErrorContains(t, err, "platform specifier component must match") + assert.ErrorContains(t, err, "badcachefrom") + assert.ErrorContains(t, err, "badcacheto") + assert.ErrorContains(t, err, "invalid reference format") + assert.ErrorContains(t, err, "does/not/exist/Dockerfile: no such file or directory") + }) + + t.Run("buildOnPreview", func(t *testing.T) { + args := ImageArgs{ + Tags: []string{"my-tag"}, + Exports: []string{"type=registry", "type=local", "type=docker"}, + } + actual, err := args.toBuildOptions(true) + assert.NoError(t, err) + assert.Equal(t, "image", actual.Exports[0].Type) + assert.Equal(t, "false", actual.Exports[0].Attrs["push"]) + + actual, err = args.toBuildOptions(false) + assert.NoError(t, err) + assert.Equal(t, "image", actual.Exports[0].Type) + assert.Equal(t, "true", actual.Exports[0].Attrs["push"]) + }) + + t.Run("unknowns", func(t *testing.T) { + // pulumi-go-provider gives us zero-values when a property is unknown. + // We can't distinguish this from user-provided zero-values, but we + // should: + // - not fail previews due to these zero values, + // - not attempt builds with invalid zero values, + // - not allow invalid zero values in non-preview operations. + unknowns := ImageArgs{ + BuildArgs: map[string]string{ + "known": "value", + "": "", + }, + Builder: "", + CacheFrom: []string{"type=gha", ""}, + CacheTo: []string{"type=gha", ""}, + Context: "", + Exports: []string{"type=gha", ""}, + File: "", + Platforms: []string{"linux/amd64", ""}, + Registries: []properties.RegistryAuth{ + { + Address: "", + Password: "", + Username: "", + }, + }, + Tags: []string{"known", ""}, + } + + _, err := unknowns.toBuildOptions(true) + assert.NoError(t, err) + assert.False(t, unknowns.buildable()) + + _, err = unknowns.toBuildOptions(false) + assert.Error(t, err) + }) +} + +func TestBuildable(t *testing.T) { + tests := []struct { + name string + args ImageArgs - _, err := args.toBuildOptions() - assert.ErrorContains(t, err, "invalid value badexport") - assert.ErrorContains(t, err, "platform specifier component must match") - assert.ErrorContains(t, err, "badcachefrom") - assert.ErrorContains(t, err, "badcacheto") - assert.ErrorContains(t, err, "invalid reference format") - assert.ErrorContains(t, err, "does/not/exist/Dockerfile: no such file or directory") + want bool + }{ + { + name: "unknown tags", + args: ImageArgs{Tags: []string{""}}, + want: false, + }, + { + name: "unknown exports", + args: ImageArgs{ + Tags: []string{"known"}, + Exports: []string{""}, + }, + want: false, + }, + { + name: "unknown registry", + args: ImageArgs{ + Tags: []string{"known"}, + Exports: []string{"type=gha"}, + Registries: []properties.RegistryAuth{ + { + Address: "docker.io", + Username: "foo", + Password: "", + }, + }, + }, + want: false, + }, + { + name: "known tags", + args: ImageArgs{ + Tags: []string{"known"}, + }, + want: true, + }, + { + name: "known exports", + args: ImageArgs{ + Tags: []string{"known"}, + Exports: []string{"type=registry"}, + }, + want: true, + }, + { + name: "known registry", + args: ImageArgs{ + Tags: []string{"known"}, + Exports: []string{"type=gha"}, + Registries: []properties.RegistryAuth{ + { + Address: "docker.io", + Username: "foo", + Password: "bar", + }, + }, + }, + want: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + actual := tt.args.buildable() + assert.Equal(t, tt.want, actual) + }) + } } diff --git a/provider/internal/preview.go b/provider/internal/preview.go new file mode 100644 index 00000000..921e427d --- /dev/null +++ b/provider/internal/preview.go @@ -0,0 +1,67 @@ +package internal + +import ( + "github.com/pulumi/pulumi-docker/provider/v4/internal/properties" +) + +// keeper decides whether an element should be included for a preview +// operation, optionally returning a mutated copy of that element. +type keeper[T any] interface { + keep(T) bool +} + +// filter applies a keeper to each element, returning a new slice. +func filter[T any](k keeper[T], elems ...T) []T { + var result []T + for _, e := range elems { + if !k.keep(e) { + continue + } + result = append(result, e) + } + return result +} + +// stringKeeper preserves any non-empty string values for preview. +type stringKeeper struct{ preview bool } + +func (k stringKeeper) keep(s string) bool { + if !k.preview { + return true + } + return s != "" +} + +// registryKeeper preserves any registries with known values for address and +// password. This is imprecise and doesn't permit alternative auth strategies +// like registry tokens, email, etc. +type registryKeeper struct{ preview bool } + +//nolint:unused // False positive due to generics. +func (k registryKeeper) keep(r properties.RegistryAuth) bool { + if !k.preview { + return true + } + return r.Password != "" && r.Address != "" +} + +// mapKeeper preserves map elements with known keys and values. +type mapKeeper struct{ preview bool } + +func (k mapKeeper) keep(m map[string]string) map[string]string { + if !k.preview || len(m) == 0 { + return m + } + kk := stringKeeper(k) + filtered := make(map[string]string) + for key, val := range m { + if !kk.keep(key) { + continue + } + if !kk.keep(val) { + continue + } + filtered[key] = val + } + return filtered +} diff --git a/sdk/dotnet/Buildx/Image.cs b/sdk/dotnet/Buildx/Image.cs index c5fc06b8..7580759c 100644 --- a/sdk/dotnet/Buildx/Image.cs +++ b/sdk/dotnet/Buildx/Image.cs @@ -25,6 +25,14 @@ public partial class Image : global::Pulumi.CustomResource [Output("buildArgs")] public Output?> BuildArgs { get; private set; } = null!; + /// + /// + /// When true, attempt to build the image during previews. Outputs are not + /// pushed to registries, however caches are still populated. + /// + [Output("buildOnPreview")] + public Output BuildOnPreview { get; private set; } = null!; + /// /// /// Build with a specific builder instance @@ -164,6 +172,14 @@ public InputMap BuildArgs set => _buildArgs = value; } + /// + /// + /// When true, attempt to build the image during previews. Outputs are not + /// pushed to registries, however caches are still populated. + /// + [Input("buildOnPreview")] + public Input? BuildOnPreview { get; set; } + /// /// /// Build with a specific builder instance diff --git a/sdk/go/docker/buildx/image.go b/sdk/go/docker/buildx/image.go index 2ad7e85f..8f77b0a4 100644 --- a/sdk/go/docker/buildx/image.go +++ b/sdk/go/docker/buildx/image.go @@ -21,6 +21,9 @@ type Image struct { // can be accessed like environment variables inside the RUN // instruction. BuildArgs pulumi.StringMapOutput `pulumi:"buildArgs"` + // When true, attempt to build the image during previews. Outputs are not + // pushed to registries, however caches are still populated. + BuildOnPreview pulumi.BoolPtrOutput `pulumi:"buildOnPreview"` // Build with a specific builder instance Builder pulumi.StringPtrOutput `pulumi:"builder"` // External cache sources (e.g., "user/app:cache", "type=local,src=path/to/dir") @@ -98,6 +101,9 @@ type imageArgs struct { // can be accessed like environment variables inside the RUN // instruction. BuildArgs map[string]string `pulumi:"buildArgs"` + // When true, attempt to build the image during previews. Outputs are not + // pushed to registries, however caches are still populated. + BuildOnPreview *bool `pulumi:"buildOnPreview"` // Build with a specific builder instance Builder *string `pulumi:"builder"` // External cache sources (e.g., "user/app:cache", "type=local,src=path/to/dir") @@ -129,6 +135,9 @@ type ImageArgs struct { // can be accessed like environment variables inside the RUN // instruction. BuildArgs pulumi.StringMapInput + // When true, attempt to build the image during previews. Outputs are not + // pushed to registries, however caches are still populated. + BuildOnPreview pulumi.BoolPtrInput // Build with a specific builder instance Builder pulumi.StringPtrInput // External cache sources (e.g., "user/app:cache", "type=local,src=path/to/dir") @@ -248,6 +257,12 @@ func (o ImageOutput) BuildArgs() pulumi.StringMapOutput { return o.ApplyT(func(v *Image) pulumi.StringMapOutput { return v.BuildArgs }).(pulumi.StringMapOutput) } +// When true, attempt to build the image during previews. Outputs are not +// pushed to registries, however caches are still populated. +func (o ImageOutput) BuildOnPreview() pulumi.BoolPtrOutput { + return o.ApplyT(func(v *Image) pulumi.BoolPtrOutput { return v.BuildOnPreview }).(pulumi.BoolPtrOutput) +} + // Build with a specific builder instance func (o ImageOutput) Builder() pulumi.StringPtrOutput { return o.ApplyT(func(v *Image) pulumi.StringPtrOutput { return v.Builder }).(pulumi.StringPtrOutput) diff --git a/sdk/java/src/main/java/com/pulumi/docker/buildx/Image.java b/sdk/java/src/main/java/com/pulumi/docker/buildx/Image.java index afdd7c34..ad6d855c 100644 --- a/sdk/java/src/main/java/com/pulumi/docker/buildx/Image.java +++ b/sdk/java/src/main/java/com/pulumi/docker/buildx/Image.java @@ -45,6 +45,23 @@ public class Image extends com.pulumi.resources.CustomResource { public Output>> buildArgs() { return Codegen.optional(this.buildArgs); } + /** + * When true, attempt to build the image during previews. Outputs are not + * pushed to registries, however caches are still populated. + * + */ + @Export(name="buildOnPreview", refs={Boolean.class}, tree="[0]") + private Output buildOnPreview; + + /** + * @return + * When true, attempt to build the image during previews. Outputs are not + * pushed to registries, however caches are still populated. + * + */ + public Output> buildOnPreview() { + return Codegen.optional(this.buildOnPreview); + } /** * Build with a specific builder instance * diff --git a/sdk/java/src/main/java/com/pulumi/docker/buildx/ImageArgs.java b/sdk/java/src/main/java/com/pulumi/docker/buildx/ImageArgs.java index 7f8dc5b8..6185fc51 100644 --- a/sdk/java/src/main/java/com/pulumi/docker/buildx/ImageArgs.java +++ b/sdk/java/src/main/java/com/pulumi/docker/buildx/ImageArgs.java @@ -43,6 +43,24 @@ public Optional>> buildArgs() { return Optional.ofNullable(this.buildArgs); } + /** + * When true, attempt to build the image during previews. Outputs are not + * pushed to registries, however caches are still populated. + * + */ + @Import(name="buildOnPreview") + private @Nullable Output buildOnPreview; + + /** + * @return + * When true, attempt to build the image during previews. Outputs are not + * pushed to registries, however caches are still populated. + * + */ + public Optional> buildOnPreview() { + return Optional.ofNullable(this.buildOnPreview); + } + /** * Build with a specific builder instance * @@ -211,6 +229,7 @@ private ImageArgs() {} private ImageArgs(ImageArgs $) { this.buildArgs = $.buildArgs; + this.buildOnPreview = $.buildOnPreview; this.builder = $.builder; this.cacheFrom = $.cacheFrom; this.cacheTo = $.cacheTo; @@ -270,6 +289,31 @@ public Builder buildArgs(Map buildArgs) { return buildArgs(Output.of(buildArgs)); } + /** + * @param buildOnPreview + * When true, attempt to build the image during previews. Outputs are not + * pushed to registries, however caches are still populated. + * + * @return builder + * + */ + public Builder buildOnPreview(@Nullable Output buildOnPreview) { + $.buildOnPreview = buildOnPreview; + return this; + } + + /** + * @param buildOnPreview + * When true, attempt to build the image during previews. Outputs are not + * pushed to registries, however caches are still populated. + * + * @return builder + * + */ + public Builder buildOnPreview(Boolean buildOnPreview) { + return buildOnPreview(Output.of(buildOnPreview)); + } + /** * @param builder * Build with a specific builder instance diff --git a/sdk/nodejs/buildx/image.ts b/sdk/nodejs/buildx/image.ts index ace3fa6f..4221ea32 100644 --- a/sdk/nodejs/buildx/image.ts +++ b/sdk/nodejs/buildx/image.ts @@ -45,6 +45,12 @@ export class Image extends pulumi.CustomResource { * instruction. */ public readonly buildArgs!: pulumi.Output<{[key: string]: string} | undefined>; + /** + * + * When true, attempt to build the image during previews. Outputs are not + * pushed to registries, however caches are still populated. + */ + public readonly buildOnPreview!: pulumi.Output; /** * * Build with a specific builder instance @@ -115,6 +121,7 @@ export class Image extends pulumi.CustomResource { throw new Error("Missing required property 'tags'"); } resourceInputs["buildArgs"] = args ? args.buildArgs : undefined; + resourceInputs["buildOnPreview"] = args ? args.buildOnPreview : undefined; resourceInputs["builder"] = args ? args.builder : undefined; resourceInputs["cacheFrom"] = args ? args.cacheFrom : undefined; resourceInputs["cacheTo"] = args ? args.cacheTo : undefined; @@ -129,6 +136,7 @@ export class Image extends pulumi.CustomResource { resourceInputs["manifests"] = undefined /*out*/; } else { resourceInputs["buildArgs"] = undefined /*out*/; + resourceInputs["buildOnPreview"] = undefined /*out*/; resourceInputs["builder"] = undefined /*out*/; resourceInputs["cacheFrom"] = undefined /*out*/; resourceInputs["cacheTo"] = undefined /*out*/; @@ -159,6 +167,12 @@ export interface ImageArgs { * instruction. */ buildArgs?: pulumi.Input<{[key: string]: pulumi.Input}>; + /** + * + * When true, attempt to build the image during previews. Outputs are not + * pushed to registries, however caches are still populated. + */ + buildOnPreview?: pulumi.Input; /** * * Build with a specific builder instance diff --git a/sdk/python/pulumi_docker/buildx/image.py b/sdk/python/pulumi_docker/buildx/image.py index d2be0667..fcbcf1e1 100644 --- a/sdk/python/pulumi_docker/buildx/image.py +++ b/sdk/python/pulumi_docker/buildx/image.py @@ -18,6 +18,7 @@ class ImageArgs: def __init__(__self__, *, tags: pulumi.Input[Sequence[pulumi.Input[str]]], build_args: Optional[pulumi.Input[Mapping[str, pulumi.Input[str]]]] = None, + build_on_preview: Optional[pulumi.Input[bool]] = None, builder: Optional[pulumi.Input[str]] = None, cache_from: Optional[pulumi.Input[Sequence[pulumi.Input[str]]]] = None, cache_to: Optional[pulumi.Input[Sequence[pulumi.Input[str]]]] = None, @@ -37,6 +38,9 @@ def __init__(__self__, *, the Docker build. This flag allows you to pass build-time variables that can be accessed like environment variables inside the RUN instruction. + :param pulumi.Input[bool] build_on_preview: + When true, attempt to build the image during previews. Outputs are not + pushed to registries, however caches are still populated. :param pulumi.Input[str] builder: Build with a specific builder instance :param pulumi.Input[Sequence[pulumi.Input[str]]] cache_from: @@ -60,6 +64,8 @@ def __init__(__self__, *, pulumi.set(__self__, "tags", tags) if build_args is not None: pulumi.set(__self__, "build_args", build_args) + if build_on_preview is not None: + pulumi.set(__self__, "build_on_preview", build_on_preview) if builder is not None: pulumi.set(__self__, "builder", builder) if cache_from is not None: @@ -111,6 +117,20 @@ def build_args(self) -> Optional[pulumi.Input[Mapping[str, pulumi.Input[str]]]]: def build_args(self, value: Optional[pulumi.Input[Mapping[str, pulumi.Input[str]]]]): pulumi.set(self, "build_args", value) + @property + @pulumi.getter(name="buildOnPreview") + def build_on_preview(self) -> Optional[pulumi.Input[bool]]: + """ + + When true, attempt to build the image during previews. Outputs are not + pushed to registries, however caches are still populated. + """ + return pulumi.get(self, "build_on_preview") + + @build_on_preview.setter + def build_on_preview(self, value: Optional[pulumi.Input[bool]]): + pulumi.set(self, "build_on_preview", value) + @property @pulumi.getter def builder(self) -> Optional[pulumi.Input[str]]: @@ -236,6 +256,7 @@ def __init__(__self__, resource_name: str, opts: Optional[pulumi.ResourceOptions] = None, build_args: Optional[pulumi.Input[Mapping[str, pulumi.Input[str]]]] = None, + build_on_preview: Optional[pulumi.Input[bool]] = None, builder: Optional[pulumi.Input[str]] = None, cache_from: Optional[pulumi.Input[Sequence[pulumi.Input[str]]]] = None, cache_to: Optional[pulumi.Input[Sequence[pulumi.Input[str]]]] = None, @@ -257,6 +278,9 @@ def __init__(__self__, the Docker build. This flag allows you to pass build-time variables that can be accessed like environment variables inside the RUN instruction. + :param pulumi.Input[bool] build_on_preview: + When true, attempt to build the image during previews. Outputs are not + pushed to registries, however caches are still populated. :param pulumi.Input[str] builder: Build with a specific builder instance :param pulumi.Input[Sequence[pulumi.Input[str]]] cache_from: @@ -305,6 +329,7 @@ def _internal_init(__self__, resource_name: str, opts: Optional[pulumi.ResourceOptions] = None, build_args: Optional[pulumi.Input[Mapping[str, pulumi.Input[str]]]] = None, + build_on_preview: Optional[pulumi.Input[bool]] = None, builder: Optional[pulumi.Input[str]] = None, cache_from: Optional[pulumi.Input[Sequence[pulumi.Input[str]]]] = None, cache_to: Optional[pulumi.Input[Sequence[pulumi.Input[str]]]] = None, @@ -325,6 +350,7 @@ def _internal_init(__self__, __props__ = ImageArgs.__new__(ImageArgs) __props__.__dict__["build_args"] = build_args + __props__.__dict__["build_on_preview"] = build_on_preview __props__.__dict__["builder"] = builder __props__.__dict__["cache_from"] = cache_from __props__.__dict__["cache_to"] = cache_to @@ -364,6 +390,7 @@ def get(resource_name: str, __props__ = ImageArgs.__new__(ImageArgs) __props__.__dict__["build_args"] = None + __props__.__dict__["build_on_preview"] = None __props__.__dict__["builder"] = None __props__.__dict__["cache_from"] = None __props__.__dict__["cache_to"] = None @@ -390,6 +417,16 @@ def build_args(self) -> pulumi.Output[Optional[Mapping[str, str]]]: """ return pulumi.get(self, "build_args") + @property + @pulumi.getter(name="buildOnPreview") + def build_on_preview(self) -> pulumi.Output[Optional[bool]]: + """ + + When true, attempt to build the image during previews. Outputs are not + pushed to registries, however caches are still populated. + """ + return pulumi.get(self, "build_on_preview") + @property @pulumi.getter def builder(self) -> pulumi.Output[Optional[str]]: