Skip to content

Commit

Permalink
refactor: use google/wire for cache (#7024)
Browse files Browse the repository at this point in the history
Signed-off-by: knqyf263 <[email protected]>
  • Loading branch information
knqyf263 authored Jun 27, 2024
1 parent e9fc3e3 commit 4be02ba
Show file tree
Hide file tree
Showing 22 changed files with 496 additions and 418 deletions.
2 changes: 1 addition & 1 deletion pkg/cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
)

const (
cacheDirName = "fanal"
scanCacheDirName = "fanal"

// artifactBucket stores artifact information with artifact ID such as image ID
artifactBucket = "artifact"
Expand Down
167 changes: 33 additions & 134 deletions pkg/cache/client.go
Original file line number Diff line number Diff line change
@@ -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
}

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
Backend string
CacheDir string
RedisCACert string
RedisCert string
RedisKey string
RedisTLS bool
TTL time.Duration
}

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
}

// 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
cache = fsCache
default:
return nil, cleanup, xerrors.Errorf("unknown cache type: %s", t)
}

caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)

return caCertPool, cert, nil
return cache, func() { _ = cache.Close() }, nil
}
Loading

0 comments on commit 4be02ba

Please sign in to comment.