From 397c959c21e804a46e18e948fedd6009ed56df5e Mon Sep 17 00:00:00 2001 From: Nitish Gupta Date: Sat, 23 Jul 2022 11:30:48 +0530 Subject: [PATCH 1/2] flags: Add support for 'volume' cache format using `--cache` flag - Extend support for `--cache` flag to volume cache format for both build and launch cache types Signed-off-by: Nitish Gupta --- internal/build/lifecycle_execution.go | 8 +-- internal/cache/cache_opts.go | 93 ++++++++++++--------------- internal/cache/volume_cache.go | 14 ++-- internal/commands/build.go | 6 +- 4 files changed, 59 insertions(+), 62 deletions(-) diff --git a/internal/build/lifecycle_execution.go b/internal/build/lifecycle_execution.go index c4bd6ba442..b04eb22ae2 100644 --- a/internal/build/lifecycle_execution.go +++ b/internal/build/lifecycle_execution.go @@ -124,10 +124,10 @@ func (l *LifecycleExecution) PrevImageName() string { func (l *LifecycleExecution) Run(ctx context.Context, phaseFactoryCreator PhaseFactoryCreator) error { phaseFactory := phaseFactoryCreator(l) var buildCache Cache - if l.opts.CacheImage != "" || (l.opts.Cache.CacheType == cache.Build && l.opts.Cache.Format == cache.CacheImage) { + if l.opts.CacheImage != "" || (l.opts.Cache.Build.Format == cache.CacheImage) { cacheImageName := l.opts.CacheImage if cacheImageName == "" { - cacheImageName = l.opts.Cache.Source + cacheImageName = l.opts.Cache.Build.Source } cacheImage, err := name.ParseReference(cacheImageName, name.WeakValidation) if err != nil { @@ -135,7 +135,7 @@ func (l *LifecycleExecution) Run(ctx context.Context, phaseFactoryCreator PhaseF } buildCache = cache.NewImageCache(cacheImage, l.docker) } else { - buildCache = cache.NewVolumeCache(l.opts.Image, "build", l.docker) + buildCache = cache.NewVolumeCache(l.opts.Image, l.opts.Cache.Build, "build", l.docker) } l.logger.Debugf("Using build cache volume %s", style.Symbol(buildCache.Name())) @@ -146,7 +146,7 @@ func (l *LifecycleExecution) Run(ctx context.Context, phaseFactoryCreator PhaseF l.logger.Debugf("Build cache %s cleared", style.Symbol(buildCache.Name())) } - launchCache := cache.NewVolumeCache(l.opts.Image, "launch", l.docker) + launchCache := cache.NewVolumeCache(l.opts.Image, l.opts.Cache.Launch, "launch", l.docker) if !l.opts.UseCreator { if l.platformAPI.LessThan("0.7") { diff --git a/internal/cache/cache_opts.go b/internal/cache/cache_opts.go index 2aaec39b8a..4f2c7ef99d 100644 --- a/internal/cache/cache_opts.go +++ b/internal/cache/cache_opts.go @@ -8,33 +8,21 @@ import ( "github.com/pkg/errors" ) -type Cache int type Format int +type CacheInfo struct { + Format Format + Source string +} type CacheOpts struct { - CacheType Cache - Format Format - Source string + Build CacheInfo + Launch CacheInfo } -const ( - Build Cache = iota - Launch -) const ( CacheVolume Format = iota CacheImage ) -func (c Cache) String() string { - switch c { - case Build: - return "build" - case Launch: - return "launch" - } - return "" -} - func (f Format) String() string { switch f { case CacheImage: @@ -53,36 +41,46 @@ func (c *CacheOpts) Set(value string) error { return err } + cache := &c.Build for _, field := range fields { parts := strings.SplitN(field, "=", 2) - if len(parts) != 2 { return errors.Errorf("invalid field '%s' must be a key=value pair", field) } + key := strings.ToLower(parts[0]) + value := strings.ToLower(parts[1]) + if key == "type" { + switch value { + case "build": + cache = &c.Build + case "launch": + cache = &c.Launch + default: + return errors.Errorf("invalid cache type '%s'", value) + } + break + } + } - if len(parts) == 2 { - key := strings.ToLower(parts[0]) - value := strings.ToLower(parts[1]) - switch key { - case "type": - switch value { - case "build": - c.CacheType = Build - case "launch": - c.CacheType = Launch - default: - return errors.Errorf("invalid cache type '%s'", value) - } - case "format": - switch value { - case "image": - c.Format = CacheImage - default: - return errors.Errorf("invalid cache format '%s'", value) - } - case "name": - c.Source = value + for _, field := range fields { + parts := strings.SplitN(field, "=", 2) + if len(parts) != 2 { + return errors.Errorf("invalid field '%s' must be a key=value pair", field) + } + key := strings.ToLower(parts[0]) + value := strings.ToLower(parts[1]) + switch key { + case "format": + switch value { + case "image": + cache.Format = CacheImage + case "volume": + cache.Format = CacheVolume + default: + return errors.Errorf("invalid cache format '%s'", value) } + case "name": + cache.Source = value } } @@ -95,15 +93,8 @@ func (c *CacheOpts) Set(value string) error { func (c *CacheOpts) String() string { var cacheFlag string - if c.CacheType.String() != "" { - cacheFlag += fmt.Sprintf("type=%s;", c.CacheType) - } - if c.Format.String() != "" { - cacheFlag += fmt.Sprintf("format=%s;", c.Format) - } - if c.Source != "" { - cacheFlag += fmt.Sprintf("name=%s", c.Source) - } + cacheFlag = fmt.Sprintf("type=build;format=%s;name=%s;", c.Build.Format.String(), c.Build.Source) + cacheFlag += fmt.Sprintf("type=launch;format=%s;name=%s;", c.Launch.Format.String(), c.Launch.Source) return cacheFlag } @@ -112,7 +103,7 @@ func (c *CacheOpts) Type() string { } func populateMissing(c *CacheOpts) error { - if c.Source == "" { + if (c.Build.Source == "" && c.Build.Format == CacheImage) || (c.Launch.Source == "" && c.Launch.Format == CacheImage) { return errors.Errorf("cache 'name' is required") } return nil diff --git a/internal/cache/volume_cache.go b/internal/cache/volume_cache.go index fa0124e50a..4c009c7437 100644 --- a/internal/cache/volume_cache.go +++ b/internal/cache/volume_cache.go @@ -17,12 +17,18 @@ type VolumeCache struct { volume string } -func NewVolumeCache(imageRef name.Reference, suffix string, dockerClient client.CommonAPIClient) *VolumeCache { - sum := sha256.Sum256([]byte(imageRef.Name())) +func NewVolumeCache(imageRef name.Reference, cacheType CacheInfo, suffix string, dockerClient client.CommonAPIClient) *VolumeCache { + var volumeName string + if cacheType.Source == "" { + sum := sha256.Sum256([]byte(imageRef.Name())) + vol := paths.FilterReservedNames(fmt.Sprintf("%s-%x", sanitizedRef(imageRef), sum[:6])) + volumeName = fmt.Sprintf("pack-cache-%s.%s", vol, suffix) + } else { + volumeName = paths.FilterReservedNames(cacheType.Source) + } - vol := paths.FilterReservedNames(fmt.Sprintf("%s-%x", sanitizedRef(imageRef), sum[:6])) return &VolumeCache{ - volume: fmt.Sprintf("pack-cache-%s.%s", vol, suffix), + volume: volumeName, docker: dockerClient, } } diff --git a/internal/commands/build.go b/internal/commands/build.go index 571798718a..b07c2f0eb1 100644 --- a/internal/commands/build.go +++ b/internal/commands/build.go @@ -241,15 +241,15 @@ func validateBuildFlags(flags *BuildFlags, cfg config.Config, packClient PackCli return client.NewExperimentError("Support for buildpack registries is currently experimental.") } - if flags.Cache.CacheType == cache.Launch && flags.Cache.Format == cache.CacheImage { + if flags.Cache.Launch.Format == cache.CacheImage { logger.Warn("cache definition: 'launch' cache in format 'image' is not supported.") } - if flags.Cache.Format == cache.CacheImage && flags.CacheImage != "" { + if flags.Cache.Build.Format == cache.CacheImage && flags.CacheImage != "" { return errors.New("'cache' flag with 'image' format cannot be used with 'cache-image' flag.") } - if flags.Cache.Format == cache.CacheImage && !flags.Publish { + if flags.Cache.Build.Format == cache.CacheImage && !flags.Publish { return errors.New("image cache format requires the 'publish' flag") } From 1eb52c0bc03cb327f1c2e62b55c23dba33d92776 Mon Sep 17 00:00:00 2001 From: Nitish Gupta Date: Sat, 23 Jul 2022 11:32:50 +0530 Subject: [PATCH 2/2] tests: Add tests for `--cache` flag using volume cache format Signed-off-by: Nitish Gupta --- acceptance/acceptance_test.go | 8 +- internal/build/lifecycle_execution_test.go | 7 +- internal/cache/cache_opts_test.go | 100 ++++++++-- internal/cache/volume_cache_test.go | 209 ++++++++++++++------- internal/commands/build_test.go | 6 +- 5 files changed, 239 insertions(+), 91 deletions(-) diff --git a/acceptance/acceptance_test.go b/acceptance/acceptance_test.go index b8f865eb2f..ec32ad2c2f 100644 --- a/acceptance/acceptance_test.go +++ b/acceptance/acceptance_test.go @@ -780,8 +780,8 @@ func testAcceptance( ref, err := name.ParseReference(repoName, name.WeakValidation) assert.Nil(err) cacheImage := cache.NewImageCache(ref, dockerCli) - buildCacheVolume := cache.NewVolumeCache(ref, "build", dockerCli) - launchCacheVolume := cache.NewVolumeCache(ref, "launch", dockerCli) + buildCacheVolume := cache.NewVolumeCache(ref, cache.CacheInfo{}, "build", dockerCli) + launchCacheVolume := cache.NewVolumeCache(ref, cache.CacheInfo{}, "launch", dockerCli) cacheImage.Clear(context.TODO()) buildCacheVolume.Clear(context.TODO()) launchCacheVolume.Clear(context.TODO()) @@ -2423,8 +2423,8 @@ include = [ "*.jar", "media/mountain.jpg", "/media/person.png", ] imageManager.CleanupImages(origID, repoName, runBefore) ref, err := name.ParseReference(repoName, name.WeakValidation) assert.Nil(err) - buildCacheVolume := cache.NewVolumeCache(ref, "build", dockerCli) - launchCacheVolume := cache.NewVolumeCache(ref, "launch", dockerCli) + buildCacheVolume := cache.NewVolumeCache(ref, cache.CacheInfo{}, "build", dockerCli) + launchCacheVolume := cache.NewVolumeCache(ref, cache.CacheInfo{}, "launch", dockerCli) assert.Succeeds(buildCacheVolume.Clear(context.TODO())) assert.Succeeds(launchCacheVolume.Clear(context.TODO())) }) diff --git a/internal/build/lifecycle_execution_test.go b/internal/build/lifecycle_execution_test.go index f1038f65f6..9fa74edd68 100644 --- a/internal/build/lifecycle_execution_test.go +++ b/internal/build/lifecycle_execution_test.go @@ -365,9 +365,10 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { TrustBuilder: false, UseCreator: false, Cache: cache.CacheOpts{ - CacheType: cache.Build, - Format: cache.CacheImage, - Source: "%%%", + Build: cache.CacheInfo{ + Format: cache.CacheImage, + Source: "%%%", + }, }, Termui: fakeTermui, } diff --git a/internal/cache/cache_opts_test.go b/internal/cache/cache_opts_test.go index c64e53e8df..13499eed17 100644 --- a/internal/cache/cache_opts_test.go +++ b/internal/cache/cache_opts_test.go @@ -24,18 +24,18 @@ func TestMetadata(t *testing.T) { } func testCacheOpts(t *testing.T, when spec.G, it spec.S) { - when("cache options are passed", func() { - it("image cache format with complete options", func() { + when("image cache format options are passed", func() { + it("with complete options", func() { testcases := []CacheOptTestCase{ { name: "Build cache as Image", input: "type=build;format=image;name=io.test.io/myorg/my-cache:build", - output: "type=build;format=image;name=io.test.io/myorg/my-cache:build", + output: "type=build;format=image;name=io.test.io/myorg/my-cache:build;type=launch;format=volume;name=;", }, { name: "Launch cache as Image", input: "type=launch;format=image;name=io.test.io/myorg/my-cache:build", - output: "type=launch;format=image;name=io.test.io/myorg/my-cache:build", + output: "type=build;format=volume;name=;type=launch;format=image;name=io.test.io/myorg/my-cache:build;", }, } @@ -48,22 +48,17 @@ func testCacheOpts(t *testing.T, when spec.G, it spec.S) { } }) - it("image cache format with missing options", func() { + it("with missing options", func() { successTestCases := []CacheOptTestCase{ { name: "Build cache as Image missing: type", input: "format=image;name=io.test.io/myorg/my-cache:build", - output: "type=build;format=image;name=io.test.io/myorg/my-cache:build", + output: "type=build;format=image;name=io.test.io/myorg/my-cache:build;type=launch;format=volume;name=;", }, { name: "Build cache as Image missing: format", input: "type=build;name=io.test.io/myorg/my-cache:build", - output: "type=build;format=volume;name=io.test.io/myorg/my-cache:build", - }, - { - name: "Build cache as Image missing: type, format", - input: "name=io.test.io/myorg/my-cache:build", - output: "type=build;format=volume;name=io.test.io/myorg/my-cache:build", + output: "type=build;format=volume;name=io.test.io/myorg/my-cache:build;type=launch;format=volume;name=;", }, { name: "Build cache as Image missing: name", @@ -72,10 +67,14 @@ func testCacheOpts(t *testing.T, when spec.G, it spec.S) { shouldFail: true, }, { - name: "Build cache as Image missing: format, name", - input: "type=build", - output: "cache 'name' is required", - shouldFail: true, + name: "Build cache as Image missing: type, format", + input: "name=io.test.io/myorg/my-cache:build", + output: "type=build;format=volume;name=io.test.io/myorg/my-cache:build;type=launch;format=volume;name=;", + }, + { + name: "Build cache as Image missing: format, name", + input: "type=build", + output: "type=build;format=volume;name=;type=launch;format=volume;name=;", }, { name: "Build cache as Image missing: type, name", @@ -94,6 +93,9 @@ func testCacheOpts(t *testing.T, when spec.G, it spec.S) { for _, testcase := range successTestCases { var cacheFlags CacheOpts t.Logf("Testing cache type: %s", testcase.name) + if testcase.name == "Everything missing" { + print("i am here") + } err := cacheFlags.Set(testcase.input) if testcase.shouldFail { @@ -106,7 +108,7 @@ func testCacheOpts(t *testing.T, when spec.G, it spec.S) { } }) - it("image cache format with invalid options", func() { + it("with invalid options", func() { testcases := []CacheOptTestCase{ { name: "Invalid cache type", @@ -142,4 +144,68 @@ func testCacheOpts(t *testing.T, when spec.G, it spec.S) { } }) }) + + when("volume cache format options are passed", func() { + it("with complete options", func() { + testcases := []CacheOptTestCase{ + { + name: "Build cache as Volume", + input: "type=build;format=volume;name=test-build-volume-cache", + output: "type=build;format=volume;name=test-build-volume-cache;type=launch;format=volume;name=;", + }, + { + name: "Launch cache as Volume", + input: "type=launch;format=volume;name=test-launch-volume-cache", + output: "type=build;format=volume;name=;type=launch;format=volume;name=test-launch-volume-cache;", + }, + } + + for _, testcase := range testcases { + var cacheFlags CacheOpts + t.Logf("Testing cache type: %s", testcase.name) + err := cacheFlags.Set(testcase.input) + h.AssertNil(t, err) + h.AssertEq(t, testcase.output, cacheFlags.String()) + } + }) + + it("with missing options", func() { + successTestCases := []CacheOptTestCase{ + { + name: "Launch cache as Volume missing: format", + input: "type=launch;name=test-launch-volume", + output: "type=build;format=volume;name=;type=launch;format=volume;name=test-launch-volume;", + }, + { + name: "Launch cache as Volume missing: name", + input: "type=launch;format=volume", + output: "type=build;format=volume;name=;type=launch;format=volume;name=;", + }, + { + name: "Launch cache as Volume missing: format, name", + input: "type=launch", + output: "type=build;format=volume;name=;type=launch;format=volume;name=;", + }, + { + name: "Launch cache as Volume missing: type, name", + input: "format=volume", + output: "type=build;format=volume;name=;type=launch;format=volume;name=;", + }, + } + + for _, testcase := range successTestCases { + var cacheFlags CacheOpts + t.Logf("Testing cache type: %s", testcase.name) + err := cacheFlags.Set(testcase.input) + + if testcase.shouldFail { + h.AssertError(t, err, testcase.output) + } else { + h.AssertNil(t, err) + output := cacheFlags.String() + h.AssertEq(t, testcase.output, output) + } + } + }) + }) } diff --git a/internal/cache/volume_cache_test.go b/internal/cache/volume_cache_test.go index 9a36489ce9..ce30c9e5a0 100644 --- a/internal/cache/volume_cache_test.go +++ b/internal/cache/volume_cache_test.go @@ -38,86 +38,167 @@ func testCache(t *testing.T, when spec.G, it spec.S) { h.AssertNil(t, err) }) when("#NewVolumeCache", func() { - it("adds suffix to calculated name", func() { - ref, err := name.ParseReference("my/repo", name.WeakValidation) - h.AssertNil(t, err) - subject := cache.NewVolumeCache(ref, "some-suffix", dockerClient) - if !strings.HasSuffix(subject.Name(), ".some-suffix") { - t.Fatalf("Calculated volume name '%s' should end with '.some-suffix'", subject.Name()) - } - }) + when("volume cache name is empty", func() { + it("adds suffix to calculated name", func() { + ref, err := name.ParseReference("my/repo", name.WeakValidation) + h.AssertNil(t, err) + subject := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) + if !strings.HasSuffix(subject.Name(), ".some-suffix") { + t.Fatalf("Calculated volume name '%s' should end with '.some-suffix'", subject.Name()) + } + }) - it("reusing the same cache for the same repo name", func() { - ref, err := name.ParseReference("my/repo", name.WeakValidation) - h.AssertNil(t, err) + it("reusing the same cache for the same repo name", func() { + ref, err := name.ParseReference("my/repo", name.WeakValidation) + h.AssertNil(t, err) - subject := cache.NewVolumeCache(ref, "some-suffix", dockerClient) - expected := cache.NewVolumeCache(ref, "some-suffix", dockerClient) - if subject.Name() != expected.Name() { - t.Fatalf("The same repo name should result in the same volume") - } - }) + subject := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) + expected := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) + if subject.Name() != expected.Name() { + t.Fatalf("The same repo name should result in the same volume") + } + }) - it("supplies different volumes for different tags", func() { - ref, err := name.ParseReference("my/repo:other-tag", name.WeakValidation) - h.AssertNil(t, err) + it("supplies different volumes for different tags", func() { + ref, err := name.ParseReference("my/repo:other-tag", name.WeakValidation) + h.AssertNil(t, err) - subject := cache.NewVolumeCache(ref, "some-suffix", dockerClient) + subject := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) - ref, err = name.ParseReference("my/repo", name.WeakValidation) - h.AssertNil(t, err) - notExpected := cache.NewVolumeCache(ref, "some-suffix", dockerClient) - if subject.Name() == notExpected.Name() { - t.Fatalf("Different image tags should result in different volumes") - } - }) + ref, err = name.ParseReference("my/repo", name.WeakValidation) + h.AssertNil(t, err) + notExpected := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) + if subject.Name() == notExpected.Name() { + t.Fatalf("Different image tags should result in different volumes") + } + }) - it("supplies different volumes for different registries", func() { - ref, err := name.ParseReference("registry.com/my/repo:other-tag", name.WeakValidation) - h.AssertNil(t, err) + it("supplies different volumes for different registries", func() { + ref, err := name.ParseReference("registry.com/my/repo:other-tag", name.WeakValidation) + h.AssertNil(t, err) - subject := cache.NewVolumeCache(ref, "some-suffix", dockerClient) + subject := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) - ref, err = name.ParseReference("my/repo", name.WeakValidation) - h.AssertNil(t, err) - notExpected := cache.NewVolumeCache(ref, "some-suffix", dockerClient) - if subject.Name() == notExpected.Name() { - t.Fatalf("Different image registries should result in different volumes") - } - }) + ref, err = name.ParseReference("my/repo", name.WeakValidation) + h.AssertNil(t, err) + notExpected := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) + if subject.Name() == notExpected.Name() { + t.Fatalf("Different image registries should result in different volumes") + } + }) - it("resolves implied tag", func() { - ref, err := name.ParseReference("my/repo:latest", name.WeakValidation) - h.AssertNil(t, err) + it("resolves implied tag", func() { + ref, err := name.ParseReference("my/repo:latest", name.WeakValidation) + h.AssertNil(t, err) - subject := cache.NewVolumeCache(ref, "some-suffix", dockerClient) + subject := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) - ref, err = name.ParseReference("my/repo", name.WeakValidation) - h.AssertNil(t, err) - expected := cache.NewVolumeCache(ref, "some-suffix", dockerClient) - h.AssertEq(t, subject.Name(), expected.Name()) - }) + ref, err = name.ParseReference("my/repo", name.WeakValidation) + h.AssertNil(t, err) + expected := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) + h.AssertEq(t, subject.Name(), expected.Name()) + }) - it("resolves implied registry", func() { - ref, err := name.ParseReference("index.docker.io/my/repo", name.WeakValidation) - h.AssertNil(t, err) + it("resolves implied registry", func() { + ref, err := name.ParseReference("index.docker.io/my/repo", name.WeakValidation) + h.AssertNil(t, err) - subject := cache.NewVolumeCache(ref, "some-suffix", dockerClient) + subject := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) - ref, err = name.ParseReference("my/repo", name.WeakValidation) - h.AssertNil(t, err) - expected := cache.NewVolumeCache(ref, "some-suffix", dockerClient) - h.AssertEq(t, subject.Name(), expected.Name()) + ref, err = name.ParseReference("my/repo", name.WeakValidation) + h.AssertNil(t, err) + expected := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) + h.AssertEq(t, subject.Name(), expected.Name()) + }) + + it("includes human readable information", func() { + ref, err := name.ParseReference("myregistryhost:5000/fedora/httpd:version1.0", name.WeakValidation) + h.AssertNil(t, err) + + subject := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) + + h.AssertContains(t, subject.Name(), "fedora_httpd_version1.0") + h.AssertTrue(t, names.RestrictedNamePattern.MatchString(subject.Name())) + }) }) - it("includes human readable information", func() { - ref, err := name.ParseReference("myregistryhost:5000/fedora/httpd:version1.0", name.WeakValidation) - h.AssertNil(t, err) + when("volume cache name is not empty", func() { + volumeName := "test-volume-name" + cacheInfo := cache.CacheInfo{ + Format: cache.CacheVolume, + Source: volumeName, + } + + it("named volume created without suffix", func() { + ref, err := name.ParseReference("my/repo", name.WeakValidation) + h.AssertNil(t, err) - subject := cache.NewVolumeCache(ref, "some-suffix", dockerClient) + subject := cache.NewVolumeCache(ref, cacheInfo, "some-suffix", dockerClient) + + if volumeName != subject.Name() { + t.Fatalf("Volume name '%s' should be same as the name specified '%s'", subject.Name(), volumeName) + } + }) - h.AssertContains(t, subject.Name(), "fedora_httpd_version1.0") - h.AssertTrue(t, names.RestrictedNamePattern.MatchString(subject.Name())) + it("reusing the same cache for the same repo name", func() { + ref, err := name.ParseReference("my/repo", name.WeakValidation) + h.AssertNil(t, err) + + subject := cache.NewVolumeCache(ref, cacheInfo, "some-suffix", dockerClient) + + expected := cache.NewVolumeCache(ref, cacheInfo, "some-suffix", dockerClient) + if subject.Name() != expected.Name() { + t.Fatalf("The same repo name should result in the same volume") + } + }) + + it("supplies different volumes for different registries", func() { + ref, err := name.ParseReference("registry.com/my/repo:other-tag", name.WeakValidation) + h.AssertNil(t, err) + + subject := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) + + ref, err = name.ParseReference("my/repo", name.WeakValidation) + h.AssertNil(t, err) + notExpected := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) + if subject.Name() == notExpected.Name() { + t.Fatalf("Different image registries should result in different volumes") + } + }) + + it("resolves implied tag", func() { + ref, err := name.ParseReference("my/repo:latest", name.WeakValidation) + h.AssertNil(t, err) + + subject := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) + + ref, err = name.ParseReference("my/repo", name.WeakValidation) + h.AssertNil(t, err) + expected := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) + h.AssertEq(t, subject.Name(), expected.Name()) + }) + + it("resolves implied registry", func() { + ref, err := name.ParseReference("index.docker.io/my/repo", name.WeakValidation) + h.AssertNil(t, err) + + subject := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) + + ref, err = name.ParseReference("my/repo", name.WeakValidation) + h.AssertNil(t, err) + expected := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) + h.AssertEq(t, subject.Name(), expected.Name()) + }) + + it("includes human readable information", func() { + ref, err := name.ParseReference("myregistryhost:5000/fedora/httpd:version1.0", name.WeakValidation) + h.AssertNil(t, err) + + subject := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) + + h.AssertContains(t, subject.Name(), "fedora_httpd_version1.0") + h.AssertTrue(t, names.RestrictedNamePattern.MatchString(subject.Name())) + }) }) }) @@ -138,7 +219,7 @@ func testCache(t *testing.T, when spec.G, it spec.S) { ref, err := name.ParseReference(h.RandString(10), name.WeakValidation) h.AssertNil(t, err) - subject = cache.NewVolumeCache(ref, "some-suffix", dockerClient) + subject = cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) volumeName = subject.Name() }) @@ -174,7 +255,7 @@ func testCache(t *testing.T, when spec.G, it spec.S) { it("returns the cache type", func() { ref, err := name.ParseReference("my/repo", name.WeakValidation) h.AssertNil(t, err) - subject := cache.NewVolumeCache(ref, "some-suffix", dockerClient) + subject := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) expected := cache.Volume h.AssertEq(t, subject.Type(), expected) }) diff --git a/internal/commands/build_test.go b/internal/commands/build_test.go index 5f68e40b92..c647c17112 100644 --- a/internal/commands/build_test.go +++ b/internal/commands/build_test.go @@ -358,7 +358,7 @@ func testBuildCommand(t *testing.T, when spec.G, it spec.S) { when("--publish is used", func() { it("succeeds", func() { mockClient.EXPECT(). - Build(gomock.Any(), EqBuildOptionsWithCacheFlags("type=build;format=image;name=myorg/myimage:cache")). + Build(gomock.Any(), EqBuildOptionsWithCacheFlags("type=build;format=image;name=myorg/myimage:cache;type=launch;format=volume;name=;")). Return(nil) command.SetArgs([]string{"--builder", "my-builder", "image", "--cache", "type=build;format=image;name=myorg/myimage:cache", "--publish"}) @@ -372,10 +372,10 @@ func testBuildCommand(t *testing.T, when spec.G, it spec.S) { h.AssertError(t, err, "'cache' flag with 'image' format cannot be used with 'cache-image' flag") }) }) - when("''type=launch;format=image' is used", func() { + when("'type=launch;format=image' is used", func() { it("warns", func() { mockClient.EXPECT(). - Build(gomock.Any(), EqBuildOptionsWithCacheFlags("type=launch;format=image;name=myorg/myimage:cache")). + Build(gomock.Any(), EqBuildOptionsWithCacheFlags("type=build;format=volume;name=;type=launch;format=image;name=myorg/myimage:cache;")). Return(nil) command.SetArgs([]string{"--builder", "my-builder", "image", "--cache", "type=launch;format=image;name=myorg/myimage:cache", "--publish"})