diff --git a/pkg/cmd/indices/delete/delete.go b/pkg/cmd/indices/delete/delete.go index 1bb32f7f..243ec4ae 100644 --- a/pkg/cmd/indices/delete/delete.go +++ b/pkg/cmd/indices/delete/delete.go @@ -21,8 +21,9 @@ type DeleteOptions struct { SearchClient func() (*search.Client, error) - Indices []string - DoConfirm bool + Indices []string + DoConfirm bool + IncludeReplicas bool } // NewDeleteCmd creates and returns a delete command for indices @@ -48,6 +49,9 @@ func NewDeleteCmd(f *cmdutil.Factory, runF func(*DeleteOptions) error) *cobra.Co # Delete the index named "TEST_PRODUCTS_1" $ algolia indices delete TEST_PRODUCTS_1 + # Delete the index named "TEST_PRODUCTS_1" and its replicas + $ algolia indices delete TEST_PRODUCTS_1 --includeReplicas + # Delete the index named "TEST_PRODUCTS_1", skipping the confirmation prompt $ algolia indices delete TEST_PRODUCTS_1 -y @@ -73,6 +77,7 @@ func NewDeleteCmd(f *cmdutil.Factory, runF func(*DeleteOptions) error) *cobra.Co } cmd.Flags().BoolVarP(&confirm, "confirm", "y", false, "skip confirmation prompt") + cmd.Flags().BoolVarP(&opts.IncludeReplicas, "includeReplicas", "r", false, "delete replica indices too") return cmd } @@ -85,7 +90,11 @@ func runDeleteCmd(opts *DeleteOptions) error { if opts.DoConfirm { var confirmed bool - err := prompt.Confirm(fmt.Sprintf("Are you sure you want to delete the indices %q?", strings.Join(opts.Indices, ", ")), &confirmed) + msg := "Are you sure you want to delete the indices %q?" + if opts.IncludeReplicas { + msg = "Are you sure you want to delete the indices %q including their replicas?" + } + err := prompt.Confirm(fmt.Sprintf(msg, strings.Join(opts.Indices, ", ")), &confirmed) if err != nil { return fmt.Errorf("failed to prompt: %w", err) } @@ -102,10 +111,43 @@ func runDeleteCmd(opts *DeleteOptions) error { return fmt.Errorf("index %q does not exist", indexName) } indices = append(indices, index) + + if opts.IncludeReplicas { + settings, err := index.GetSettings() + + if err != nil { + return fmt.Errorf("can't get settings of index %q: %w", indexName, err) + } + + replicas := settings.Replicas + for _, replicaName := range replicas.Get() { + replica := client.InitIndex(replicaName) + indices = append(indices, replica) + } + } } for _, index := range indices { - if _, err := index.Delete(); err != nil { + var mustWait bool + + if opts.IncludeReplicas { + settings, err := index.GetSettings() + if err != nil { + return fmt.Errorf("failed to get settings of index %q: %w", index.GetName(), err) + } + if len(settings.Replicas.Get()) > 0 { + mustWait = true + } + } + + res, err := index.Delete() + + // Otherwise, the replica indices might not be 'fully detached' yet. + if mustWait { + _ = res.Wait() + } + + if err != nil { opts.IO.StartProgressIndicatorWithLabel(fmt.Sprint("Deleting replica index ", index.GetName())) err := deleteReplicaIndex(client, index) opts.IO.StopProgressIndicator() diff --git a/pkg/cmd/indices/delete/delete_test.go b/pkg/cmd/indices/delete/delete_test.go index cd549bb8..99c7a9cb 100644 --- a/pkg/cmd/indices/delete/delete_test.go +++ b/pkg/cmd/indices/delete/delete_test.go @@ -21,7 +21,6 @@ func TestNewDeleteCmd(t *testing.T) { name string tty bool cli string - isReplica bool wantsErr bool wantsOpts DeleteOptions }{ @@ -97,12 +96,13 @@ func TestNewDeleteCmd(t *testing.T) { func Test_runDeleteCmd(t *testing.T) { tests := []struct { - name string - cli string - indices []string - replica bool - isTTY bool - wantOut string + name string + cli string + indices []string + isReplica bool + hasReplicas bool + isTTY bool + wantOut string }{ { name: "no TTY", @@ -122,8 +122,8 @@ func Test_runDeleteCmd(t *testing.T) { name: "no TTY, multiple indices", cli: "foo bar --confirm", indices: []string{"foo", "bar"}, - isTTY: true, - wantOut: "✓ Deleted indices foo, bar\n", + isTTY: false, + wantOut: "", }, { name: "TTY, multiple indices", @@ -133,12 +133,20 @@ func Test_runDeleteCmd(t *testing.T) { wantOut: "✓ Deleted indices foo, bar\n", }, { - name: "TTY, replica indice", - cli: "foo --confirm", - indices: []string{"foo"}, - replica: true, - isTTY: true, - wantOut: "✓ Deleted indices foo\n", + name: "TTY, replica indices", + cli: "foo --confirm", + indices: []string{"foo"}, + isReplica: true, + isTTY: true, + wantOut: "✓ Deleted indices foo\n", + }, + { + name: "TTY, has replica indices", + cli: "foo --confirm --includeReplicas", + indices: []string{"foo"}, + hasReplicas: true, + isTTY: true, + wantOut: "✓ Deleted indices foo\n", }, } @@ -148,7 +156,21 @@ func Test_runDeleteCmd(t *testing.T) { for _, index := range tt.indices { // First settings call with `Exists()` r.Register(httpmock.REST("GET", fmt.Sprintf("1/indexes/%s/settings", index)), httpmock.JSONResponse(search.Settings{})) - if tt.replica { + if tt.hasReplicas { + // Settings calls for the primary index and its replicas + r.Register(httpmock.REST("GET", fmt.Sprintf("1/indexes/%s/settings", index)), httpmock.JSONResponse(search.Settings{ + Replicas: opt.Replicas("bar"), + })) + r.Register(httpmock.REST("GET", fmt.Sprintf("1/indexes/%s/settings", index)), httpmock.JSONResponse(search.Settings{ + Replicas: opt.Replicas("bar"), + })) + r.Register(httpmock.REST("GET", "1/indexes/bar/settings"), httpmock.JSONResponse(search.Settings{ + Primary: opt.Primary("foo"), + })) + // Additional DELETE calls for the replicas + r.Register(httpmock.REST("DELETE", "1/indexes/bar"), httpmock.JSONResponse(search.Settings{})) + } + if tt.isReplica { // We want the first `Delete()` call to fail r.Register(httpmock.REST("DELETE", fmt.Sprintf("1/indexes/%s", index)), httpmock.ErrorResponse()) // Second settings call to fetch the primary index name