Skip to content

Commit 86b4295

Browse files
committed
lakebox: integrate as a 'databricks lakebox' subcommand
Wire the cmd/lakebox tree from #4930 into the main CLI: - cmd/cmd.go registers lakebox.New() under the 'development' command group alongside bundle and sync. - cmd/fuzz_panic_test.go adds 'lakebox' to manualRoots so TestCountFuzz doesn't fuzz hand-written commands as if they were auto-generated. - cmd/lakebox tree: the original PR's standalone-CLI scaffolding is adapted for subcommand use — drop the auth-login hijacking and its helper exports, drop the 'last_profile' state field that only mattered when lakebox owned the whole CLI, switch PreRunE to root.MustWorkspaceClient directly, and update help text from 'lakebox foo' to 'databricks lakebox foo' throughout. Also conforms cmd/lakebox to project lint rules: env.UserHomeDir(ctx) in place of os.UserHomeDir, errors.Is(err, fs.ErrNotExist) instead of os.IsNotExist, atomic.Bool over sync.Once in the spinner gate, errors.New for static error strings. Co-authored-by: Isaac
1 parent b65a733 commit 86b4295

13 files changed

Lines changed: 139 additions & 181 deletions

File tree

cmd/cmd.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"github.com/databricks/cli/cmd/experimental"
1818
"github.com/databricks/cli/cmd/fs"
1919
"github.com/databricks/cli/cmd/labs"
20+
"github.com/databricks/cli/cmd/lakebox"
2021
"github.com/databricks/cli/cmd/pipelines"
2122
"github.com/databricks/cli/cmd/root"
2223
"github.com/databricks/cli/cmd/selftest"
@@ -103,6 +104,7 @@ func New(ctx context.Context) *cobra.Command {
103104
cli.AddCommand(configure.New())
104105
cli.AddCommand(fs.New())
105106
cli.AddCommand(labs.New(ctx))
107+
cli.AddCommand(lakebox.New())
106108
cli.AddCommand(sync.New())
107109
cli.AddCommand(version.New())
108110
cli.AddCommand(selftest.New())

cmd/fuzz_panic_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ func isAutoGenerated(leaf leafCommand) bool {
208208
"configure": true,
209209
"experimental": true,
210210
"labs": true,
211+
"lakebox": true,
211212
"pipelines": true,
212213
"psql": true,
213214
"selftest": true,

cmd/lakebox/config.go

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package lakebox
22

33
import (
4+
"errors"
45
"fmt"
56
"time"
67

8+
"github.com/databricks/cli/cmd/root"
79
"github.com/databricks/cli/libs/cmdctx"
810
"github.com/spf13/cobra"
911
)
@@ -39,17 +41,17 @@ Two knobs are independent — pass either or both:
3941
this is on. Setting --idle-timeout to a
4042
non-zero value in a later call clears
4143
--no-autostop automatically. Sandbox still
42-
stops on explicit 'lakebox delete'.
44+
stops on explicit 'databricks lakebox delete'.
4345
4446
Examples:
45-
lakebox config happy-panda-1234 --idle-timeout 15m
46-
lakebox config happy-panda-1234 --idle-timeout 1h30m
47-
lakebox config happy-panda-1234 --idle-timeout 0 # clear, use default
48-
lakebox config happy-panda-1234 --no-autostop # never auto-stop
49-
lakebox config happy-panda-1234 --no-autostop=false # back to timeout path
50-
lakebox config happy-panda-1234 --idle-timeout 30m --no-autostop=false`,
47+
databricks lakebox config happy-panda-1234 --idle-timeout 15m
48+
databricks lakebox config happy-panda-1234 --idle-timeout 1h30m
49+
databricks lakebox config happy-panda-1234 --idle-timeout 0 # clear, use default
50+
databricks lakebox config happy-panda-1234 --no-autostop # never auto-stop
51+
databricks lakebox config happy-panda-1234 --no-autostop=false # back to timeout path
52+
databricks lakebox config happy-panda-1234 --idle-timeout 30m --no-autostop=false`,
5153
Args: cobra.ExactArgs(1),
52-
PreRunE: mustWorkspaceClient,
54+
PreRunE: root.MustWorkspaceClient,
5355
RunE: func(cmd *cobra.Command, args []string) error {
5456
ctx := cmd.Context()
5557
w := cmdctx.WorkspaceClient(ctx)
@@ -76,7 +78,7 @@ Examples:
7678
}
7779

7880
if idleSecs == nil && noAutostop == nil {
79-
return fmt.Errorf("nothing to update — pass --idle-timeout and/or --no-autostop")
81+
return errors.New("nothing to update — pass --idle-timeout and/or --no-autostop")
8082
}
8183

8284
updated, err := api.update(ctx, id, idleSecs, noAutostop)

cmd/lakebox/create.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"os"
66

7+
"github.com/databricks/cli/cmd/root"
78
"github.com/databricks/cli/libs/cmdctx"
89
"github.com/spf13/cobra"
910
)
@@ -20,8 +21,8 @@ Creates a new personal development environment backed by a microVM.
2021
Blocks until the lakebox is running and prints the lakebox ID.
2122
2223
Example:
23-
lakebox create`,
24-
PreRunE: mustWorkspaceClient,
24+
databricks lakebox create`,
25+
PreRunE: root.MustWorkspaceClient,
2526
RunE: func(cmd *cobra.Command, args []string) error {
2627
ctx := cmd.Context()
2728
w := cmdctx.WorkspaceClient(ctx)
@@ -52,15 +53,15 @@ Example:
5253
profile = w.Config.Host
5354
}
5455

55-
currentDefault := getDefault(profile)
56+
currentDefault := getDefault(ctx, profile)
5657
shouldSetDefault := currentDefault == ""
5758
if !shouldSetDefault && currentDefault != "" {
5859
if _, err := api.get(ctx, currentDefault); err != nil {
5960
shouldSetDefault = true
6061
}
6162
}
6263
if shouldSetDefault {
63-
if err := setDefault(profile, result.SandboxID); err != nil {
64+
if err := setDefault(ctx, profile, result.SandboxID); err != nil {
6465
warn(stderr, fmt.Sprintf("Could not save default: %v", err))
6566
} else {
6667
field(stderr, "default", result.SandboxID)

cmd/lakebox/default.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package lakebox
33
import (
44
"fmt"
55

6+
"github.com/databricks/cli/cmd/root"
67
"github.com/databricks/cli/libs/cmdctx"
78
"github.com/spf13/cobra"
89
)
@@ -18,16 +19,17 @@ The default is stored locally in ~/.databricks/lakebox.json per profile.
1819
Example:
1920
databricks lakebox set-default happy-panda-1234`,
2021
Args: cobra.ExactArgs(1),
21-
PreRunE: mustWorkspaceClient,
22+
PreRunE: root.MustWorkspaceClient,
2223
RunE: func(cmd *cobra.Command, args []string) error {
23-
w := cmdctx.WorkspaceClient(cmd.Context())
24+
ctx := cmd.Context()
25+
w := cmdctx.WorkspaceClient(ctx)
2426
profile := w.Config.Profile
2527
if profile == "" {
2628
profile = w.Config.Host
2729
}
2830

2931
lakeboxID := args[0]
30-
if err := setDefault(profile, lakeboxID); err != nil {
32+
if err := setDefault(ctx, profile, lakeboxID); err != nil {
3133
return fmt.Errorf("failed to set default: %w", err)
3234
}
3335
fmt.Fprintf(cmd.OutOrStdout(), "Default lakebox set to: %s\n", lakeboxID)

cmd/lakebox/delete.go

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package lakebox
33
import (
44
"fmt"
55

6+
"github.com/databricks/cli/cmd/root"
67
"github.com/databricks/cli/libs/cmdctx"
78
"github.com/spf13/cobra"
89
)
@@ -16,32 +17,32 @@ func newDeleteCommand() *cobra.Command {
1617
Permanently terminates and removes the specified lakebox.
1718
1819
Example:
19-
lakebox delete happy-panda-1234`,
20+
databricks lakebox delete happy-panda-1234`,
2021
Args: cobra.ExactArgs(1),
21-
PreRunE: mustWorkspaceClient,
22+
PreRunE: root.MustWorkspaceClient,
2223
RunE: func(cmd *cobra.Command, args []string) error {
2324
ctx := cmd.Context()
2425
w := cmdctx.WorkspaceClient(ctx)
2526
api := newLakeboxAPI(w)
2627
stderr := cmd.ErrOrStderr()
2728

2829
lakeboxID := args[0]
29-
s := spin(stderr, fmt.Sprintf("Removing %s…", lakeboxID))
30+
s := spin(stderr, "Removing "+lakeboxID+"…")
3031

3132
if err := api.delete(ctx, lakeboxID); err != nil {
32-
s.fail(fmt.Sprintf("Failed to delete %s", lakeboxID))
33+
s.fail("Failed to delete " + lakeboxID)
3334
return fmt.Errorf("failed to delete lakebox %s: %w", lakeboxID, err)
3435
}
3536

3637
profile := w.Config.Profile
3738
if profile == "" {
3839
profile = w.Config.Host
3940
}
40-
if getDefault(profile) == lakeboxID {
41-
_ = clearDefault(profile)
42-
s.ok(fmt.Sprintf("Removed %s %s", bold(lakeboxID), dim("(default cleared)")))
41+
if getDefault(ctx, profile) == lakeboxID {
42+
_ = clearDefault(ctx, profile)
43+
s.ok("Removed " + bold(lakeboxID) + " " + dim("(default cleared)"))
4344
} else {
44-
s.ok(fmt.Sprintf("Removed %s", bold(lakeboxID)))
45+
s.ok("Removed " + bold(lakeboxID))
4546
}
4647
return nil
4748
},

cmd/lakebox/lakebox.go

Lines changed: 12 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,32 @@
11
package lakebox
22

33
import (
4-
"github.com/databricks/cli/cmd/root"
54
"github.com/spf13/cobra"
65
)
76

87
func New() *cobra.Command {
98
cmd := &cobra.Command{
10-
Use: "lakebox",
11-
Short: "Manage Databricks Lakebox environments",
9+
Use: "lakebox",
10+
Short: "Manage Databricks Lakebox environments",
11+
GroupID: "development",
1212
Long: `Manage Databricks Lakebox environments.
1313
1414
Lakebox provides SSH-accessible development environments backed by
1515
microVM isolation. Each lakebox is a personal sandbox with pre-installed
1616
tooling (Python, Node.js, Rust, Databricks CLI) and persistent storage.
1717
1818
Getting started:
19-
lakebox auth login --host https://... # authenticate to Databricks workspace and lakebox service
20-
lakebox ssh # SSH to your default lakebox
19+
databricks auth login --host https://... # authenticate to a Databricks workspace
20+
databricks lakebox register # generate and register an SSH key
21+
databricks lakebox ssh # SSH to your default lakebox
2122
2223
Common workflows:
23-
lakebox ssh # SSH to your default lakebox
24-
lakebox ssh my-project # SSH to a named lakebox
25-
lakebox list # list your lakeboxes
26-
lakebox create # create a new lakebox
27-
lakebox delete my-project # delete a lakebox
28-
lakebox status my-project # show lakebox status
29-
30-
The CLI manages your ~/.ssh/config so you can also connect directly:
31-
ssh my-project # after 'lakebox ssh'
24+
databricks lakebox ssh # SSH to your default lakebox
25+
databricks lakebox ssh my-project # SSH to a named lakebox
26+
databricks lakebox list # list your lakeboxes
27+
databricks lakebox create # create a new lakebox
28+
databricks lakebox delete my-project # delete a lakebox
29+
databricks lakebox status my-project # show lakebox status
3230
`,
3331
}
3432

@@ -43,15 +41,3 @@ The CLI manages your ~/.ssh/config so you can also connect directly:
4341

4442
return cmd
4543
}
46-
47-
// mustWorkspaceClient applies the saved last-login profile when the user
48-
// hasn't explicitly set --profile, then delegates to root.MustWorkspaceClient.
49-
func mustWorkspaceClient(cmd *cobra.Command, args []string) error {
50-
profileFlag := cmd.Flag("profile")
51-
if profileFlag != nil && !profileFlag.Changed {
52-
if last := GetLastProfile(); last != "" {
53-
_ = profileFlag.Value.Set(last)
54-
}
55-
}
56-
return root.MustWorkspaceClient(cmd, args)
57-
}

cmd/lakebox/list.go

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"strings"
77

8+
"github.com/databricks/cli/cmd/root"
89
"github.com/databricks/cli/libs/cmdctx"
910
"github.com/spf13/cobra"
1011
)
@@ -21,9 +22,9 @@ Shows all lakeboxes associated with your account, including their
2122
current status and ID.
2223
2324
Example:
24-
lakebox list
25-
lakebox list --json`,
26-
PreRunE: mustWorkspaceClient,
25+
databricks lakebox list
26+
databricks lakebox list --json`,
27+
PreRunE: root.MustWorkspaceClient,
2728
RunE: func(cmd *cobra.Command, args []string) error {
2829
ctx := cmd.Context()
2930
w := cmdctx.WorkspaceClient(ctx)
@@ -49,7 +50,7 @@ Example:
4950
if profile == "" {
5051
profile = w.Config.Host
5152
}
52-
defaultID := getDefault(profile)
53+
defaultID := getDefault(ctx, profile)
5354

5455
out := cmd.OutOrStdout()
5556

@@ -80,21 +81,12 @@ Example:
8081
def = accent("*")
8182
}
8283
// Pad ID manually to avoid ANSI codes breaking alignment.
83-
idPad := col - len(id)
84-
if idPad < 0 {
85-
idPad = 0
86-
}
84+
idPad := max(col-len(id), 0)
8785
st := status(e.Status)
8886
// Pad status to 10 visible chars.
89-
stPad := 10 - len(e.Status)
90-
if stPad < 0 {
91-
stPad = 0
92-
}
87+
stPad := max(10-len(e.Status), 0)
9388
as := e.autoStopLabel()
94-
asPad := autostopCol - len(as)
95-
if asPad < 0 {
96-
asPad = 0
97-
}
89+
asPad := max(autostopCol-len(as), 0)
9890
idStr := bold(id)
9991
if strings.EqualFold(e.Status, "running") {
10092
idStr = cyan + bo + id + rs

0 commit comments

Comments
 (0)