policy: close memoized policy filesystems
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
This commit is contained in:
+73
-14
@@ -152,11 +152,21 @@ func newPolicyPathFS(ctx context.Context, resolver *sourcemeta.Resolver, popt po
|
|||||||
}
|
}
|
||||||
|
|
||||||
return func() (fs.StatFS, func() error, error) {
|
return func() (fs.StatFS, func() error, error) {
|
||||||
return p, p.Close, nil
|
ref := &policyPathFSRef{policyPathFS: p}
|
||||||
|
return ref, ref.Close, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *policyPathFS) Open(name string) (fs.File, error) {
|
type policyPathFSRef struct {
|
||||||
|
*policyPathFS
|
||||||
|
mu sync.Mutex
|
||||||
|
cwdRoot fs.StatFS
|
||||||
|
cwdClose func() error
|
||||||
|
contextRoot fs.StatFS
|
||||||
|
contextClose func() error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *policyPathFSRef) Open(name string) (fs.File, error) {
|
||||||
backend, target, err := p.resolve(name)
|
backend, target, err := p.resolve(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -167,7 +177,7 @@ func (p *policyPathFS) Open(name string) (fs.File, error) {
|
|||||||
return backend.Open(target)
|
return backend.Open(target)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *policyPathFS) Stat(name string) (fs.FileInfo, error) {
|
func (p *policyPathFSRef) Stat(name string) (fs.FileInfo, error) {
|
||||||
backend, target, err := p.resolve(name)
|
backend, target, err := p.resolve(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -178,14 +188,7 @@ func (p *policyPathFS) Stat(name string) (fs.FileInfo, error) {
|
|||||||
return backend.Stat(target)
|
return backend.Stat(target)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *policyPathFS) Close() error {
|
func (p *policyPathFSRef) resolve(name string) (fs.StatFS, string, error) {
|
||||||
if err := p.cwdFS.close(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return p.contextFS.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *policyPathFS) resolve(name string) (fs.StatFS, string, error) {
|
|
||||||
if name == "" {
|
if name == "" {
|
||||||
return nil, "", errors.New("policy filename is empty")
|
return nil, "", errors.New("policy filename is empty")
|
||||||
}
|
}
|
||||||
@@ -193,14 +196,14 @@ func (p *policyPathFS) resolve(name string) (fs.StatFS, string, error) {
|
|||||||
if v == "" {
|
if v == "" {
|
||||||
return nil, "", errors.Errorf("invalid policy filename %q", name)
|
return nil, "", errors.Errorf("invalid policy filename %q", name)
|
||||||
}
|
}
|
||||||
cwd, err := p.cwdFS.get()
|
cwd, err := p.getCwdFS()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
return cwd, filepath.Clean(v), nil
|
return cwd, filepath.Clean(v), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
contextFS, err := p.contextFS.get()
|
contextFS, err := p.getContextFS()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
@@ -214,6 +217,62 @@ func (p *policyPathFS) resolve(name string) (fs.StatFS, string, error) {
|
|||||||
return contextFS, normalizeLocalPolicyPath(name, p.contextDir), nil
|
return contextFS, normalizeLocalPolicyPath(name, p.contextDir), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *policyPathFSRef) getCwdFS() (fs.StatFS, error) {
|
||||||
|
p.mu.Lock()
|
||||||
|
defer p.mu.Unlock()
|
||||||
|
if p.cwdClose != nil {
|
||||||
|
return p.cwdRoot, nil
|
||||||
|
}
|
||||||
|
cwd, err := p.cwdFS.get()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
p.cwdRoot = cwd
|
||||||
|
p.cwdClose = p.cwdFS.close
|
||||||
|
return cwd, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *policyPathFSRef) getContextFS() (fs.StatFS, error) {
|
||||||
|
p.mu.Lock()
|
||||||
|
defer p.mu.Unlock()
|
||||||
|
if p.contextClose != nil {
|
||||||
|
return p.contextRoot, nil
|
||||||
|
}
|
||||||
|
contextFS, err := p.contextFS.get()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
p.contextRoot = contextFS
|
||||||
|
p.contextClose = p.contextFS.close
|
||||||
|
return contextFS, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *policyPathFSRef) Close() error {
|
||||||
|
p.mu.Lock()
|
||||||
|
cwdClose := p.cwdClose
|
||||||
|
contextClose := p.contextClose
|
||||||
|
p.cwdRoot = nil
|
||||||
|
p.contextRoot = nil
|
||||||
|
p.cwdClose = nil
|
||||||
|
p.contextClose = nil
|
||||||
|
p.mu.Unlock()
|
||||||
|
|
||||||
|
var firstErr error
|
||||||
|
if cwdClose != nil {
|
||||||
|
if err := cwdClose(); err != nil {
|
||||||
|
firstErr = err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if contextClose != nil {
|
||||||
|
if err := contextClose(); err != nil {
|
||||||
|
if firstErr == nil {
|
||||||
|
firstErr = err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return firstErr
|
||||||
|
}
|
||||||
|
|
||||||
func normalizeLocalPolicyPath(name, contextDir string) string {
|
func normalizeLocalPolicyPath(name, contextDir string) string {
|
||||||
if filepath.IsAbs(name) && contextDir != "" {
|
if filepath.IsAbs(name) && contextDir != "" {
|
||||||
if rel, err := filepath.Rel(contextDir, name); err == nil {
|
if rel, err := filepath.Rel(contextDir, name); err == nil {
|
||||||
@@ -405,7 +464,7 @@ func (i policyFileInfo) Sys() any {
|
|||||||
return &types.Stat{Mode: uint32(i.mode), Size: i.size, ModTime: i.tm.UnixNano()}
|
return &types.Stat{Mode: uint32(i.mode), Size: i.size, ModTime: i.tm.UnixNano()}
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ fs.StatFS = (*policyPathFS)(nil)
|
var _ fs.StatFS = (*policyPathFSRef)(nil)
|
||||||
var _ fs.StatFS = (*remotePolicyFS)(nil)
|
var _ fs.StatFS = (*remotePolicyFS)(nil)
|
||||||
var _ fs.File = (*policyReadFile)(nil)
|
var _ fs.File = (*policyReadFile)(nil)
|
||||||
var _ io.ReaderAt = (*bytes.Reader)(nil)
|
var _ io.ReaderAt = (*bytes.Reader)(nil)
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
package build
|
package build
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
"testing/fstest"
|
"testing/fstest"
|
||||||
|
|
||||||
@@ -83,3 +86,61 @@ func TestMemoizedPolicyFSReinitializesAfterAllRefsClosed(t *testing.T) {
|
|||||||
require.NoError(t, m.close())
|
require.NoError(t, m.close())
|
||||||
require.Equal(t, 2, closeCalls)
|
require.Equal(t, 2, closeCalls)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestLoadPolicyDataReleasesPolicyDir(t *testing.T) {
|
||||||
|
dir := t.TempDir()
|
||||||
|
require.NoError(t, os.WriteFile(filepath.Join(dir, "policy.rego"), []byte("package docker\n"), 0600))
|
||||||
|
|
||||||
|
provider := newPolicyPathFS(context.Background(), nil, policyOpt{
|
||||||
|
ContextDir: dir,
|
||||||
|
})
|
||||||
|
|
||||||
|
_, ok, err := loadPolicyData(provider, "policy.rego")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, ok)
|
||||||
|
require.NoError(t, os.RemoveAll(dir))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPolicyPathFSRefClose(t *testing.T) {
|
||||||
|
var initCalls int
|
||||||
|
var closeCalls int
|
||||||
|
|
||||||
|
p := &policyPathFS{}
|
||||||
|
p.contextFS.init = func() (fs.StatFS, func() error, error) {
|
||||||
|
initCalls++
|
||||||
|
root := fstest.MapFS{
|
||||||
|
"policy.rego": &fstest.MapFile{Data: []byte("package docker\n")},
|
||||||
|
}
|
||||||
|
return root, func() error {
|
||||||
|
closeCalls++
|
||||||
|
return nil
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
first := &policyPathFSRef{policyPathFS: p}
|
||||||
|
_, err := first.Stat("policy.rego")
|
||||||
|
require.NoError(t, err)
|
||||||
|
f, err := first.Open("policy.rego")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, f.Close())
|
||||||
|
require.Equal(t, 1, initCalls)
|
||||||
|
|
||||||
|
require.NoError(t, first.Close())
|
||||||
|
require.Equal(t, 1, closeCalls)
|
||||||
|
|
||||||
|
shared := &policyPathFSRef{policyPathFS: p}
|
||||||
|
_, err = shared.Stat("policy.rego")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 2, initCalls)
|
||||||
|
|
||||||
|
second := &policyPathFSRef{policyPathFS: p}
|
||||||
|
_, err = second.Stat("policy.rego")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 2, initCalls)
|
||||||
|
|
||||||
|
require.NoError(t, second.Close())
|
||||||
|
require.Equal(t, 1, closeCalls)
|
||||||
|
|
||||||
|
require.NoError(t, shared.Close())
|
||||||
|
require.Equal(t, 2, closeCalls)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user