package kustomize import ( "errors" "fmt" "io/fs" "path/filepath" "sigs.k8s.io/kustomize/kyaml/filesys" ) // ErrReadOnly is returned when write operations are called against [FS] var ErrReadOnly = fmt.Errorf("read-only FS") // File is a read-only implementation of kyaml/filesys.File to used to impelement // a read-only Kustomize FS that can be instantiated form [fs.FS] implementations // such as [embed.FS]. Calls to [File.Write] will return [ErrReadOnly]. type File struct { fs.File } var _ filesys.File = &File{} // Write returns [ErrReadOnly] func (f *File) Write(_ []byte) (n int, err error) { return 0, fmt.Errorf("%w: cannot call file.Write", ErrReadOnly) } // FS is a read-only implementation of kyaml/filesys.FileSystem to allow creating // Kustomize file systems from fs.FS implementations, such as [embed.FS]. Calls // to functions that write to the FileSystem will return a [ErrReadOnly]. type FS struct { fs.FS } var _ filesys.FileSystem = &FS{} // Open opens the named file for reading. func (f *FS) Open(path string) (filesys.File, error) { file, err := f.FS.Open(path) return &File{file}, err } // IsDir returns true if the path is a directory. The error from fs.Stat is // discarded. func (f *FS) IsDir(path string) bool { stat, err := fs.Stat(f.FS, path) if err != nil { return false } return stat.IsDir() } // ReadDir returns a list of files and directories within a directory. func (f *FS) ReadDir(path string) ([]string, error) { dirEntries, err := fs.ReadDir(f.FS, path) if err != nil { return nil, err } result := make([]string, 0, len(dirEntries)) for i, e := range dirEntries { result[i] = e.Name() } return result, nil } // CleanedAbs converts the given path into a directory and a file name, // where the directory is represented as a ConfirmedDir and all that implies. // If the entire path is a directory, the file component is an empty string. // // NOTE: Results are relative to the root of the backing [fs.FS] and symlinks // are not evaluated. func (f *FS) CleanedAbs(path string) (filesys.ConfirmedDir, string, error) { if f.IsDir(path) { return filesys.ConfirmedDir(path), "", nil } return filesys.ConfirmedDir(filepath.Dir(path)), filepath.Base(path), nil } // Exists is true if the path exists in the file system. func (f *FS) Exists(path string) bool { var perr *fs.PathError _, err := fs.Stat(f.FS, path) return !errors.Is(err, fs.ErrNotExist) && !errors.As(err, &perr) } // Glob returns the list of matching files, // emulating https://golang.org/pkg/path/filepath/#Glob func (f *FS) Glob(pattern string) ([]string, error) { return fs.Glob(f.FS, pattern) } // ReadFile returns the contents of the file at the given path. func (f *FS) ReadFile(path string) ([]byte, error) { return fs.ReadFile((f.FS), path) } // Walk walks the file system with the given WalkFunc. It uses fs.WalkDir under // the covers instead of filepath.Walk. func (f *FS) Walk(path string, walkFn filepath.WalkFunc) error { return fs.WalkDir(f.FS, path, func(path string, d fs.DirEntry, err error) error { if err != nil { return err } info, err := d.Info() if err != nil { return err } return walkFn(path, info, err) }) } // WriteFile returns ErrReadOnly func (f *FS) WriteFile(_ string, _ []byte) error { return fmt.Errorf("%w: cannot call WriteFile", ErrReadOnly) } // MkDir returns ErrReadOnly. func (f *FS) Mkdir(_ string) error { return fmt.Errorf("%w: cannot call Mkdir", ErrReadOnly) } // MkDirAll returns ErrReadOnly. func (f *FS) MkdirAll(_ string) error { return fmt.Errorf("%w: cannot create MkdirAll", ErrReadOnly) } // RemoveAll returns ErrReadOnly. func (f *FS) RemoveAll(_ string) error { return fmt.Errorf("%w: cannot call RemoveAll", ErrReadOnly) } // Create a file. func (f *FS) Create(_ string) (filesys.File, error) { return nil, fmt.Errorf("%w: cannot call Create", ErrReadOnly) }