Skip to content
Merged
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
24 changes: 22 additions & 2 deletions pkg/cmd/delete/delete.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
package delete

import (
"context"
_ "embed"
"fmt"
"os"
"strings"

devplanev1 "buf.build/gen/go/brevdev/devplane/protocolbuffers/go/devplaneapi/v1"
"github.com/brevdev/brev-cli/pkg/auth"
"github.com/brevdev/brev-cli/pkg/cmd/completions"
"github.com/brevdev/brev-cli/pkg/cmd/util"
"github.com/brevdev/brev-cli/pkg/entity"
"github.com/brevdev/brev-cli/pkg/environment"
breverrors "github.com/brevdev/brev-cli/pkg/errors"
"github.com/brevdev/brev-cli/pkg/terminal"
"github.com/hashicorp/go-multierror"
Expand All @@ -27,6 +30,7 @@ type DeleteStore interface {
completions.CompletionStore
DeleteWorkspace(workspaceID string) (*entity.Workspace, error)
GetWorkspaceByNameOrID(orgID string, nameOrID string) ([]entity.Workspace, error)
GetEnvironment(ctx context.Context, environmentID string) (*devplanev1.Environment, error)
}

func NewCmdDelete(t *terminal.Terminal, loginDeleteStore DeleteStore, noLoginDeleteStore DeleteStore) *cobra.Command {
Expand All @@ -47,7 +51,7 @@ func NewCmdDelete(t *terminal.Terminal, loginDeleteStore DeleteStore, noLoginDel
var allError error
var deletedNames []string
for _, workspace := range names {
err := deleteWorkspace(workspace, t, loginDeleteStore, piped)
err := deleteWorkspace(cmd.Context(), workspace, t, loginDeleteStore, piped)
if err != nil {
allError = multierror.Append(allError, err)
} else {
Expand All @@ -70,7 +74,7 @@ func NewCmdDelete(t *terminal.Terminal, loginDeleteStore DeleteStore, noLoginDel
return cmd
}

func deleteWorkspace(workspaceName string, t *terminal.Terminal, deleteStore DeleteStore, piped bool) error {
func deleteWorkspace(ctx context.Context, workspaceName string, t *terminal.Terminal, deleteStore DeleteStore, piped bool) error {
workspace, err := util.GetUserWorkspaceByNameOrIDErr(deleteStore, workspaceName)
if err != nil {
err1 := handleAdminUser(err, deleteStore, piped)
Expand All @@ -81,6 +85,15 @@ func deleteWorkspace(workspaceName string, t *terminal.Terminal, deleteStore Del

var workspaceID string
if workspace != nil {
env, err := deleteStore.GetEnvironment(ctx, workspace.ID)
if err != nil {
return breverrors.WrapAndTrace(err)
}
if phase := setupPhase(workspace, env); phase != "" {
return breverrors.NewValidationError(fmt.Sprintf(
"instance %q is still %s and can't be deleted yet. Run 'brev ls' and try again once it's running.",
workspace.Name, phase))
}
workspaceID = workspace.ID
} else {
workspaceID = workspaceName
Expand All @@ -98,6 +111,13 @@ func deleteWorkspace(workspaceName string, t *terminal.Terminal, deleteStore Del
return nil
}

func setupPhase(workspace *entity.Workspace, env *devplanev1.Environment) string {
if env.GetStatus() == devplanev1.EnvironmentStatus_ENVIRONMENT_STATUS_UNSPECIFIED && workspace.Status == entity.Deploying {
return "deploying"
}
return environment.SetupPhase(env)
}

func handleAdminUser(err error, deleteStore DeleteStore, piped bool) error {
if strings.Contains(err.Error(), "not found") {
if auth.IsAPIKeyAuthStore(deleteStore) {
Expand Down
13 changes: 13 additions & 0 deletions pkg/environment/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,16 @@ func IsFailed(env *devplanev1.Environment) bool {
}
return false
}

// SetupPhase returns the setup phase the environment is in, or "" once setup is over.
func SetupPhase(env *devplanev1.Environment) string {
switch env.GetStatus() {
case devplanev1.EnvironmentStatus_ENVIRONMENT_STATUS_PENDING:
return "pending"
case devplanev1.EnvironmentStatus_ENVIRONMENT_STATUS_PREPARING:
return "preparing"
case devplanev1.EnvironmentStatus_ENVIRONMENT_STATUS_PROVISIONING:
return "provisioning"
}
return ""
}
24 changes: 24 additions & 0 deletions pkg/environment/status_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,30 @@ func TestStatusDisplayMapsEnvironmentStatuses(t *testing.T) {
}
}

func TestSetupPhase(t *testing.T) {
cases := []struct {
name string
status devplanev1.EnvironmentStatus
want string
}{
{"pending", devplanev1.EnvironmentStatus_ENVIRONMENT_STATUS_PENDING, "pending"},
{"preparing", devplanev1.EnvironmentStatus_ENVIRONMENT_STATUS_PREPARING, "preparing"},
{"provisioning", devplanev1.EnvironmentStatus_ENVIRONMENT_STATUS_PROVISIONING, "provisioning"},
{"preparing_failed", devplanev1.EnvironmentStatus_ENVIRONMENT_STATUS_PREPARING_FAILED, ""},
{"provisioning_failed", devplanev1.EnvironmentStatus_ENVIRONMENT_STATUS_PROVISIONING_FAILED, ""},
{"building", devplanev1.EnvironmentStatus_ENVIRONMENT_STATUS_BUILDING, ""},
{"starting", devplanev1.EnvironmentStatus_ENVIRONMENT_STATUS_STARTING, ""},
{"running", devplanev1.EnvironmentStatus_ENVIRONMENT_STATUS_RUNNING, ""},
{"unspecified", devplanev1.EnvironmentStatus_ENVIRONMENT_STATUS_UNSPECIFIED, ""},
}

for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
assert.Equal(t, tc.want, SetupPhase(&devplanev1.Environment{Status: tc.status}))
})
}
}

// TestFailureMessage covers the provider-message preference and fallbacks.
func TestFailureMessage(t *testing.T) {
cases := []struct {
Expand Down
Loading