Skip to content
Draft
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ dist/
# binary
brev-cli
brev
brev-local

# golang executable
go1.*
Expand Down
9 changes: 5 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,25 @@ fast-build: ## go build -o brev
CGO_ENABLED=0 go build -o brev -ldflags "-X github.com/brevdev/brev-cli/pkg/cmd/version.Version=${VERSION}"

.PHONY: local
local: ## build with env wrapper (use: make local env=dev0|dev1|dev2|stg, or make local for defaults)
local: ## build with env wrapper (use: make local env=dev0|dev1|dev2|stg arch=linux/amd64, or make local for defaults)
$(call print-target)
ifdef env
@echo "Building with env=$(env) wrapper..."
@echo ${VERSION}
CGO_ENABLED=0 go build -o brev -ldflags "-X github.com/brevdev/brev-cli/pkg/cmd/version.Version=${VERSION}"
$(if $(arch),GOOS=$(word 1,$(subst /, ,$(arch))) GOARCH=$(word 2,$(subst /, ,$(arch))),) CGO_ENABLED=0 go build -o brev-local -ldflags "-X github.com/brevdev/brev-cli/pkg/cmd/version.Version=${VERSION}"
@echo '#!/bin/sh' > brev
@echo '# Auto-generated wrapper with environment overrides' >> brev
@echo 'export BREV_CONSOLE_URL="https://localhost.nvidia.com:3000"' >> brev
@echo 'export BREV_AUTH_URL="https://api.stg.ngc.nvidia.com"' >> brev
@echo 'export BREV_AUTH_ISSUER_URL="https://stg.login.nvidia.com"' >> brev
@echo 'export BREV_API_URL="https://bd.$(env).brev.nvidia.com"' >> brev
@echo 'export BREV_PUBLIC_API_URL="https://api.$(env).brev.nvidia.com"' >> brev
@echo 'export BREV_GRPC_URL="api.$(env).brev.nvidia.com:443"' >> brev
@echo 'exec "$$(cd "$$(dirname "$$0")" && pwd)/brev" "$$@"' >> brev
@echo 'exec "$$(cd "$$(dirname "$$0")" && pwd)/brev-local" "$$@"' >> brev
@chmod +x brev
else
@echo "Building without environment overrides (using config.go defaults)..."
$(MAKE) fast-build
$(if $(arch),GOOS=$(word 1,$(subst /, ,$(arch))) GOARCH=$(word 2,$(subst /, ,$(arch))),) CGO_ENABLED=0 go build -o brev -ldflags "-X github.com/brevdev/brev-cli/pkg/cmd/version.Version=${VERSION}"
endif

.PHONY: install-dev
Expand Down
8 changes: 6 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ module github.com/brevdev/brev-cli
go 1.24.0

