Add read-only mode for extension aware workspace filer (#1609)

## Changes

By default, construct a read/write instance. If constructed in read-only
mode, the underlying filer is wrapped in a readahead cache.

## Tests

* Filer integration tests pass.
* Manual test that caching is enabled when running on WSFS.
This commit is contained in:
Pieter Noordhuis 2024-07-18 16:17:42 +02:00 committed by GitHub
parent 5b65358146
commit 6953a5d5af
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 40 additions and 6 deletions

View File

@ -39,7 +39,7 @@ func (m *configureWSFS) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagno
// If so, swap out vfs.Path instance of the sync root with one that // If so, swap out vfs.Path instance of the sync root with one that
// makes all Workspace File System interactions extension aware. // makes all Workspace File System interactions extension aware.
p, err := vfs.NewFilerPath(ctx, root, func(path string) (filer.Filer, error) { p, err := vfs.NewFilerPath(ctx, root, func(path string) (filer.Filer, error) {
return filer.NewWorkspaceFilesExtensionsClient(b.WorkspaceClient(), path) return filer.NewReadOnlyWorkspaceFilesExtensionsClient(b.WorkspaceClient(), path)
}) })
if err != nil { if err != nil {
return diag.FromErr(err) return diag.FromErr(err)

View File

@ -20,6 +20,7 @@ type workspaceFilesExtensionsClient struct {
wsfs Filer wsfs Filer
root string root string
readonly bool
} }
var extensionsToLanguages = map[string]workspace.Language{ var extensionsToLanguages = map[string]workspace.Language{
@ -143,6 +144,14 @@ func (e DuplicatePathError) Error() string {
return fmt.Sprintf("failed to read files from the workspace file system. Duplicate paths encountered. Both %s at %s and %s at %s resolve to the same name %s. Changing the name of one of these objects will resolve this issue", e.oi1.ObjectType, e.oi1.Path, e.oi2.ObjectType, e.oi2.Path, e.commonName) return fmt.Sprintf("failed to read files from the workspace file system. Duplicate paths encountered. Both %s at %s and %s at %s resolve to the same name %s. Changing the name of one of these objects will resolve this issue", e.oi1.ObjectType, e.oi1.Path, e.oi2.ObjectType, e.oi2.Path, e.commonName)
} }
type ReadOnlyError struct {
op string
}
func (e ReadOnlyError) Error() string {
return fmt.Sprintf("failed to %s: filer is in read-only mode", e.op)
}
// This is a filer for the workspace file system that allows you to pretend the // This is a filer for the workspace file system that allows you to pretend the
// workspace file system is a traditional file system. It allows you to list, read, write, // workspace file system is a traditional file system. It allows you to list, read, write,
// delete, and stat notebooks (and files in general) in the workspace, using their paths // delete, and stat notebooks (and files in general) in the workspace, using their paths
@ -157,17 +166,30 @@ func (e DuplicatePathError) Error() string {
// errors for namespace clashes (e.g. a file and a notebook or a directory and a notebook). // errors for namespace clashes (e.g. a file and a notebook or a directory and a notebook).
// Thus users of these methods should be careful to avoid such clashes. // Thus users of these methods should be careful to avoid such clashes.
func NewWorkspaceFilesExtensionsClient(w *databricks.WorkspaceClient, root string) (Filer, error) { func NewWorkspaceFilesExtensionsClient(w *databricks.WorkspaceClient, root string) (Filer, error) {
return newWorkspaceFilesExtensionsClient(w, root, false)
}
func NewReadOnlyWorkspaceFilesExtensionsClient(w *databricks.WorkspaceClient, root string) (Filer, error) {
return newWorkspaceFilesExtensionsClient(w, root, true)
}
func newWorkspaceFilesExtensionsClient(w *databricks.WorkspaceClient, root string, readonly bool) (Filer, error) {
filer, err := NewWorkspaceFilesClient(w, root) filer, err := NewWorkspaceFilesClient(w, root)
if err != nil { if err != nil {
return nil, err return nil, err
} }
cache := newWorkspaceFilesReadaheadCache(filer) if readonly {
// Wrap in a readahead cache to avoid making unnecessary calls to the workspace.
filer = newWorkspaceFilesReadaheadCache(filer)
}
return &workspaceFilesExtensionsClient{ return &workspaceFilesExtensionsClient{
workspaceClient: w, workspaceClient: w,
wsfs: cache, wsfs: filer,
root: root, root: root,
readonly: readonly,
}, nil }, nil
} }
@ -214,6 +236,10 @@ func (w *workspaceFilesExtensionsClient) ReadDir(ctx context.Context, name strin
// (e.g. a file and a notebook or a directory and a notebook). Thus users of this // (e.g. a file and a notebook or a directory and a notebook). Thus users of this
// method should be careful to avoid such clashes. // method should be careful to avoid such clashes.
func (w *workspaceFilesExtensionsClient) Write(ctx context.Context, name string, reader io.Reader, mode ...WriteMode) error { func (w *workspaceFilesExtensionsClient) Write(ctx context.Context, name string, reader io.Reader, mode ...WriteMode) error {
if w.readonly {
return ReadOnlyError{"write"}
}
return w.wsfs.Write(ctx, name, reader, mode...) return w.wsfs.Write(ctx, name, reader, mode...)
} }
@ -247,6 +273,10 @@ func (w *workspaceFilesExtensionsClient) Read(ctx context.Context, name string)
// Try to delete the file as a regular file. If the file is not found, try to delete it as a notebook. // Try to delete the file as a regular file. If the file is not found, try to delete it as a notebook.
func (w *workspaceFilesExtensionsClient) Delete(ctx context.Context, name string, mode ...DeleteMode) error { func (w *workspaceFilesExtensionsClient) Delete(ctx context.Context, name string, mode ...DeleteMode) error {
if w.readonly {
return ReadOnlyError{"delete"}
}
err := w.wsfs.Delete(ctx, name, mode...) err := w.wsfs.Delete(ctx, name, mode...)
// If the file is not found, it might be a notebook. // If the file is not found, it might be a notebook.
@ -293,5 +323,9 @@ func (w *workspaceFilesExtensionsClient) Stat(ctx context.Context, name string)
// (e.g. a file and a notebook or a directory and a notebook). Thus users of this // (e.g. a file and a notebook or a directory and a notebook). Thus users of this
// method should be careful to avoid such clashes. // method should be careful to avoid such clashes.
func (w *workspaceFilesExtensionsClient) Mkdir(ctx context.Context, name string) error { func (w *workspaceFilesExtensionsClient) Mkdir(ctx context.Context, name string) error {
if w.readonly {
return ReadOnlyError{"mkdir"}
}
return w.wsfs.Mkdir(ctx, name) return w.wsfs.Mkdir(ctx, name)
} }