diff --git a/cmd/conduit/cecdysis/decorators.go b/cmd/conduit/cecdysis/decorators.go index 38dfd61b3..e588b924b 100644 --- a/cmd/conduit/cecdysis/decorators.go +++ b/cmd/conduit/cecdysis/decorators.go @@ -16,17 +16,13 @@ package cecdysis import ( "context" - "fmt" - "os" "path/filepath" - "strings" "github.com/conduitio/conduit/cmd/conduit/api" "github.com/conduitio/conduit/pkg/conduit" "github.com/conduitio/conduit/pkg/foundation/cerrors" "github.com/conduitio/ecdysis" "github.com/spf13/cobra" - "google.golang.org/grpc/status" ) // ------------------- CommandWithClient @@ -65,38 +61,17 @@ func (CommandWithExecuteWithClientDecorator) Decorate(_ *ecdysis.Ecdysis, cmd *c client, err := api.NewClient(cmd.Context(), grpcAddress) if err != nil { - return handleError(err) + return err } defer client.Close() ctx := ecdysis.ContextWithCobraCommand(cmd.Context(), cmd) - return handleError(v.ExecuteWithClient(ctx, client)) + return v.ExecuteWithClient(ctx, client) } return nil } -// check what type of error is to see if it's worth showing `cmd.Usage()` or not. -// if any error is returned, usage will be shown automatically. -func handleError(err error) error { - errMsg := err.Error() - st, ok := status.FromError(err) - - // an API error, we try to parse `desc` - if ok { - errMsg = st.Message() - - // st.Message() is already an entire representation of the error - // need to grab the desc - descIndex := strings.Index(errMsg, "desc =") - if descIndex != -1 { - errMsg = errMsg[descIndex+len("desc = "):] - } - } - _, _ = fmt.Fprintf(os.Stderr, "%v\n", errMsg) - return nil -} - // getGRPCAddress returns the gRPC address configured by the user. If no address is found, the default address is returned. func getGRPCAddress(cmd *cobra.Command) (string, error) { var ( diff --git a/cmd/conduit/cecdysis/decorators_test.go b/cmd/conduit/cecdysis/decorators_test.go deleted file mode 100644 index 94f2bf430..000000000 --- a/cmd/conduit/cecdysis/decorators_test.go +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright © 2025 Meroxa, 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 cecdysis - -import ( - "bytes" - "io" - "os" - "strings" - "testing" - - "github.com/conduitio/conduit/pkg/foundation/cerrors" - "github.com/matryer/is" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" -) - -func TestHandleError(t *testing.T) { - is := is.New(t) - - testCases := []struct { - name string - inputError error - expected error - expectedStderr string - }{ - { - name: "regular error", - inputError: cerrors.New("some error"), - expectedStderr: "some error", - }, - { - name: "Canceled error", - inputError: status.Error(codes.Canceled, "canceled error"), - expectedStderr: "canceled error", - }, - { - name: "Unknown error", - inputError: status.Error(codes.Unknown, "unknown error"), - expectedStderr: "unknown error", - }, - { - name: "InvalidArgument error", - inputError: status.Error(codes.InvalidArgument, "invalid argument error"), - expectedStderr: "invalid argument error", - }, - { - name: "DeadlineExceeded error", - inputError: status.Error(codes.DeadlineExceeded, "deadline exceeded error"), - expectedStderr: "deadline exceeded error", - }, - { - name: "NotFound error", - inputError: status.Error(codes.NotFound, "not found error"), - expectedStderr: "not found error", - }, - { - name: "NotFound error with description", - inputError: status.Error(codes.NotFound, "failed to get pipeline: rpc error: code = NotFound "+ - "desc = failed to get pipeline by ID: pipeline instance not found (ID: foo): pipeline instance not found"), - expectedStderr: "failed to get pipeline by ID: pipeline instance not found (ID: foo): pipeline instance not found", - }, - { - name: "AlreadyExists error", - inputError: status.Error(codes.AlreadyExists, "already exists error"), - expectedStderr: "already exists error", - }, - { - name: "PermissionDenied error", - inputError: status.Error(codes.PermissionDenied, "permission denied error"), - expectedStderr: "permission denied error", - }, - { - name: "ResourceExhausted error", - inputError: status.Error(codes.ResourceExhausted, "resource exhausted error"), - expectedStderr: "resource exhausted error", - }, - { - name: "FailedPrecondition error", - inputError: status.Error(codes.FailedPrecondition, "failed precondition error"), - expectedStderr: "failed precondition error", - }, - { - name: "Aborted error", - inputError: status.Error(codes.Aborted, "aborted error"), - expectedStderr: "aborted error", - }, - { - name: "OutOfRange error", - inputError: status.Error(codes.OutOfRange, "out of range error"), - expectedStderr: "out of range error", - }, - { - name: "Unimplemented error", - inputError: status.Error(codes.Unimplemented, "unimplemented error"), - expectedStderr: "unimplemented error", - }, - { - name: "Internal error", - inputError: status.Error(codes.Internal, "internal error"), - expectedStderr: "internal error", - }, - { - name: "Unavailable error", - inputError: status.Error(codes.Unavailable, "unavailable error"), - expectedStderr: "unavailable error", - }, - { - name: "DataLoss error", - inputError: status.Error(codes.DataLoss, "data loss error"), - expectedStderr: "data loss error", - }, - { - name: "Unauthenticated error", - inputError: status.Error(codes.Unauthenticated, "unauthenticated error"), - expectedStderr: "unauthenticated error", - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - is := is.New(t) - - oldStderr := os.Stderr - r, w, _ := os.Pipe() - os.Stderr = w - - result := handleError(tc.inputError) - - w.Close() - os.Stderr = oldStderr - - var buf bytes.Buffer - _, err := io.Copy(&buf, r) - is.NoErr(err) - - is.Equal(result, tc.expected) - is.Equal(strings.TrimSpace(buf.String()), tc.expectedStderr) - }) - } -} diff --git a/cmd/conduit/main.go b/cmd/conduit/main.go index 55ccc53f0..0c6fc5136 100644 --- a/cmd/conduit/main.go +++ b/cmd/conduit/main.go @@ -15,7 +15,6 @@ package main import ( - "fmt" "os" "github.com/conduitio/conduit/cmd/conduit/cecdysis" @@ -29,8 +28,12 @@ func main() { cmd := e.MustBuildCobraCommand(&root.RootCommand{}) cmd.CompletionOptions.DisableDefaultCmd = true + // Don't want to show usage when there's some unexpected error executing the command + // Help will still be shown via --help + cmd.SilenceUsage = true + if err := cmd.Execute(); err != nil { - _, _ = fmt.Fprintf(os.Stderr, "%v\n", err) + // error is already printed out os.Exit(1) } os.Exit(0)