Fix optional/pointer indirection errors

This commit is contained in:
Bryce Lampe
2024-04-16 13:03:21 -07:00
parent 59fd3c84e5
commit eca03ce6db
15 changed files with 138 additions and 90 deletions

View File

@@ -342,7 +342,7 @@ func (c *CacheFromS3) String() string {
// CacheWithMode is a cache that can configure its mode. // CacheWithMode is a cache that can configure its mode.
type CacheWithMode struct { type CacheWithMode struct {
Mode CacheMode `pulumi:"mode,optional"` Mode *CacheMode `pulumi:"mode,optional"`
} }
// Annotate sets docstrings and defaults on CacheWithMode. // Annotate sets docstrings and defaults on CacheWithMode.
@@ -354,10 +354,10 @@ func (c *CacheWithMode) Annotate(a infer.Annotator) {
} }
func (c CacheWithMode) String() string { func (c CacheWithMode) String() string {
if c.Mode == "" { if c.Mode == nil {
return "" return ""
} }
return fmt.Sprintf("mode=%s", c.Mode) return fmt.Sprintf("mode=%s", *c.Mode)
} }
// CacheWithIgnoreError exposes an option to ignore errors during caching. // CacheWithIgnoreError exposes an option to ignore errors during caching.
@@ -552,9 +552,9 @@ func (c *CacheToRegistry) String() string {
// CacheWithCompression is a cache with options to configure compression // CacheWithCompression is a cache with options to configure compression
// settings. // settings.
type CacheWithCompression struct { type CacheWithCompression struct {
Compression CompressionType `pulumi:"compression,optional"` Compression *CompressionType `pulumi:"compression,optional"`
CompressionLevel int `pulumi:"compressionLevel,optional"` CompressionLevel int `pulumi:"compressionLevel,optional"`
ForceCompression *bool `pulumi:"forceCompression,optional"` ForceCompression *bool `pulumi:"forceCompression,optional"`
} }
// Annotate sets docstrings and defaults on CacheWithCompression. // Annotate sets docstrings and defaults on CacheWithCompression.
@@ -575,8 +575,8 @@ func (c CacheWithCompression) String() string {
return "" return ""
} }
parts := []string{} parts := []string{}
if c.Compression != "" { if c.Compression != nil {
parts = append(parts, fmt.Sprintf("compression=%s", c.Compression)) parts = append(parts, fmt.Sprintf("compression=%s", *c.Compression))
} }
if c.CompressionLevel > 0 { if c.CompressionLevel > 0 {
cl := c.CompressionLevel cl := c.CompressionLevel

View File

@@ -26,6 +26,7 @@ import (
func TestCacheString(t *testing.T) { func TestCacheString(t *testing.T) {
t.Parallel() t.Parallel()
gzip := Gzip
tests := []struct { tests := []struct {
name string name string
@@ -81,12 +82,12 @@ func TestCacheString(t *testing.T) {
given: CacheTo{Local: &CacheToLocal{ given: CacheTo{Local: &CacheToLocal{
Dest: "/foo", Dest: "/foo",
CacheWithCompression: CacheWithCompression{ CacheWithCompression: CacheWithCompression{
Compression: "gz2", Compression: &gzip,
CompressionLevel: 100, CompressionLevel: 100,
ForceCompression: pulumi.BoolRef(true), ForceCompression: pulumi.BoolRef(true),
}, },
}}, }},
want: "type=local,dest=/foo,compression=gz2,compression-level=22,force-compression=true", want: "type=local,dest=/foo,compression=gzip,compression-level=22,force-compression=true",
}, },
{ {
name: "ignore-error", name: "ignore-error",

View File

@@ -90,8 +90,9 @@ func TestBuild(t *testing.T) {
pctx.EXPECT().Deadline().Return(ctx.Deadline()).AnyTimes() pctx.EXPECT().Deadline().Return(ctx.Deadline()).AnyTimes()
tmpdir := t.TempDir() tmpdir := t.TempDir()
max := Max
exampleContext := BuildContext{Context: Context{Location: "../../examples/app"}} exampleContext := &BuildContext{Context: Context{Location: "../../examples/app"}}
tests := []struct { tests := []struct {
name string name string
@@ -104,7 +105,7 @@ func TestBuild(t *testing.T) {
name: "multiPlatform", name: "multiPlatform",
args: ImageArgs{ args: ImageArgs{
Context: exampleContext, Context: exampleContext,
Dockerfile: Dockerfile{ Dockerfile: &Dockerfile{
Location: "../../examples/app/Dockerfile.multiPlatform", Location: "../../examples/app/Dockerfile.multiPlatform",
}, },
Platforms: []Platform{"plan9/amd64", "plan9/arm64"}, Platforms: []Platform{"plan9/amd64", "plan9/arm64"},
@@ -131,7 +132,7 @@ func TestBuild(t *testing.T) {
Tags: []string{"cached"}, Tags: []string{"cached"},
CacheTo: []CacheTo{{Local: &CacheToLocal{ CacheTo: []CacheTo{{Local: &CacheToLocal{
Dest: filepath.Join(tmpdir, "cache"), Dest: filepath.Join(tmpdir, "cache"),
CacheWithMode: CacheWithMode{Mode: "max"}, CacheWithMode: CacheWithMode{Mode: &max},
}}}, }}},
CacheFrom: []CacheFrom{{Local: &CacheFromLocal{ CacheFrom: []CacheFrom{{Local: &CacheFromLocal{
Src: filepath.Join(tmpdir, "cache"), Src: filepath.Join(tmpdir, "cache"),
@@ -142,7 +143,7 @@ func TestBuild(t *testing.T) {
name: "buildArgs", name: "buildArgs",
args: ImageArgs{ args: ImageArgs{
Context: exampleContext, Context: exampleContext,
Dockerfile: Dockerfile{ Dockerfile: &Dockerfile{
Location: "../../examples/app/Dockerfile.buildArgs", Location: "../../examples/app/Dockerfile.buildArgs",
}, },
BuildArgs: map[string]string{ BuildArgs: map[string]string{
@@ -154,7 +155,7 @@ func TestBuild(t *testing.T) {
name: "extraHosts", name: "extraHosts",
args: ImageArgs{ args: ImageArgs{
Context: exampleContext, Context: exampleContext,
Dockerfile: Dockerfile{ Dockerfile: &Dockerfile{
Location: "../../examples/app/Dockerfile.extraHosts", Location: "../../examples/app/Dockerfile.extraHosts",
}, },
AddHosts: []string{ AddHosts: []string{
@@ -167,7 +168,7 @@ func TestBuild(t *testing.T) {
skip: os.Getenv("SSH_AUTH_SOCK") == "", skip: os.Getenv("SSH_AUTH_SOCK") == "",
args: ImageArgs{ args: ImageArgs{
Context: exampleContext, Context: exampleContext,
Dockerfile: Dockerfile{ Dockerfile: &Dockerfile{
Location: "../../examples/app/Dockerfile.sshMount", Location: "../../examples/app/Dockerfile.sshMount",
}, },
SSH: []SSH{{ID: "default"}}, SSH: []SSH{{ID: "default"}},
@@ -177,7 +178,7 @@ func TestBuild(t *testing.T) {
name: "secrets", name: "secrets",
args: ImageArgs{ args: ImageArgs{
Context: exampleContext, Context: exampleContext,
Dockerfile: Dockerfile{ Dockerfile: &Dockerfile{
Location: "../../examples/app/Dockerfile.secrets", Location: "../../examples/app/Dockerfile.secrets",
}, },
Secrets: map[string]string{ Secrets: map[string]string{
@@ -199,7 +200,7 @@ func TestBuild(t *testing.T) {
name: "target", name: "target",
args: ImageArgs{ args: ImageArgs{
Context: exampleContext, Context: exampleContext,
Dockerfile: Dockerfile{ Dockerfile: &Dockerfile{
Location: "../../examples/app/Dockerfile.target", Location: "../../examples/app/Dockerfile.target",
}, },
Target: "build-me", Target: "build-me",
@@ -208,7 +209,7 @@ func TestBuild(t *testing.T) {
{ {
name: "namedContext", name: "namedContext",
args: ImageArgs{ args: ImageArgs{
Context: BuildContext{ Context: &BuildContext{
Context: Context{ Context: Context{
Location: "../../examples/app", Location: "../../examples/app",
}, },
@@ -218,7 +219,7 @@ func TestBuild(t *testing.T) {
}, },
}, },
}, },
Dockerfile: Dockerfile{ Dockerfile: &Dockerfile{
Location: "../../examples/app/Dockerfile.namedContexts", Location: "../../examples/app/Dockerfile.namedContexts",
}, },
}, },
@@ -226,7 +227,7 @@ func TestBuild(t *testing.T) {
{ {
name: "remoteContext", name: "remoteContext",
args: ImageArgs{ args: ImageArgs{
Context: BuildContext{ Context: &BuildContext{
Context: Context{ Context: Context{
Location: "https://raw.githubusercontent.com/pulumi/pulumi-docker/api-types/provider/testdata/Dockerfile", Location: "https://raw.githubusercontent.com/pulumi/pulumi-docker/api-types/provider/testdata/Dockerfile",
}, },
@@ -236,12 +237,12 @@ func TestBuild(t *testing.T) {
{ {
name: "remoteContextWithInline", name: "remoteContextWithInline",
args: ImageArgs{ args: ImageArgs{
Context: BuildContext{ Context: &BuildContext{
Context: Context{ Context: Context{
Location: "https://github.com/docker-library/hello-world.git", Location: "https://github.com/docker-library/hello-world.git",
}, },
}, },
Dockerfile: Dockerfile{ Dockerfile: &Dockerfile{
Inline: dedent(` Inline: dedent(`
FROM busybox FROM busybox
COPY hello.c ./ COPY hello.c ./
@@ -253,7 +254,7 @@ func TestBuild(t *testing.T) {
name: "inline", name: "inline",
args: ImageArgs{ args: ImageArgs{
Context: exampleContext, Context: exampleContext,
Dockerfile: Dockerfile{ Dockerfile: &Dockerfile{
Inline: dedent(` Inline: dedent(`
FROM alpine FROM alpine
RUN echo 👍 RUN echo 👍

View File

@@ -54,6 +54,13 @@ type BuildContext struct {
Named NamedContexts `pulumi:"named,optional"` Named NamedContexts `pulumi:"named,optional"`
} }
func (bc *BuildContext) namedMap() map[string]string {
if bc == nil {
return nil
}
return bc.Named.Map()
}
// NamedContexts correspond to Docker's `--build-context name=path` options. // NamedContexts correspond to Docker's `--build-context name=path` options.
// The path can be local or a remote URL. // The path can be local or a remote URL.
type NamedContexts map[string]Context type NamedContexts map[string]Context
@@ -84,23 +91,31 @@ func (c *Context) Annotate(a infer.Annotator) {
// validate returns a non-nil CheckError if the Context is invalid. The // validate returns a non-nil CheckError if the Context is invalid. The
// returned Dockerfile may have defaults set to match Docker's default // returned Dockerfile may have defaults set to match Docker's default
// handling. The returned Dockerfile should be validated separately. // handling. The returned Dockerfile should be validated separately.
func (c *Context) validate(preview bool, d Dockerfile) (Dockerfile, error) { func (bc *BuildContext) validate(preview bool, d *Dockerfile) (*Dockerfile, *Context, error) {
if d == nil {
d = &Dockerfile{}
}
c := &Context{}
if bc != nil {
c = &bc.Context
}
if c.Location == "" && preview { if c.Location == "" && preview {
// The field is required so we normally wouldn't need to check if it // The field is required so we normally wouldn't need to check if it
// exists, but during previews it can still be empty if the value is // exists, but during previews it can still be empty if the value is
// unknown. This isn't an error, but it does prevent us from performing // unknown. This isn't an error, but it does prevent us from performing
// a build later. // a build later.
return d, nil return d, c, nil
} }
if buildx.IsRemoteURL(c.Location) { if buildx.IsRemoteURL(c.Location) {
// We assume remote URLs are always valid. // We assume remote URLs are always valid.
return d, nil return d, c, nil
} }
abs, err := filepath.Abs(c.Location) abs, err := filepath.Abs(c.Location)
if err != nil { if err != nil {
return d, newCheckFailure(err, "context.location") return d, c, newCheckFailure(err, "context.location")
} }
if d.Location == "" && d.Inline == "" { if d.Location == "" && d.Inline == "" {
@@ -111,17 +126,17 @@ func (c *Context) validate(preview bool, d Dockerfile) (Dockerfile, error) {
if isLocalDir(afero.NewOsFs(), abs) { if isLocalDir(afero.NewOsFs(), abs) {
// Our context exists -- nothing else to check. // Our context exists -- nothing else to check.
return d, nil return d, c, nil
} }
if c.Location != "-" { if c.Location != "-" {
return d, newCheckFailure( return d, c, newCheckFailure(
fmt.Errorf("%q: not a valid directory or URL", c.Location), fmt.Errorf("%q: not a valid directory or URL", c.Location),
"context.location", "context.location",
) )
} }
return d, nil return d, c, nil
} }
// Annotate sets docstrings on BuildContext. // Annotate sets docstrings on BuildContext.

View File

@@ -96,7 +96,8 @@ func TestValidateContext(t *testing.T) {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
t.Parallel() t.Parallel()
d, err := tt.c.validate(tt.preview, tt.givenD) bc := &BuildContext{Context: tt.c}
d, _, err := bc.validate(tt.preview, &tt.givenD)
if tt.wantErr == "" { if tt.wantErr == "" {
assert.NoError(t, err) assert.NoError(t, err)

View File

@@ -54,7 +54,7 @@ func (d *Dockerfile) Annotate(a infer.Annotator) {
`)) `))
} }
func (d *Dockerfile) validate(preview bool, c Context) error { func (d *Dockerfile) validate(preview bool, c *Context) error {
if d.Location != "" && d.Inline != "" { if d.Location != "" && d.Inline != "" {
return newCheckFailure( return newCheckFailure(
errors.New(`only specify "file" or "inline", not both`), errors.New(`only specify "file" or "inline", not both`),
@@ -88,7 +88,7 @@ func (d *Dockerfile) validate(preview bool, c Context) error {
return nil return nil
} }
if !preview && !buildx.IsRemoteURL(c.Location) { if !preview && c != nil && !buildx.IsRemoteURL(c.Location) {
return newCheckFailure(errors.New("missing 'location' or 'inline'"), "dockerfile") return newCheckFailure(errors.New("missing 'location' or 'inline'"), "dockerfile")
} }

View File

@@ -90,7 +90,7 @@ func TestValidateDockerfile(t *testing.T) {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
t.Parallel() t.Parallel()
err := tt.d.validate(tt.preview, tt.givenC) err := tt.d.validate(tt.preview, &tt.givenC)
if tt.wantErr == "" { if tt.wantErr == "" {
assert.NoError(t, err) assert.NoError(t, err)

View File

@@ -386,9 +386,9 @@ func (c ExportWithOCI) String() string {
// ExportWithCompression is an export with options to configure compression // ExportWithCompression is an export with options to configure compression
// settings. // settings.
type ExportWithCompression struct { type ExportWithCompression struct {
Compression CompressionType `pulumi:"compression,optional"` Compression *CompressionType `pulumi:"compression,optional"`
CompressionLevel int `pulumi:"compressionLevel,optional"` CompressionLevel int `pulumi:"compressionLevel,optional"`
ForceCompression *bool `pulumi:"forceCompression,optional"` ForceCompression *bool `pulumi:"forceCompression,optional"`
} }
// Annotate sets docstrings and defaults on ExportWithCompression. // Annotate sets docstrings and defaults on ExportWithCompression.
@@ -407,8 +407,8 @@ func (e ExportWithCompression) String() string {
return "" return ""
} }
parts := []string{} parts := []string{}
if e.Compression != "" { if e.Compression != nil {
parts = append(parts, fmt.Sprintf("compression=%s", e.Compression)) parts = append(parts, fmt.Sprintf("compression=%s", *e.Compression))
} }
if e.CompressionLevel > 0 { if e.CompressionLevel > 0 {
cl := e.CompressionLevel cl := e.CompressionLevel

View File

@@ -93,6 +93,7 @@ func TestValidateExport(t *testing.T) {
func TestExportString(t *testing.T) { func TestExportString(t *testing.T) {
t.Parallel() t.Parallel()
gzip := Gzip
tests := []struct { tests := []struct {
name string name string
given Export given Export
@@ -113,13 +114,13 @@ func TestExportString(t *testing.T) {
given: Export{Registry: &ExportRegistry{ given: Export{Registry: &ExportRegistry{
ExportImage: ExportImage{ ExportImage: ExportImage{
ExportWithCompression: ExportWithCompression{ ExportWithCompression: ExportWithCompression{
Compression: "gz2", Compression: &gzip,
CompressionLevel: 100, CompressionLevel: 100,
ForceCompression: pulumi.BoolRef(true), ForceCompression: pulumi.BoolRef(true),
}, },
}, },
}}, }},
want: "type=registry,compression=gz2,compression-level=22,force-compression=true", want: "type=registry,compression=gzip,compression-level=22,force-compression=true",
}, },
{ {
name: "registry-without-push", name: "registry-without-push",

View File

@@ -93,15 +93,15 @@ type ImageArgs struct {
AddHosts []string `pulumi:"addHosts,optional"` AddHosts []string `pulumi:"addHosts,optional"`
BuildArgs map[string]string `pulumi:"buildArgs,optional"` BuildArgs map[string]string `pulumi:"buildArgs,optional"`
BuildOnPreview *bool `pulumi:"buildOnPreview,optional"` BuildOnPreview *bool `pulumi:"buildOnPreview,optional"`
Builder BuilderConfig `pulumi:"builder,optional"` Builder *BuilderConfig `pulumi:"builder,optional"`
CacheFrom []CacheFrom `pulumi:"cacheFrom,optional"` CacheFrom []CacheFrom `pulumi:"cacheFrom,optional"`
CacheTo []CacheTo `pulumi:"cacheTo,optional"` CacheTo []CacheTo `pulumi:"cacheTo,optional"`
Context BuildContext `pulumi:"context,optional"` Context *BuildContext `pulumi:"context,optional"`
Dockerfile Dockerfile `pulumi:"dockerfile,optional"` Dockerfile *Dockerfile `pulumi:"dockerfile,optional"`
Exports []Export `pulumi:"exports,optional"` Exports []Export `pulumi:"exports,optional"`
Labels map[string]string `pulumi:"labels,optional"` Labels map[string]string `pulumi:"labels,optional"`
Load bool `pulumi:"load,optional"` Load bool `pulumi:"load,optional"`
Network NetworkMode `pulumi:"network,optional"` Network *NetworkMode `pulumi:"network,optional"`
NoCache bool `pulumi:"noCache,optional"` NoCache bool `pulumi:"noCache,optional"`
Platforms []Platform `pulumi:"platforms,optional"` Platforms []Platform `pulumi:"platforms,optional"`
Pull bool `pulumi:"pull,optional"` Pull bool `pulumi:"pull,optional"`
@@ -555,13 +555,13 @@ func (ia *ImageArgs) validate(preview bool) (controllerapi.BuildOptions, error)
) )
} }
dockerfile, err := ia.Context.validate(preview, ia.Dockerfile) dockerfile, context, err := ia.Context.validate(preview, ia.Dockerfile)
if err != nil { if err != nil {
multierr = errors.Join(multierr, err) multierr = errors.Join(multierr, err)
} }
ia.Dockerfile = dockerfile ia.Dockerfile = dockerfile
if err := ia.Dockerfile.validate(preview, ia.Context.Context); err != nil { if err := ia.Dockerfile.validate(preview, context); err != nil {
multierr = errors.Join(multierr, err) multierr = errors.Join(multierr, err)
} }
@@ -644,19 +644,24 @@ func (ia *ImageArgs) validate(preview bool) (controllerapi.BuildOptions, error)
}) })
} }
builder := BuilderConfig{}
if normalized.Builder != nil {
builder = *normalized.Builder
}
opts := controllerapi.BuildOptions{ opts := controllerapi.BuildOptions{
BuildArgs: normalized.BuildArgs, BuildArgs: normalized.BuildArgs,
Builder: normalized.Builder.Name, Builder: builder.Name,
CacheFrom: cacheFrom, CacheFrom: cacheFrom,
CacheTo: cacheTo, CacheTo: cacheTo,
ContextPath: normalized.Context.Location, ContextPath: context.Location,
DockerfileName: dockerfile.Location, DockerfileName: dockerfile.Location,
Exports: exports, Exports: exports,
ExtraHosts: normalized.AddHosts, ExtraHosts: normalized.AddHosts,
Labels: normalized.Labels, Labels: normalized.Labels,
NetworkMode: string(normalized.Network), NetworkMode: normalized.Network.String(),
NoCache: normalized.NoCache, NoCache: normalized.NoCache,
NamedContexts: normalized.Context.Named.Map(), NamedContexts: normalized.Context.namedMap(),
Platforms: platforms, Platforms: platforms,
Pull: normalized.Pull, Pull: normalized.Pull,
Secrets: secrets, Secrets: secrets,
@@ -926,7 +931,7 @@ func (*Image) Diff(
if olds.Load != news.Load { if olds.Load != news.Load {
diff["load"] = update diff["load"] = update
} }
if olds.Network != news.Network { if !reflect.DeepEqual(olds.Network, news.Network) {
diff["network"] = update diff["network"] = update
} }
if !reflect.DeepEqual(olds.NoCache, news.NoCache) { if !reflect.DeepEqual(olds.NoCache, news.NoCache) {

View File

@@ -365,12 +365,13 @@ func TestRead(t *testing.T) {
func TestImageDiff(t *testing.T) { func TestImageDiff(t *testing.T) {
t.Parallel() t.Parallel()
emptyDir := t.TempDir() emptyDir := t.TempDir()
host := Host
hash, err := hashBuildContext(emptyDir, "", nil) hash, err := hashBuildContext(emptyDir, "", nil)
require.NoError(t, err) require.NoError(t, err)
baseArgs := ImageArgs{ baseArgs := ImageArgs{
Context: BuildContext{Context: Context{Location: emptyDir}}, Context: &BuildContext{Context: Context{Location: emptyDir}},
Dockerfile: Dockerfile{Location: "testdata/noop"}, Dockerfile: &Dockerfile{Location: "testdata/noop"},
Tags: []string{}, Tags: []string{},
} }
baseState := ImageState{ baseState := ImageState{
@@ -414,6 +415,7 @@ func TestImageDiff(t *testing.T) {
{ {
name: "no diff if pull=true but no exports", name: "no diff if pull=true but no exports",
olds: func(_ *testing.T, is ImageState) ImageState { olds: func(_ *testing.T, is ImageState) ImageState {
fmt.Println("WHOA NELLY")
is.Pull = true is.Pull = true
return is return is
}, },
@@ -566,7 +568,7 @@ func TestImageDiff(t *testing.T) {
name: "diff if context changes", name: "diff if context changes",
olds: func(*testing.T, ImageState) ImageState { return baseState }, olds: func(*testing.T, ImageState) ImageState { return baseState },
news: func(_ *testing.T, a ImageArgs) ImageArgs { news: func(_ *testing.T, a ImageArgs) ImageArgs {
a.Context.Location = "testdata/ignores" a.Context = &BuildContext{Context: Context{Location: "testdata/ignores"}}
return a return a
}, },
wantChanges: true, wantChanges: true,
@@ -575,7 +577,7 @@ func TestImageDiff(t *testing.T) {
name: "diff if named context changes", name: "diff if named context changes",
olds: func(*testing.T, ImageState) ImageState { return baseState }, olds: func(*testing.T, ImageState) ImageState { return baseState },
news: func(_ *testing.T, a ImageArgs) ImageArgs { news: func(_ *testing.T, a ImageArgs) ImageArgs {
a.Context.Named = NamedContexts{"foo": Context{Location: "bar"}} a.Context = &BuildContext{Named: NamedContexts{"foo": Context{Location: "bar"}}}
return a return a
}, },
wantChanges: true, wantChanges: true,
@@ -584,7 +586,7 @@ func TestImageDiff(t *testing.T) {
name: "diff if network changes", name: "diff if network changes",
olds: func(*testing.T, ImageState) ImageState { return baseState }, olds: func(*testing.T, ImageState) ImageState { return baseState },
news: func(_ *testing.T, a ImageArgs) ImageArgs { news: func(_ *testing.T, a ImageArgs) ImageArgs {
a.Network = Host a.Network = &host
return a return a
}, },
wantChanges: true, wantChanges: true,
@@ -593,7 +595,7 @@ func TestImageDiff(t *testing.T) {
name: "diff if dockerfile location changes", name: "diff if dockerfile location changes",
olds: func(*testing.T, ImageState) ImageState { return baseState }, olds: func(*testing.T, ImageState) ImageState { return baseState },
news: func(_ *testing.T, a ImageArgs) ImageArgs { news: func(_ *testing.T, a ImageArgs) ImageArgs {
a.Dockerfile.Location = "testdata/ignores/basedir/Dockerfile" a.Dockerfile = &Dockerfile{Location: "testdata/ignores/basedir/Dockerfile"}
return a return a
}, },
wantChanges: true, wantChanges: true,
@@ -602,7 +604,7 @@ func TestImageDiff(t *testing.T) {
name: "diff if dockerfile inline changes", name: "diff if dockerfile inline changes",
olds: func(*testing.T, ImageState) ImageState { return baseState }, olds: func(*testing.T, ImageState) ImageState { return baseState },
news: func(_ *testing.T, a ImageArgs) ImageArgs { news: func(_ *testing.T, a ImageArgs) ImageArgs {
a.Dockerfile.Inline = "FROM scratch" a.Dockerfile = &Dockerfile{Inline: "FROM scratch"}
return a return a
}, },
wantChanges: true, wantChanges: true,
@@ -629,7 +631,7 @@ func TestImageDiff(t *testing.T) {
name: "diff if builder changes", name: "diff if builder changes",
olds: func(*testing.T, ImageState) ImageState { return baseState }, olds: func(*testing.T, ImageState) ImageState { return baseState },
news: func(_ *testing.T, a ImageArgs) ImageArgs { news: func(_ *testing.T, a ImageArgs) ImageArgs {
a.Builder.Name = "foo" a.Builder = &BuilderConfig{Name: "foo"}
return a return a
}, },
wantChanges: true, wantChanges: true,
@@ -709,6 +711,8 @@ func TestImageDiff(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
tt := tt tt := tt
baseState := baseState
baseArgs := baseArgs
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
t.Parallel() t.Parallel()
resp, err := s.Diff(provider.DiffRequest{ resp, err := s.Diff(provider.DiffRequest{
@@ -729,7 +733,7 @@ func TestValidateImageArgs(t *testing.T) {
args := ImageArgs{ args := ImageArgs{
Tags: []string{"a/bad:tag:format"}, Tags: []string{"a/bad:tag:format"},
Exports: []Export{{Raw: "badexport,-"}}, Exports: []Export{{Raw: "badexport,-"}},
Context: BuildContext{Context: Context{Location: "./testdata"}}, Context: &BuildContext{Context: Context{Location: "./testdata"}},
Platforms: []Platform{","}, Platforms: []Platform{","},
CacheFrom: []CacheFrom{{Raw: "=badcachefrom"}}, CacheFrom: []CacheFrom{{Raw: "=badcachefrom"}},
CacheTo: []CacheTo{{Raw: "=badcacheto"}}, CacheTo: []CacheTo{{Raw: "=badcacheto"}},
@@ -747,7 +751,7 @@ func TestValidateImageArgs(t *testing.T) {
t.Run("buildOnPreview", func(t *testing.T) { t.Run("buildOnPreview", func(t *testing.T) {
t.Parallel() t.Parallel()
args := ImageArgs{ args := ImageArgs{
Context: BuildContext{Context: Context{Location: "testdata/noop"}}, Context: &BuildContext{Context: Context{Location: "testdata/noop"}},
Tags: []string{"my-tag"}, Tags: []string{"my-tag"},
Exports: []Export{{Registry: &ExportRegistry{ExportImage{Push: pulumi.BoolRef(true)}}}}, Exports: []Export{{Registry: &ExportRegistry{ExportImage{Push: pulumi.BoolRef(true)}}}},
} }
@@ -775,12 +779,12 @@ func TestValidateImageArgs(t *testing.T) {
"known": "value", "known": "value",
"": "", "": "",
}, },
Builder: BuilderConfig{}, Builder: nil,
CacheFrom: []CacheFrom{{GHA: &CacheFromGitHubActions{}}, {Raw: ""}}, CacheFrom: []CacheFrom{{GHA: &CacheFromGitHubActions{}}, {Raw: ""}},
CacheTo: []CacheTo{{GHA: &CacheToGitHubActions{}}, {Raw: ""}}, CacheTo: []CacheTo{{GHA: &CacheToGitHubActions{}}, {Raw: ""}},
Context: BuildContext{}, Context: nil,
Exports: []Export{{Raw: ""}}, Exports: []Export{{Raw: ""}},
Dockerfile: Dockerfile{}, Dockerfile: nil,
Platforms: []Platform{"linux/amd64", ""}, Platforms: []Platform{"linux/amd64", ""},
Registries: []Registry{ Registries: []Registry{
{ {
@@ -803,7 +807,7 @@ func TestValidateImageArgs(t *testing.T) {
t.Run("disabled caches", func(t *testing.T) { t.Run("disabled caches", func(t *testing.T) {
t.Parallel() t.Parallel()
args := ImageArgs{ args := ImageArgs{
Context: BuildContext{Context: Context{Location: "testdata/noop"}}, Context: &BuildContext{Context: Context{Location: "testdata/noop"}},
CacheFrom: []CacheFrom{{Raw: "type=registry", Disabled: true}}, CacheFrom: []CacheFrom{{Raw: "type=registry", Disabled: true}},
CacheTo: []CacheTo{{Raw: "type=registry", Disabled: true}}, CacheTo: []CacheTo{{Raw: "type=registry", Disabled: true}},
Exports: []Export{{Raw: "type=registry", Disabled: true}}, Exports: []Export{{Raw: "type=registry", Disabled: true}},
@@ -853,7 +857,8 @@ func TestValidateImageArgs(t *testing.T) {
for _, d := range []Dockerfile{ for _, d := range []Dockerfile{
{Location: path}, {Inline: string(data)}, {Location: path}, {Inline: string(data)},
} { } {
args := ImageArgs{Dockerfile: d} d := d
args := ImageArgs{Dockerfile: &d}
_, err := args.validate(false) _, err := args.validate(false)
assert.ErrorContains(t, err, "unknown instruction: RUNN (did you mean RUN?)") assert.ErrorContains(t, err, "unknown instruction: RUNN (did you mean RUN?)")
} }
@@ -943,13 +948,14 @@ func TestToBuild(t *testing.T) {
ctrl := gomock.NewController(t) ctrl := gomock.NewController(t)
pctx := NewMockProviderContext(ctrl) pctx := NewMockProviderContext(ctrl)
pctx.EXPECT().Log(gomock.Any(), gomock.Any()).AnyTimes() pctx.EXPECT().Log(gomock.Any(), gomock.Any()).AnyTimes()
max := Max
ia := ImageArgs{ ia := ImageArgs{
Tags: []string{"foo", "bar"}, Tags: []string{"foo", "bar"},
Platforms: []Platform{"linux/amd64"}, Platforms: []Platform{"linux/amd64"},
Context: BuildContext{Context: Context{Location: "testdata/noop"}}, Context: &BuildContext{Context: Context{Location: "testdata/noop"}},
CacheTo: []CacheTo{ CacheTo: []CacheTo{
{GHA: &CacheToGitHubActions{CacheWithMode: CacheWithMode{Max}}}, {GHA: &CacheToGitHubActions{CacheWithMode: CacheWithMode{&max}}},
{ {
Registry: &CacheToRegistry{ Registry: &CacheToRegistry{
CacheFromRegistry: CacheFromRegistry{Ref: "docker.io/foo/bar"}, CacheFromRegistry: CacheFromRegistry{Ref: "docker.io/foo/bar"},

View File

@@ -47,10 +47,10 @@ type Index struct{}
// IndexArgs instantiate an Index. // IndexArgs instantiate an Index.
type IndexArgs struct { type IndexArgs struct {
Tag string `pulumi:"tag"` Tag string `pulumi:"tag"`
Sources []string `pulumi:"sources"` Sources []string `pulumi:"sources"`
Push bool `pulumi:"push,optional"` Push bool `pulumi:"push,optional"`
Registry Registry `pulumi:"registry,optional"` Registry *Registry `pulumi:"registry,optional"`
} }
// IndexState captures the state of an Index. // IndexState captures the state of an Index.
@@ -268,14 +268,20 @@ func (i *Index) Diff(
if !reflect.DeepEqual(olds.Sources, news.Sources) { if !reflect.DeepEqual(olds.Sources, news.Sources) {
diff["sources"] = update diff["sources"] = update
} }
if olds.Registry.Address != news.Registry.Address { if olds.Registry != nil && news.Registry != nil {
diff["registry.address"] = update if olds.Registry.Address != news.Registry.Address {
if olds.Registry.Address != "" { diff["registry.address"] = update
diff["registry.address"] = replace if olds.Registry.Address != "" {
diff["registry.address"] = replace
}
}
if olds.Registry.Username != news.Registry.Username {
diff["registry.username"] = update
} }
} }
if olds.Registry.Username != news.Registry.Username { if (olds.Registry == nil && news.Registry != nil) ||
diff["registry.username"] = update (olds.Registry != nil && news.Registry == nil) {
diff["registry"] = update
} }
// Intentionally ignore changes to registry.password // Intentionally ignore changes to registry.password
@@ -301,9 +307,14 @@ func (i *Index) client(
// We prefer auth from args, the provider, and state in that order. We // We prefer auth from args, the provider, and state in that order. We
// build a slice in reverse order because wrap() will overwrite earlier // build a slice in reverse order because wrap() will overwrite earlier
// entries with later ones. // entries with later ones.
auths := []Registry{state.Registry} auths := []Registry{}
if state.Registry != nil {
auths = append(auths, *state.Registry)
}
auths = append(auths, cfg.Registries...) auths = append(auths, cfg.Registries...)
auths = append(auths, args.Registry) if args.Registry != nil {
auths = append(auths, *args.Registry)
}
return wrap(cfg.host, auths...) return wrap(cfg.host, auths...)
} }

View File

@@ -138,7 +138,7 @@ func TestIndexDiff(t *testing.T) {
{ {
name: "no diff if registry password changes", name: "no diff if registry password changes",
olds: func(_ *testing.T, s IndexState) IndexState { olds: func(_ *testing.T, s IndexState) IndexState {
s.Registry = Registry{ s.Registry = &Registry{
Address: "foo", Address: "foo",
Username: "foo", Username: "foo",
Password: "foo", Password: "foo",
@@ -146,7 +146,7 @@ func TestIndexDiff(t *testing.T) {
return s return s
}, },
news: func(_ *testing.T, a IndexArgs) IndexArgs { news: func(_ *testing.T, a IndexArgs) IndexArgs {
a.Registry = Registry{ a.Registry = &Registry{
Address: "foo", Address: "foo",
Username: "foo", Username: "foo",
Password: "DIFFERENT PASSWORD", Password: "DIFFERENT PASSWORD",
@@ -159,7 +159,7 @@ func TestIndexDiff(t *testing.T) {
name: "diff if registry added", name: "diff if registry added",
olds: func(*testing.T, IndexState) IndexState { return baseState }, olds: func(*testing.T, IndexState) IndexState { return baseState },
news: func(_ *testing.T, a IndexArgs) IndexArgs { news: func(_ *testing.T, a IndexArgs) IndexArgs {
a.Registry = Registry{Address: "foo.com", Username: "foo", Password: "foo"} a.Registry = &Registry{Address: "foo.com", Username: "foo", Password: "foo"}
return a return a
}, },
wantChanges: true, wantChanges: true,
@@ -167,7 +167,7 @@ func TestIndexDiff(t *testing.T) {
{ {
name: "diff if registry user changes", name: "diff if registry user changes",
olds: func(_ *testing.T, s IndexState) IndexState { olds: func(_ *testing.T, s IndexState) IndexState {
s.Registry = Registry{ s.Registry = &Registry{
Address: "foo", Address: "foo",
Username: "foo", Username: "foo",
Password: "foo", Password: "foo",
@@ -175,7 +175,7 @@ func TestIndexDiff(t *testing.T) {
return s return s
}, },
news: func(_ *testing.T, a IndexArgs) IndexArgs { news: func(_ *testing.T, a IndexArgs) IndexArgs {
a.Registry = Registry{ a.Registry = &Registry{
Address: "DIFFERENT USER", Address: "DIFFERENT USER",
Username: "foo", Username: "foo",
Password: "foo", Password: "foo",

View File

@@ -44,3 +44,10 @@ func (NetworkMode) Values() []infer.EnumValue[NetworkMode] {
}, },
} }
} }
func (n *NetworkMode) String() string {
if n == nil {
return string(Default)
}
return string(*n)
}

View File

@@ -96,8 +96,8 @@ func (k mapKeeper) keep(m map[string]string) map[string]string {
type contextKeeper struct{ preview bool } type contextKeeper struct{ preview bool }
func (k contextKeeper) keep(bc BuildContext) BuildContext { func (k contextKeeper) keep(bc *BuildContext) *BuildContext {
if !k.preview || len(bc.Named) == 0 { if !k.preview || bc == nil || len(bc.Named) == 0 {
return bc return bc
} }
@@ -110,7 +110,7 @@ func (k contextKeeper) keep(bc BuildContext) BuildContext {
named[k] = v named[k] = v
} }
return BuildContext{ return &BuildContext{
Context: Context{bc.Location}, Context: Context{bc.Location},
Named: named, Named: named,
} }