refactor: use google/wire for cache (#7024)

Signed-off-by: knqyf263 <knqyf263@gmail.com>
This commit is contained in:
Teppei Fukuda
2024-06-27 11:04:01 +04:00
committed by GitHub
parent e9fc3e3397
commit 4be02bab8c
22 changed files with 525 additions and 447 deletions

167
pkg/cache/client.go vendored
View File

@@ -1,166 +1,65 @@
package cache
import (
"crypto/tls"
"crypto/x509"
"fmt"
"os"
"strings"
"time"
"github.com/go-redis/redis/v8"
"github.com/samber/lo"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy/pkg/log"
)
const (
TypeFS Type = "fs"
TypeRedis Type = "redis"
TypeUnknown Type = "unknown"
TypeFS Type = "fs"
TypeRedis Type = "redis"
)
type Type string
type Options struct {
Type Type
TTL time.Duration
Redis RedisOptions
Backend string
CacheDir string
RedisCACert string
RedisCert string
RedisKey string
RedisTLS bool
TTL time.Duration
}
func NewOptions(backend, redisCACert, redisCert, redisKey string, redisTLS bool, ttl time.Duration) (Options, error) {
t, err := NewType(backend)
if err != nil {
return Options{}, xerrors.Errorf("cache type error: %w", err)
}
var redisOpts RedisOptions
if t == TypeRedis {
redisTLSOpts, err := NewRedisTLSOptions(redisCACert, redisCert, redisKey)
if err != nil {
return Options{}, xerrors.Errorf("redis TLS option error: %w", err)
}
redisOpts = RedisOptions{
Backend: backend,
TLS: redisTLS,
TLSOptions: redisTLSOpts,
}
} else if ttl != 0 {
log.Warn("'--cache-ttl' is only available with Redis cache backend")
}
return Options{
Type: t,
TTL: ttl,
Redis: redisOpts,
}, nil
}
type RedisOptions struct {
Backend string
TLS bool
TLSOptions RedisTLSOptions
}
// BackendMasked returns the redis connection string masking credentials
func (o *RedisOptions) BackendMasked() string {
endIndex := strings.Index(o.Backend, "@")
if endIndex == -1 {
return o.Backend
}
startIndex := strings.Index(o.Backend, "//")
return fmt.Sprintf("%s****%s", o.Backend[:startIndex+2], o.Backend[endIndex:])
}
// RedisTLSOptions holds the options for redis cache
type RedisTLSOptions struct {
CACert string
Cert string
Key string
}
func NewRedisTLSOptions(caCert, cert, key string) (RedisTLSOptions, error) {
opts := RedisTLSOptions{
CACert: caCert,
Cert: cert,
Key: key,
}
// If one of redis option not nil, make sure CA, cert, and key provided
if !lo.IsEmpty(opts) {
if opts.CACert == "" || opts.Cert == "" || opts.Key == "" {
return RedisTLSOptions{}, xerrors.Errorf("you must provide Redis CA, cert and key file path when using TLS")
}
}
return opts, nil
}
func NewType(backend string) (Type, error) {
func NewType(backend string) Type {
// "redis://" or "fs" are allowed for now
// An empty value is also allowed for testability
switch {
case strings.HasPrefix(backend, "redis://"):
return TypeRedis, nil
return TypeRedis
case backend == "fs", backend == "":
return TypeFS, nil
return TypeFS
default:
return "", xerrors.Errorf("unknown cache backend: %s", backend)
return TypeUnknown
}
}
// New returns a new cache client
func New(dir string, opts Options) (Cache, error) {
if opts.Type == TypeRedis {
log.Info("Redis cache", log.String("url", opts.Redis.BackendMasked()))
options, err := redis.ParseURL(opts.Redis.Backend)
func New(opts Options) (Cache, func(), error) {
cleanup := func() {} // To avoid panic
var cache Cache
t := NewType(opts.Backend)
switch t {
case TypeRedis:
redisCache, err := NewRedisCache(opts.Backend, opts.RedisCACert, opts.RedisCert, opts.RedisKey, opts.RedisTLS, opts.TTL)
if err != nil {
return nil, err
return nil, cleanup, xerrors.Errorf("unable to initialize redis cache: %w", err)
}
if tlsOpts := opts.Redis.TLSOptions; !lo.IsEmpty(tlsOpts) {
caCert, cert, err := GetTLSConfig(tlsOpts.CACert, tlsOpts.Cert, tlsOpts.Key)
if err != nil {
return nil, err
}
options.TLSConfig = &tls.Config{
RootCAs: caCert,
Certificates: []tls.Certificate{cert},
MinVersion: tls.VersionTLS12,
}
} else if opts.Redis.TLS {
options.TLSConfig = &tls.Config{
MinVersion: tls.VersionTLS12,
}
cache = redisCache
case TypeFS:
// standalone mode
fsCache, err := NewFSCache(opts.CacheDir)
if err != nil {
return nil, cleanup, xerrors.Errorf("unable to initialize fs cache: %w", err)
}
return NewRedisCache(options, opts.TTL), nil
cache = fsCache
default:
return nil, cleanup, xerrors.Errorf("unknown cache type: %s", t)
}
// standalone mode
fsCache, err := NewFSCache(dir)
if err != nil {
return nil, xerrors.Errorf("unable to initialize fs cache: %w", err)
}
return fsCache, nil
}
// GetTLSConfig gets tls config from CA, Cert and Key file
func GetTLSConfig(caCertPath, certPath, keyPath string) (*x509.CertPool, tls.Certificate, error) {
cert, err := tls.LoadX509KeyPair(certPath, keyPath)
if err != nil {
return nil, tls.Certificate{}, err
}
caCert, err := os.ReadFile(caCertPath)
if err != nil {
return nil, tls.Certificate{}, err
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
return caCertPool, cert, nil
return cache, func() { _ = cache.Close() }, nil
}