package sync import ( "context" "fmt" "path" "strings" "github.com/databricks/cli/libs/log" "github.com/databricks/databricks-sdk-go" "github.com/databricks/databricks-sdk-go/apierr" "github.com/databricks/databricks-sdk-go/service/iam" "github.com/databricks/databricks-sdk-go/service/workspace" ) func repoPathForPath(me *iam.User, remotePath string) string { base := path.Clean(fmt.Sprintf("/Repos/%s", me.UserName)) remotePath = path.Clean(remotePath) for strings.HasPrefix(path.Dir(remotePath), base) && path.Dir(remotePath) != base { remotePath = path.Dir(remotePath) } return remotePath } // 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, remotePath string, me *iam.User) error { var err error // TODO: we should cache CurrentUser.Me at the SDK level // for now we let clients pass in any existing user they might already have if me == nil { me, err = wsc.CurrentUser.Me(ctx) 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, remotePath) if err != nil { // We only deal with 404s below. if !apierr.IsMissing(err) { return err } // If the path is nested under a repo, the repo has to exist. if strings.HasPrefix(remotePath, "/Repos/") { repoPath := repoPathForPath(me, remotePath) _, err = wsc.Workspace.GetStatusByPath(ctx, repoPath) if err != nil && apierr.IsMissing(err) { return fmt.Errorf("%s does not exist; please create it first", repoPath) } } // The workspace path doesn't exist. Create it and try again. err = wsc.Workspace.MkdirsByPath(ctx, remotePath) if err != nil { return fmt.Errorf("unable to create directory at %s: %w", remotePath, err) } info, err = wsc.Workspace.GetStatusByPath(ctx, remotePath) if err != nil { return err } } log.Debugf( ctx, "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", remotePath, strings.ToLower(info.ObjectType.String())) }