mirror of
https://github.com/coder/coder.git
synced 2025-07-12 00:14:10 +00:00
chore: add cacheCloser to cleanup all opened files (#18473)
This commit is contained in:
@ -131,46 +131,48 @@ func (r *loader) Renderer(ctx context.Context, db database.Store, cache *files.C
|
|||||||
return r.staticRender(ctx, db)
|
return r.staticRender(ctx, db)
|
||||||
}
|
}
|
||||||
|
|
||||||
return r.dynamicRenderer(ctx, db, cache)
|
return r.dynamicRenderer(ctx, db, files.NewCacheCloser(cache))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Renderer caches all the necessary files when rendering a template version's
|
// Renderer caches all the necessary files when rendering a template version's
|
||||||
// parameters. It must be closed after use to release the cached files.
|
// parameters. It must be closed after use to release the cached files.
|
||||||
func (r *loader) dynamicRenderer(ctx context.Context, db database.Store, cache *files.Cache) (*dynamicRenderer, error) {
|
func (r *loader) dynamicRenderer(ctx context.Context, db database.Store, cache *files.CacheCloser) (*dynamicRenderer, error) {
|
||||||
|
closeFiles := true // If the function returns with no error, this will toggle to false.
|
||||||
|
defer func() {
|
||||||
|
if closeFiles {
|
||||||
|
cache.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
// If they can read the template version, then they can read the file for
|
// If they can read the template version, then they can read the file for
|
||||||
// parameter loading purposes.
|
// parameter loading purposes.
|
||||||
//nolint:gocritic
|
//nolint:gocritic
|
||||||
fileCtx := dbauthz.AsFileReader(ctx)
|
fileCtx := dbauthz.AsFileReader(ctx)
|
||||||
templateFS, err := cache.Acquire(fileCtx, r.job.FileID)
|
|
||||||
|
var templateFS fs.FS
|
||||||
|
var err error
|
||||||
|
|
||||||
|
templateFS, err = cache.Acquire(fileCtx, r.job.FileID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Errorf("acquire template file: %w", err)
|
return nil, xerrors.Errorf("acquire template file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var terraformFS fs.FS = templateFS
|
|
||||||
var moduleFilesFS *files.CloseFS
|
var moduleFilesFS *files.CloseFS
|
||||||
if r.terraformValues.CachedModuleFiles.Valid {
|
if r.terraformValues.CachedModuleFiles.Valid {
|
||||||
moduleFilesFS, err = cache.Acquire(fileCtx, r.terraformValues.CachedModuleFiles.UUID)
|
moduleFilesFS, err = cache.Acquire(fileCtx, r.terraformValues.CachedModuleFiles.UUID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
templateFS.Close()
|
|
||||||
return nil, xerrors.Errorf("acquire module files: %w", err)
|
return nil, xerrors.Errorf("acquire module files: %w", err)
|
||||||
}
|
}
|
||||||
terraformFS = files.NewOverlayFS(templateFS, []files.Overlay{{Path: ".terraform/modules", FS: moduleFilesFS}})
|
templateFS = files.NewOverlayFS(templateFS, []files.Overlay{{Path: ".terraform/modules", FS: moduleFilesFS}})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
closeFiles = false // Caller will have to call close
|
||||||
return &dynamicRenderer{
|
return &dynamicRenderer{
|
||||||
data: r,
|
data: r,
|
||||||
templateFS: terraformFS,
|
templateFS: templateFS,
|
||||||
db: db,
|
db: db,
|
||||||
ownerErrors: make(map[uuid.UUID]error),
|
ownerErrors: make(map[uuid.UUID]error),
|
||||||
close: func() {
|
close: cache.Close,
|
||||||
// Up to 2 files are cached, and must be released when rendering is complete.
|
|
||||||
// TODO: Might be smart to always call release when the context is
|
|
||||||
// canceled.
|
|
||||||
templateFS.Close()
|
|
||||||
if moduleFilesFS != nil {
|
|
||||||
moduleFilesFS.Close()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,6 +19,10 @@ import (
|
|||||||
"github.com/coder/coder/v2/coderd/util/lazy"
|
"github.com/coder/coder/v2/coderd/util/lazy"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type FileAcquirer interface {
|
||||||
|
Acquire(ctx context.Context, fileID uuid.UUID) (*CloseFS, error)
|
||||||
|
}
|
||||||
|
|
||||||
// NewFromStore returns a file cache that will fetch files from the provided
|
// NewFromStore returns a file cache that will fetch files from the provided
|
||||||
// database.
|
// database.
|
||||||
func NewFromStore(store database.Store, registerer prometheus.Registerer, authz rbac.Authorizer) *Cache {
|
func NewFromStore(store database.Store, registerer prometheus.Registerer, authz rbac.Authorizer) *Cache {
|
||||||
|
57
coderd/files/closer.go
Normal file
57
coderd/files/closer.go
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package files
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"golang.org/x/xerrors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CacheCloser is a cache wrapper used to close all acquired files.
|
||||||
|
// This is a more simple interface to use if opening multiple files at once.
|
||||||
|
type CacheCloser struct {
|
||||||
|
cache FileAcquirer
|
||||||
|
|
||||||
|
closers []func()
|
||||||
|
mu sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCacheCloser(cache FileAcquirer) *CacheCloser {
|
||||||
|
return &CacheCloser{
|
||||||
|
cache: cache,
|
||||||
|
closers: make([]func(), 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CacheCloser) Close() {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
|
||||||
|
for _, doClose := range c.closers {
|
||||||
|
doClose()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevent further acquisitions
|
||||||
|
c.cache = nil
|
||||||
|
// Remove any references
|
||||||
|
c.closers = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CacheCloser) Acquire(ctx context.Context, fileID uuid.UUID) (*CloseFS, error) {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
|
||||||
|
if c.cache == nil {
|
||||||
|
return nil, xerrors.New("cache is closed, and cannot acquire new files")
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := c.cache.Acquire(ctx, fileID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.closers = append(c.closers, f.close)
|
||||||
|
|
||||||
|
return f, nil
|
||||||
|
}
|
Reference in New Issue
Block a user