Allow sync to workspace path (#170)

With this change:
* Paths under `/Workspace/<me>` and `/Repos/<me>` are allowed
* The sync destination is checked to be either a directory or a repository
* If it is under `/Repos` and doesn't exist, the command returns an error
This commit is contained in:
Pieter Noordhuis 2023-01-19 15:57:41 +01:00 committed by GitHub
parent c6a0d53566
commit 3b53b23b5b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 84 additions and 8 deletions

View File

@ -1,17 +1,95 @@
package sync
import (
"context"
"fmt"
"log"
"strings"
"time"
"github.com/databricks/bricks/cmd/root"
"github.com/databricks/bricks/cmd/sync/repofiles"
"github.com/databricks/bricks/git"
"github.com/databricks/bricks/project"
"github.com/databricks/databricks-sdk-go"
"github.com/databricks/databricks-sdk-go/apierr"
"github.com/databricks/databricks-sdk-go/service/scim"
"github.com/databricks/databricks-sdk-go/service/workspace"
"github.com/spf13/cobra"
)
func matchesBasePaths(me *scim.User, path string) error {
basePaths := []string{
fmt.Sprintf("/Users/%s/", me.UserName),
fmt.Sprintf("/Repos/%s/", me.UserName),
}
basePathMatch := false
for _, basePath := range basePaths {
if strings.HasPrefix(path, basePath) {
basePathMatch = true
break
}
}
if !basePathMatch {
return fmt.Errorf("path must be nested under %s or %s", basePaths[0], basePaths[1])
}
return nil
}
// ensureRemotePathIsUsable checks if the specified path is nested under
// expected base paths and if it is a directory or repository.
func ensureRemotePathIsUsable(ctx context.Context, wsc *databricks.WorkspaceClient, me *scim.User, path string) error {
err := matchesBasePaths(me, path)
if err != nil {
return err
}
// Ensure that the remote path exits.
// If it is a repo, it has to exist.
// If it is a workspace path, it may not exist.
info, err := wsc.Workspace.GetStatusByPath(ctx, path)
if err != nil {
// We only deal with 404s below.
if !apierr.IsMissing(err) {
return err
}
switch {
case strings.HasPrefix(path, "/Repos/"):
return fmt.Errorf("%s does not exist; please create it first", path)
case strings.HasPrefix(path, "/Users/"):
// The workspace path doesn't exist. Create it and try again.
err = wsc.Workspace.MkdirsByPath(ctx, path)
if err != nil {
return fmt.Errorf("unable to create directory at %s: %w", path, err)
}
info, err = wsc.Workspace.GetStatusByPath(ctx, path)
if err != nil {
return err
}
default:
return err
}
}
log.Printf(
"[DEBUG] Path %s has type %s (ID: %d)",
info.Path,
strings.ToLower(info.ObjectType.String()),
info.ObjectId,
)
// We expect the object at path to be a directory or a repo.
switch info.ObjectType {
case workspace.ObjectTypeDirectory:
return nil
case workspace.ObjectTypeRepo:
return nil
}
return fmt.Errorf("%s points to a %s", path, strings.ToLower(info.ObjectType.String()))
}
// syncCmd represents the sync command
var syncCmd = &cobra.Command{
Use: "sync",
@ -23,11 +101,12 @@ var syncCmd = &cobra.Command{
prj := project.Get(ctx)
wsc := prj.WorkspacesClient()
me, err := prj.Me()
if err != nil {
return err
}
if *remotePath == "" {
me, err := prj.Me()
if err != nil {
return err
}
repositoryName, err := git.RepositoryName()
if err != nil {
return err
@ -36,13 +115,10 @@ var syncCmd = &cobra.Command{
}
log.Printf("[INFO] Remote file sync location: %v", *remotePath)
repoExists, err := git.RepoExists(*remotePath, ctx, wsc)
err = ensureRemotePathIsUsable(ctx, wsc, me, *remotePath)
if err != nil {
return err
}
if !repoExists {
return fmt.Errorf("repo not found, please ensure %s exists", *remotePath)
}
root := prj.Root()
repoFiles := repofiles.Create(*remotePath, root, wsc)