require (
buf.build/gen/go/brevdev/devplane/connectrpc/go v1.19.1-20260227212944-1f05724e97ab.2
buf.build/gen/go/brevdev/devplane/protocolbuffers/go v1.36.11-20260227212944-1f05724e97ab.1
connectrpc.com/connect v1.19.1
github.com/alessio/shellescape v1.4.1
github.com/brevdev/parse v0.0.11
github.com/briandowns/spinner v1.16.0
Expand All @@ -12,7 +15,7 @@ require (
github.com/go-git/go-git/v5 v5.13.2
github.com/go-resty/resty/v2 v2.17.0
github.com/golang-jwt/jwt/v5 v5.3.0
github.com/google/go-cmp v0.6.0
github.com/google/go-cmp v0.7.0
github.com/google/huproxy v0.0.0-20210816191033-a131ee126ce3
github.com/google/uuid v1.6.0
github.com/gorilla/websocket v1.5.0
Expand Down Expand Up @@ -45,6 +48,7 @@ require (
)

require (
buf.build/gen/go/brevdev/protoc-gen-gotag/protocolbuffers/go v1.36.11-20220906235457-8b4922735da5.1 // indirect
dario.cat/mergo v1.0.0 // indirect
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
Expand Down Expand Up @@ -148,7 +152,7 @@ require (
golang.org/x/sys v0.40.0 // indirect
golang.org/x/term v0.39.0 // indirect
golang.org/x/time v0.12.0 // indirect
google.golang.org/protobuf v1.34.2
google.golang.org/protobuf v1.36.11
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
Expand Down
16 changes: 12 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
buf.build/gen/go/brevdev/devplane/connectrpc/go v1.19.1-20260227212944-1f05724e97ab.2 h1:R8G6M5utUEZaurs/9o/fhGMCp/ZHsaYjjArMFBsrrr8=
buf.build/gen/go/brevdev/devplane/connectrpc/go v1.19.1-20260227212944-1f05724e97ab.2/go.mod h1:Jooemm4ArTV81Co3zkto/PgOQtelQf5j3fgAGsv73T4=
buf.build/gen/go/brevdev/devplane/protocolbuffers/go v1.36.11-20260227212944-1f05724e97ab.1 h1:lOMJE1EzhYelGdR5FQEHUfUl60/3E69Y8dM4O1C8nMc=
buf.build/gen/go/brevdev/devplane/protocolbuffers/go v1.36.11-20260227212944-1f05724e97ab.1/go.mod h1:V/y7Wxg0QvU4XPVwqErF5NHLobUT1QEyfgrGuQIxdPo=
buf.build/gen/go/brevdev/protoc-gen-gotag/protocolbuffers/go v1.36.11-20220906235457-8b4922735da5.1 h1:6amhprQmCKJ4wgJ6ngkh32d9V+dQcOLUZ/SfHdOnYgo=
buf.build/gen/go/brevdev/protoc-gen-gotag/protocolbuffers/go v1.36.11-20220906235457-8b4922735da5.1/go.mod h1:O+pnSHMru/naTMrm4tmpBoH3wz6PHa+R75HR7Mv8X2g=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
Expand Down Expand Up @@ -35,6 +41,8 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
connectrpc.com/connect v1.19.1 h1:R5M57z05+90EfEvCY1b7hBxDVOUl45PrtXtAV2fOC14=
connectrpc.com/connect v1.19.1/go.mod h1:tN20fjdGlewnSFeZxLKb0xwIZ6ozc3OQs2hTXy4du9w=
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
Expand Down Expand Up @@ -208,8 +216,8 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
Expand Down Expand Up @@ -776,8 +784,8 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
Expand Down
6 changes: 5 additions & 1 deletion pkg/cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import (
"github.com/brevdev/brev-cli/pkg/cmd/connect"
"github.com/brevdev/brev-cli/pkg/cmd/copy"
"github.com/brevdev/brev-cli/pkg/cmd/delete"
"github.com/brevdev/brev-cli/pkg/cmd/deregister"
"github.com/brevdev/brev-cli/pkg/cmd/enablessh"
"github.com/brevdev/brev-cli/pkg/cmd/envvars"
"github.com/brevdev/brev-cli/pkg/cmd/exec"
"github.com/brevdev/brev-cli/pkg/cmd/fu"
Expand Down Expand Up @@ -305,7 +307,9 @@ func createCmdTree(cmd *cobra.Command, t *terminal.Terminal, loginCmdStore *stor
cmd.AddCommand(reset.NewCmdReset(t, loginCmdStore, noLoginCmdStore))
cmd.AddCommand(profile.NewCmdProfile(t, loginCmdStore, noLoginCmdStore))
cmd.AddCommand(refresh.NewCmdRefresh(t, loginCmdStore))
cmd.AddCommand(register.NewCmdRegister(t))
cmd.AddCommand(register.NewCmdRegister(t, loginCmdStore))
cmd.AddCommand(deregister.NewCmdDeregister(t, loginCmdStore))
cmd.AddCommand(enablessh.NewCmdEnableSSH(t, loginCmdStore))
cmd.AddCommand(runtasks.NewCmdRunTasks(t, noLoginCmdStore))
cmd.AddCommand(proxy.NewCmdProxy(t, noLoginCmdStore))
cmd.AddCommand(healthcheck.NewCmdHealthcheck(t, noLoginCmdStore))
Expand Down
152 changes: 152 additions & 0 deletions pkg/cmd/deregister/deregister.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
// Package deregister provides the brev deregister command for device deregistration
package deregister

import (
"context"
"fmt"
"runtime"

nodev1connect "buf.build/gen/go/brevdev/devplane/connectrpc/go/devplaneapi/v1/devplaneapiv1connect"
nodev1 "buf.build/gen/go/brevdev/devplane/protocolbuffers/go/devplaneapi/v1"
"connectrpc.com/connect"

"github.com/brevdev/brev-cli/pkg/cmd/register"
"github.com/brevdev/brev-cli/pkg/config"
"github.com/brevdev/brev-cli/pkg/entity"
breverrors "github.com/brevdev/brev-cli/pkg/errors"
"github.com/brevdev/brev-cli/pkg/terminal"

"github.com/spf13/cobra"
)

// DeregisterStore defines the store methods needed by the deregister command.
type DeregisterStore interface {
GetCurrentUser() (*entity.User, error)
GetBrevHomePath() (string, error)
GetAccessToken() (string, error)
}

// deregisterDeps bundles the side-effecting dependencies of runDeregister so
// they can be replaced in tests.
type deregisterDeps struct {
goos string
promptSelect func(label string, items []string) string
uninstallNetbird func() error
newNodeClient func(provider register.TokenProvider, baseURL string) nodev1connect.ExternalNodeServiceClient
registrationStore register.RegistrationStore
}

func prodDeregisterDeps(brevHome string) deregisterDeps {
return deregisterDeps{
goos: runtime.GOOS,
promptSelect: func(label string, items []string) string {
return terminal.PromptSelectInput(terminal.PromptSelectContent{
Label: label,
Items: items,
})
},
uninstallNetbird: register.UninstallNetbird,
newNodeClient: register.NewNodeServiceClient,
registrationStore: register.NewFileRegistrationStore(brevHome),
}
}

var (
deregisterLong = `Deregister your device from NVIDIA Brev

This command removes the local registration data and optionally uninstalls
NetBird (network agent).`

deregisterExample = ` brev deregister`
)

func NewCmdDeregister(t *terminal.Terminal, store DeregisterStore) *cobra.Command {
cmd := &cobra.Command{
Annotations: map[string]string{"configuration": ""},
Use: "deregister",
DisableFlagsInUseLine: true,
Short: "Deregister your device from Brev",
Long: deregisterLong,
Example: deregisterExample,
RunE: func(cmd *cobra.Command, args []string) error {
brevHome, err := store.GetBrevHomePath()
if err != nil {
return breverrors.WrapAndTrace(err)
}
return runDeregister(cmd.Context(), t, store, prodDeregisterDeps(brevHome))
},
}

return cmd
}

func runDeregister(ctx context.Context, t *terminal.Terminal, s DeregisterStore, deps deregisterDeps) error { //nolint:funlen // deregistration flow
if deps.goos != "linux" {
return fmt.Errorf("brev deregister is only supported on Linux")
}

registered, err := deps.registrationStore.Exists()
if err != nil {
return breverrors.WrapAndTrace(err)
}
if !registered {
return fmt.Errorf("no registration found; this machine does not appear to be registered\nRun 'brev register' to register your device")
}

reg, err := deps.registrationStore.Load()
if err != nil {
return fmt.Errorf("failed to read registration file: %w", err)
}

t.Vprint("")
t.Vprint(t.Green("Deregistering device"))
t.Vprint("")
t.Vprintf(" Node ID: %s\n", reg.ExternalNodeID)
t.Vprintf(" Name: %s\n", reg.DisplayName)
t.Vprint("")

confirm := deps.promptSelect(
"Proceed with deregistration?",
[]string{"Yes, proceed", "No, cancel"},
)
if confirm != "Yes, proceed" {
t.Vprint("Deregistration canceled.")
return nil
}

t.Vprint("")
t.Vprint(t.Yellow("Removing node from Brev..."))
client := deps.newNodeClient(s, config.GlobalConfig.GetBrevPublicAPIURL())
if _, err := client.RemoveNode(ctx, connect.NewRequest(&nodev1.RemoveNodeRequest{
ExternalNodeId: reg.ExternalNodeID,
})); err != nil {
return fmt.Errorf("failed to deregister node: %w", err)
}
t.Vprint(t.Green(" Node removed from Brev."))
t.Vprint("")

removeNetbird := deps.promptSelect(
"Would you also like to uninstall NetBird?",
[]string{"Yes, uninstall NetBird", "No, keep NetBird installed"},
)
if removeNetbird == "Yes, uninstall NetBird" {
t.Vprint("Removing NetBird...")
if err := deps.uninstallNetbird(); err != nil {
t.Vprintf(" Warning: failed to uninstall NetBird: %v\n", err)
} else {
t.Vprint(t.Green(" NetBird uninstalled."))
}
t.Vprint("")
}

t.Vprint("Removing registration data...")
if err := deps.registrationStore.Delete(); err != nil {
t.Vprintf(" Warning: failed to remove local registration file: %v\n", err)
t.Vprint(" You can manually remove it with: rm ~/.brev/device_registration.json")
}

t.Vprint(t.Green("Deregistration complete."))
t.Vprint("")

return nil
}
Loading
Loading