diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..75b9fc1 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,12 @@ +## Unreleased + +## 0.0.2 (2024-04-25) + +### Fixed + +- Upgraded pulumi-go-provider to fix a panic during cancellation. + +## 0.0.1 (2024-04-23) + +Initial release. + diff --git a/provider/internal/cache.go b/provider/internal/cache.go index 471d10d..424c03f 100644 --- a/provider/internal/cache.go +++ b/provider/internal/cache.go @@ -17,6 +17,7 @@ package internal import ( "errors" "fmt" + "reflect" "strings" controllerapi "github.com/docker/buildx/controller/pb" @@ -460,6 +461,23 @@ func (c CacheFrom) String() string { return join(c.Local, c.Registry, c.GHA, c.AZBlob, c.S3, c.Raw) } +// Diff returns false if both cache entries are identical enough to ignore +// changes during diff. Differences to tokens are allowed. +func (c CacheFrom) Diff(other CacheFrom) bool { + if c.GHA != nil && other.GHA != nil { + other.GHA.Token = c.GHA.Token + } + if c.S3 != nil && other.S3 != nil { + other.S3.AccessKeyID = c.S3.AccessKeyID + other.S3.SecretAccessKey = c.S3.SecretAccessKey + other.S3.SessionToken = c.S3.SessionToken + } + if c.AZBlob != nil && other.AZBlob != nil { + other.AZBlob.SecretAccessKey = c.AZBlob.SecretAccessKey + } + return reflect.DeepEqual(c, other) +} + func (c CacheFrom) validate(preview bool) (*controllerapi.CacheOptionsEntry, error) { if strings.Count(c.String(), "type=") > 1 { return nil, errors.New("cacheFrom should only specify one cache type") @@ -670,6 +688,23 @@ func (c CacheTo) String() string { return join(c.Inline, c.Local, c.Registry, c.GHA, c.AZBlob, c.S3, c.Raw) } +// Diff returns false if both cache entries are identical enough to ignore +// changes during diff. Differences to tokens are allowed. +func (c CacheTo) Diff(other CacheTo) bool { + if c.GHA != nil && other.GHA != nil { + other.GHA.Token = c.GHA.Token + } + if c.S3 != nil && other.S3 != nil { + other.S3.AccessKeyID = c.S3.AccessKeyID + other.S3.SecretAccessKey = c.S3.SecretAccessKey + other.S3.SessionToken = c.S3.SessionToken + } + if c.AZBlob != nil && other.AZBlob != nil { + other.AZBlob.SecretAccessKey = c.AZBlob.SecretAccessKey + } + return reflect.DeepEqual(c, other) +} + func (c CacheTo) validate(preview bool) (*controllerapi.CacheOptionsEntry, error) { if strings.Count(c.String(), "type=") > 1 { return nil, errors.New("cacheTo should only specify one cache type") diff --git a/provider/internal/image.go b/provider/internal/image.go index 0002ec3..21105c8 100644 --- a/provider/internal/image.go +++ b/provider/internal/image.go @@ -889,12 +889,6 @@ func (*Image) Diff( if !reflect.DeepEqual(olds.Builder, news.Builder) { diff["builder"] = update } - if !reflect.DeepEqual(olds.CacheFrom, news.CacheFrom) { - diff["cacheFrom"] = update - } - if !reflect.DeepEqual(olds.CacheTo, news.CacheTo) { - diff["cacheTo"] = update - } if olds.Context.Location != news.Context.Location { diff["context.location"] = update } @@ -975,6 +969,31 @@ func (*Image) Diff( break } } + // The GHA cache needs similar handling to ignore changes to the token. + if len(olds.CacheFrom) != len(news.CacheFrom) { + diff["cacheFrom"] = update + } else { + for idx, oldc := range olds.CacheFrom { + newc := news.CacheFrom[idx] + if newc.Diff(oldc) { + continue + } + diff[fmt.Sprintf("cacheFrom[%d]", idx)] = update + break + } + } + if len(olds.CacheTo) != len(news.CacheTo) { + diff["cacheTo"] = update + } else { + for idx, oldc := range olds.CacheTo { + newc := news.CacheTo[idx] + if !newc.Diff(oldc) { + continue + } + diff[fmt.Sprintf("cacheTo[%d]", idx)] = update + break + } + } return provider.DiffResponse{ HasChanges: len(diff) > 0, diff --git a/provider/internal/image_test.go b/provider/internal/image_test.go index e3fd21b..93e977e 100644 --- a/provider/internal/image_test.go +++ b/provider/internal/image_test.go @@ -370,7 +370,7 @@ func TestRead(t *testing.T) { } func TestImageDiff(t *testing.T) { - t.Parallel() + // t.Parallel() emptyDir := t.TempDir() host := Host @@ -419,6 +419,106 @@ func TestImageDiff(t *testing.T) { }, wantChanges: false, }, + { + name: "no diff if cache tokens change", + olds: func(_ *testing.T, s ImageState) ImageState { + s.CacheTo = []CacheTo{ + { + GHA: &CacheToGitHubActions{ + CacheFromGitHubActions: CacheFromGitHubActions{ + URL: "unchanged-cacheto-url", + Token: "old-cacheto-token", + }, + }, + }, + { + S3: &CacheToS3{ + CacheFromS3: CacheFromS3{ + AccessKeyID: "old-cacheto-access-key-id", + SecretAccessKey: "old-cacheto-secret-access-key", + SessionToken: "old-cacheto-session-token", + }, + }, + }, + { + AZBlob: &CacheToAzureBlob{ + CacheFromAzureBlob: CacheFromAzureBlob{ + SecretAccessKey: "old-cacheto-secret-access-key", + }, + }, + }, + } + s.CacheFrom = []CacheFrom{ + { + GHA: &CacheFromGitHubActions{ + URL: "unchanged-cachefrom-url", + Token: "old-cachefrom-token", + }, + }, + { + S3: &CacheFromS3{ + AccessKeyID: "old-cachefrom-access-key-id", + SecretAccessKey: "old-cachefrom-secret-access-key", + SessionToken: "old-cachefrom-session-token", + }, + }, + { + AZBlob: &CacheFromAzureBlob{ + SecretAccessKey: "old-cachefrom-secret-access-key", + }, + }, + } + return s + }, + news: func(_ *testing.T, a ImageArgs) ImageArgs { + a.CacheTo = []CacheTo{ + { + GHA: &CacheToGitHubActions{ + CacheFromGitHubActions: CacheFromGitHubActions{ + URL: "unchanged-cacheto-url", + Token: "new-cacheto-token", + }, + }, + }, + { + S3: &CacheToS3{ + CacheFromS3: CacheFromS3{ + AccessKeyID: "new-cacheto-access-key-id", + SecretAccessKey: "new-cacheto-secret-access-key", + SessionToken: "new-cacheto-session-token", + }, + }, + }, + { + AZBlob: &CacheToAzureBlob{ + CacheFromAzureBlob: CacheFromAzureBlob{ + SecretAccessKey: "new-cacheto-secret-access-key", + }, + }, + }, + } + a.CacheFrom = []CacheFrom{ + { + GHA: &CacheFromGitHubActions{ + URL: "unchanged-cachefrom-url", + Token: "new-cachefrom-token", + }, + }, { + S3: &CacheFromS3{ + AccessKeyID: "new-cachefrom-access-key-id", + SecretAccessKey: "new-cachefrom-secret-access-key", + SessionToken: "new-cachefrom-session-token", + }, + }, { + AZBlob: &CacheFromAzureBlob{ + SecretAccessKey: "new-cachefrom-secret-access-key", + }, + }, + } + return a + }, + wantChanges: false, + }, { name: "no diff if pull=true but no exports", olds: func(_ *testing.T, is ImageState) ImageState { @@ -431,6 +531,108 @@ func TestImageDiff(t *testing.T) { }, wantChanges: false, }, + { + name: "diff if gha cache parameters change", + olds: func(_ *testing.T, s ImageState) ImageState { + s.CacheTo = []CacheTo{{ + GHA: &CacheToGitHubActions{ + CacheFromGitHubActions: CacheFromGitHubActions{ + Scope: "old-cacheto-scope", + }, + }, + }} + s.CacheFrom = []CacheFrom{{ + GHA: &CacheFromGitHubActions{ + Scope: "old-cachefrom-scope", + }, + }} + return s + }, + news: func(_ *testing.T, a ImageArgs) ImageArgs { + a.CacheTo = []CacheTo{{ + GHA: &CacheToGitHubActions{ + CacheFromGitHubActions: CacheFromGitHubActions{ + Scope: "new-cacheto-scope", + }, + }, + }} + a.CacheFrom = []CacheFrom{{ + GHA: &CacheFromGitHubActions{ + Scope: "new-cachefrom-scope", + }, + }} + return a + }, + wantChanges: true, + }, + { + name: "diff if s3 cache parameters change", + olds: func(_ *testing.T, s ImageState) ImageState { + s.CacheTo = []CacheTo{{ + S3: &CacheToS3{ + CacheFromS3: CacheFromS3{ + Region: "old-cacheto-region", + }, + }, + }} + s.CacheFrom = []CacheFrom{{ + S3: &CacheFromS3{ + Region: "old-cachefrom-region", + }, + }} + return s + }, + news: func(_ *testing.T, a ImageArgs) ImageArgs { + a.CacheTo = []CacheTo{{ + S3: &CacheToS3{ + CacheFromS3: CacheFromS3{ + Region: "new-cacheto-region", + }, + }, + }} + a.CacheFrom = []CacheFrom{{ + S3: &CacheFromS3{ + Region: "new-cachefrom-region", + }, + }} + return a + }, + wantChanges: true, + }, + { + name: "diff if azblob cache parameters change", + olds: func(_ *testing.T, s ImageState) ImageState { + s.CacheTo = []CacheTo{{ + AZBlob: &CacheToAzureBlob{ + CacheFromAzureBlob: CacheFromAzureBlob{ + Name: "old-cacheto-name", + }, + }, + }} + s.CacheFrom = []CacheFrom{{ + AZBlob: &CacheFromAzureBlob{ + Name: "old-cachefrom-name", + }, + }} + return s + }, + news: func(_ *testing.T, a ImageArgs) ImageArgs { + a.CacheTo = []CacheTo{{ + AZBlob: &CacheToAzureBlob{ + CacheFromAzureBlob: CacheFromAzureBlob{ + Name: "new-cacheto-name", + }, + }, + }} + a.CacheFrom = []CacheFrom{{ + AZBlob: &CacheFromAzureBlob{ + Name: "new-cachefrom-name", + }, + }} + return a + }, + wantChanges: true, + }, { name: "diff if pull=true with exports", olds: func(_ *testing.T, is ImageState) ImageState {