-
Notifications
You must be signed in to change notification settings - Fork 604
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: separate golang license caches from mod dir (#2852)
Signed-off-by: Keith Zantow <[email protected]>
- Loading branch information
Showing
31 changed files
with
1,484 additions
and
137 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
package options | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"path/filepath" | ||
"regexp" | ||
"strconv" | ||
"strings" | ||
"time" | ||
|
||
"github.com/adrg/xdg" | ||
"github.com/mitchellh/go-homedir" | ||
|
||
"github.com/anchore/clio" | ||
"github.com/anchore/syft/internal/cache" | ||
"github.com/anchore/syft/internal/log" | ||
) | ||
|
||
// Cache provides configuration for the Syft caching behavior | ||
type Cache struct { | ||
Dir string `yaml:"dir" mapstructure:"dir"` | ||
TTL string `yaml:"ttl" mapstructure:"ttl"` | ||
} | ||
|
||
func (c *Cache) DescribeFields(descriptions clio.FieldDescriptionSet) { | ||
descriptions.Add(&c.Dir, "root directory to cache any downloaded content") | ||
descriptions.Add(&c.TTL, "time to live for cached data") | ||
} | ||
|
||
func (c *Cache) PostLoad() error { | ||
if c.Dir != "" { | ||
ttl, err := parseDuration(c.TTL) | ||
if err != nil { | ||
log.Warnf("unable to parse duration '%v', using default (%s) due to: %v", c.TTL, durationToString(defaultTTL()), err) | ||
ttl = defaultTTL() | ||
} | ||
dir, err := homedir.Expand(c.Dir) | ||
if err != nil { | ||
log.Warnf("unable to expand cache directory %s: %v", c.Dir, err) | ||
cache.SetManager(cache.NewInMemory(ttl)) | ||
} else { | ||
m, err := cache.NewFromDir(dir, ttl) | ||
if err != nil { | ||
log.Warnf("unable to get filesystem cache at %s: %v", c.Dir, err) | ||
cache.SetManager(cache.NewInMemory(ttl)) | ||
} else { | ||
cache.SetManager(m) | ||
} | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
var _ interface { | ||
clio.PostLoader | ||
clio.FieldDescriber | ||
} = (*Cache)(nil) | ||
|
||
func DefaultCache() Cache { | ||
return Cache{ | ||
Dir: defaultDir(), | ||
TTL: durationToString(defaultTTL()), | ||
} | ||
} | ||
|
||
func defaultTTL() time.Duration { | ||
return 7 * 24 * time.Hour | ||
} | ||
|
||
func defaultDir() string { | ||
var err error | ||
cacheRoot := xdg.CacheHome | ||
if cacheRoot == "" { | ||
cacheRoot, err = homedir.Dir() | ||
if err != nil { | ||
cacheRoot = os.TempDir() | ||
log.Debugf("unable to get stable cache directory due to: %v, defaulting cache to temp dir: %s", err, cacheRoot) | ||
} else { | ||
cacheRoot = filepath.Join(cacheRoot, ".cache") | ||
} | ||
} | ||
|
||
return filepath.Join(cacheRoot, "syft") | ||
} | ||
|
||
func durationToString(duration time.Duration) string { | ||
days := int64(duration / (24 * time.Hour)) | ||
remain := duration % (24 * time.Hour) | ||
out := "" | ||
if days > 0 { | ||
out = fmt.Sprintf("%vd", days) | ||
} | ||
if remain != 0 { | ||
out += remain.String() | ||
} | ||
if out == "" { | ||
return "0" | ||
} | ||
return out | ||
} | ||
|
||
var whitespace = regexp.MustCompile(`\s+`) | ||
|
||
func parseDuration(duration string) (time.Duration, error) { | ||
duration = strings.ToLower(whitespace.ReplaceAllString(duration, "")) | ||
parts := strings.SplitN(duration, "d", 2) | ||
var days time.Duration | ||
var remain time.Duration | ||
var err error | ||
if len(parts) > 1 { | ||
numDays, daysErr := strconv.Atoi(parts[0]) | ||
if daysErr != nil { | ||
return 0, daysErr | ||
} | ||
days = time.Duration(numDays) * 24 * time.Hour | ||
remain, err = time.ParseDuration(parts[1]) | ||
} else { | ||
remain, err = time.ParseDuration(duration) | ||
} | ||
return days + remain, err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
package options | ||
|
||
import ( | ||
"os" | ||
"path/filepath" | ||
"strings" | ||
"testing" | ||
"time" | ||
|
||
"github.com/adrg/xdg" | ||
"github.com/mitchellh/go-homedir" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func Test_defaultDir(t *testing.T) { | ||
tmpDir := filepath.Join(t.TempDir(), "cache-temp") | ||
xdgCacheDir := filepath.Join(tmpDir, "fake-xdg-cache") | ||
homeDir := filepath.Join(tmpDir, "fake-home") | ||
|
||
tests := []struct { | ||
name string | ||
env map[string]string | ||
expected string | ||
}{ | ||
{ | ||
name: "no-xdg", | ||
env: map[string]string{ | ||
"HOME": homeDir, | ||
}, | ||
expected: homeDir, | ||
}, | ||
{ | ||
name: "xdg-cache", | ||
env: map[string]string{ | ||
"XDG_CACHE_HOME": xdgCacheDir, | ||
}, | ||
expected: xdgCacheDir, | ||
}, | ||
} | ||
|
||
// capture all the initial environment variables to reset them before we reset library caches | ||
env := map[string]string{ | ||
"HOME": "", | ||
"XDG_DATA_HOME": "", | ||
"XDG_DATA_DIRS": "", | ||
"XDG_CONFIG_HOME": "", | ||
"XDG_CONFIG_DIRS": "", | ||
"XDG_STATE_HOME": "", | ||
"XDG_CACHE_HOME": "", | ||
"XDG_RUNTIME_DIR": "", | ||
} | ||
for k := range env { | ||
env[k] = os.Getenv(k) | ||
} | ||
|
||
unsetEnv := func(t *testing.T) { | ||
for k := range env { | ||
t.Setenv(k, "") | ||
} | ||
} | ||
|
||
resetEnv := func() { | ||
for k, v := range env { | ||
if v == "" { | ||
_ = os.Unsetenv(k) | ||
} else { | ||
_ = os.Setenv(k, v) | ||
} | ||
} | ||
homedir.Reset() | ||
xdg.Reload() | ||
} | ||
|
||
t.Cleanup(resetEnv) | ||
|
||
for _, test := range tests { | ||
t.Run(test.name, func(t *testing.T) { | ||
defer resetEnv() | ||
|
||
unsetEnv(t) | ||
for k, v := range test.env { | ||
t.Setenv(k, v) | ||
} | ||
homedir.Reset() | ||
xdg.Reload() | ||
|
||
got := defaultDir() | ||
|
||
require.True(t, strings.HasPrefix(got, test.expected)) | ||
}) | ||
} | ||
} | ||
|
||
func Test_parseDuration(t *testing.T) { | ||
tests := []struct { | ||
duration string | ||
expect time.Duration | ||
err require.ErrorAssertionFunc | ||
}{ | ||
{ | ||
duration: "1d", | ||
expect: 24 * time.Hour, | ||
}, | ||
{ | ||
duration: "7d", | ||
expect: 7 * 24 * time.Hour, | ||
}, | ||
{ | ||
duration: "365D", | ||
expect: 365 * 24 * time.Hour, | ||
}, | ||
{ | ||
duration: "7d1h1m1s", | ||
expect: 7*24*time.Hour + time.Hour + time.Minute + time.Second, | ||
}, | ||
{ | ||
duration: "7d 1h 1m 1s", | ||
expect: 7*24*time.Hour + time.Hour + time.Minute + time.Second, | ||
}, | ||
{ | ||
duration: "2h", | ||
expect: 2 * time.Hour, | ||
}, | ||
{ | ||
duration: "2h5m", | ||
expect: 2*time.Hour + 5*time.Minute, | ||
}, | ||
{ | ||
duration: "2h 5m", | ||
expect: 2*time.Hour + 5*time.Minute, | ||
}, | ||
{ | ||
duration: "d24h", | ||
err: require.Error, | ||
}, | ||
} | ||
|
||
for _, test := range tests { | ||
t.Run(test.duration, func(t *testing.T) { | ||
got, err := parseDuration(test.duration) | ||
if test.err != nil { | ||
test.err(t, err) | ||
return | ||
} | ||
require.Equal(t, test.expect, got) | ||
}) | ||
} | ||
} | ||
|
||
func Test_durationToString(t *testing.T) { | ||
tests := []struct { | ||
duration time.Duration | ||
expect string | ||
err require.ErrorAssertionFunc | ||
}{ | ||
{ | ||
expect: "1d", | ||
duration: 24 * time.Hour, | ||
}, | ||
{ | ||
expect: "7d", | ||
duration: 7 * 24 * time.Hour, | ||
}, | ||
{ | ||
expect: "7d1h1m1s", | ||
duration: 7*24*time.Hour + time.Hour + time.Minute + time.Second, | ||
}, | ||
{ | ||
expect: "2h0m0s", | ||
duration: 2 * time.Hour, | ||
}, | ||
{ | ||
expect: "2h5m0s", | ||
duration: 2*time.Hour + 5*time.Minute, | ||
}, | ||
} | ||
|
||
for _, test := range tests { | ||
t.Run(test.expect, func(t *testing.T) { | ||
got := durationToString(test.duration) | ||
require.Equal(t, test.expect, got) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.