mirror of
https://github.com/aquasecurity/trivy.git
synced 2025-12-21 23:00:42 -08:00
refactor: replace global cache directory with parameter passing (#6986)
Signed-off-by: knqyf263 <knqyf263@gmail.com>
This commit is contained in:
33
pkg/cache/client.go
vendored
33
pkg/cache/client.go
vendored
@@ -21,6 +21,7 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
|
dir string
|
||||||
Cache
|
Cache
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,7 +116,8 @@ func NewType(backend string) (Type, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewClient returns a new cache client
|
// NewClient returns a new cache client
|
||||||
func NewClient(opts Options) (*Client, error) {
|
func NewClient(dir string, opts Options) (*Client, error) {
|
||||||
|
client := &Client{dir: dir}
|
||||||
if opts.Type == TypeRedis {
|
if opts.Type == TypeRedis {
|
||||||
log.Info("Redis cache", log.String("url", opts.Redis.BackendMasked()))
|
log.Info("Redis cache", log.String("url", opts.Redis.BackendMasked()))
|
||||||
options, err := redis.ParseURL(opts.Redis.Backend)
|
options, err := redis.ParseURL(opts.Redis.Backend)
|
||||||
@@ -140,34 +142,27 @@ func NewClient(opts Options) (*Client, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
redisCache := NewRedisCache(options, opts.TTL)
|
client.Cache = NewRedisCache(options, opts.TTL)
|
||||||
return &Client{Cache: redisCache}, nil
|
return client, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// standalone mode
|
// standalone mode
|
||||||
fsCache, err := NewFSCache(Dir())
|
var err error
|
||||||
|
client.Cache, err = NewFSCache(dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Errorf("unable to initialize fs cache: %w", err)
|
return nil, xerrors.Errorf("unable to initialize fs cache: %w", err)
|
||||||
}
|
}
|
||||||
return &Client{Cache: fsCache}, nil
|
return client, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset resets the cache
|
// Reset resets the cache
|
||||||
func (c *Client) Reset() (err error) {
|
func (c *Client) Reset() error {
|
||||||
if err := c.ClearDB(); err != nil {
|
log.Info("Removing all caches...")
|
||||||
return xerrors.Errorf("failed to clear the database: %w", err)
|
if err := c.Clear(); err != nil {
|
||||||
|
return xerrors.Errorf("failed to remove the cache: %w", err)
|
||||||
}
|
}
|
||||||
if err := c.ClearArtifacts(); err != nil {
|
if err := os.RemoveAll(c.dir); err != nil {
|
||||||
return xerrors.Errorf("failed to clear the artifact cache: %w", err)
|
return xerrors.Errorf("failed to remove the directory (%s) : %w", c.dir, err)
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClearDB clears the DB cache
|
|
||||||
func (c *Client) ClearDB() (err error) {
|
|
||||||
log.Info("Removing DB file...")
|
|
||||||
if err = os.RemoveAll(Dir()); err != nil {
|
|
||||||
return xerrors.Errorf("failed to remove the directory (%s) : %w", Dir(), err)
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
50
pkg/cache/client_test.go
vendored
50
pkg/cache/client_test.go
vendored
@@ -1,6 +1,8 @@
|
|||||||
package cache_test
|
package cache_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -127,3 +129,51 @@ func TestRedisOptions_BackendMasked(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestClient_Reset(t *testing.T) {
|
||||||
|
// Create a temporary directory for testing
|
||||||
|
tempDir := t.TempDir()
|
||||||
|
|
||||||
|
// Create test files and subdirectories
|
||||||
|
subDir := filepath.Join(tempDir, "subdir")
|
||||||
|
err := os.MkdirAll(subDir, 0755)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
testFile := filepath.Join(tempDir, "testfile.txt")
|
||||||
|
err = os.WriteFile(testFile, []byte("test content"), 0644)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Create a cache client
|
||||||
|
client, err := cache.NewClient(tempDir, cache.Options{Type: cache.TypeFS})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Call Reset method
|
||||||
|
err = client.Reset()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Verify that the subdirectory no longer exists
|
||||||
|
require.NoDirExists(t, subDir, "Subdirectory should not exist after Reset")
|
||||||
|
|
||||||
|
// Verify that the test file no longer exists
|
||||||
|
require.NoFileExists(t, testFile, "Test file should not exist after Reset")
|
||||||
|
|
||||||
|
// Verify that the cache directory no longer exists
|
||||||
|
require.NoDirExists(t, tempDir, "Cache directory should not exist after Reset")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClient_ClearArtifacts(t *testing.T) {
|
||||||
|
// Create a temporary directory for testing
|
||||||
|
tempDir := t.TempDir()
|
||||||
|
|
||||||
|
// Create a client
|
||||||
|
client, err := cache.NewClient(tempDir, cache.Options{Type: cache.TypeFS})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.FileExists(t, filepath.Join(tempDir, "fanal", "fanal.db"), "Database file should exist")
|
||||||
|
|
||||||
|
// Call ClearArtifacts method
|
||||||
|
err = client.ClearArtifacts()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.NoDirExists(t, filepath.Join(tempDir, "fanal"), "Artifact cache should not exist after ClearArtifacts")
|
||||||
|
}
|
||||||
|
|||||||
19
pkg/cache/dir.go
vendored
19
pkg/cache/dir.go
vendored
@@ -5,26 +5,11 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
)
|
)
|
||||||
|
|
||||||
var cacheDir string
|
// DefaultDir returns/creates the cache-dir to be used for trivy operations
|
||||||
|
func DefaultDir() string {
|
||||||
// defaultDir returns/creates the cache-dir to be used for trivy operations
|
|
||||||
func defaultDir() string {
|
|
||||||
tmpDir, err := os.UserCacheDir()
|
tmpDir, err := os.UserCacheDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tmpDir = os.TempDir()
|
tmpDir = os.TempDir()
|
||||||
}
|
}
|
||||||
return filepath.Join(tmpDir, "trivy")
|
return filepath.Join(tmpDir, "trivy")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dir returns the directory used for caching
|
|
||||||
func Dir() string {
|
|
||||||
if cacheDir == "" {
|
|
||||||
return defaultDir()
|
|
||||||
}
|
|
||||||
return cacheDir
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetDir sets the trivy cache dir
|
|
||||||
func SetDir(dir string) {
|
|
||||||
cacheDir = dir
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -110,10 +110,9 @@ func NewApp() *cobra.Command {
|
|||||||
|
|
||||||
func loadPluginCommands() []*cobra.Command {
|
func loadPluginCommands() []*cobra.Command {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
manager := plugin.NewManager()
|
|
||||||
|
|
||||||
var commands []*cobra.Command
|
var commands []*cobra.Command
|
||||||
plugins, err := manager.LoadAll(ctx)
|
plugins, err := plugin.NewManager().LoadAll(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.DebugContext(ctx, "No plugins loaded")
|
log.DebugContext(ctx, "No plugins loaded")
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -350,12 +350,11 @@ func (r *runner) initCache(opts flag.Options) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// standalone mode
|
// standalone mode
|
||||||
cache.SetDir(opts.CacheDir)
|
cacheClient, err := cache.NewClient(opts.CacheDir, opts.CacheOptions.CacheBackendOptions)
|
||||||
cacheClient, err := cache.NewClient(opts.CacheOptions.CacheBackendOptions)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Errorf("unable to initialize the cache: %w", err)
|
return xerrors.Errorf("unable to initialize the cache: %w", err)
|
||||||
}
|
}
|
||||||
log.Debug("Cache dir", log.String("dir", cache.Dir()))
|
log.Debug("Cache dir", log.String("dir", opts.CacheDir))
|
||||||
|
|
||||||
if opts.Reset {
|
if opts.Reset {
|
||||||
defer cacheClient.Close()
|
defer cacheClient.Close()
|
||||||
@@ -366,7 +365,7 @@ func (r *runner) initCache(opts flag.Options) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if opts.ResetChecksBundle {
|
if opts.ResetChecksBundle {
|
||||||
c, err := policy.NewClient(cache.Dir(), true, opts.MisconfOptions.ChecksBundleRepository)
|
c, err := policy.NewClient(opts.CacheDir, true, opts.MisconfOptions.ChecksBundleRepository)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Errorf("failed to instantiate check client: %w", err)
|
return xerrors.Errorf("failed to instantiate check client: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,16 +19,15 @@ func Run(ctx context.Context, opts flag.Options) (err error) {
|
|||||||
log.InitLogger(opts.Debug, opts.Quiet)
|
log.InitLogger(opts.Debug, opts.Quiet)
|
||||||
|
|
||||||
// configure cache dir
|
// configure cache dir
|
||||||
cache.SetDir(opts.CacheDir)
|
cacheClient, err := cache.NewClient(opts.CacheDir, opts.CacheOptions.CacheBackendOptions)
|
||||||
cacheClient, err := cache.NewClient(opts.CacheOptions.CacheBackendOptions)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Errorf("server cache error: %w", err)
|
return xerrors.Errorf("server cache error: %w", err)
|
||||||
}
|
}
|
||||||
defer cacheClient.Close()
|
defer cacheClient.Close()
|
||||||
log.Debug("Cache", log.String("dir", cache.Dir()))
|
log.Debug("Cache", log.String("dir", opts.CacheDir))
|
||||||
|
|
||||||
if opts.Reset {
|
if opts.Reset {
|
||||||
return cacheClient.ClearDB()
|
return cacheClient.Reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
// download the database file
|
// download the database file
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ var (
|
|||||||
CacheDirFlag = Flag[string]{
|
CacheDirFlag = Flag[string]{
|
||||||
Name: "cache-dir",
|
Name: "cache-dir",
|
||||||
ConfigName: "cache.dir",
|
ConfigName: "cache.dir",
|
||||||
Default: cache.Dir(),
|
Default: cache.DefaultDir(),
|
||||||
Usage: "cache directory",
|
Usage: "cache directory",
|
||||||
Persistent: true,
|
Persistent: true,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/aquasecurity/trivy/pkg/cache"
|
|
||||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||||
"github.com/aquasecurity/trivy/pkg/oci"
|
"github.com/aquasecurity/trivy/pkg/oci"
|
||||||
)
|
)
|
||||||
@@ -97,7 +96,6 @@ func TestArtifact_Download(t *testing.T) {
|
|||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
tempDir := t.TempDir()
|
tempDir := t.TempDir()
|
||||||
cache.SetDir(tempDir)
|
|
||||||
|
|
||||||
// Mock image
|
// Mock image
|
||||||
img := new(fakei.FakeImage)
|
img := new(fakei.FakeImage)
|
||||||
|
|||||||
@@ -12,13 +12,12 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/aquasecurity/trivy/pkg/cache"
|
|
||||||
"github.com/aquasecurity/trivy/pkg/plugin"
|
"github.com/aquasecurity/trivy/pkg/plugin"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestManager_Update(t *testing.T) {
|
func TestManager_Update(t *testing.T) {
|
||||||
tempDir := t.TempDir()
|
tempDir := t.TempDir()
|
||||||
cache.SetDir(tempDir)
|
t.Setenv("XDG_DATA_HOME", tempDir)
|
||||||
|
|
||||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
_, err := w.Write([]byte(`this is index`))
|
_, err := w.Write([]byte(`this is index`))
|
||||||
@@ -30,7 +29,7 @@ func TestManager_Update(t *testing.T) {
|
|||||||
err := manager.Update(context.Background())
|
err := manager.Update(context.Background())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
indexPath := filepath.Join(tempDir, "plugin", "index.yaml")
|
indexPath := filepath.Join(tempDir, ".trivy", "plugins", "index.yaml")
|
||||||
assert.FileExists(t, indexPath)
|
assert.FileExists(t, indexPath)
|
||||||
|
|
||||||
b, err := os.ReadFile(indexPath)
|
b, err := os.ReadFile(indexPath)
|
||||||
@@ -73,7 +72,7 @@ bar A bar plugin
|
|||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
cache.SetDir(tt.dir)
|
t.Setenv("XDG_DATA_HOME", tt.dir)
|
||||||
|
|
||||||
var got bytes.Buffer
|
var got bytes.Buffer
|
||||||
m := plugin.NewManager(plugin.WithWriter(&got))
|
m := plugin.NewManager(plugin.WithWriter(&got))
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import (
|
|||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
|
|
||||||
"github.com/aquasecurity/go-version/pkg/semver"
|
"github.com/aquasecurity/go-version/pkg/semver"
|
||||||
"github.com/aquasecurity/trivy/pkg/cache"
|
|
||||||
"github.com/aquasecurity/trivy/pkg/downloader"
|
"github.com/aquasecurity/trivy/pkg/downloader"
|
||||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||||
"github.com/aquasecurity/trivy/pkg/log"
|
"github.com/aquasecurity/trivy/pkg/log"
|
||||||
@@ -59,12 +58,13 @@ type Manager struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewManager(opts ...ManagerOption) *Manager {
|
func NewManager(opts ...ManagerOption) *Manager {
|
||||||
|
root := filepath.Join(fsutils.HomeDir(), pluginsRelativeDir)
|
||||||
m := &Manager{
|
m := &Manager{
|
||||||
w: os.Stdout,
|
w: os.Stdout,
|
||||||
indexURL: indexURL,
|
indexURL: indexURL,
|
||||||
logger: log.WithPrefix("plugin"),
|
logger: log.WithPrefix("plugin"),
|
||||||
pluginRoot: filepath.Join(fsutils.HomeDir(), pluginsRelativeDir),
|
pluginRoot: root,
|
||||||
indexPath: filepath.Join(cache.Dir(), "plugin", "index.yaml"),
|
indexPath: filepath.Join(root, "index.yaml"),
|
||||||
}
|
}
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
opt(m)
|
opt(m)
|
||||||
|
|||||||
@@ -20,11 +20,11 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/aquasecurity/trivy/internal/gittest"
|
"github.com/aquasecurity/trivy/internal/gittest"
|
||||||
"github.com/aquasecurity/trivy/pkg/cache"
|
|
||||||
"github.com/aquasecurity/trivy/pkg/clock"
|
"github.com/aquasecurity/trivy/pkg/clock"
|
||||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||||
"github.com/aquasecurity/trivy/pkg/log"
|
"github.com/aquasecurity/trivy/pkg/log"
|
||||||
"github.com/aquasecurity/trivy/pkg/plugin"
|
"github.com/aquasecurity/trivy/pkg/plugin"
|
||||||
|
"github.com/aquasecurity/trivy/pkg/utils/fsutils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func setupGitRepository(t *testing.T, repo, dir string) *httptest.Server {
|
func setupGitRepository(t *testing.T, repo, dir string) *httptest.Server {
|
||||||
@@ -200,7 +200,11 @@ func TestManager_Install(t *testing.T) {
|
|||||||
t.Setenv("XDG_DATA_HOME", dst)
|
t.Setenv("XDG_DATA_HOME", dst)
|
||||||
|
|
||||||
// For plugin index
|
// For plugin index
|
||||||
cache.SetDir("testdata")
|
pluginDir := filepath.Join(dst, ".trivy", "plugins")
|
||||||
|
err := os.MkdirAll(pluginDir, 0755)
|
||||||
|
require.NoError(t, err)
|
||||||
|
_, err = fsutils.CopyFile("testdata/.trivy/plugins/index.yaml", filepath.Join(pluginDir, "index.yaml"))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
if tt.installed != nil {
|
if tt.installed != nil {
|
||||||
setupInstalledPlugin(t, dst, *tt.installed)
|
setupInstalledPlugin(t, dst, *tt.installed)
|
||||||
|
|||||||
Reference in New Issue
Block a user