Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions cmd/lakebox/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ package lakebox

import (
"encoding/json"
"errors"
"fmt"

"github.com/databricks/cli/cmd/root"
"github.com/databricks/cli/libs/cmdctx"
"github.com/databricks/cli/libs/cmdio"
"github.com/databricks/databricks-sdk-go/apierr"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -68,10 +70,13 @@ Examples:
_ = setGatewayHost(ctx, profile, result.GatewayHost)
_ = upsertSandbox(ctx, profile, result.SandboxID, name)

// Only clobber an existing default if it's actually gone
// (404). Transient errors (5xx, network blip, rate limit)
// must not silently overwrite the user's chosen default.
currentDefault := getDefault(ctx, profile)
shouldSetDefault := currentDefault == ""
if !shouldSetDefault && currentDefault != "" {
if _, err := api.get(ctx, currentDefault); err != nil {
if !shouldSetDefault {
if _, err := api.get(ctx, currentDefault); errors.Is(err, apierr.ErrNotFound) {
shouldSetDefault = true
}
}
Expand Down
3 changes: 2 additions & 1 deletion cmd/lakebox/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ Examples:
return err
}
if !confirmed {
return errors.New("aborted")
cmdio.LogString(ctx, "Cancelled.")
return nil
}
}

Expand Down
4 changes: 4 additions & 0 deletions cmd/lakebox/keyhash.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,18 @@ package lakebox
import (
"crypto/sha256"
"encoding/hex"
"strings"
)

// keyHash returns the identifier the lakebox SSH-keys API assigns to a
// public key. The algorithm is sha256("<type> <base64-blob>") truncated to
// the first 16 bytes and hex-encoded; the OpenSSH comment (anything after
// the second whitespace-separated token) is stripped before hashing, so
// registering the same key under different comments yields the same hash.
// Leading and trailing whitespace are trimmed first — `.pub` files end
// with a newline that would otherwise be hashed in for comment-less keys.
func keyHash(publicKey string) string {
publicKey = strings.TrimSpace(publicKey)
end := len(publicKey)
spaces := 0
for i, c := range publicKey {
Expand Down
13 changes: 13 additions & 0 deletions cmd/lakebox/keyhash_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,19 @@ func TestKeyHash(t *testing.T) {
input: "",
want: "e3b0c44298fc1c149afbf4c8996fb924",
},
{
// `.pub` files end with a newline. Without trimming, a
// comment-less key would hash with `\n` mixed in and
// stop matching the server's value.
name: "trailing newline does not change hash",
input: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDUMMY\n",
want: "2b366430eb9743668b652921d3b22d54",
},
{
name: "leading and trailing whitespace stripped",
input: " ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDUMMY \n",
want: "2b366430eb9743668b652921d3b22d54",
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion cmd/lakebox/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ Examples:
// Remote-SSH and plain `ssh <id>@lakebox-gw` both work without
// the user pasting any config block (see maybeWriteSSHConfig).
if err := maybeWriteSSHConfig(ctx, keyPath, w.Config.Host); err != nil {
warn(ctx, fmt.Sprintf("registered key, but failed to update ~/.ssh/config: %v", err))
warn(ctx, fmt.Sprintf("Registered key, but failed to update ~/.ssh/config: %v", err))
}

blank(stderr)
Expand Down
6 changes: 3 additions & 3 deletions cmd/lakebox/ssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ Examples:
_ = clearDefault(ctx, profile)
return fmt.Errorf("saved default %q no longer exists (cleared) — run `databricks lakebox create` to provision a new one, or `databricks lakebox default <id>` to point at an existing sandbox", def)
default:
warn(ctx, fmt.Sprintf("could not validate default %s: %v", def, err))
warn(ctx, fmt.Sprintf("Could not validate default %s: %v", def, err))
lakeboxID = def
}
} else {
Expand All @@ -151,7 +151,7 @@ Examples:
case errors.Is(err, apierr.ErrNotFound):
return fmt.Errorf("no lakebox named %q — `databricks lakebox list` shows available IDs", lakeboxID)
default:
warn(ctx, fmt.Sprintf("could not validate lakebox %s: %v", lakeboxID, err))
warn(ctx, fmt.Sprintf("Could not validate lakebox %s: %v", lakeboxID, err))
}
}

Expand Down Expand Up @@ -221,7 +221,7 @@ func verifyKeyRegistered(ctx context.Context, api *lakeboxAPI, keyPath string) e

keys, err := api.listKeys(ctx)
if err != nil {
warn(ctx, fmt.Sprintf("could not verify SSH key registration: %v", err))
warn(ctx, fmt.Sprintf("Could not verify SSH key registration: %v", err))
return nil
}
for _, k := range keys {
Expand Down
2 changes: 1 addition & 1 deletion cmd/lakebox/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func newStartCommand() *cobra.Command {
Long: `Start a stopped Lakebox environment.

Boots the backing microVM and blocks until the sandbox reaches
Running (or up to 5 minutes). 'databricks lakebox ssh' already
Running (or up to 10 minutes). 'databricks lakebox ssh' already
auto-starts a stopped sandbox on connection, so this command is
mostly useful for pre-warming an environment without immediately
connecting, or when a script needs to be sure the sandbox is up
Expand Down
11 changes: 8 additions & 3 deletions cmd/lakebox/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ package lakebox

import (
"encoding/json"
"errors"
"fmt"

"github.com/databricks/cli/cmd/root"
"github.com/databricks/cli/libs/cmdctx"
"github.com/databricks/cli/libs/cmdio"
"github.com/databricks/databricks-sdk-go/apierr"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -44,6 +46,9 @@ Example:

entry, err := api.get(ctx, lakeboxID)
if err != nil {
if errors.Is(err, apierr.ErrNotFound) {
return fmt.Errorf("no lakebox named %q — `databricks lakebox list` shows available IDs", lakeboxID)
}
return fmt.Errorf("failed to get lakebox %s: %w", lakeboxID, err)
}

Expand All @@ -66,9 +71,9 @@ Example:
if entry.GatewayHost != "" {
field(ctx, out, "gateway", cmdio.Faint(ctx, entry.GatewayHost))
}
if entry.FQDN != "" {
field(ctx, out, "fqdn", cmdio.Faint(ctx, entry.FQDN))
}
// FQDN is the manager's internal routing host (per api.go);
// keep it in the JSON shape above for completeness but
// don't surface it to humans.
field(ctx, out, "autostop", cmdio.Faint(ctx, entry.autoStopLabel()))
blank(out)
return nil
Expand Down
5 changes: 3 additions & 2 deletions cmd/lakebox/ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,10 @@ func ok(ctx context.Context, msg string) {
cmdio.LogString(ctx, " "+cmdio.Cyan(ctx, "✓")+" "+msg)
}

// warn prints " ! message" to stderr via the cmdio context.
// warn prints " ! message" to stderr via the cmdio context. Yellow so
// it visually differs from `ok`'s cyan ✓ and `spinner` cyan markers.
func warn(ctx context.Context, msg string) {
cmdio.LogString(ctx, " "+cmdio.Cyan(ctx, "!")+" "+msg)
cmdio.LogString(ctx, " "+cmdio.Yellow(ctx, "!")+" "+msg)
}

// blank prints an empty line to w.
Expand Down
Loading