Skip to content

Commit

Permalink
Initial implementation (still has TODOs)
Browse files Browse the repository at this point in the history
Signed-off-by: Natalie Arellano <narellano@vmware.com>
  • Loading branch information
natalieparellano committed Jun 27, 2024
1 parent ce8db3c commit 1cc1cc8
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 2 deletions.
9 changes: 9 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 == "" {
Expand Down
64 changes: 63 additions & 1 deletion pkg/cache/volume_cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 {

Check failure on line 29 in pkg/cache/volume_cache.go

View workflow job for this annotation

GitHub Actions / test (macos)

SA9003: empty branch (staticcheck)

Check failure on line 29 in pkg/cache/volume_cache.go

View workflow job for this annotation

GitHub Actions / test (linux)

SA9003: empty branch (staticcheck)

Check failure on line 29 in pkg/cache/volume_cache.go

View workflow job for this annotation

GitHub Actions / test (windows-wcow)

SA9003: empty branch (staticcheck)
// 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 {
Expand All @@ -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
}
Expand Down
71 changes: 70 additions & 1 deletion pkg/cache/volume_cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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) {
Expand Down Expand Up @@ -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() {
Expand Down

0 comments on commit 1cc1cc8

Please sign in to comment.