Merge pull request #3674 from tonistiigi/policy-fs-refs-fix
build: ref-count policy FS lifecycle
This commit is contained in:
+28
-8
@@ -228,28 +228,48 @@ func normalizeLocalPolicyPath(name, contextDir string) string {
|
|||||||
|
|
||||||
type memoizedPolicyFS struct {
|
type memoizedPolicyFS struct {
|
||||||
init func() (fs.StatFS, func() error, error)
|
init func() (fs.StatFS, func() error, error)
|
||||||
once sync.Once
|
mu sync.Mutex
|
||||||
|
loaded bool
|
||||||
|
refs int
|
||||||
fs fs.StatFS
|
fs fs.StatFS
|
||||||
closeFn func() error
|
closeFn func() error
|
||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *memoizedPolicyFS) get() (fs.StatFS, error) {
|
func (m *memoizedPolicyFS) get() (fs.StatFS, error) {
|
||||||
m.once.Do(func() {
|
m.mu.Lock()
|
||||||
if m.init == nil {
|
defer m.mu.Unlock()
|
||||||
return
|
if !m.loaded {
|
||||||
|
m.loaded = true
|
||||||
|
if m.init != nil {
|
||||||
|
m.fs, m.closeFn, m.err = m.init()
|
||||||
}
|
}
|
||||||
m.fs, m.closeFn, m.err = m.init()
|
}
|
||||||
})
|
|
||||||
if m.err != nil {
|
if m.err != nil {
|
||||||
return nil, m.err
|
return nil, m.err
|
||||||
}
|
}
|
||||||
|
m.refs++
|
||||||
return m.fs, nil
|
return m.fs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *memoizedPolicyFS) close() error {
|
func (m *memoizedPolicyFS) close() error {
|
||||||
if m.closeFn != nil {
|
m.mu.Lock()
|
||||||
return m.closeFn()
|
if m.refs > 0 {
|
||||||
|
m.refs--
|
||||||
|
}
|
||||||
|
if m.refs > 0 {
|
||||||
|
m.mu.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
closeFn := m.closeFn
|
||||||
|
m.fs = nil
|
||||||
|
m.closeFn = nil
|
||||||
|
m.err = nil
|
||||||
|
m.loaded = false
|
||||||
|
m.mu.Unlock()
|
||||||
|
|
||||||
|
if closeFn != nil {
|
||||||
|
return closeFn()
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
package build
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/fs"
|
||||||
|
"testing"
|
||||||
|
"testing/fstest"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMemoizedPolicyFSRefCountedClose(t *testing.T) {
|
||||||
|
var initCalls int
|
||||||
|
var closeCalls int
|
||||||
|
|
||||||
|
m := &memoizedPolicyFS{
|
||||||
|
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, err := m.get()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, first)
|
||||||
|
require.Equal(t, 1, initCalls)
|
||||||
|
|
||||||
|
second, err := m.get()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, second)
|
||||||
|
require.Equal(t, 1, initCalls)
|
||||||
|
|
||||||
|
require.NoError(t, m.close())
|
||||||
|
require.Equal(t, 0, closeCalls)
|
||||||
|
|
||||||
|
third, err := m.get()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, third)
|
||||||
|
require.Equal(t, 1, initCalls)
|
||||||
|
|
||||||
|
require.NoError(t, m.close())
|
||||||
|
require.Equal(t, 0, closeCalls)
|
||||||
|
|
||||||
|
require.NoError(t, m.close())
|
||||||
|
require.Equal(t, 1, closeCalls)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMemoizedPolicyFSReinitializesAfterAllRefsClosed(t *testing.T) {
|
||||||
|
var initCalls int
|
||||||
|
var closeCalls int
|
||||||
|
|
||||||
|
m := &memoizedPolicyFS{
|
||||||
|
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, err := m.get()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, first)
|
||||||
|
require.Equal(t, 1, initCalls)
|
||||||
|
|
||||||
|
require.NoError(t, m.close())
|
||||||
|
require.Equal(t, 1, closeCalls)
|
||||||
|
|
||||||
|
second, err := m.get()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, second)
|
||||||
|
require.Equal(t, 2, initCalls)
|
||||||
|
|
||||||
|
require.NoError(t, m.close())
|
||||||
|
require.Equal(t, 2, closeCalls)
|
||||||
|
}
|
||||||
+5
-3
@@ -486,13 +486,15 @@ func (p *Policy) readFile(path string, limit int64) ([]byte, error) {
|
|||||||
if p.opt.FS == nil {
|
if p.opt.FS == nil {
|
||||||
return nil, errors.Errorf("no policy FS defined for reading context files")
|
return nil, errors.Errorf("no policy FS defined for reading context files")
|
||||||
}
|
}
|
||||||
fs, cf, err := p.opt.FS()
|
root, closeFS, err := p.opt.FS()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "failed to get policy FS for reading context files")
|
return nil, errors.Wrapf(err, "failed to get policy FS for reading context files")
|
||||||
}
|
}
|
||||||
defer cf()
|
if closeFS != nil {
|
||||||
|
defer closeFS()
|
||||||
|
}
|
||||||
|
|
||||||
f, err := fs.Open(path)
|
f, err := root.Open(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "failed opening file %q", path)
|
return nil, errors.Wrapf(err, "failed opening file %q", path)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user