From cf6a82fae8a297c25cd0b96564e3c6d5c0dfa407 Mon Sep 17 00:00:00 2001 From: Jenna Goldstrich Date: Tue, 21 Jan 2025 10:18:17 -0800 Subject: [PATCH] Make error less brittle to fix upcoming error with HCP SDK Go changing how we return 404 error --- internal/hcp/api/errors.go | 17 ++++++++-- internal/hcp/api/errors_test.go | 57 +++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 internal/hcp/api/errors_test.go diff --git a/internal/hcp/api/errors.go b/internal/hcp/api/errors.go index cff55fc9e09..9568779a1e1 100644 --- a/internal/hcp/api/errors.go +++ b/internal/hcp/api/errors.go @@ -5,7 +5,8 @@ package api import ( "fmt" - "strings" + "regexp" + "strconv" "google.golang.org/grpc/codes" ) @@ -26,6 +27,8 @@ func (c *ClientError) Error() string { return fmt.Sprintf("status %d: err %v", c.StatusCode, c.Err) } +var errCodeRegex = regexp.MustCompilePOSIX(`[Cc]ode"?:([0-9]+)`) + // CheckErrorCode checks the error string for err for some code and returns true // if the code is found. Ideally this function should use status.FromError // https://pkg.go.dev/google.golang.org/grpc/status#pkg-functions but that @@ -35,5 +38,15 @@ func CheckErrorCode(err error, code codes.Code) bool { return false } - return strings.Contains(err.Error(), fmt.Sprintf("Code:%d", code)) + // If the error string doesn't match the code we're looking for, we + // can ignore it and return false immediately. + matches := errCodeRegex.FindStringSubmatch(err.Error()) + if len(matches) == 0 { + return false + } + + // Safe to ignore the error here since the regex's submatch is always a + // valid integer given the format ([0-9]+) + errCode, _ := strconv.Atoi(matches[1]) + return errCode == int(code) } diff --git a/internal/hcp/api/errors_test.go b/internal/hcp/api/errors_test.go new file mode 100644 index 00000000000..fbf64458d72 --- /dev/null +++ b/internal/hcp/api/errors_test.go @@ -0,0 +1,57 @@ +package api + +import ( + "fmt" + "testing" + + "google.golang.org/grpc/codes" +) + +func TestCheckErrorCode(t *testing.T) { + tests := []struct { + name string + codeString string + expectCode codes.Code + expectSuccess bool + }{ + { + "old format, code matches what is looked for", + `{Code:5,"details":[],"message":"Error: The bucket etc."}`, + codes.Code(5), + true, + }, + { + "old format, code doesn't match what is looked for", + `{Code:55,"details":[],"message":"Error: The bucket etc."}`, + codes.Code(5), + false, + }, + { + "new format, code matches what is looked for", + `{"code":5,"details":[],"message":"Error: The bucket etc."}`, + codes.Code(5), + true, + }, + { + "new format, code doesn't match what is looked for", + `{"code":55,"details":[],"message":"Error: The bucket etc."}`, + codes.Code(5), + false, + }, + { + "bad format, should always be false", + `"ceod":55`, + codes.Code(5), + false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + found := CheckErrorCode(fmt.Errorf(tt.codeString), tt.expectCode) + if found != tt.expectSuccess { + t.Errorf("check error code returned %t, expected %t", found, tt.expectSuccess) + } + }) + } +}