Files
atlantis/server/core/runtime/cache/version_path.go
Ken Kaizu 3954955e13 fix(deps): update module github.com/petergtz/pegomock/v3 to v4 (#3534)
* fix(deps): update module github.com/petergtz/pegomock/v3 to v4 in go.mod

* remove pegomock generate m option, which is not support after v4

* make regen-mocks

* replace pegomock v4 primitive eq/matchers

* convert pegomock v4 Eq/Any matchers

* remove custom models.Repo matcher

* pegomock v4 cannot use result method args

ref https://github.com/petergtz/pegomock/issues/123

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-20 15:05:23 -04:00

133 lines
3.7 KiB
Go

package cache
import (
"fmt"
"sync"
"github.com/hashicorp/go-version"
"github.com/pkg/errors"
"github.com/runatlantis/atlantis/server/core/runtime/models"
)
//go:generate pegomock generate --package mocks -o mocks/mock_version_path.go ExecutionVersionCache
//go:generate pegomock generate --package mocks -o mocks/mock_key_serializer.go KeySerializer
type ExecutionVersionCache interface {
Get(key *version.Version) (string, error)
}
type KeySerializer interface {
Serialize(key *version.Version) (string, error)
}
type DefaultDiskLookupKeySerializer struct {
binaryName string
}
func (s *DefaultDiskLookupKeySerializer) Serialize(key *version.Version) (string, error) {
return fmt.Sprintf("%s%s", s.binaryName, key.Original()), nil
}
// ExecutionVersionDiskLayer is a cache layer which attempts to find the version on disk,
// before calling the configured loading function.
type ExecutionVersionDiskLayer struct {
versionRootDir models.FilePath
exec models.Exec
keySerializer KeySerializer
loader func(v *version.Version, destPath string) (models.FilePath, error)
binaryName string
}
// Gets a path from cache
func (v *ExecutionVersionDiskLayer) Get(key *version.Version) (string, error) {
binaryVersion, err := v.keySerializer.Serialize(key)
if err != nil {
return "", errors.Wrapf(err, "serializing key for disk lookup")
}
// first check for the binary in our path
path, err := v.exec.LookPath(binaryVersion)
if err == nil {
return path, nil
}
// if the binary is not in our path, let's look in the version root directory
binaryPath := v.versionRootDir.Join(binaryVersion)
// if the binary doesn't exist there, we need to load it.
if binaryPath.NotExists() {
// load it into a directory first and then sym link it to the serialized key aka binary version
loaderPath := v.versionRootDir.Join(v.binaryName, "versions", key.Original())
loadedBinary, err := v.loader(key, loaderPath.Resolve())
if err != nil {
return "", errors.Wrapf(err, "loading %s", loaderPath)
}
binaryPath, err = loadedBinary.Symlink(binaryPath.Resolve())
if err != nil {
return "", errors.Wrapf(err, "linking %s to %s", loaderPath, loadedBinary)
}
}
return binaryPath.Resolve(), nil
}
// ExecutionVersionMemoryLayer is an in-memory cache which delegates to a disk layer
// if a version's path doesn't exist yet.
type ExecutionVersionMemoryLayer struct {
// RWMutex allows us to have separation between reader locks/writer locks which is great
// since writing of data shouldn't happen too often
lock sync.RWMutex
diskLayer ExecutionVersionCache
cache map[string]string
}
func (v *ExecutionVersionMemoryLayer) Get(key *version.Version) (string, error) {
// If we need to we can rip this out into a KeySerializer impl, for now this
// seems overkill
serializedKey := key.String()
v.lock.RLock()
_, ok := v.cache[serializedKey]
v.lock.RUnlock()
if !ok {
v.lock.Lock()
defer v.lock.Unlock()
value, err := v.diskLayer.Get(key)
if err != nil {
return "", errors.Wrapf(err, "fetching %s from cache", serializedKey)
}
v.cache[serializedKey] = value
}
return v.cache[serializedKey], nil
}
func NewExecutionVersionLayeredLoadingCache(
binaryName string,
versionRootDir string,
loader func(v *version.Version, destPath string) (models.FilePath, error),
) ExecutionVersionCache {
diskLayer := &ExecutionVersionDiskLayer{
exec: models.LocalExec{},
versionRootDir: models.LocalFilePath(versionRootDir),
keySerializer: &DefaultDiskLookupKeySerializer{binaryName: binaryName},
loader: loader,
binaryName: binaryName,
}
return &ExecutionVersionMemoryLayer{
diskLayer: diskLayer,
cache: make(map[string]string),
}
}