Provide passive per-source resource contexts
This commit is contained in:
133
internal/engine/resource_context_test.go
Normal file
133
internal/engine/resource_context_test.go
Normal file
@@ -0,0 +1,133 @@
|
||||
package engine
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"testing/fstest"
|
||||
)
|
||||
|
||||
type trackedHostRoot struct {
|
||||
root *os.Root
|
||||
closed *int
|
||||
failOpen bool
|
||||
}
|
||||
|
||||
func (r *trackedHostRoot) Open(name string) (*os.File, error) {
|
||||
if r.failOpen {
|
||||
return nil, fs.ErrPermission
|
||||
}
|
||||
return r.root.Open(name)
|
||||
}
|
||||
func (r *trackedHostRoot) Close() error {
|
||||
(*r.closed)++
|
||||
return r.root.Close()
|
||||
}
|
||||
|
||||
func TestRootedHostFSClosesRootOnSuccessfulAndFailedOpen(t *testing.T) {
|
||||
rootPath := t.TempDir()
|
||||
if err := os.WriteFile(filepath.Join(rootPath, "resource.txt"), []byte("resource"), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
opened, closed := 0, 0
|
||||
failOpen := false
|
||||
filesystem := rootedHostFS{root: rootPath, openRoot: func(name string) (hostResourceRoot, error) {
|
||||
root, err := os.OpenRoot(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
opened++
|
||||
return &trackedHostRoot{root: root, closed: &closed, failOpen: failOpen}, nil
|
||||
}}
|
||||
if _, err := filesystem.Open("../resource.txt"); !errors.Is(err, fs.ErrInvalid) || opened != 0 {
|
||||
t.Fatalf("invalid path = %v; roots opened %d", err, opened)
|
||||
}
|
||||
if _, err := filesystem.Open("missing.txt"); !errors.Is(err, fs.ErrNotExist) || opened != 1 || closed != 1 {
|
||||
t.Fatalf("missing resource = %v; opened %d, closed %d", err, opened, closed)
|
||||
}
|
||||
failOpen = true
|
||||
if _, err := filesystem.Open("resource.txt"); !errors.Is(err, fs.ErrPermission) || opened != 2 || closed != 2 {
|
||||
t.Fatalf("failed inner open = %v; opened %d, closed %d", err, opened, closed)
|
||||
}
|
||||
failOpen = false
|
||||
file, err := filesystem.Open("resource.txt")
|
||||
if err != nil || opened != 3 || closed != 2 {
|
||||
t.Fatalf("resource open = %v; opened %d, closed %d", err, opened, closed)
|
||||
}
|
||||
if err := file.Close(); err != nil || closed != 3 {
|
||||
t.Fatalf("resource close = %v; opened %d, closed %d", err, opened, closed)
|
||||
}
|
||||
if _, err := file.Read(make([]byte, 1)); err == nil {
|
||||
t.Fatal("closed resource file remained readable")
|
||||
}
|
||||
directory, err := filesystem.Open(".")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
reader, ok := directory.(fs.ReadDirFile)
|
||||
if !ok {
|
||||
t.Fatal("rooted host directory did not expose ReadDirFile")
|
||||
}
|
||||
entries, err := reader.ReadDir(-1)
|
||||
if err != nil || len(entries) != 1 || entries[0].Name() != "resource.txt" {
|
||||
t.Fatalf("directory entries = %#v, %v", entries, err)
|
||||
}
|
||||
if err := directory.Close(); err != nil || opened != 4 || closed != 4 {
|
||||
t.Fatalf("directory close = %v; opened %d, closed %d", err, opened, closed)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNativeResourceViewIsBorrowedAndRooted(t *testing.T) {
|
||||
filesystem := &observedFilesystem{files: fstest.MapFS{
|
||||
"bundle/config/resource.txt": &fstest.MapFile{Data: []byte("content")},
|
||||
}}
|
||||
view := rootedNativeFS{filesystem: filesystem, root: "bundle"}
|
||||
if filesystem.opened != 0 {
|
||||
t.Fatal("constructing a native resource view opened files")
|
||||
}
|
||||
data, err := fs.ReadFile(view, "config/resource.txt")
|
||||
if err != nil || string(data) != "content" {
|
||||
t.Fatalf("resource = %q, %v", data, err)
|
||||
}
|
||||
filesystem.assertClosed(t)
|
||||
if _, err := fs.ReadFile(view, "../outside.txt"); !errors.Is(err, fs.ErrInvalid) {
|
||||
t.Fatalf("resource escape = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadingClosesNativeFilesAcrossPipelineOutcomes(t *testing.T) {
|
||||
type config struct {
|
||||
Value string `yaml:"value"`
|
||||
Version string `yaml:"version"`
|
||||
}
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
main string
|
||||
child string
|
||||
failRead string
|
||||
options Options[config]
|
||||
wantErr bool
|
||||
}{
|
||||
{name: "success", main: "value: retained\n"},
|
||||
{name: "acquisition", main: "value: retained\n", failRead: "main.yml", wantErr: true},
|
||||
{name: "parse", main: "value: [broken\n", wantErr: true},
|
||||
{name: "composition", main: "configkit: {imports: [{path: child.yml}]}\nvalue: first\n", child: "value: second\n", options: Options[config]{Imports: true}, wantErr: true},
|
||||
{name: "version", main: "version: wrong\n", options: Options[config]{Definition: Definition[config]{Version: &VersionRule{Path: Keys("version"), Check: func(Version, *Validation) error { return fs.ErrInvalid }}}}, wantErr: true},
|
||||
{name: "validation", main: "value: retained\n", options: Options[config]{Definition: Definition[config]{Validate: func(_ *config, validation *Validation) { validation.Error(Keys("value"), "rejected") }}}, wantErr: true},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
files := fstest.MapFS{"main.yml": &fstest.MapFile{Data: []byte(test.main)}}
|
||||
if test.child != "" {
|
||||
files["child.yml"] = &fstest.MapFile{Data: []byte(test.child)}
|
||||
}
|
||||
filesystem := &observedFilesystem{files: files, failRead: test.failRead}
|
||||
_, err := testLoad(filesystemFile(newNativeNamespace("bundle", filesystem), "main.yml"), test.options)
|
||||
if (err != nil) != test.wantErr {
|
||||
t.Fatalf("load error = %v, want failure %v", err, test.wantErr)
|
||||
}
|
||||
filesystem.assertClosed(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user