From 1cc1cc8dd9978eb321ed5e6c1d99250311df5781 Mon Sep 17 00:00:00 2001 From: Natalie Arellano Date: Thu, 27 Jun 2024 14:24:02 -0400 Subject: [PATCH 1/3] Initial implementation (still has TODOs) Signed-off-by: Natalie Arellano --- internal/config/config.go | 9 +++++ pkg/cache/volume_cache.go | 64 +++++++++++++++++++++++++++++- pkg/cache/volume_cache_test.go | 71 +++++++++++++++++++++++++++++++++- 3 files changed, 142 insertions(+), 2 deletions(-) diff --git a/internal/config/config.go b/internal/config/config.go index de370ef727..c0e61ade88 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -23,6 +23,7 @@ type Config struct { LifecycleImage string `toml:"lifecycle-image,omitempty"` RegistryMirrors map[string]string `toml:"registry-mirrors,omitempty"` LayoutRepositoryDir string `toml:"layout-repo-dir,omitempty"` + VolumeKeys map[string]string `toml:"volume-keys,omitempty"` // TODO: move to own struct } type Registry struct { @@ -58,6 +59,14 @@ func DefaultConfigPath() (string, error) { return filepath.Join(home, "config.toml"), nil } +func DefaultVolumeKeysPath() (string, error) { + home, err := PackHome() + if err != nil { + return "", errors.Wrap(err, "getting pack home") + } + return filepath.Join(home, "volume-keys.toml"), nil +} + func PackHome() (string, error) { packHome := os.Getenv("PACK_HOME") if packHome == "" { diff --git a/pkg/cache/volume_cache.go b/pkg/cache/volume_cache.go index b274d3d3ff..bc6121a4aa 100644 --- a/pkg/cache/volume_cache.go +++ b/pkg/cache/volume_cache.go @@ -2,16 +2,21 @@ package cache import ( "context" + "crypto/rand" "crypto/sha256" "fmt" + "os" "strings" "github.com/docker/docker/client" "github.com/google/go-containerregistry/pkg/name" + "github.com/buildpacks/pack/internal/config" "github.com/buildpacks/pack/internal/paths" ) +const EnvVolumeKey = "PACK_VOLUME_KEY" + type VolumeCache struct { docker DockerClient volume string @@ -20,7 +25,11 @@ type VolumeCache struct { func NewVolumeCache(imageRef name.Reference, cacheType CacheInfo, suffix string, dockerClient DockerClient) *VolumeCache { var volumeName string if cacheType.Source == "" { - sum := sha256.Sum256([]byte(imageRef.Name())) + volumeKey, err := getVolumeKey(imageRef) + if err != nil { + // TODO + } + sum := sha256.Sum256([]byte(imageRef.Name() + volumeKey)) // TODO: investigate if there are better ways to do this vol := paths.FilterReservedNames(fmt.Sprintf("%s-%x", sanitizedRef(imageRef), sum[:6])) volumeName = fmt.Sprintf("pack-cache-%s.%s", vol, suffix) } else { @@ -33,6 +42,59 @@ func NewVolumeCache(imageRef name.Reference, cacheType CacheInfo, suffix string, } } +func getVolumeKey(imageRef name.Reference) (string, error) { + var foundKey string + + // first, look for key in env + + foundKey = os.Getenv(EnvVolumeKey) + if foundKey != "" { + return foundKey, nil + } + + // then, look for key in existing config + + volumeKeysPath, err := config.DefaultVolumeKeysPath() + if err != nil { + return "", err + } + cfg, err := config.Read(volumeKeysPath) + if err != nil { + return "", err + } + + foundKey = cfg.VolumeKeys[imageRef.Name()] + if foundKey != "" { + return foundKey, nil + } + + // finally, create new key and store it in config + + newKey := randString(20) + if cfg.VolumeKeys == nil { + cfg.VolumeKeys = make(map[string]string) + } + cfg.VolumeKeys[imageRef.Name()] = newKey + if err = config.Write(cfg, volumeKeysPath); err != nil { + return "", err + } + + return newKey, nil +} + +// Returns a string iwith lowercase a-z, of length n +func randString(n int) string { + b := make([]byte, n) + _, err := rand.Read(b) + if err != nil { + panic(err) + } + for i := range b { + b[i] = 'a' + (b[i] % 26) + } + return string(b) +} + func (c *VolumeCache) Name() string { return c.volume } diff --git a/pkg/cache/volume_cache_test.go b/pkg/cache/volume_cache_test.go index 5ae1e14a92..ee05fa12cc 100644 --- a/pkg/cache/volume_cache_test.go +++ b/pkg/cache/volume_cache_test.go @@ -2,9 +2,12 @@ package cache_test import ( "context" + "os" + "path/filepath" "strings" "testing" + "github.com/buildpacks/pack/internal/config" "github.com/buildpacks/pack/pkg/cache" "github.com/docker/docker/api/types/filters" @@ -24,7 +27,7 @@ func TestVolumeCache(t *testing.T) { color.Disable(true) defer color.Disable(false) - spec.Run(t, "VolumeCache", testCache, spec.Parallel(), spec.Report(report.Terminal{})) + spec.Run(t, "VolumeCache", testCache, spec.Sequential(), spec.Report(report.Terminal{})) } func testCache(t *testing.T, when spec.G, it spec.S) { @@ -118,6 +121,72 @@ func testCache(t *testing.T, when spec.G, it spec.S) { h.AssertContains(t, subject.Name(), "fedora_httpd_version1.0") h.AssertTrue(t, names.RestrictedNamePattern.MatchString(subject.Name())) }) + + when("PACK_VOLUME_KEY", func() { + when("is set", func() { + it.After(func() { + h.AssertNil(t, os.Unsetenv("PACK_VOLUME_KEY")) + }) + + it("uses it to construct the volume name", func() { + ref, err := name.ParseReference("my/repo:some-tag", name.WeakValidation) + h.AssertNil(t, err) + + nameFromNewKey := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) // sources a new key + h.AssertNil(t, os.Setenv("PACK_VOLUME_KEY", "some-volume-key")) + nameFromEnvKey := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) // sources key from env + h.AssertNotEq(t, nameFromNewKey.Name(), nameFromEnvKey.Name()) + }) + }) + + when("is unset", func() { + var tmpPackHome string + + it.Before(func() { + var err error + tmpPackHome, err = os.MkdirTemp("", "") + h.AssertNil(t, err) + h.AssertNil(t, os.Setenv("PACK_HOME", tmpPackHome)) + }) + + it.After(func() { + h.AssertNil(t, os.RemoveAll(tmpPackHome)) + }) + + when("~/.pack/volume-keys.toml contains key for repo name", func() { + it("sources the key from ~/.pack/volume-keys.toml", func() { + ref, err := name.ParseReference("my/repo:some-tag", name.WeakValidation) + h.AssertNil(t, err) + + nameFromNewKey := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) // sources a new key + + cfgContents := ` +[volume-keys] +"index.docker.io/my/repo:some-tag" = "SOME_VOLUME_KEY" +` + h.AssertNil(t, os.WriteFile(filepath.Join(tmpPackHome, "volume-keys.toml"), []byte(cfgContents), 0755)) // overrides the key that was set + + nameFromConfigKey := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) // sources key from config + h.AssertNotEq(t, nameFromNewKey.Name(), nameFromConfigKey.Name()) + }) + }) + + when("~/.pack/volume-keys.toml missing key for repo name", func() { + it("generates a new key and saves it to ~/.pack/volume-keys.toml", func() { + ref, err := name.ParseReference("my/repo:some-tag", name.WeakValidation) + h.AssertNil(t, err) + + nameFromNewKey := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) // sources a new key + nameFromConfigKey := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) // sources same key from config + h.AssertEq(t, nameFromNewKey.Name(), nameFromConfigKey.Name()) + + cfg, err := config.Read(filepath.Join(tmpPackHome, "volume-keys.toml")) + h.AssertNil(t, err) + h.AssertNotNil(t, cfg.VolumeKeys["index.docker.io/my/repo:some-tag"]) + }) + }) + }) + }) }) when("volume cache name is not empty", func() { From 8e0d859317f88569fd09a656471ab31f8df1c0af Mon Sep 17 00:00:00 2001 From: Natalie Arellano Date: Thu, 27 Jun 2024 15:01:39 -0400 Subject: [PATCH 2/3] Resolve some TODOs Signed-off-by: Natalie Arellano --- acceptance/acceptance_test.go | 12 +++--- internal/build/lifecycle_execution.go | 21 +++++++-- internal/config/config.go | 16 ++++++- pkg/cache/volume_cache.go | 8 ++-- pkg/cache/volume_cache_test.go | 62 +++++++++++++-------------- 5 files changed, 72 insertions(+), 47 deletions(-) diff --git a/acceptance/acceptance_test.go b/acceptance/acceptance_test.go index e7f52815d0..37cb048a05 100644 --- a/acceptance/acceptance_test.go +++ b/acceptance/acceptance_test.go @@ -1162,8 +1162,8 @@ func testAcceptance( ref, err := name.ParseReference(repoName, name.WeakValidation) assert.Nil(err) cacheImage := cache.NewImageCache(ref, dockerCli) - buildCacheVolume := cache.NewVolumeCache(ref, cache.CacheInfo{}, "build", dockerCli) - launchCacheVolume := cache.NewVolumeCache(ref, cache.CacheInfo{}, "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()) @@ -1282,8 +1282,8 @@ func testAcceptance( ref, err := name.ParseReference(repoName, name.WeakValidation) assert.Nil(err) cacheImage := cache.NewImageCache(ref, dockerCli) - buildCacheVolume := cache.NewVolumeCache(ref, cache.CacheInfo{}, "build", dockerCli) - launchCacheVolume := cache.NewVolumeCache(ref, cache.CacheInfo{}, "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()) @@ -3167,8 +3167,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, cache.CacheInfo{}, "build", dockerCli) - launchCacheVolume := cache.NewVolumeCache(ref, cache.CacheInfo{}, "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.go b/internal/build/lifecycle_execution.go index 2f017dd70f..1042fe0aa3 100644 --- a/internal/build/lifecycle_execution.go +++ b/internal/build/lifecycle_execution.go @@ -179,7 +179,11 @@ func (l *LifecycleExecution) Run(ctx context.Context, phaseFactoryCreator PhaseF } else { switch l.opts.Cache.Build.Format { case cache.CacheVolume: - buildCache = cache.NewVolumeCache(l.opts.Image, l.opts.Cache.Build, "build", l.docker) + var err error + buildCache, err = cache.NewVolumeCache(l.opts.Image, l.opts.Cache.Build, "build", l.docker) + if err != nil { + return err + } l.logger.Debugf("Using build cache volume %s", style.Symbol(buildCache.Name())) case cache.CacheBind: buildCache = cache.NewBindCache(l.opts.Cache.Build, l.docker) @@ -194,7 +198,10 @@ 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, l.opts.Cache.Launch, "launch", l.docker) + launchCache, err := cache.NewVolumeCache(l.opts.Image, l.opts.Cache.Launch, "launch", l.docker) + if err != nil { + return err + } if !l.opts.UseCreator { if l.platformAPI.LessThan("0.7") { @@ -224,7 +231,10 @@ func (l *LifecycleExecution) Run(ctx context.Context, phaseFactoryCreator PhaseF // lifecycle 0.17.0 (introduces support for Platform API 0.12) and above will ensure that // this volume is owned by the CNB user, // and hence the restorer (after dropping privileges) will be able to write to it. - kanikoCache = cache.NewVolumeCache(l.opts.Image, l.opts.Cache.Kaniko, "kaniko", l.docker) + kanikoCache, err = cache.NewVolumeCache(l.opts.Image, l.opts.Cache.Kaniko, "kaniko", l.docker) + if err != nil { + return err + } } else { switch { case buildCache.Type() == cache.Volume: @@ -236,7 +246,10 @@ func (l *LifecycleExecution) Run(ctx context.Context, phaseFactoryCreator PhaseF return fmt.Errorf("build cache must be volume cache when building with extensions") default: // The kaniko cache is unused, so it doesn't matter that it's not usable. - kanikoCache = cache.NewVolumeCache(l.opts.Image, l.opts.Cache.Kaniko, "kaniko", l.docker) + kanikoCache, err = cache.NewVolumeCache(l.opts.Image, l.opts.Cache.Kaniko, "kaniko", l.docker) + if err != nil { + return err + } } } diff --git a/internal/config/config.go b/internal/config/config.go index c0e61ade88..e15da68f03 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -23,7 +23,10 @@ type Config struct { LifecycleImage string `toml:"lifecycle-image,omitempty"` RegistryMirrors map[string]string `toml:"registry-mirrors,omitempty"` LayoutRepositoryDir string `toml:"layout-repo-dir,omitempty"` - VolumeKeys map[string]string `toml:"volume-keys,omitempty"` // TODO: move to own struct +} + +type VolumeConfig struct { + VolumeKeys map[string]string `toml:"volume-keys,omitempty"` } type Registry struct { @@ -88,7 +91,16 @@ func Read(path string) (Config, error) { return cfg, nil } -func Write(cfg Config, path string) error { +func ReadVolumeKeys(path string) (VolumeConfig, error) { + cfg := VolumeConfig{} + _, err := toml.DecodeFile(path, &cfg) + if err != nil && !os.IsNotExist(err) { + return VolumeConfig{}, errors.Wrapf(err, "failed to read config file at path %s", path) + } + return cfg, nil +} + +func Write(cfg interface{}, path string) error { if err := MkdirAll(filepath.Dir(path)); err != nil { return err } diff --git a/pkg/cache/volume_cache.go b/pkg/cache/volume_cache.go index bc6121a4aa..a9307a34cc 100644 --- a/pkg/cache/volume_cache.go +++ b/pkg/cache/volume_cache.go @@ -22,12 +22,12 @@ type VolumeCache struct { volume string } -func NewVolumeCache(imageRef name.Reference, cacheType CacheInfo, suffix string, dockerClient DockerClient) *VolumeCache { +func NewVolumeCache(imageRef name.Reference, cacheType CacheInfo, suffix string, dockerClient DockerClient) (*VolumeCache, error) { var volumeName string if cacheType.Source == "" { volumeKey, err := getVolumeKey(imageRef) if err != nil { - // TODO + return nil, err } sum := sha256.Sum256([]byte(imageRef.Name() + volumeKey)) // TODO: investigate if there are better ways to do this vol := paths.FilterReservedNames(fmt.Sprintf("%s-%x", sanitizedRef(imageRef), sum[:6])) @@ -39,7 +39,7 @@ func NewVolumeCache(imageRef name.Reference, cacheType CacheInfo, suffix string, return &VolumeCache{ volume: volumeName, docker: dockerClient, - } + }, nil } func getVolumeKey(imageRef name.Reference) (string, error) { @@ -58,7 +58,7 @@ func getVolumeKey(imageRef name.Reference) (string, error) { if err != nil { return "", err } - cfg, err := config.Read(volumeKeysPath) + cfg, err := config.ReadVolumeKeys(volumeKeysPath) if err != nil { return "", err } diff --git a/pkg/cache/volume_cache_test.go b/pkg/cache/volume_cache_test.go index ee05fa12cc..4518a7ce40 100644 --- a/pkg/cache/volume_cache_test.go +++ b/pkg/cache/volume_cache_test.go @@ -43,7 +43,7 @@ func testCache(t *testing.T, when spec.G, it spec.S) { 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) + 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()) } @@ -53,8 +53,8 @@ func testCache(t *testing.T, when spec.G, it spec.S) { ref, err := name.ParseReference("my/repo", name.WeakValidation) h.AssertNil(t, err) - subject := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) - expected := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) + 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") } @@ -64,11 +64,11 @@ func testCache(t *testing.T, when spec.G, it spec.S) { ref, err := name.ParseReference("my/repo:other-tag", name.WeakValidation) h.AssertNil(t, err) - subject := cache.NewVolumeCache(ref, cache.CacheInfo{}, "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, cache.CacheInfo{}, "some-suffix", dockerClient) + notExpected, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) if subject.Name() == notExpected.Name() { t.Fatalf("Different image tags should result in different volumes") } @@ -78,11 +78,11 @@ func testCache(t *testing.T, when spec.G, it spec.S) { 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) + 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) + notExpected, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) if subject.Name() == notExpected.Name() { t.Fatalf("Different image registries should result in different volumes") } @@ -92,11 +92,11 @@ func testCache(t *testing.T, when spec.G, it spec.S) { ref, err := name.ParseReference("my/repo:latest", name.WeakValidation) h.AssertNil(t, err) - subject := cache.NewVolumeCache(ref, cache.CacheInfo{}, "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, cache.CacheInfo{}, "some-suffix", dockerClient) + expected, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) h.AssertEq(t, subject.Name(), expected.Name()) }) @@ -104,11 +104,11 @@ func testCache(t *testing.T, when spec.G, it spec.S) { ref, err := name.ParseReference("index.docker.io/my/repo", name.WeakValidation) h.AssertNil(t, err) - subject := cache.NewVolumeCache(ref, cache.CacheInfo{}, "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, cache.CacheInfo{}, "some-suffix", dockerClient) + expected, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) h.AssertEq(t, subject.Name(), expected.Name()) }) @@ -116,7 +116,7 @@ func testCache(t *testing.T, when spec.G, it spec.S) { 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) + 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())) @@ -132,9 +132,9 @@ func testCache(t *testing.T, when spec.G, it spec.S) { ref, err := name.ParseReference("my/repo:some-tag", name.WeakValidation) h.AssertNil(t, err) - nameFromNewKey := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) // sources a new key + nameFromNewKey, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) // sources a new key h.AssertNil(t, os.Setenv("PACK_VOLUME_KEY", "some-volume-key")) - nameFromEnvKey := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) // sources key from env + nameFromEnvKey, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) // sources key from env h.AssertNotEq(t, nameFromNewKey.Name(), nameFromEnvKey.Name()) }) }) @@ -158,7 +158,7 @@ func testCache(t *testing.T, when spec.G, it spec.S) { ref, err := name.ParseReference("my/repo:some-tag", name.WeakValidation) h.AssertNil(t, err) - nameFromNewKey := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) // sources a new key + nameFromNewKey, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) // sources a new key cfgContents := ` [volume-keys] @@ -166,7 +166,7 @@ func testCache(t *testing.T, when spec.G, it spec.S) { ` h.AssertNil(t, os.WriteFile(filepath.Join(tmpPackHome, "volume-keys.toml"), []byte(cfgContents), 0755)) // overrides the key that was set - nameFromConfigKey := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) // sources key from config + nameFromConfigKey, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) // sources key from config h.AssertNotEq(t, nameFromNewKey.Name(), nameFromConfigKey.Name()) }) }) @@ -176,11 +176,11 @@ func testCache(t *testing.T, when spec.G, it spec.S) { ref, err := name.ParseReference("my/repo:some-tag", name.WeakValidation) h.AssertNil(t, err) - nameFromNewKey := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) // sources a new key - nameFromConfigKey := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) // sources same key from config + nameFromNewKey, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) // sources a new key + nameFromConfigKey, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) // sources same key from config h.AssertEq(t, nameFromNewKey.Name(), nameFromConfigKey.Name()) - cfg, err := config.Read(filepath.Join(tmpPackHome, "volume-keys.toml")) + cfg, err := config.ReadVolumeKeys(filepath.Join(tmpPackHome, "volume-keys.toml")) h.AssertNil(t, err) h.AssertNotNil(t, cfg.VolumeKeys["index.docker.io/my/repo:some-tag"]) }) @@ -200,7 +200,7 @@ func testCache(t *testing.T, when spec.G, it spec.S) { ref, err := name.ParseReference("my/repo", name.WeakValidation) h.AssertNil(t, err) - subject := cache.NewVolumeCache(ref, cacheInfo, "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) @@ -211,9 +211,9 @@ func testCache(t *testing.T, when spec.G, it spec.S) { ref, err := name.ParseReference("my/repo", name.WeakValidation) h.AssertNil(t, err) - subject := cache.NewVolumeCache(ref, cacheInfo, "some-suffix", dockerClient) + subject, _ := cache.NewVolumeCache(ref, cacheInfo, "some-suffix", dockerClient) - expected := 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") } @@ -223,11 +223,11 @@ func testCache(t *testing.T, when spec.G, it spec.S) { 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) + 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) + notExpected, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) if subject.Name() == notExpected.Name() { t.Fatalf("Different image registries should result in different volumes") } @@ -237,11 +237,11 @@ func testCache(t *testing.T, when spec.G, it spec.S) { ref, err := name.ParseReference("my/repo:latest", name.WeakValidation) h.AssertNil(t, err) - subject := cache.NewVolumeCache(ref, cache.CacheInfo{}, "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, cache.CacheInfo{}, "some-suffix", dockerClient) + expected, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) h.AssertEq(t, subject.Name(), expected.Name()) }) @@ -249,11 +249,11 @@ func testCache(t *testing.T, when spec.G, it spec.S) { ref, err := name.ParseReference("index.docker.io/my/repo", name.WeakValidation) h.AssertNil(t, err) - subject := cache.NewVolumeCache(ref, cache.CacheInfo{}, "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, cache.CacheInfo{}, "some-suffix", dockerClient) + expected, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) h.AssertEq(t, subject.Name(), expected.Name()) }) @@ -261,7 +261,7 @@ func testCache(t *testing.T, when spec.G, it spec.S) { 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) + 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())) @@ -286,7 +286,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, cache.CacheInfo{}, "some-suffix", dockerClient) + subject, _ = cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) volumeName = subject.Name() }) @@ -324,7 +324,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, cache.CacheInfo{}, "some-suffix", dockerClient) + subject, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) expected := cache.Volume h.AssertEq(t, subject.Type(), expected) }) From 331ac6d0e711b35b1da4037fe377774d933ddf19 Mon Sep 17 00:00:00 2001 From: Natalie Arellano Date: Mon, 15 Jul 2024 16:33:07 -0400 Subject: [PATCH 3/3] Log a warning (once) when PACK_VOLUME_KEY is unset and running in a container Signed-off-by: Natalie Arellano --- acceptance/acceptance_test.go | 16 +++-- internal/build/lifecycle_execution.go | 8 +-- pkg/cache/volume_cache.go | 20 ++++-- pkg/cache/volume_cache_test.go | 88 +++++++++++++++++---------- 4 files changed, 87 insertions(+), 45 deletions(-) diff --git a/acceptance/acceptance_test.go b/acceptance/acceptance_test.go index 7f7074ece6..f837988e09 100644 --- a/acceptance/acceptance_test.go +++ b/acceptance/acceptance_test.go @@ -38,6 +38,7 @@ import ( "github.com/buildpacks/pack/internal/style" "github.com/buildpacks/pack/pkg/archive" "github.com/buildpacks/pack/pkg/cache" + "github.com/buildpacks/pack/pkg/logging" h "github.com/buildpacks/pack/testhelpers" ) @@ -1162,8 +1163,9 @@ func testAcceptance( ref, err := name.ParseReference(repoName, name.WeakValidation) assert.Nil(err) cacheImage := cache.NewImageCache(ref, dockerCli) - buildCacheVolume, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "build", dockerCli) - launchCacheVolume, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "launch", dockerCli) + logger := logging.NewSimpleLogger(&bytes.Buffer{}) + buildCacheVolume, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "build", dockerCli, logger) + launchCacheVolume, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "launch", dockerCli, logger) cacheImage.Clear(context.TODO()) buildCacheVolume.Clear(context.TODO()) launchCacheVolume.Clear(context.TODO()) @@ -1282,8 +1284,9 @@ func testAcceptance( ref, err := name.ParseReference(repoName, name.WeakValidation) assert.Nil(err) cacheImage := cache.NewImageCache(ref, dockerCli) - buildCacheVolume, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "build", dockerCli) - launchCacheVolume, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "launch", dockerCli) + logger := logging.NewSimpleLogger(&bytes.Buffer{}) + buildCacheVolume, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "build", dockerCli, logger) + launchCacheVolume, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "launch", dockerCli, logger) cacheImage.Clear(context.TODO()) buildCacheVolume.Clear(context.TODO()) launchCacheVolume.Clear(context.TODO()) @@ -3168,8 +3171,9 @@ 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, cache.CacheInfo{}, "build", dockerCli) - launchCacheVolume, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "launch", dockerCli) + logger := logging.NewSimpleLogger(&bytes.Buffer{}) + buildCacheVolume, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "build", dockerCli, logger) + launchCacheVolume, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "launch", dockerCli, logger) assert.Succeeds(buildCacheVolume.Clear(context.TODO())) assert.Succeeds(launchCacheVolume.Clear(context.TODO())) }) diff --git a/internal/build/lifecycle_execution.go b/internal/build/lifecycle_execution.go index 620c6eae74..1911c0eed2 100644 --- a/internal/build/lifecycle_execution.go +++ b/internal/build/lifecycle_execution.go @@ -182,7 +182,7 @@ func (l *LifecycleExecution) Run(ctx context.Context, phaseFactoryCreator PhaseF switch l.opts.Cache.Build.Format { case cache.CacheVolume: var err error - buildCache, err = cache.NewVolumeCache(l.opts.Image, l.opts.Cache.Build, "build", l.docker) + buildCache, err = cache.NewVolumeCache(l.opts.Image, l.opts.Cache.Build, "build", l.docker, l.logger) if err != nil { return err } @@ -200,7 +200,7 @@ func (l *LifecycleExecution) Run(ctx context.Context, phaseFactoryCreator PhaseF l.logger.Debugf("Build cache %s cleared", style.Symbol(buildCache.Name())) } - launchCache, err := cache.NewVolumeCache(l.opts.Image, l.opts.Cache.Launch, "launch", l.docker) + launchCache, err := cache.NewVolumeCache(l.opts.Image, l.opts.Cache.Launch, "launch", l.docker, l.logger) if err != nil { return err } @@ -256,7 +256,7 @@ func (l *LifecycleExecution) Run(ctx context.Context, phaseFactoryCreator PhaseF // lifecycle 0.17.0 (introduces support for Platform API 0.12) and above will ensure that // this volume is owned by the CNB user, // and hence the restorer (after dropping privileges) will be able to write to it. - kanikoCache, err = cache.NewVolumeCache(l.opts.Image, l.opts.Cache.Kaniko, "kaniko", l.docker) + kanikoCache, err = cache.NewVolumeCache(l.opts.Image, l.opts.Cache.Kaniko, "kaniko", l.docker, l.logger) if err != nil { return err } @@ -271,7 +271,7 @@ func (l *LifecycleExecution) Run(ctx context.Context, phaseFactoryCreator PhaseF return fmt.Errorf("build cache must be volume cache when building with extensions") default: // The kaniko cache is unused, so it doesn't matter that it's not usable. - kanikoCache, err = cache.NewVolumeCache(l.opts.Image, l.opts.Cache.Kaniko, "kaniko", l.docker) + kanikoCache, err = cache.NewVolumeCache(l.opts.Image, l.opts.Cache.Kaniko, "kaniko", l.docker, l.logger) if err != nil { return err } diff --git a/pkg/cache/volume_cache.go b/pkg/cache/volume_cache.go index a9307a34cc..f414bcd903 100644 --- a/pkg/cache/volume_cache.go +++ b/pkg/cache/volume_cache.go @@ -8,11 +8,13 @@ import ( "os" "strings" + "github.com/GoogleContainerTools/kaniko/pkg/util/proc" "github.com/docker/docker/client" "github.com/google/go-containerregistry/pkg/name" "github.com/buildpacks/pack/internal/config" "github.com/buildpacks/pack/internal/paths" + "github.com/buildpacks/pack/pkg/logging" ) const EnvVolumeKey = "PACK_VOLUME_KEY" @@ -22,14 +24,14 @@ type VolumeCache struct { volume string } -func NewVolumeCache(imageRef name.Reference, cacheType CacheInfo, suffix string, dockerClient DockerClient) (*VolumeCache, error) { +func NewVolumeCache(imageRef name.Reference, cacheType CacheInfo, suffix string, dockerClient DockerClient, logger logging.Logger) (*VolumeCache, error) { var volumeName string if cacheType.Source == "" { - volumeKey, err := getVolumeKey(imageRef) + volumeKey, err := getVolumeKey(imageRef, logger) if err != nil { return nil, err } - sum := sha256.Sum256([]byte(imageRef.Name() + volumeKey)) // TODO: investigate if there are better ways to do this + sum := sha256.Sum256([]byte(imageRef.Name() + volumeKey)) vol := paths.FilterReservedNames(fmt.Sprintf("%s-%x", sanitizedRef(imageRef), sum[:6])) volumeName = fmt.Sprintf("pack-cache-%s.%s", vol, suffix) } else { @@ -42,7 +44,7 @@ func NewVolumeCache(imageRef name.Reference, cacheType CacheInfo, suffix string, }, nil } -func getVolumeKey(imageRef name.Reference) (string, error) { +func getVolumeKey(imageRef name.Reference, logger logging.Logger) (string, error) { var foundKey string // first, look for key in env @@ -70,6 +72,12 @@ func getVolumeKey(imageRef name.Reference) (string, error) { // finally, create new key and store it in config + // if we're running in a container, we should log a warning + // so that we don't always re-create the cache + if RunningInContainer() { + logger.Warnf("%s is unset; set this environment variable to a secret value to avoid creating a new volume cache on every build", EnvVolumeKey) + } + newKey := randString(20) if cfg.VolumeKeys == nil { cfg.VolumeKeys = make(map[string]string) @@ -118,3 +126,7 @@ func sanitizedRef(ref name.Reference) string { result = strings.ReplaceAll(result, "/", "_") return fmt.Sprintf("%s_%s", result, ref.Identifier()) } + +var RunningInContainer = func() bool { + return proc.GetContainerRuntime(0, 0) != proc.RuntimeNotFound +} diff --git a/pkg/cache/volume_cache_test.go b/pkg/cache/volume_cache_test.go index 4518a7ce40..685f34d2be 100644 --- a/pkg/cache/volume_cache_test.go +++ b/pkg/cache/volume_cache_test.go @@ -1,6 +1,7 @@ package cache_test import ( + "bytes" "context" "os" "path/filepath" @@ -9,6 +10,7 @@ import ( "github.com/buildpacks/pack/internal/config" "github.com/buildpacks/pack/pkg/cache" + "github.com/buildpacks/pack/pkg/logging" "github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/volume" @@ -31,19 +33,25 @@ func TestVolumeCache(t *testing.T) { } func testCache(t *testing.T, when spec.G, it spec.S) { - var dockerClient client.CommonAPIClient + var ( + dockerClient client.CommonAPIClient + outBuf bytes.Buffer + logger logging.Logger + ) it.Before(func() { var err error dockerClient, err = client.NewClientWithOpts(client.FromEnv, client.WithVersion("1.38")) h.AssertNil(t, err) + logger = logging.NewSimpleLogger(&outBuf) }) + when("#NewVolumeCache", func() { 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) + subject, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient, logger) if !strings.HasSuffix(subject.Name(), ".some-suffix") { t.Fatalf("Calculated volume name '%s' should end with '.some-suffix'", subject.Name()) } @@ -53,8 +61,8 @@ func testCache(t *testing.T, when spec.G, it spec.S) { ref, err := name.ParseReference("my/repo", name.WeakValidation) h.AssertNil(t, err) - subject, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) - expected, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) + subject, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient, logger) + expected, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient, logger) if subject.Name() != expected.Name() { t.Fatalf("The same repo name should result in the same volume") } @@ -64,11 +72,11 @@ func testCache(t *testing.T, when spec.G, it spec.S) { ref, err := name.ParseReference("my/repo:other-tag", name.WeakValidation) h.AssertNil(t, err) - subject, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) + subject, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient, logger) ref, err = name.ParseReference("my/repo", name.WeakValidation) h.AssertNil(t, err) - notExpected, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) + notExpected, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient, logger) if subject.Name() == notExpected.Name() { t.Fatalf("Different image tags should result in different volumes") } @@ -78,11 +86,11 @@ func testCache(t *testing.T, when spec.G, it spec.S) { 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) + subject, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient, logger) ref, err = name.ParseReference("my/repo", name.WeakValidation) h.AssertNil(t, err) - notExpected, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) + notExpected, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient, logger) if subject.Name() == notExpected.Name() { t.Fatalf("Different image registries should result in different volumes") } @@ -92,11 +100,11 @@ func testCache(t *testing.T, when spec.G, it spec.S) { ref, err := name.ParseReference("my/repo:latest", name.WeakValidation) h.AssertNil(t, err) - subject, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) + subject, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient, logger) ref, err = name.ParseReference("my/repo", name.WeakValidation) h.AssertNil(t, err) - expected, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) + expected, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient, logger) h.AssertEq(t, subject.Name(), expected.Name()) }) @@ -104,11 +112,11 @@ func testCache(t *testing.T, when spec.G, it spec.S) { ref, err := name.ParseReference("index.docker.io/my/repo", name.WeakValidation) h.AssertNil(t, err) - subject, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) + subject, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient, logger) ref, err = name.ParseReference("my/repo", name.WeakValidation) h.AssertNil(t, err) - expected, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) + expected, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient, logger) h.AssertEq(t, subject.Name(), expected.Name()) }) @@ -116,7 +124,7 @@ func testCache(t *testing.T, when spec.G, it spec.S) { 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) + subject, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient, logger) h.AssertContains(t, subject.Name(), "fedora_httpd_version1.0") h.AssertTrue(t, names.RestrictedNamePattern.MatchString(subject.Name())) @@ -132,9 +140,9 @@ func testCache(t *testing.T, when spec.G, it spec.S) { ref, err := name.ParseReference("my/repo:some-tag", name.WeakValidation) h.AssertNil(t, err) - nameFromNewKey, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) // sources a new key + nameFromNewKey, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient, logger) // sources a new key h.AssertNil(t, os.Setenv("PACK_VOLUME_KEY", "some-volume-key")) - nameFromEnvKey, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) // sources key from env + nameFromEnvKey, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient, logger) // sources key from env h.AssertNotEq(t, nameFromNewKey.Name(), nameFromEnvKey.Name()) }) }) @@ -158,7 +166,7 @@ func testCache(t *testing.T, when spec.G, it spec.S) { ref, err := name.ParseReference("my/repo:some-tag", name.WeakValidation) h.AssertNil(t, err) - nameFromNewKey, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) // sources a new key + nameFromNewKey, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient, logger) // sources a new key cfgContents := ` [volume-keys] @@ -166,7 +174,7 @@ func testCache(t *testing.T, when spec.G, it spec.S) { ` h.AssertNil(t, os.WriteFile(filepath.Join(tmpPackHome, "volume-keys.toml"), []byte(cfgContents), 0755)) // overrides the key that was set - nameFromConfigKey, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) // sources key from config + nameFromConfigKey, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient, logger) // sources key from config h.AssertNotEq(t, nameFromNewKey.Name(), nameFromConfigKey.Name()) }) }) @@ -176,14 +184,32 @@ func testCache(t *testing.T, when spec.G, it spec.S) { ref, err := name.ParseReference("my/repo:some-tag", name.WeakValidation) h.AssertNil(t, err) - nameFromNewKey, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) // sources a new key - nameFromConfigKey, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) // sources same key from config + nameFromNewKey, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient, logger) // sources a new key + nameFromConfigKey, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient, logger) // sources same key from config h.AssertEq(t, nameFromNewKey.Name(), nameFromConfigKey.Name()) cfg, err := config.ReadVolumeKeys(filepath.Join(tmpPackHome, "volume-keys.toml")) h.AssertNil(t, err) h.AssertNotNil(t, cfg.VolumeKeys["index.docker.io/my/repo:some-tag"]) }) + + when("containerized pack", func() { + it.Before(func() { + cache.RunningInContainer = func() bool { + return true + } + }) + + it("logs a warning", func() { + ref, err := name.ParseReference("my/repo:some-tag", name.WeakValidation) + h.AssertNil(t, err) + + _, _ = cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient, logger) // sources a new key + _, _ = cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient, logger) // sources same key from config + h.AssertContains(t, outBuf.String(), "PACK_VOLUME_KEY is unset; set this environment variable to a secret value to avoid creating a new volume cache on every build") + h.AssertEq(t, strings.Count(outBuf.String(), "PACK_VOLUME_KEY is unset"), 1) // the second call to NewVolumeCache reads from the config + }) + }) }) }) }) @@ -200,7 +226,7 @@ func testCache(t *testing.T, when spec.G, it spec.S) { ref, err := name.ParseReference("my/repo", name.WeakValidation) h.AssertNil(t, err) - subject, _ := cache.NewVolumeCache(ref, cacheInfo, "some-suffix", dockerClient) + subject, _ := cache.NewVolumeCache(ref, cacheInfo, "some-suffix", dockerClient, logger) if volumeName != subject.Name() { t.Fatalf("Volume name '%s' should be same as the name specified '%s'", subject.Name(), volumeName) @@ -211,9 +237,9 @@ func testCache(t *testing.T, when spec.G, it spec.S) { ref, err := name.ParseReference("my/repo", name.WeakValidation) h.AssertNil(t, err) - subject, _ := cache.NewVolumeCache(ref, cacheInfo, "some-suffix", dockerClient) + subject, _ := cache.NewVolumeCache(ref, cacheInfo, "some-suffix", dockerClient, logger) - expected, _ := cache.NewVolumeCache(ref, cacheInfo, "some-suffix", dockerClient) + expected, _ := cache.NewVolumeCache(ref, cacheInfo, "some-suffix", dockerClient, logger) if subject.Name() != expected.Name() { t.Fatalf("The same repo name should result in the same volume") } @@ -223,11 +249,11 @@ func testCache(t *testing.T, when spec.G, it spec.S) { 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) + subject, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient, logger) ref, err = name.ParseReference("my/repo", name.WeakValidation) h.AssertNil(t, err) - notExpected, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) + notExpected, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient, logger) if subject.Name() == notExpected.Name() { t.Fatalf("Different image registries should result in different volumes") } @@ -237,11 +263,11 @@ func testCache(t *testing.T, when spec.G, it spec.S) { ref, err := name.ParseReference("my/repo:latest", name.WeakValidation) h.AssertNil(t, err) - subject, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) + subject, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient, logger) ref, err = name.ParseReference("my/repo", name.WeakValidation) h.AssertNil(t, err) - expected, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) + expected, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient, logger) h.AssertEq(t, subject.Name(), expected.Name()) }) @@ -249,11 +275,11 @@ func testCache(t *testing.T, when spec.G, it spec.S) { ref, err := name.ParseReference("index.docker.io/my/repo", name.WeakValidation) h.AssertNil(t, err) - subject, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) + subject, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient, logger) ref, err = name.ParseReference("my/repo", name.WeakValidation) h.AssertNil(t, err) - expected, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient) + expected, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient, logger) h.AssertEq(t, subject.Name(), expected.Name()) }) @@ -261,7 +287,7 @@ func testCache(t *testing.T, when spec.G, it spec.S) { 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) + subject, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient, logger) h.AssertContains(t, subject.Name(), "fedora_httpd_version1.0") h.AssertTrue(t, names.RestrictedNamePattern.MatchString(subject.Name())) @@ -286,7 +312,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, cache.CacheInfo{}, "some-suffix", dockerClient) + subject, _ = cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient, logger) volumeName = subject.Name() }) @@ -324,7 +350,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, cache.CacheInfo{}, "some-suffix", dockerClient) + subject, _ := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient, logger) expected := cache.Volume h.AssertEq(t, subject.Type(), expected) })