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) }) } }