From 62bdf7bbdfd1ddfa5f84319ecc348e3c7f8c893b Mon Sep 17 00:00:00 2001 From: TJ Moore Date: Fri, 3 Nov 2023 16:27:47 -0400 Subject: [PATCH] Update/refactor utility functions - Replace 'containsString' function with slices.Contains. - Move 'confirm' function to util package. --- internal/cmd/delete.go | 47 +--------------- internal/cmd/restore.go | 3 +- internal/cmd/show.go | 5 +- internal/util/util.go | 55 +++++++++++++++++++ .../{cmd/delete_test.go => util/util_test.go} | 23 +------- 5 files changed, 64 insertions(+), 69 deletions(-) create mode 100644 internal/util/util.go rename internal/{cmd/delete_test.go => util/util_test.go} (76%) diff --git a/internal/cmd/delete.go b/internal/cmd/delete.go index b14675c8..3d134e4e 100644 --- a/internal/cmd/delete.go +++ b/internal/cmd/delete.go @@ -15,10 +15,8 @@ package cmd import ( - "bufio" "context" "fmt" - "io" "os" "github.com/spf13/cobra" @@ -26,6 +24,7 @@ import ( "github.com/crunchydata/postgres-operator-client/internal" "github.com/crunchydata/postgres-operator-client/internal/apis/postgres-operator.crunchydata.com/v1beta1" + "github.com/crunchydata/postgres-operator-client/internal/util" ) // newDeleteCommand returns the delete subcommand of the PGO plugin. @@ -80,7 +79,7 @@ postgresclusters/hippo deleted`) for i := 0; confirmed == nil && i < 10; i++ { // retry 10 times or until a confirmation is given or denied, // whichever comes first - confirmed = confirm(os.Stdin, os.Stdout) + confirmed = util.Confirm(os.Stdin, os.Stdout) } if confirmed == nil || !*confirmed { @@ -111,45 +110,3 @@ postgresclusters/hippo deleted`) return cmd } - -// confirm uses a Scanner to parse user input. A user must type in "yes" or "no" -// and then press enter. It has fuzzy matching, so "y", "Y", "yes", "YES", -// and "Yes" all count as confirmations and return 'true'. Similarly, "n", "N", -// "no", "No", "NO" all deny confirmation and return 'false'. If the input is not -// recognized, nil is returned. -func confirm(reader io.Reader, writer io.Writer) *bool { - var response string - var boolVar bool - - scanner := bufio.NewScanner(reader) - if scanner.Scan() { - response = scanner.Text() - } - - if scanner.Err() != nil || response == "" { - fmt.Fprint(writer, "Please type yes or no and then press enter: ") - return nil - } - - yesResponses := []string{"y", "Y", "yes", "Yes", "YES"} - noResponses := []string{"n", "N", "no", "No", "NO"} - if containsString(yesResponses, response) { - boolVar = true - return &boolVar - } else if containsString(noResponses, response) { - return &boolVar - } else { - fmt.Fprint(writer, "Please type yes or no and then press enter: ") - return nil - } -} - -// containsString returns true if slice contains element -func containsString(slice []string, element string) bool { - for _, elem := range slice { - if elem == element { - return true - } - } - return false -} diff --git a/internal/cmd/restore.go b/internal/cmd/restore.go index d608047b..ca6b3dad 100644 --- a/internal/cmd/restore.go +++ b/internal/cmd/restore.go @@ -27,6 +27,7 @@ import ( "github.com/crunchydata/postgres-operator-client/internal" "github.com/crunchydata/postgres-operator-client/internal/apis/postgres-operator.crunchydata.com/v1beta1" + "github.com/crunchydata/postgres-operator-client/internal/util" ) func newRestoreCommand(config *internal.Config) *cobra.Command { @@ -225,7 +226,7 @@ func (config pgBackRestRestore) Run(ctx context.Context) error { func (config pgBackRestRestore) confirm(attempts int) *bool { for i := 0; i < attempts; i++ { - if confirmed := confirm(config.In, config.Out); confirmed != nil { + if confirmed := util.Confirm(config.In, config.Out); confirmed != nil { return confirmed } } diff --git a/internal/cmd/show.go b/internal/cmd/show.go index 5b1b1ba7..9f7730f4 100644 --- a/internal/cmd/show.go +++ b/internal/cmd/show.go @@ -27,6 +27,7 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" v1 "k8s.io/client-go/kubernetes/typed/core/v1" + "k8s.io/utils/strings/slices" "sigs.k8s.io/yaml" "github.com/crunchydata/postgres-operator-client/internal" @@ -353,7 +354,7 @@ func showUser(config *internal.Config, args []string, showSensitive bool) (strin for i := 0; confirmed == nil && i < 10; i++ { // retry 10 times or until a confirmation is given or denied, // whichever comes first - confirmed = confirm(os.Stdin, os.Stdout) + confirmed = util.Confirm(os.Stdin, os.Stdout) } if confirmed == nil || !*confirmed { @@ -420,7 +421,7 @@ func userData(fields []string, list *corev1.SecretList) (string, error) { if err != nil { return output, err } - if containsString(fields, k) { + if slices.Contains(fields, k) { output += fmt.Sprintf(" %s: %s\n", strings.ToUpper(k), string(d)) } } diff --git a/internal/util/util.go b/internal/util/util.go new file mode 100644 index 00000000..856c6644 --- /dev/null +++ b/internal/util/util.go @@ -0,0 +1,55 @@ +// Copyright 2021 - 2023 Crunchy Data Solutions, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package util + +import ( + "bufio" + "fmt" + "io" + + "k8s.io/utils/strings/slices" +) + +// Confirm uses a Scanner to parse user input. A user must type in "yes" or "no" +// and then press enter. It has fuzzy matching, so "y", "Y", "yes", "YES", +// and "Yes" all count as confirmations and return 'true'. Similarly, "n", "N", +// "no", "No", "NO" all deny confirmation and return 'false'. If the input is not +// recognized, nil is returned. +func Confirm(reader io.Reader, writer io.Writer) *bool { + var response string + var boolVar bool + + scanner := bufio.NewScanner(reader) + if scanner.Scan() { + response = scanner.Text() + } + + if scanner.Err() != nil || response == "" { + fmt.Fprint(writer, "Please type yes or no and then press enter: ") + return nil + } + + yesResponses := []string{"y", "Y", "yes", "Yes", "YES"} + noResponses := []string{"n", "N", "no", "No", "NO"} + if slices.Contains(yesResponses, response) { + boolVar = true + return &boolVar + } else if slices.Contains(noResponses, response) { + return &boolVar + } else { + fmt.Fprint(writer, "Please type yes or no and then press enter: ") + return nil + } +} diff --git a/internal/cmd/delete_test.go b/internal/util/util_test.go similarity index 76% rename from internal/cmd/delete_test.go rename to internal/util/util_test.go index 3e180d08..3aeb9cfb 100644 --- a/internal/cmd/delete_test.go +++ b/internal/util/util_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package cmd +package util import ( "bytes" @@ -50,7 +50,7 @@ func TestConfirmDelete(t *testing.T) { t.Run("input is "+tc.input, func(t *testing.T) { var reader io.Reader = strings.NewReader(tc.input) var writer bytes.Buffer - confirmed := confirm(reader, &writer) + confirmed := Confirm(reader, &writer) if tc.invalidResponse { assert.Assert(t, confirmed == nil) response, err := writer.ReadString(':') @@ -64,22 +64,3 @@ func TestConfirmDelete(t *testing.T) { }) } } - -func TestContainsString(t *testing.T) { - testsCases := []struct { - desc string - slice []string - element string - found bool - }{ - {"found", []string{"a", "b", "c"}, "a", true}, - {"not found", []string{"a", "b", "c"}, "x", false}, - {"not found substring", []string{"ab", "bc", "cd"}, "b", false}, - } - - for _, tc := range testsCases { - t.Run(tc.desc, func(t *testing.T) { - assert.Equal(t, containsString(tc.slice, tc.element), tc.found) - }) - } -}