Skip to content

Commit

Permalink
fix(server): wait for delete before returning (#482)
Browse files Browse the repository at this point in the history
* refactor: pass actionWaiter to delete command

* fix(server): wait for delete before returning

The `hcloud server delete` command returned before the action was
actually awaited. This caused a race condition when creating new
servers with the same name.
apricote authored Jun 1, 2023

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent 44b76d5 commit 62cb07f
Showing 25 changed files with 48 additions and 30 deletions.
10 changes: 5 additions & 5 deletions internal/cmd/base/delete.go
Original file line number Diff line number Diff line change
@@ -21,12 +21,12 @@ type DeleteCmd struct {
NameSuggestions func(client hcapi2.Client) func() []string
AdditionalFlags func(*cobra.Command)
Fetch func(ctx context.Context, client hcapi2.Client, cmd *cobra.Command, idOrName string) (interface{}, *hcloud.Response, error)
Delete func(ctx context.Context, client hcapi2.Client, cmd *cobra.Command, resource interface{}) error
Delete func(ctx context.Context, client hcapi2.Client, actionWaiter state.ActionWaiter, cmd *cobra.Command, resource interface{}) error
}

// CobraCommand creates a command that can be registered with cobra.
func (dc *DeleteCmd) CobraCommand(
ctx context.Context, client hcapi2.Client, tokenEnsurer state.TokenEnsurer,
ctx context.Context, client hcapi2.Client, tokenEnsurer state.TokenEnsurer, actionWaiter state.ActionWaiter,
) *cobra.Command {
cmd := &cobra.Command{
Use: fmt.Sprintf("delete [FLAGS] %s", strings.ToUpper(dc.ResourceNameSingular)),
@@ -37,7 +37,7 @@ func (dc *DeleteCmd) CobraCommand(
DisableFlagsInUseLine: true,
PreRunE: util.ChainRunE(tokenEnsurer.EnsureToken),
RunE: func(cmd *cobra.Command, args []string) error {
return dc.Run(ctx, client, cmd, args)
return dc.Run(ctx, client, actionWaiter, cmd, args)
},
}
if dc.AdditionalFlags != nil {
@@ -47,7 +47,7 @@ func (dc *DeleteCmd) CobraCommand(
}

// Run executes a describe command.
func (dc *DeleteCmd) Run(ctx context.Context, client hcapi2.Client, cmd *cobra.Command, args []string) error {
func (dc *DeleteCmd) Run(ctx context.Context, client hcapi2.Client, actionWaiter state.ActionWaiter, cmd *cobra.Command, args []string) error {

idOrName := args[0]
resource, _, err := dc.Fetch(ctx, client, cmd, idOrName)
@@ -61,7 +61,7 @@ func (dc *DeleteCmd) Run(ctx context.Context, client hcapi2.Client, cmd *cobra.C
return fmt.Errorf("%s not found: %s", dc.ResourceNameSingular, idOrName)
}

if err := dc.Delete(ctx, client, cmd, resource); err != nil {
if err := dc.Delete(ctx, client, actionWaiter, cmd, resource); err != nil {
return fmt.Errorf("deleting %s %s failed: %s", dc.ResourceNameSingular, idOrName, err)
}
fmt.Printf("%s %v deleted\n", dc.ResourceNameSingular, idOrName)
2 changes: 1 addition & 1 deletion internal/cmd/certificate/certificate.go
Original file line number Diff line number Diff line change
@@ -20,7 +20,7 @@ func NewCommand(cli *state.State, client hcapi2.Client) *cobra.Command {
updateCmd.CobraCommand(cli.Context, client, cli),
labelCmds.AddCobraCommand(cli.Context, client, cli),
labelCmds.RemoveCobraCommand(cli.Context, client, cli),
deleteCmd.CobraCommand(cli.Context, client, cli),
deleteCmd.CobraCommand(cli.Context, client, cli, cli),
describeCmd.CobraCommand(cli.Context, client, cli),
)

3 changes: 2 additions & 1 deletion internal/cmd/certificate/delete.go
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@ import (

"github.com/hetznercloud/cli/internal/cmd/base"
"github.com/hetznercloud/cli/internal/hcapi2"
"github.com/hetznercloud/cli/internal/state"
"github.com/hetznercloud/hcloud-go/hcloud"
"github.com/spf13/cobra"
)
@@ -16,7 +17,7 @@ var deleteCmd = base.DeleteCmd{
Fetch: func(ctx context.Context, client hcapi2.Client, cmd *cobra.Command, idOrName string) (interface{}, *hcloud.Response, error) {
return client.Certificate().Get(ctx, idOrName)
},
Delete: func(ctx context.Context, client hcapi2.Client, cmd *cobra.Command, resource interface{}) error {
Delete: func(ctx context.Context, client hcapi2.Client, _ state.ActionWaiter, cmd *cobra.Command, resource interface{}) error {
certificate := resource.(*hcloud.Certificate)
if _, err := client.Certificate().Delete(ctx, certificate); err != nil {
return err
3 changes: 2 additions & 1 deletion internal/cmd/firewall/delete.go
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@ import (

"github.com/hetznercloud/cli/internal/cmd/base"
"github.com/hetznercloud/cli/internal/hcapi2"
"github.com/hetznercloud/cli/internal/state"
"github.com/hetznercloud/hcloud-go/hcloud"
"github.com/spf13/cobra"
)
@@ -16,7 +17,7 @@ var deleteCmd = base.DeleteCmd{
Fetch: func(ctx context.Context, client hcapi2.Client, cmd *cobra.Command, idOrName string) (interface{}, *hcloud.Response, error) {
return client.Firewall().Get(ctx, idOrName)
},
Delete: func(ctx context.Context, client hcapi2.Client, cmd *cobra.Command, resource interface{}) error {
Delete: func(ctx context.Context, client hcapi2.Client, _ state.ActionWaiter, cmd *cobra.Command, resource interface{}) error {
firewall := resource.(*hcloud.Firewall)
if _, err := client.Firewall().Delete(ctx, firewall); err != nil {
return err
2 changes: 1 addition & 1 deletion internal/cmd/firewall/firewall.go
Original file line number Diff line number Diff line change
@@ -20,7 +20,7 @@ func NewCommand(cli *state.State, client hcapi2.Client) *cobra.Command {
newCreateCommand(cli),
updateCmd.CobraCommand(cli.Context, client, cli),
ReplaceRulesCommand.CobraCommand(cli.Context, client, cli, cli),
deleteCmd.CobraCommand(cli.Context, client, cli),
deleteCmd.CobraCommand(cli.Context, client, cli, cli),
AddRuleCommand.CobraCommand(cli.Context, client, cli, cli),
DeleteRuleCommand.CobraCommand(cli.Context, client, cli, cli),
ApplyToResourceCommand.CobraCommand(cli.Context, client, cli, cli),
3 changes: 2 additions & 1 deletion internal/cmd/floatingip/delete.go
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@ import (

"github.com/hetznercloud/cli/internal/cmd/base"
"github.com/hetznercloud/cli/internal/hcapi2"
"github.com/hetznercloud/cli/internal/state"
"github.com/hetznercloud/hcloud-go/hcloud"
"github.com/spf13/cobra"
)
@@ -16,7 +17,7 @@ var deleteCmd = base.DeleteCmd{
Fetch: func(ctx context.Context, client hcapi2.Client, cmd *cobra.Command, idOrName string) (interface{}, *hcloud.Response, error) {
return client.FloatingIP().Get(ctx, idOrName)
},
Delete: func(ctx context.Context, client hcapi2.Client, cmd *cobra.Command, resource interface{}) error {
Delete: func(ctx context.Context, client hcapi2.Client, _ state.ActionWaiter, cmd *cobra.Command, resource interface{}) error {
floatingIP := resource.(*hcloud.FloatingIP)
if _, err := client.FloatingIP().Delete(ctx, floatingIP); err != nil {
return err
2 changes: 1 addition & 1 deletion internal/cmd/floatingip/floatingip.go
Original file line number Diff line number Diff line change
@@ -21,7 +21,7 @@ func NewCommand(cli *state.State, client hcapi2.Client) *cobra.Command {
describeCmd.CobraCommand(cli.Context, client, cli),
AssignCommand.CobraCommand(cli.Context, client, cli, cli),
UnassignCommand.CobraCommand(cli.Context, client, cli, cli),
deleteCmd.CobraCommand(cli.Context, client, cli),
deleteCmd.CobraCommand(cli.Context, client, cli, cli),
EnableProtectionCommand.CobraCommand(cli.Context, client, cli, cli),
DisableProtectionCommand.CobraCommand(cli.Context, client, cli, cli),
labelCmds.AddCobraCommand(cli.Context, client, cli),
3 changes: 2 additions & 1 deletion internal/cmd/image/delete.go
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@ import (

"github.com/hetznercloud/cli/internal/cmd/base"
"github.com/hetznercloud/cli/internal/hcapi2"
"github.com/hetznercloud/cli/internal/state"
"github.com/hetznercloud/hcloud-go/hcloud"
"github.com/spf13/cobra"
)
@@ -16,7 +17,7 @@ var deleteCmd = base.DeleteCmd{
Fetch: func(ctx context.Context, client hcapi2.Client, cmd *cobra.Command, idOrName string) (interface{}, *hcloud.Response, error) {
return client.Image().Get(ctx, idOrName)
},
Delete: func(ctx context.Context, client hcapi2.Client, cmd *cobra.Command, resource interface{}) error {
Delete: func(ctx context.Context, client hcapi2.Client, _ state.ActionWaiter, cmd *cobra.Command, resource interface{}) error {
image := resource.(*hcloud.Image)
if _, err := client.Image().Delete(ctx, image); err != nil {
return err
2 changes: 1 addition & 1 deletion internal/cmd/image/image.go
Original file line number Diff line number Diff line change
@@ -16,7 +16,7 @@ func NewCommand(cli *state.State, client hcapi2.Client) *cobra.Command {
}
cmd.AddCommand(
listCmd.CobraCommand(cli.Context, client, cli),
deleteCmd.CobraCommand(cli.Context, client, cli),
deleteCmd.CobraCommand(cli.Context, client, cli, cli),
describeCmd.CobraCommand(cli.Context, client, cli),
updateCmd.CobraCommand(cli.Context, client, cli),
EnableProtectionCommand.CobraCommand(cli.Context, client, cli, cli),
3 changes: 2 additions & 1 deletion internal/cmd/loadbalancer/delete.go
Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@ import (

"github.com/hetznercloud/cli/internal/cmd/base"
"github.com/hetznercloud/cli/internal/hcapi2"
"github.com/hetznercloud/cli/internal/state"
"github.com/hetznercloud/hcloud-go/hcloud"
)

@@ -17,7 +18,7 @@ var deleteCmd = base.DeleteCmd{
Fetch: func(ctx context.Context, client hcapi2.Client, cmd *cobra.Command, idOrName string) (interface{}, *hcloud.Response, error) {
return client.LoadBalancer().Get(ctx, idOrName)
},
Delete: func(ctx context.Context, client hcapi2.Client, cmd *cobra.Command, resource interface{}) error {
Delete: func(ctx context.Context, client hcapi2.Client, _ state.ActionWaiter, cmd *cobra.Command, resource interface{}) error {
loadBalancer := resource.(*hcloud.LoadBalancer)
if _, err := client.LoadBalancer().Delete(ctx, loadBalancer); err != nil {
return err
2 changes: 1 addition & 1 deletion internal/cmd/loadbalancer/load_balancer.go
Original file line number Diff line number Diff line change
@@ -19,7 +19,7 @@ func NewCommand(cli *state.State, client hcapi2.Client) *cobra.Command {
CreateCommand.CobraCommand(cli.Context, client, cli, cli),
ListCmd.CobraCommand(cli.Context, client, cli),
DescribeCmd.CobraCommand(cli.Context, client, cli),
deleteCmd.CobraCommand(cli.Context, client, cli),
deleteCmd.CobraCommand(cli.Context, client, cli, cli),
updateCmd.CobraCommand(cli.Context, client, cli),
labelCmds.AddCobraCommand(cli.Context, client, cli),
labelCmds.RemoveCobraCommand(cli.Context, client, cli),
3 changes: 2 additions & 1 deletion internal/cmd/network/delete.go
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@ import (

"github.com/hetznercloud/cli/internal/cmd/base"
"github.com/hetznercloud/cli/internal/hcapi2"
"github.com/hetznercloud/cli/internal/state"
"github.com/hetznercloud/hcloud-go/hcloud"

"github.com/spf13/cobra"
@@ -17,7 +18,7 @@ var deleteCmd = base.DeleteCmd{
Fetch: func(ctx context.Context, client hcapi2.Client, cmd *cobra.Command, idOrName string) (interface{}, *hcloud.Response, error) {
return client.Network().Get(ctx, idOrName)
},
Delete: func(ctx context.Context, client hcapi2.Client, cmd *cobra.Command, resource interface{}) error {
Delete: func(ctx context.Context, client hcapi2.Client, _ state.ActionWaiter, cmd *cobra.Command, resource interface{}) error {
network := resource.(*hcloud.Network)
if _, err := client.Network().Delete(ctx, network); err != nil {
return err
2 changes: 1 addition & 1 deletion internal/cmd/network/network.go
Original file line number Diff line number Diff line change
@@ -19,7 +19,7 @@ func NewCommand(cli *state.State, client hcapi2.Client) *cobra.Command {
DescribeCmd.CobraCommand(cli.Context, client, cli),
newCreateCommand(cli),
updateCmd.CobraCommand(cli.Context, client, cli),
deleteCmd.CobraCommand(cli.Context, client, cli),
deleteCmd.CobraCommand(cli.Context, client, cli, cli),
ChangeIPRangeCommand.CobraCommand(cli.Context, client, cli, cli),
AddRouteCommand.CobraCommand(cli.Context, client, cli, cli),
RemoveRouteCommand.CobraCommand(cli.Context, client, cli, cli),
3 changes: 2 additions & 1 deletion internal/cmd/placementgroup/delete.go
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@ import (

"github.com/hetznercloud/cli/internal/cmd/base"
"github.com/hetznercloud/cli/internal/hcapi2"
"github.com/hetznercloud/cli/internal/state"
"github.com/hetznercloud/hcloud-go/hcloud"
"github.com/spf13/cobra"
)
@@ -16,7 +17,7 @@ var DeleteCmd = base.DeleteCmd{
Fetch: func(ctx context.Context, client hcapi2.Client, cmd *cobra.Command, idOrName string) (interface{}, *hcloud.Response, error) {
return client.PlacementGroup().Get(ctx, idOrName)
},
Delete: func(ctx context.Context, client hcapi2.Client, cmd *cobra.Command, resource interface{}) error {
Delete: func(ctx context.Context, client hcapi2.Client, _ state.ActionWaiter, cmd *cobra.Command, resource interface{}) error {
placementGroup := resource.(*hcloud.PlacementGroup)
if _, err := client.PlacementGroup().Delete(ctx, placementGroup); err != nil {
return err
3 changes: 2 additions & 1 deletion internal/cmd/placementgroup/delete_test.go
Original file line number Diff line number Diff line change
@@ -19,7 +19,8 @@ func TestDelete(t *testing.T) {
cmd := placementgroup.DeleteCmd.CobraCommand(
context.Background(),
fx.Client,
fx.TokenEnsurer)
fx.TokenEnsurer,
fx.ActionWaiter)
fx.ExpectEnsureToken()

placementGroup := hcloud.PlacementGroup{
2 changes: 1 addition & 1 deletion internal/cmd/placementgroup/placementgroup.go
Original file line number Diff line number Diff line change
@@ -19,7 +19,7 @@ func NewCommand(cli *state.State, client hcapi2.Client) *cobra.Command {
ListCmd.CobraCommand(cli.Context, client, cli),
DescribeCmd.CobraCommand(cli.Context, client, cli),
UpdateCmd.CobraCommand(cli.Context, client, cli),
DeleteCmd.CobraCommand(cli.Context, client, cli),
DeleteCmd.CobraCommand(cli.Context, client, cli, cli),
LabelCmds.AddCobraCommand(cli.Context, client, cli),
LabelCmds.RemoveCobraCommand(cli.Context, client, cli),
)
3 changes: 2 additions & 1 deletion internal/cmd/primaryip/delete.go
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@ import (

"github.com/hetznercloud/cli/internal/cmd/base"
"github.com/hetznercloud/cli/internal/hcapi2"
"github.com/hetznercloud/cli/internal/state"
"github.com/hetznercloud/hcloud-go/hcloud"
"github.com/spf13/cobra"
)
@@ -16,7 +17,7 @@ var deleteCmd = base.DeleteCmd{
Fetch: func(ctx context.Context, client hcapi2.Client, cmd *cobra.Command, idOrName string) (interface{}, *hcloud.Response, error) {
return client.PrimaryIP().Get(ctx, idOrName)
},
Delete: func(ctx context.Context, client hcapi2.Client, cmd *cobra.Command, resource interface{}) error {
Delete: func(ctx context.Context, client hcapi2.Client, _ state.ActionWaiter, cmd *cobra.Command, resource interface{}) error {
primaryIP := resource.(*hcloud.PrimaryIP)
if _, err := client.PrimaryIP().Delete(ctx, primaryIP); err != nil {
return err
2 changes: 1 addition & 1 deletion internal/cmd/primaryip/delete_test.go
Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@ func TestDelete(t *testing.T) {
fx := testutil.NewFixture(t)
defer fx.Finish()

cmd := deleteCmd.CobraCommand(context.Background(), fx.Client, fx.TokenEnsurer)
cmd := deleteCmd.CobraCommand(context.Background(), fx.Client, fx.TokenEnsurer, fx.ActionWaiter)
primaryip := &hcloud.PrimaryIP{ID: 13}
fx.ExpectEnsureToken()
fx.Client.PrimaryIPClient.EXPECT().
2 changes: 1 addition & 1 deletion internal/cmd/primaryip/primaryip.go
Original file line number Diff line number Diff line change
@@ -19,7 +19,7 @@ func NewCommand(cli *state.State, client hcapi2.Client) *cobra.Command {
describeCmd.CobraCommand(cli.Context, client, cli),
CreateCmd.CobraCommand(cli.Context, client, cli, cli),
updateCmd.CobraCommand(cli.Context, client, cli),
deleteCmd.CobraCommand(cli.Context, client, cli),
deleteCmd.CobraCommand(cli.Context, client, cli, cli),
AssignCmd.CobraCommand(cli.Context, client, cli, cli),
UnAssignCmd.CobraCommand(cli.Context, client, cli, cli),
ChangeDNSCmd.CobraCommand(cli.Context, client, cli, cli),
11 changes: 9 additions & 2 deletions internal/cmd/server/delete.go
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@ import (

"github.com/hetznercloud/cli/internal/cmd/base"
"github.com/hetznercloud/cli/internal/hcapi2"
"github.com/hetznercloud/cli/internal/state"
"github.com/hetznercloud/hcloud-go/hcloud"

"github.com/spf13/cobra"
@@ -17,11 +18,17 @@ var deleteCmd = base.DeleteCmd{
Fetch: func(ctx context.Context, client hcapi2.Client, cmd *cobra.Command, idOrName string) (interface{}, *hcloud.Response, error) {
return client.Server().Get(ctx, idOrName)
},
Delete: func(ctx context.Context, client hcapi2.Client, cmd *cobra.Command, resource interface{}) error {
Delete: func(ctx context.Context, client hcapi2.Client, actionWaiter state.ActionWaiter, cmd *cobra.Command, resource interface{}) error {
server := resource.(*hcloud.Server)
if _, err := client.Server().Delete(ctx, server); err != nil {
result, _, err := client.Server().DeleteWithResult(ctx, server)
if err != nil {
return err
}

if err := actionWaiter.ActionProgress(ctx, result.Action); err != nil {
return err
}

return nil
},
}
2 changes: 1 addition & 1 deletion internal/cmd/server/server.go
Original file line number Diff line number Diff line change
@@ -18,7 +18,7 @@ func NewCommand(cli *state.State, client hcapi2.Client) *cobra.Command {
ListCmd.CobraCommand(cli.Context, client, cli),
describeCmd.CobraCommand(cli.Context, client, cli),
CreateCmd.CobraCommand(cli.Context, client, cli, cli),
deleteCmd.CobraCommand(cli.Context, client, cli),
deleteCmd.CobraCommand(cli.Context, client, cli, cli),
RebootCommand.CobraCommand(cli.Context, client, cli, cli),
PoweronCommand.CobraCommand(cli.Context, client, cli, cli),
PoweroffCommand.CobraCommand(cli.Context, client, cli, cli),
3 changes: 2 additions & 1 deletion internal/cmd/sshkey/delete.go
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@ import (

"github.com/hetznercloud/cli/internal/cmd/base"
"github.com/hetznercloud/cli/internal/hcapi2"
"github.com/hetznercloud/cli/internal/state"
"github.com/hetznercloud/hcloud-go/hcloud"
"github.com/spf13/cobra"
)
@@ -16,7 +17,7 @@ var deleteCmd = base.DeleteCmd{
Fetch: func(ctx context.Context, client hcapi2.Client, cmd *cobra.Command, idOrName string) (interface{}, *hcloud.Response, error) {
return client.SSHKey().Get(ctx, idOrName)
},
Delete: func(ctx context.Context, client hcapi2.Client, cmd *cobra.Command, resource interface{}) error {
Delete: func(ctx context.Context, client hcapi2.Client, _ state.ActionWaiter, cmd *cobra.Command, resource interface{}) error {
sshKey := resource.(*hcloud.SSHKey)
if _, err := client.SSHKey().Delete(ctx, sshKey); err != nil {
return err
2 changes: 1 addition & 1 deletion internal/cmd/sshkey/sshkey.go
Original file line number Diff line number Diff line change
@@ -18,7 +18,7 @@ func NewCommand(cli *state.State, client hcapi2.Client) *cobra.Command {
listCmd.CobraCommand(cli.Context, client, cli),
newCreateCommand(cli),
updateCmd.CobraCommand(cli.Context, client, cli),
deleteCmd.CobraCommand(cli.Context, client, cli),
deleteCmd.CobraCommand(cli.Context, client, cli, cli),
describeCmd.CobraCommand(cli.Context, client, cli),
labelCmds.AddCobraCommand(cli.Context, client, cli),
labelCmds.RemoveCobraCommand(cli.Context, client, cli),
3 changes: 2 additions & 1 deletion internal/cmd/volume/delete.go
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@ import (

"github.com/hetznercloud/cli/internal/cmd/base"
"github.com/hetznercloud/cli/internal/hcapi2"
"github.com/hetznercloud/cli/internal/state"
"github.com/hetznercloud/hcloud-go/hcloud"
"github.com/spf13/cobra"
)
@@ -16,7 +17,7 @@ var deleteCmd = base.DeleteCmd{
Fetch: func(ctx context.Context, client hcapi2.Client, cmd *cobra.Command, idOrName string) (interface{}, *hcloud.Response, error) {
return client.Volume().Get(ctx, idOrName)
},
Delete: func(ctx context.Context, client hcapi2.Client, cmd *cobra.Command, resource interface{}) error {
Delete: func(ctx context.Context, client hcapi2.Client, _ state.ActionWaiter, cmd *cobra.Command, resource interface{}) error {
volume := resource.(*hcloud.Volume)
if _, err := client.Volume().Delete(ctx, volume); err != nil {
return err
2 changes: 1 addition & 1 deletion internal/cmd/volume/volume.go
Original file line number Diff line number Diff line change
@@ -18,7 +18,7 @@ func NewCommand(cli *state.State, client hcapi2.Client) *cobra.Command {
listCmd.CobraCommand(cli.Context, client, cli),
CreateCommand.CobraCommand(cli.Context, client, cli, cli),
updateCmd.CobraCommand(cli.Context, client, cli),
deleteCmd.CobraCommand(cli.Context, client, cli),
deleteCmd.CobraCommand(cli.Context, client, cli, cli),
describeCmd.CobraCommand(cli.Context, client, cli),
AttachCommand.CobraCommand(cli.Context, client, cli, cli),
DetachCommand.CobraCommand(cli.Context, client, cli, cli),

0 comments on commit 62cb07f

Please sign in to comment.