From 6953a5d5af7e64d905bce5d49917729454c79284 Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Thu, 18 Jul 2024 16:17:42 +0200 Subject: [PATCH] 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. --- bundle/config/mutator/configure_wsfs.go | 2 +- .../workspace_files_extensions_client.go | 44 ++++++++++++++++--- 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/bundle/config/mutator/configure_wsfs.go b/bundle/config/mutator/configure_wsfs.go index 17af4828..c7b764f0 100644 --- a/bundle/config/mutator/configure_wsfs.go +++ b/bundle/config/mutator/configure_wsfs.go @@ -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 // makes all Workspace File System interactions extension aware. 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 { return diag.FromErr(err) diff --git a/libs/filer/workspace_files_extensions_client.go b/libs/filer/workspace_files_extensions_client.go index d5d0ce55..844e736b 100644 --- a/libs/filer/workspace_files_extensions_client.go +++ b/libs/filer/workspace_files_extensions_client.go @@ -18,8 +18,9 @@ import ( type workspaceFilesExtensionsClient struct { workspaceClient *databricks.WorkspaceClient - wsfs Filer - root string + wsfs Filer + root string + readonly bool } 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) } +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 // 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 @@ -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). // Thus users of these methods should be careful to avoid such clashes. 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) if err != nil { 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{ workspaceClient: w, - wsfs: cache, - root: root, + wsfs: filer, + root: root, + readonly: readonly, }, 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 // method should be careful to avoid such clashes. 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...) } @@ -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. 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...) // 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 // method should be careful to avoid such clashes. func (w *workspaceFilesExtensionsClient) Mkdir(ctx context.Context, name string) error { + if w.readonly { + return ReadOnlyError{"mkdir"} + } + return w.wsfs.Mkdir(ctx, name) }