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
5 changes: 2 additions & 3 deletions assets/test_workspace.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
{
"workspaceId": "test_workspace_id",
"workspaceGroupId": "test_workspace_group_id"
}
"workspaceId": "test_workspace_id"
}
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ module github.com/brevdev/brev-cli
go 1.25.0

require (
buf.build/gen/go/brevdev/devplane/connectrpc/go v1.20.0-20260626205643-49b0d20e08f1.1
buf.build/gen/go/brevdev/devplane/protocolbuffers/go v1.36.11-20260626205643-49b0d20e08f1.1
buf.build/gen/go/brevdev/devplane/connectrpc/go v1.20.0-20260708012811-ecba52f49600.1
buf.build/gen/go/brevdev/devplane/protocolbuffers/go v1.36.11-20260708012811-ecba52f49600.1
connectrpc.com/connect v1.20.0
github.com/NVIDIA/go-nvml v0.13.0-1
github.com/alessio/shellescape v1.4.1
Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
buf.build/gen/go/brevdev/devplane/connectrpc/go v1.20.0-20260626205643-49b0d20e08f1.1 h1:Qj4BTbhIF0KE5YHiJJ+SN2goGYF8dJC1l8cv69YU/Ms=
buf.build/gen/go/brevdev/devplane/connectrpc/go v1.20.0-20260626205643-49b0d20e08f1.1/go.mod h1:KW+lsYUmrF994Z/zj/wibrS7zhitXrYLicqR5BbVSp0=
buf.build/gen/go/brevdev/devplane/protocolbuffers/go v1.36.11-20260626205643-49b0d20e08f1.1 h1:+GNKe6qV3aRH+N/FBlH6NfqyKOxMecAtbHndj3NPZc4=
buf.build/gen/go/brevdev/devplane/protocolbuffers/go v1.36.11-20260626205643-49b0d20e08f1.1/go.mod h1:V/y7Wxg0QvU4XPVwqErF5NHLobUT1QEyfgrGuQIxdPo=
buf.build/gen/go/brevdev/devplane/connectrpc/go v1.20.0-20260708012811-ecba52f49600.1 h1:xanul5g4JQ0OPAQ3tjN8bTznw+aA6B/oq3pzOy8kC8Q=
buf.build/gen/go/brevdev/devplane/connectrpc/go v1.20.0-20260708012811-ecba52f49600.1/go.mod h1:ZxWENaPM6882Wtl2z6rZYVpXoagSyF6DiY/6m4BjGMU=
buf.build/gen/go/brevdev/devplane/protocolbuffers/go v1.36.11-20260708012811-ecba52f49600.1 h1:KMs3AGf1zys1H8TnjBCorCd12zzWoUQae956KgsNfRM=
buf.build/gen/go/brevdev/devplane/protocolbuffers/go v1.36.11-20260708012811-ecba52f49600.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=
Expand Down
9 changes: 0 additions & 9 deletions pkg/cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,15 +133,6 @@ func NewBrevCommand() *cobra.Command { //nolint:funlen,gocognit,gocyclo // defin

analytics.SetUserStore(noLoginCmdStore)

workspaceGroupID, err := fsStore.GetCurrentWorkspaceGroupID()
if err != nil {
fmt.Printf("%v\n", err)
}
if workspaceGroupID != "" {
loginCmdStore.WithStaticHeader("X-Workspace-Group-ID", workspaceGroupID)
noLoginCmdStore.WithStaticHeader("X-Workspace-Group-ID", workspaceGroupID)
}

cmds := &cobra.Command{
SilenceErrors: true,
SilenceUsage: true,
Expand Down
1 change: 0 additions & 1 deletion pkg/cmd/create/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,6 @@ func createEmptyWorkspace(user *entity.User, t *terminal.Terminal, options Creat
} else {
t.Vprintf("\tCPU instance %s\n", t.Green(cwOptions.WorkspaceClassID))
}
t.Vprintf("\tCloud %s\n\n", t.Green(cwOptions.WorkspaceGroupID))

s := t.NewSpinner()
s.Suffix = " Creating your instance. Hang tight 🤙"
Expand Down
63 changes: 40 additions & 23 deletions pkg/cmd/gpucreate/gpucreate.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ type GPUCreateStore interface {
GetWorkspace(workspaceID string) (*entity.Workspace, error)
CreateWorkspace(organizationID string, options *store.CreateWorkspacesOptions) (*entity.Workspace, error)
DeleteWorkspace(workspaceID string) (*entity.Workspace, error)
GetAllInstanceTypesWithWorkspaceGroups(orgID string) (*gpusearch.AllInstanceTypesResponse, error)
GetAllInstanceTypesWithCloudCreds(orgID string) (*gpusearch.AllInstanceTypesResponse, error)
GetLaunchable(launchableID string) (*store.LaunchableResponse, error)
GetLaunchableLifeCycleScript(launchableID, scriptID string) (*store.LifeCycleScriptResponse, error)
RedeemCouponCode(organizationID string, code string) (*store.RedeemCouponCodeResponse, error)
Expand Down Expand Up @@ -768,11 +768,11 @@ func newCreateContext(t *terminal.Terminal, store GPUCreateStore, opts GPUCreate
}
ctx.org = org

// Fetch instance types with workspace groups
allInstanceTypes, err := store.GetAllInstanceTypesWithWorkspaceGroups(org.ID)
// Fetch instance types with cloud credentials.
allInstanceTypes, err := store.GetAllInstanceTypesWithCloudCreds(org.ID)
if err != nil {
ctx.logf("Warning: could not fetch instance types with workspace groups: %s\n", err.Error())
ctx.logf("Falling back to default workspace group\n")
ctx.logf("Warning: could not fetch instance types with cloud credentials: %s\n", err.Error())
ctx.logf("Falling back to default cloud credential\n")
}
ctx.allInstanceTypes = allInstanceTypes

Expand All @@ -791,7 +791,7 @@ func (c *createContext) validateInstanceTypeAvailability(instanceType string) er
if c.allInstanceTypes == nil {
return nil
}
if c.allInstanceTypes.GetWorkspaceGroupID(instanceType) != "" {
if c.allInstanceTypes.GetCloudCredID(instanceType) != "" {
return nil
}
if !c.allInstanceTypes.HasInstanceType(instanceType) {
Expand Down Expand Up @@ -1042,8 +1042,8 @@ func (c *createContext) createWorkspace(name string, spec InstanceSpec) (*entity
}

if c.allInstanceTypes != nil {
if wgID := c.allInstanceTypes.GetWorkspaceGroupID(spec.Type); wgID != "" {
cwOptions.WorkspaceGroupID = wgID
if cloudCredID := c.allInstanceTypes.GetCloudCredID(spec.Type); cloudCredID != "" {
cwOptions.WithCloudCredID(cloudCredID)
}
}

Expand All @@ -1057,10 +1057,10 @@ func (c *createContext) createWorkspace(name string, spec InstanceSpec) (*entity
}
}

if cwOptions.WorkspaceGroupID == "" {
if cwOptions.CloudCredID == "" {
if c.allInstanceTypes == nil {
return nil, breverrors.NewValidationError(fmt.Sprintf(
"could not resolve workspace group for %q (instance-type listing was unavailable); please retry",
"could not resolve cloud credential for %q (instance-type listing was unavailable); please retry",
spec.Type,
))
}
Expand Down Expand Up @@ -1176,13 +1176,17 @@ func applyLaunchableConfig(cwOptions *store.CreateWorkspacesOptions, launchableI
return
}

wsReq := info.CreateWorkspaceRequest
applyLaunchableWorkspaceRequest(cwOptions, info.CreateWorkspaceRequest)
applyLaunchableBuildRequest(cwOptions, info.BuildRequest)
applyLaunchableFile(cwOptions, info.File)
applyLaunchableLabels(cwOptions, launchableID, info)
}

// Use launchable's workspace group if not already resolved from instance types
if cwOptions.WorkspaceGroupID == "" && wsReq.WorkspaceGroupID != "" {
cwOptions.WorkspaceGroupID = wsReq.WorkspaceGroupID
func applyLaunchableWorkspaceRequest(cwOptions *store.CreateWorkspacesOptions, wsReq store.LaunchableWorkspaceRequest) {
// Use launchable's cloud credential if not already resolved from instance types.
if cwOptions.CloudCredID == "" && wsReq.CloudCredID != "" {
cwOptions.WithCloudCredID(wsReq.CloudCredID)
}

// Location / sub-location
if wsReq.Location != "" {
cwOptions.Location = wsReq.Location
Expand All @@ -1198,8 +1202,13 @@ func applyLaunchableConfig(cwOptions *store.CreateWorkspacesOptions, launchableI
cwOptions.DiskStorage = normalizeDiskStorage(wsReq.Storage)
}

if len(wsReq.FirewallRules) > 0 {
cwOptions.FirewallRules = resolveFirewallRulesClientIP(wsReq.FirewallRules, publicIPLookup)
}
}

func applyLaunchableBuildRequest(cwOptions *store.CreateWorkspacesOptions, build store.LaunchableBuildRequest) {
// Build configuration from launchable
build := info.BuildRequest
switch {
case build.VMBuild != nil:
cwOptions.VMBuild = build.VMBuild
Expand All @@ -1208,7 +1217,7 @@ func applyLaunchableConfig(cwOptions *store.CreateWorkspacesOptions, launchableI
cwOptions.CustomContainer = build.CustomContainer
case build.DockerCompose != nil:
cwOptions.VMBuild = nil
cwOptions.DockerCompose = build.DockerCompose
cwOptions.DockerCompose = normalizeLaunchableDockerCompose(build.DockerCompose)
}

// Port mappings from build request ports
Expand All @@ -1219,18 +1228,26 @@ func applyLaunchableConfig(cwOptions *store.CreateWorkspacesOptions, launchableI
}
cwOptions.PortMappings = portMappings
}
}

if len(wsReq.FirewallRules) > 0 {
cwOptions.FirewallRules = resolveFirewallRulesClientIP(wsReq.FirewallRules, publicIPLookup)
func normalizeLaunchableDockerCompose(dockerCompose *store.DockerCompose) *store.DockerCompose {
normalized := *dockerCompose
if normalized.FileURL != "" {
normalized.YamlString = ""
}
return &normalized
}

func applyLaunchableFile(cwOptions *store.CreateWorkspacesOptions, file *store.LaunchableFile) {
// Files from launchable
if info.File != nil {
if file != nil {
cwOptions.Files = []map[string]string{
{"url": info.File.URL, "path": info.File.Path},
{"url": file.URL, "path": file.Path},
}
}
}

func applyLaunchableLabels(cwOptions *store.CreateWorkspacesOptions, launchableID string, info *store.LaunchableResponse) {
// Labels for tracking and UI rendering — merge with any existing labels
var labels map[string]string
if existing, ok := cwOptions.Labels.(map[string]string); ok && existing != nil {
Expand All @@ -1239,8 +1256,8 @@ func applyLaunchableConfig(cwOptions *store.CreateWorkspacesOptions, launchableI
labels = make(map[string]string)
}
labels["launchableId"] = launchableID
labels["launchableInstanceType"] = wsReq.InstanceType
labels["workspaceGroupId"] = cwOptions.WorkspaceGroupID
labels["launchableInstanceType"] = info.CreateWorkspaceRequest.InstanceType
labels["cloudCredId"] = cwOptions.CloudCredID
labels["launchableCreatedByUserId"] = info.CreatedByUserID
labels["launchableCreatedByOrgId"] = info.CreatedByOrgID
labels["launchableRawURL"] = "/launchable/deploy/now?launchableID=" + launchableID
Expand Down
Loading
Loading