Files
coder/coderd/files/overlay.go

87 lines
2.1 KiB
Go

package files
import (
"io/fs"
"path"
"strings"
"golang.org/x/xerrors"
)
// overlayFS allows you to "join" together the template files tar file fs.FS
// with the Terraform modules tar file fs.FS. We could potentially turn this
// into something more parameterized/configurable, but the requirements here are
// a _bit_ odd, because every file in the modulesFS includes the
// .terraform/modules/ folder at the beginning of it's path.
type overlayFS struct {
baseFS fs.FS
overlays []Overlay
}
type Overlay struct {
Path string
fs.FS
}
func NewOverlayFS(baseFS fs.FS, overlays []Overlay) (fs.FS, error) {
if err := valid(baseFS); err != nil {
return nil, xerrors.Errorf("baseFS: %w", err)
}
for _, overlay := range overlays {
if err := valid(overlay.FS); err != nil {
return nil, xerrors.Errorf("overlayFS: %w", err)
}
}
return overlayFS{
baseFS: baseFS,
overlays: overlays,
}, nil
}
func (f overlayFS) Open(p string) (fs.File, error) {
for _, overlay := range f.overlays {
if strings.HasPrefix(path.Clean(p), overlay.Path) {
return overlay.FS.Open(p)
}
}
return f.baseFS.Open(p)
}
func (f overlayFS) ReadDir(p string) ([]fs.DirEntry, error) {
for _, overlay := range f.overlays {
if strings.HasPrefix(path.Clean(p), overlay.Path) {
//nolint:forcetypeassert
return overlay.FS.(fs.ReadDirFS).ReadDir(p)
}
}
//nolint:forcetypeassert
return f.baseFS.(fs.ReadDirFS).ReadDir(p)
}
func (f overlayFS) ReadFile(p string) ([]byte, error) {
for _, overlay := range f.overlays {
if strings.HasPrefix(path.Clean(p), overlay.Path) {
//nolint:forcetypeassert
return overlay.FS.(fs.ReadFileFS).ReadFile(p)
}
}
//nolint:forcetypeassert
return f.baseFS.(fs.ReadFileFS).ReadFile(p)
}
// valid checks that the fs.FS implements the required interfaces.
// The fs.FS interface is not sufficient.
func valid(fsys fs.FS) error {
_, ok := fsys.(fs.ReadDirFS)
if !ok {
return xerrors.New("overlayFS does not implement ReadDirFS")
}
_, ok = fsys.(fs.ReadFileFS)
if !ok {
return xerrors.New("overlayFS does not implement ReadFileFS")
}
return nil
}