From 0bdb0eafbd5e9de8c00dd3e9c3d372876ba56e23 Mon Sep 17 00:00:00 2001 From: jo Date: Wed, 19 Jun 2024 14:21:54 +0200 Subject: [PATCH] feat: delete resources in batches of 10 --- internal/cmd/base/delete.go | 74 ++++++++++++++++++-------------- internal/cmd/util/resource.go | 6 +++ internal/cmd/util/slices.go | 8 ++++ internal/cmd/util/slices_test.go | 17 ++++++++ 4 files changed, 73 insertions(+), 32 deletions(-) create mode 100644 internal/cmd/util/resource.go create mode 100644 internal/cmd/util/slices.go create mode 100644 internal/cmd/util/slices_test.go diff --git a/internal/cmd/base/delete.go b/internal/cmd/base/delete.go index 0653a10c..3da28a6c 100644 --- a/internal/cmd/base/delete.go +++ b/internal/cmd/base/delete.go @@ -4,7 +4,6 @@ import ( "errors" "fmt" "strings" - "sync" "github.com/spf13/cobra" @@ -53,48 +52,59 @@ func (dc *DeleteCmd) CobraCommand(s state.State) *cobra.Command { // Run executes a delete command. func (dc *DeleteCmd) Run(s state.State, cmd *cobra.Command, args []string) error { + batches := util.Batches(args, 10) - wg := sync.WaitGroup{} - wg.Add(len(args)) - actions, errs := - make([]*hcloud.Action, len(args)), - make([]error, len(args)) + errs := make([]error, 0, len(args)) + deleted := make([]string, 0, len(args)) + + for _, batch := range batches { + results := make([]util.ResourceState, 0, len(batch)) + actions := make([]*hcloud.Action, 0, len(batch)) + + for _, idOrName := range batch { + result := util.ResourceState{IDOrName: idOrName} - for i, idOrName := range args { - i, idOrName := i, idOrName - go func() { - defer wg.Done() resource, _, err := dc.Fetch(s, cmd, idOrName) if err != nil { - errs[i] = err - return + result.Error = err + continue } if util.IsNil(resource) { - errs[i] = fmt.Errorf("%s not found: %s", dc.ResourceNameSingular, idOrName) - return + result.Error = fmt.Errorf("%s not found: %s", dc.ResourceNameSingular, idOrName) + continue } - actions[i], errs[i] = dc.Delete(s, cmd, resource) - }() - } - wg.Wait() - filtered := util.FilterNil(actions) - var err error - if len(filtered) > 0 { - err = s.WaitForActions(cmd, s, filtered...) - } + action, err := dc.Delete(s, cmd, resource) + if err != nil { + result.Error = err + continue + } + if action != nil { + actions = append(actions, action) + } - var actuallyDeleted []string - for i, idOrName := range args { - if errs[i] == nil { - actuallyDeleted = append(actuallyDeleted, idOrName) + results = append(results, result) + } + + if len(actions) > 0 { + // TODO: We do not check when an action fail for a specific resource + if err := s.WaitForActions(cmd, s, actions...); err != nil { + errs = append(errs, err) + } + } + + for _, result := range results { + if result.Error == nil { + deleted = append(deleted, result.IDOrName) + } } } - if len(actuallyDeleted) == 1 { - cmd.Printf("%s %s deleted\n", dc.ResourceNameSingular, actuallyDeleted[0]) - } else if len(actuallyDeleted) > 1 { - cmd.Printf("%s %s deleted\n", dc.ResourceNamePlural, strings.Join(actuallyDeleted, ", ")) + if len(deleted) == 1 { + cmd.Printf("%s %s deleted\n", dc.ResourceNameSingular, deleted[0]) + } else if len(deleted) > 1 { + cmd.Printf("%s %s deleted\n", dc.ResourceNamePlural, strings.Join(deleted, ", ")) } - return errors.Join(append(errs, err)...) + + return errors.Join(errs...) } diff --git a/internal/cmd/util/resource.go b/internal/cmd/util/resource.go new file mode 100644 index 00000000..d88e1400 --- /dev/null +++ b/internal/cmd/util/resource.go @@ -0,0 +1,6 @@ +package util + +type ResourceState struct { + IDOrName string + Error error +} diff --git a/internal/cmd/util/slices.go b/internal/cmd/util/slices.go new file mode 100644 index 00000000..924565e9 --- /dev/null +++ b/internal/cmd/util/slices.go @@ -0,0 +1,8 @@ +package util + +func Batches[T any](all []T, size int) (batches [][]T) { + for size < len(all) { + all, batches = all[size:], append(batches, all[:size]) + } + return append(batches, all) +} diff --git a/internal/cmd/util/slices_test.go b/internal/cmd/util/slices_test.go new file mode 100644 index 00000000..dc176fe7 --- /dev/null +++ b/internal/cmd/util/slices_test.go @@ -0,0 +1,17 @@ +package util + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestBatches(t *testing.T) { + all := []int{1, 2, 3, 4, 5} + batches := Batches(all, 2) + + assert.Len(t, batches, 3) + assert.Equal(t, []int{1, 2}, batches[0]) + assert.Equal(t, []int{3, 4}, batches[1]) + assert.Equal(t, []int{5}, batches[2]) +}