diff --git a/cmd/cachewd/main.go b/cmd/cachewd/main.go index e55785b..e5cf161 100644 --- a/cmd/cachewd/main.go +++ b/cmd/cachewd/main.go @@ -15,6 +15,7 @@ import ( "github.com/alecthomas/hcl/v2" "github.com/alecthomas/kong" + "github.com/block/cachew/internal/cache" "github.com/block/cachew/internal/config" "github.com/block/cachew/internal/httputil" "github.com/block/cachew/internal/jobscheduler" @@ -36,10 +37,15 @@ func main() { ctx := context.Background() logger, ctx := logging.Configure(ctx, cli.LoggingConfig) + cr := cache.NewRegistry() + cache.RegisterMemory(cr) + cache.RegisterDisk(cr) + cache.RegisterS3(cr) + // Commands switch { //nolint:gocritic case cli.Schema: - schema := config.Schema() + schema := config.Schema(cr) slices.SortStableFunc(schema.Entries, func(a, b hcl.Entry) int { return strings.Compare(a.EntryKey(), b.EntryKey()) }) @@ -59,7 +65,7 @@ func main() { scheduler := jobscheduler.New(ctx, cli.SchedulerConfig) - err := config.Load(ctx, cli.Config, scheduler, mux, parseEnvars()) + err := config.Load(ctx, cr, cli.Config, scheduler, mux, parseEnvars()) kctx.FatalIfErrorf(err) logger.InfoContext(ctx, "Starting cachewd", slog.String("bind", cli.Bind)) diff --git a/internal/cache/api.go b/internal/cache/api.go index 4f33473..a676053 100644 --- a/internal/cache/api.go +++ b/internal/cache/api.go @@ -24,13 +24,21 @@ type registryEntry struct { factory func(ctx context.Context, config *hcl.Block) (Cache, error) } -var registry = map[string]registryEntry{} +type Registry struct { + registry map[string]registryEntry +} + +func NewRegistry() *Registry { + return &Registry{ + registry: make(map[string]registryEntry), + } +} // Factory is a function that creates a new cache instance from the given hcl-tagged configuration struct. type Factory[Config any, C Cache] func(ctx context.Context, config Config) (C, error) // Register a cache factory function. -func Register[Config any, C Cache](id, description string, factory Factory[Config, C]) { +func Register[Config any, C Cache](r *Registry, id, description string, factory Factory[Config, C]) { var c Config schema, err := hcl.BlockSchema(id, &c) if err != nil { @@ -38,7 +46,7 @@ func Register[Config any, C Cache](id, description string, factory Factory[Confi } block := schema.Entries[0].(*hcl.Block) //nolint:errcheck // This seems spurious block.Comments = hcl.CommentList{description} - registry[id] = registryEntry{ + r.registry[id] = registryEntry{ schema: block, factory: func(ctx context.Context, config *hcl.Block) (Cache, error) { var cfg Config @@ -51,9 +59,9 @@ func Register[Config any, C Cache](id, description string, factory Factory[Confi } // Schema returns the schema for all registered cache backends. -func Schema() *hcl.AST { +func (r *Registry) Schema() *hcl.AST { ast := &hcl.AST{} - for _, entry := range registry { + for _, entry := range r.registry { ast.Entries = append(ast.Entries, entry.schema) } return ast @@ -62,8 +70,8 @@ func Schema() *hcl.AST { // Create a new cache instance from the given name and configuration. // // Will return "ErrNotFound" if the cache backend is not found. -func Create(ctx context.Context, name string, config *hcl.Block) (Cache, error) { - if entry, ok := registry[name]; ok { +func (r *Registry) Create(ctx context.Context, name string, config *hcl.Block) (Cache, error) { + if entry, ok := r.registry[name]; ok { return errors.WithStack2(entry.factory(ctx, config)) } return nil, errors.Errorf("%s: %w", name, ErrNotFound) diff --git a/internal/cache/disk.go b/internal/cache/disk.go index 8ed7a29..10ca9e2 100644 --- a/internal/cache/disk.go +++ b/internal/cache/disk.go @@ -19,8 +19,10 @@ import ( "github.com/block/cachew/internal/logging" ) -func init() { +// RegisterDisk cache with the given registry. +func RegisterDisk(r *Registry) { Register( + r, "disk", "Caches objects on local disk, with a maximum size limit and LRU eviction", NewDisk, diff --git a/internal/cache/memory.go b/internal/cache/memory.go index d9940d1..ced3d7a 100644 --- a/internal/cache/memory.go +++ b/internal/cache/memory.go @@ -16,8 +16,9 @@ import ( "github.com/block/cachew/internal/logging" ) -func init() { +func RegisterMemory(r *Registry) { Register( + r, "memory", "Caches objects in memory, with a maximum size limit and LRU eviction", NewMemory, diff --git a/internal/cache/s3.go b/internal/cache/s3.go index f82fae7..cbb6734 100644 --- a/internal/cache/s3.go +++ b/internal/cache/s3.go @@ -20,8 +20,9 @@ import ( "github.com/block/cachew/internal/logging" ) -func init() { +func RegisterS3(r *Registry) { Register( + r, "s3", "Caches objects in S3", NewS3, diff --git a/internal/config/config.go b/internal/config/config.go index 19e2d99..933f2b4 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -37,14 +37,14 @@ func (l *loggingMux) HandleFunc(pattern string, handler func(http.ResponseWriter var _ strategy.Mux = (*loggingMux)(nil) // Schema returns the configuration file schema. -func Schema() *hcl.AST { +func Schema(cr *cache.Registry) *hcl.AST { return &hcl.AST{ - Entries: append(strategy.Schema().Entries, cache.Schema().Entries...), + Entries: append(strategy.Schema().Entries, cr.Schema().Entries...), } } // Load HCL configuration and uses that to construct the cache backend, and proxy strategies. -func Load(ctx context.Context, r io.Reader, scheduler jobscheduler.Scheduler, mux *http.ServeMux, vars map[string]string) error { +func Load(ctx context.Context, cr *cache.Registry, r io.Reader, scheduler jobscheduler.Scheduler, mux *http.ServeMux, vars map[string]string) error { logger := logging.FromContext(ctx) ast, err := hcl.Parse(r) if err != nil { @@ -63,7 +63,7 @@ func Load(ctx context.Context, r io.Reader, scheduler jobscheduler.Scheduler, mu for _, node := range ast.Entries { switch node := node.(type) { case *hcl.Block: - c, err := cache.Create(ctx, node.Name, node) + c, err := cr.Create(ctx, node.Name, node) if errors.Is(err, cache.ErrNotFound) { strategyCandidates = append(strategyCandidates, node) continue