This is an automated email from the ASF dual-hosted git repository.
AlexStocks pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/dubbo-go-pixiu.git
The following commit(s) were added to refs/heads/develop by this push:
new 7edad2d89 fix(cmd): replace panic with error returns in gateway
startup (#997)
7edad2d89 is described below
commit 7edad2d89f11f92515c2e3f77e36817c01f107a1
Author: aias00 <[email protected]>
AuthorDate: Sat Jul 18 22:35:18 2026 -0700
fix(cmd): replace panic with error returns in gateway startup (#997)
* fix(cmd): replace panic with error returns in gateway startup
Fixes #989 Issue 3: gateway startup path uses panic for initialization
and startup errors where normal error propagation would be safer.
Changes:
- PreRun → PreRunE: Use cobra's error handling instead of panic
- initialize() failure now returns wrapped error to cobra
- Run → RunE: Use cobra's error handling instead of panic
- start() failure now returns wrapped error to cobra
- stop() method: Return error instead of panic
- Changed from panic("implement me") to errors.New("stop not implemented")
Classification:
- PreRun panic: configuration/startup error → return error to cobra
- Run panic: startup error → return error to cobra (currently unreachable)
- stop panic: placeholder → return error
Benefits:
- Cobra handles errors gracefully, printing clean error messages
- Stack traces are no longer shown for configuration errors
- Users get clearer feedback on what went wrong
- Future changes to server.Start() can return errors properly
Note: server.Start() currently blocks and never returns errors. The RunE
handler is prepared for future improvements where server.Start might return
an error instead of blocking indefinitely.
Co-Authored-By: Claude <[email protected]>
* fix(cli): handle Execute() errors to ensure non-zero exit codes on failure
- Update cmd/pixiu/pixiu.go to call os.Exit(1) when Execute() returns an
error
- Update cmd/admin/admin.go with same fix for consistency
- Change deploy variable to interface type for better testability
- Add comprehensive tests for PreRunE and RunE error handling in
pkg/cmd/gateway_test.go
This addresses the regression where startup failures could exit with status 0
when PreRunE or RunE returned errors, breaking scripts/health checks that
rely
on exit codes.
Co-Authored-By: Claude <[email protected]>
* fix(fmt): apply gofmt formatting
- Fix import ordering in cmd/pixiu/pixiu.go
- Fix struct field alignment in pkg/cmd/gateway_test.go
- Add newline at end of gateway_test.go
Co-Authored-By: Claude <[email protected]>
* fix(fmt): add blank line between import groups in gateway_test.go
Follow project's imports-formatter convention for import grouping.
Co-Authored-By: Claude <[email protected]>
---------
Co-authored-by: Claude <[email protected]>
---
cmd/admin/admin.go | 5 +-
cmd/pixiu/pixiu.go | 6 +-
pkg/cmd/gateway.go | 17 ++--
pkg/cmd/gateway_test.go | 215 ++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 232 insertions(+), 11 deletions(-)
diff --git a/cmd/admin/admin.go b/cmd/admin/admin.go
index ed1aa2e3c..2e9aa6ea0 100644
--- a/cmd/admin/admin.go
+++ b/cmd/admin/admin.go
@@ -100,6 +100,7 @@ func initDefaultValue() {
func main() {
app := getRootCmd()
- // ignore error so we don't exit non-zero and break gfmrun README
example tests
- _ = app.Execute()
+ if err := app.Execute(); err != nil {
+ os.Exit(1)
+ }
}
diff --git a/cmd/pixiu/pixiu.go b/cmd/pixiu/pixiu.go
index c5a4a9020..a046c61af 100644
--- a/cmd/pixiu/pixiu.go
+++ b/cmd/pixiu/pixiu.go
@@ -19,6 +19,7 @@ package main
import (
_ "net/http/pprof"
+ "os"
"strconv"
"time"
)
@@ -37,8 +38,9 @@ import (
func main() {
app := getRootCmd()
- // ignore error so we don't exit non-zero and break gfmrun README
example tests
- _ = app.Execute()
+ if err := app.Execute(); err != nil {
+ os.Exit(1)
+ }
}
func getRootCmd() *cobra.Command {
diff --git a/pkg/cmd/gateway.go b/pkg/cmd/gateway.go
index 7accdc371..2be1a3b17 100644
--- a/pkg/cmd/gateway.go
+++ b/pkg/cmd/gateway.go
@@ -25,6 +25,8 @@ import (
)
import (
+ "github.com/pkg/errors"
+
"github.com/spf13/cobra"
)
@@ -56,27 +58,28 @@ var (
Short: "Run dubbo go pixiu in gateway mode",
}
- deploy = &DefaultDeployer{
+ deploy Deployer = &DefaultDeployer{
configManger: config.NewConfigManger(),
}
startGatewayCmd = &cobra.Command{
Use: "start",
Short: "Start gateway",
- PreRun: func(cmd *cobra.Command, args []string) {
+ PreRunE: func(cmd *cobra.Command, args []string) error {
initDefaultValue()
err := deploy.initialize()
if err != nil {
- panic(err)
+ return errors.Wrap(err, "failed to initialize
gateway")
}
+ return nil
},
- Run: func(cmd *cobra.Command, args []string) {
-
+ RunE: func(cmd *cobra.Command, args []string) error {
err := deploy.start()
if err != nil {
- panic(err)
+ return errors.Wrap(err, "failed to start
gateway")
}
+ return nil
},
}
)
@@ -134,7 +137,7 @@ func (d *DefaultDeployer) start() error {
func (d *DefaultDeployer) stop() error {
// TODO implement me
- panic("implement me")
+ return errors.New("stop not implemented")
}
// initDefaultValue If not set both in args and env, set default values
diff --git a/pkg/cmd/gateway_test.go b/pkg/cmd/gateway_test.go
new file mode 100644
index 000000000..8e1821bd3
--- /dev/null
+++ b/pkg/cmd/gateway_test.go
@@ -0,0 +1,215 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 cmd
+
+import (
+ "errors"
+ "testing"
+)
+
+import (
+ "github.com/spf13/cobra"
+
+ "github.com/stretchr/testify/assert"
+)
+
+// MockDeployer for testing
+type MockDeployer struct {
+ initializeErr error
+ startErr error
+ stopErr error
+}
+
+func (m *MockDeployer) initialize() error {
+ return m.initializeErr
+}
+
+func (m *MockDeployer) start() error {
+ return m.startErr
+}
+
+func (m *MockDeployer) stop() error {
+ return m.stopErr
+}
+
+func TestDefaultDeployerInitialize(t *testing.T) {
+ // Test successful initialization
+ d := &DefaultDeployer{
+ configManger: nil, // Will use default behavior
+ }
+ // Note: This test requires actual config files to be present
+ // In a real test environment, we would mock the config manager
+ assert.NotNil(t, d)
+}
+
+func TestDefaultDeployerStart(t *testing.T) {
+ d := &DefaultDeployer{
+ bootstrap: nil,
+ }
+ // Note: server.Start requires a valid bootstrap config
+ // In a unit test, we would need to mock the server.Start function
+ assert.NotNil(t, d)
+}
+
+func TestDefaultDeployerStop(t *testing.T) {
+ d := &DefaultDeployer{}
+ err := d.stop()
+ assert.Error(t, err)
+ assert.Equal(t, "stop not implemented", err.Error())
+}
+
+func TestStartGatewayCmdPreRunE(t *testing.T) {
+ tests := []struct {
+ name string
+ deployer Deployer
+ expectedError bool
+ }{
+ {
+ name: "successful initialization",
+ deployer: &MockDeployer{
+ initializeErr: nil,
+ },
+ expectedError: false,
+ },
+ {
+ name: "failed initialization",
+ deployer: &MockDeployer{
+ initializeErr: errors.New("initialization
failed"),
+ },
+ expectedError: true,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ // Save original deployer
+ originalDeployer := deploy
+ defer func() {
+ deploy = originalDeployer
+ }()
+
+ // Replace with mock deployer
+ deploy = tt.deployer
+
+ // Create a new command to test
+ testCmd := &cobra.Command{
+ Use: "test",
+ PreRunE: startGatewayCmd.PreRunE,
+ }
+
+ // Execute PreRunE
+ err := testCmd.PreRunE(testCmd, []string{})
+
+ if tt.expectedError {
+ assert.Error(t, err)
+ assert.Contains(t, err.Error(), "failed to
initialize gateway")
+ } else {
+ assert.NoError(t, err)
+ }
+ })
+ }
+}
+
+func TestStartGatewayCmdRunE(t *testing.T) {
+ tests := []struct {
+ name string
+ deployer Deployer
+ expectedError bool
+ }{
+ {
+ name: "successful start",
+ deployer: &MockDeployer{
+ startErr: nil,
+ },
+ expectedError: false,
+ },
+ {
+ name: "failed start",
+ deployer: &MockDeployer{
+ startErr: errors.New("start failed"),
+ },
+ expectedError: true,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ // Save original deployer
+ originalDeployer := deploy
+ defer func() {
+ deploy = originalDeployer
+ }()
+
+ // Replace with mock deployer
+ deploy = tt.deployer
+
+ // Create a new command to test
+ testCmd := &cobra.Command{
+ Use: "test",
+ RunE: startGatewayCmd.RunE,
+ }
+
+ // Execute RunE
+ err := testCmd.RunE(testCmd, []string{})
+
+ if tt.expectedError {
+ assert.Error(t, err)
+ assert.Contains(t, err.Error(), "failed to
start gateway")
+ } else {
+ assert.NoError(t, err)
+ }
+ })
+ }
+}
+
+func TestInitDefaultValue(t *testing.T) {
+ // Reset all values to empty
+ configPath = ""
+ apiConfigPath = ""
+ logConfigPath = ""
+ logLevel = ""
+ limitCpus = ""
+ logFormat = ""
+
+ // Call initDefaultValue
+ initDefaultValue()
+
+ // Check that default values are set
+ assert.NotEmpty(t, configPath)
+ assert.NotEmpty(t, apiConfigPath)
+ assert.NotEmpty(t, logConfigPath)
+ assert.NotEmpty(t, logLevel)
+ assert.NotEmpty(t, limitCpus)
+ // logFormat can be empty as DefaultLogFormat is ""
+}
+
+func TestGatewayCmdAddedToRootCmd(t *testing.T) {
+ // Check that GatewayCmd is properly initialized
+ assert.NotNil(t, GatewayCmd)
+ assert.Equal(t, "gateway", GatewayCmd.Use)
+ assert.Equal(t, "Run dubbo go pixiu in gateway mode", GatewayCmd.Short)
+}
+
+func TestStartGatewayCmdAddedToGatewayCmd(t *testing.T) {
+ // Check that startGatewayCmd is properly initialized
+ assert.NotNil(t, startGatewayCmd)
+ assert.Equal(t, "start", startGatewayCmd.Use)
+ assert.Equal(t, "Start gateway", startGatewayCmd.Short)
+ assert.NotNil(t, startGatewayCmd.PreRunE)
+ assert.NotNil(t, startGatewayCmd.RunE)
+}