mirror of https://github.com/databricks/cli.git
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:
parent
c6a0d53566
commit
3b53b23b5b
|
@ -1,17 +1,95 @@
|
||||||
package sync
|
package sync
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/databricks/bricks/cmd/root"
|
"github.com/databricks/bricks/cmd/root"
|
||||||
"github.com/databricks/bricks/cmd/sync/repofiles"
|
"github.com/databricks/bricks/cmd/sync/repofiles"
|
||||||
"github.com/databricks/bricks/git"
|
"github.com/databricks/bricks/git"
|
||||||
"github.com/databricks/bricks/project"
|
"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"
|
"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
|
// syncCmd represents the sync command
|
||||||
var syncCmd = &cobra.Command{
|
var syncCmd = &cobra.Command{
|
||||||
Use: "sync",
|
Use: "sync",
|
||||||
|
@ -23,11 +101,12 @@ var syncCmd = &cobra.Command{
|
||||||
prj := project.Get(ctx)
|
prj := project.Get(ctx)
|
||||||
wsc := prj.WorkspacesClient()
|
wsc := prj.WorkspacesClient()
|
||||||
|
|
||||||
|
me, err := prj.Me()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if *remotePath == "" {
|
if *remotePath == "" {
|
||||||
me, err := prj.Me()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
repositoryName, err := git.RepositoryName()
|
repositoryName, err := git.RepositoryName()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -36,13 +115,10 @@ var syncCmd = &cobra.Command{
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[INFO] Remote file sync location: %v", *remotePath)
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !repoExists {
|
|
||||||
return fmt.Errorf("repo not found, please ensure %s exists", *remotePath)
|
|
||||||
}
|
|
||||||
|
|
||||||
root := prj.Root()
|
root := prj.Root()
|
||||||
repoFiles := repofiles.Create(*remotePath, root, wsc)
|
repoFiles := repofiles.Create(*remotePath, root, wsc)
|
||||||
|
|
Loading…
Reference in New Issue