mirror of https://github.com/databricks/cli.git
110 lines
2.8 KiB
Go
110 lines
2.8 KiB
Go
|
package sync
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"fmt"
|
||
|
"log"
|
||
|
"path"
|
||
|
"strings"
|
||
|
|
||
|
"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"
|
||
|
)
|
||
|
|
||
|
// Return if the child path is nested under the parent path.
|
||
|
func isPathNestedUnder(child, parent string) bool {
|
||
|
child = path.Clean(child)
|
||
|
parent = path.Clean(parent)
|
||
|
|
||
|
// Traverse up the tree as long as "child" is contained in "parent".
|
||
|
for len(child) > len(parent) && strings.HasPrefix(child, parent) {
|
||
|
child = path.Dir(child)
|
||
|
if child == parent {
|
||
|
return true
|
||
|
}
|
||
|
}
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
// Check if the specified path is nested under one of the allowed base paths.
|
||
|
func checkPathNestedUnderBasePaths(me *scim.User, p string) error {
|
||
|
validBasePaths := []string{
|
||
|
path.Clean(fmt.Sprintf("/Users/%s", me.UserName)),
|
||
|
path.Clean(fmt.Sprintf("/Repos/%s", me.UserName)),
|
||
|
}
|
||
|
|
||
|
givenBasePath := path.Clean(p)
|
||
|
match := false
|
||
|
for _, basePath := range validBasePaths {
|
||
|
if isPathNestedUnder(givenBasePath, basePath) {
|
||
|
match = true
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
if !match {
|
||
|
return fmt.Errorf("path must be nested under %s", strings.Join(validBasePaths, " or "))
|
||
|
}
|
||
|
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, path string) error {
|
||
|
me, err := wsc.CurrentUser.Me(ctx)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
err = checkPathNestedUnderBasePaths(me, path)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// Ensure that the remote path exists.
|
||
|
// 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()))
|
||
|
}
|