From d0fce7dadd14907ddfd99bd515f75670fc2d05c6 Mon Sep 17 00:00:00 2001 From: Filip Strozik Date: Wed, 15 May 2024 08:02:39 +0200 Subject: [PATCH] implement own clierror interface --- internal/btp/auth/credentials.go | 2 +- internal/btp/auth/credentials_test.go | 12 ++--- internal/btp/auth/xsuaa.go | 10 ++-- internal/btp/auth/xsuaa_test.go | 2 +- internal/btp/cis/provision.go | 4 +- internal/btp/cis/provision_test.go | 2 +- internal/clierror/check.go | 14 ++++++ internal/clierror/error.go | 12 ++++- internal/clierror/error_test.go | 4 +- internal/clierror/wrap.go | 7 +-- internal/clierror/wrap_test.go | 12 +++-- internal/cmd/access/access.go | 14 +++--- internal/cmd/hana/check.go | 20 ++++---- internal/cmd/hana/credentials.go | 14 +++--- internal/cmd/hana/delete.go | 20 ++++---- internal/cmd/hana/map.go | 32 ++++++------ internal/cmd/hana/provision.go | 20 ++++---- internal/cmd/imageimport/imageimport.go | 20 ++++---- internal/cmd/provision/provision.go | 8 +-- .../referenceinstance/referenceinstance.go | 5 +- internal/cmdcommon/kubeconfig.go | 5 +- internal/kube/client.go | 2 +- internal/kube/service.go | 6 +-- internal/registry/config.go | 12 ++--- internal/registry/config_test.go | 2 +- internal/registry/image.go | 22 ++++----- internal/registry/image_test.go | 49 ++++++++----------- 27 files changed, 174 insertions(+), 158 deletions(-) create mode 100644 internal/clierror/check.go diff --git a/internal/btp/auth/credentials.go b/internal/btp/auth/credentials.go index 53c2db04a..c682252ee 100644 --- a/internal/btp/auth/credentials.go +++ b/internal/btp/auth/credentials.go @@ -46,7 +46,7 @@ type UAA struct { ZoneID string `json:"zoneid"` } -func LoadCISCredentials(path string) (*CISCredentials, error) { +func LoadCISCredentials(path string) (*CISCredentials, clierror.Error) { credentialsBytes, err := os.ReadFile(path) if err != nil { return nil, clierror.Wrap(err, clierror.Message("failed to read credentials file"), clierror.Hints("Make sure the path to the credentials file is correct.")) diff --git a/internal/btp/auth/credentials_test.go b/internal/btp/auth/credentials_test.go index 546e2dcce..24205739d 100644 --- a/internal/btp/auth/credentials_test.go +++ b/internal/btp/auth/credentials_test.go @@ -40,8 +40,8 @@ func TestLoadCISCredentials(t *testing.T) { err := os.WriteFile(filename, []byte(correctCredentials), os.ModePerm) require.NoError(t, err) - credentials, err := LoadCISCredentials(filename) - require.Nil(t, err) + credentials, cliError := LoadCISCredentials(filename) + require.Nil(t, cliError) require.Equal(t, correctCredentialsStruct, *credentials) }) @@ -51,16 +51,16 @@ func TestLoadCISCredentials(t *testing.T) { err := os.WriteFile(filename, []byte("\n{\n"), os.ModePerm) require.NoError(t, err) - credentials, err := LoadCISCredentials(filename) - require.Error(t, err) + credentials, cliError := LoadCISCredentials(filename) + require.NotNil(t, cliError) require.Nil(t, credentials) }) t.Run("incorrect credentials file error", func(t *testing.T) { filename := fmt.Sprintf("%s/doesnotexist-creds.txt", tmpDirPath) - credentials, err := LoadCISCredentials(filename) - require.Error(t, err) + credentials, cliError := LoadCISCredentials(filename) + require.NotNil(t, cliError) require.Nil(t, credentials) }) } diff --git a/internal/btp/auth/xsuaa.go b/internal/btp/auth/xsuaa.go index 5574477c3..9a2cbe46e 100644 --- a/internal/btp/auth/xsuaa.go +++ b/internal/btp/auth/xsuaa.go @@ -2,7 +2,6 @@ package auth import ( "encoding/json" - "errors" "fmt" "net/http" "net/url" @@ -26,7 +25,7 @@ type XSUAAToken struct { JTI string `json:"jti"` } -func GetOAuthToken(grantType, serverURL, username, password string) (*XSUAAToken, error) { +func GetOAuthToken(grantType, serverURL, username, password string) (*XSUAAToken, clierror.Error) { urlBody := url.Values{} urlBody.Set("grant_type", grantType) @@ -56,7 +55,7 @@ func GetOAuthToken(grantType, serverURL, username, password string) (*XSUAAToken return decodeAuthSuccessResponse(response) } -func decodeAuthSuccessResponse(response *http.Response) (*XSUAAToken, error) { +func decodeAuthSuccessResponse(response *http.Response) (*XSUAAToken, clierror.Error) { token := XSUAAToken{} err := json.NewDecoder(response.Body).Decode(&token) if err != nil { @@ -66,12 +65,11 @@ func decodeAuthSuccessResponse(response *http.Response) (*XSUAAToken, error) { return &token, nil } -func decodeAuthErrorResponse(response *http.Response) error { +func decodeAuthErrorResponse(response *http.Response) clierror.Error { errorData := xsuaaErrorResponse{} err := json.NewDecoder(response.Body).Decode(&errorData) if err != nil { return clierror.Wrap(err, clierror.Message("failed to decode error response")) } - // TODO: replace it with New func - return clierror.Wrap(errors.New(errorData.ErrorDescription), clierror.MessageF("error response: %s", response.Status)) + return clierror.Wrap(errorData.ErrorDescription, clierror.MessageF("error response: %s", response.Status)) } diff --git a/internal/btp/auth/xsuaa_test.go b/internal/btp/auth/xsuaa_test.go index b1bdd93fe..dff2f425e 100644 --- a/internal/btp/auth/xsuaa_test.go +++ b/internal/btp/auth/xsuaa_test.go @@ -31,7 +31,7 @@ func TestGetOAuthToken(t *testing.T) { name string credentials *CISCredentials want *XSUAAToken - expectedErr error + expectedErr clierror.Error }{ { name: "Correct credentials", diff --git a/internal/btp/cis/provision.go b/internal/btp/cis/provision.go index 79ca09ff2..73aaf8ee0 100644 --- a/internal/btp/cis/provision.go +++ b/internal/btp/cis/provision.go @@ -56,7 +56,7 @@ type ProvisionResponse struct { PlanName string `json:"planName"` } -func (c *LocalClient) Provision(pe *ProvisionEnvironment) (*ProvisionResponse, error) { +func (c *LocalClient) Provision(pe *ProvisionEnvironment) (*ProvisionResponse, clierror.Error) { reqData, err := json.Marshal(pe) if err != nil { return nil, clierror.New(clierror.Message(err.Error())) @@ -91,7 +91,7 @@ func (c *LocalClient) Provision(pe *ProvisionEnvironment) (*ProvisionResponse, e return decodeProvisionSuccessResponse(response) } -func decodeProvisionSuccessResponse(response *http.Response) (*ProvisionResponse, error) { +func decodeProvisionSuccessResponse(response *http.Response) (*ProvisionResponse, clierror.Error) { provisionResponse := ProvisionResponse{} err := json.NewDecoder(response.Body).Decode(&provisionResponse) if err != nil { diff --git a/internal/btp/cis/provision_test.go b/internal/btp/cis/provision_test.go index f2d12e562..73aadb448 100644 --- a/internal/btp/cis/provision_test.go +++ b/internal/btp/cis/provision_test.go @@ -65,7 +65,7 @@ func TestCISClient_Provision(t *testing.T) { token *auth.XSUAAToken pe *ProvisionEnvironment wantedResponse *ProvisionResponse - expectedErr error + expectedErr clierror.Error }{ { name: "Correct data", diff --git a/internal/clierror/check.go b/internal/clierror/check.go new file mode 100644 index 000000000..249d9f86d --- /dev/null +++ b/internal/clierror/check.go @@ -0,0 +1,14 @@ +package clierror + +import ( + "fmt" + "os" +) + +// Check prints error and executes os.Exit(1) if the error is not nil +func Check(err Error) { + if err != nil { + fmt.Println(err.String()) + os.Exit(1) + } +} diff --git a/internal/clierror/error.go b/internal/clierror/error.go index 83f29f085..600631dd1 100644 --- a/internal/clierror/error.go +++ b/internal/clierror/error.go @@ -4,6 +4,10 @@ import ( "fmt" ) +type Error interface { + String() string +} + type clierror struct { message string details string @@ -11,7 +15,11 @@ type clierror struct { } // New creates a new error with the given modifiers -func New(modifiers ...modifier) *clierror { +func New(modifiers ...modifier) Error { + return new(modifiers...) +} + +func new(modifiers ...modifier) *clierror { err := &clierror{} for _, m := range modifiers { m(err) @@ -20,7 +28,7 @@ func New(modifiers ...modifier) *clierror { } // Error returns the error string, compatible with the error interface -func (e clierror) Error() string { +func (e *clierror) String() string { output := fmt.Sprintf("Error:\n %s\n\n", e.message) if e.details != "" { output += fmt.Sprintf("Error Details:\n %s\n\n", e.details) diff --git a/internal/clierror/error_test.go b/internal/clierror/error_test.go index b1407c2c8..233b32d39 100644 --- a/internal/clierror/error_test.go +++ b/internal/clierror/error_test.go @@ -44,7 +44,7 @@ func Test_CLIError_String(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - assert.Equal(t, tt.want, tt.err.Error()) + assert.Equal(t, tt.want, tt.err.String()) }) } } @@ -157,7 +157,7 @@ func Test_CLIError_Wrap(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := tt.err.wrap(tt.outside) - assert.Equal(t, tt.want, err.Error()) + assert.Equal(t, tt.want, err.String()) }) } } diff --git a/internal/clierror/wrap.go b/internal/clierror/wrap.go index 8508e9b40..a56c9fd7a 100644 --- a/internal/clierror/wrap.go +++ b/internal/clierror/wrap.go @@ -1,9 +1,10 @@ package clierror -func Wrap(inside error, modifiers ...modifier) error { +func Wrap(inside any, modifiers ...modifier) Error { if err, ok := inside.(*clierror); ok { - return err.wrap(New(modifiers...)) + return err.wrap(new(modifiers...)) } - return New(Message(inside.Error())).wrap(New(modifiers...)) + // convert any type of inside error to a string + return new(MessageF("%v", inside)).wrap(new(modifiers...)) } diff --git a/internal/clierror/wrap_test.go b/internal/clierror/wrap_test.go index 6f5a5f920..51689a376 100644 --- a/internal/clierror/wrap_test.go +++ b/internal/clierror/wrap_test.go @@ -10,16 +10,22 @@ import ( func Test_Wrap(t *testing.T) { tests := []struct { name string - inside error + inside any outside []modifier want string }{ { - name: "Wrap string error", + name: "Wrap basic error", inside: fmt.Errorf("error"), outside: []modifier{Message("outside"), Hints("hint1", "hint2")}, want: "Error:\n outside\n\nError Details:\n error\n\nHints:\n - hint1\n - hint2\n", }, + { + name: "Wrap string error", + inside: "error", + outside: []modifier{Message("outside"), Hints("hint1", "hint2")}, + want: "Error:\n outside\n\nError Details:\n error\n\nHints:\n - hint1\n - hint2\n", + }, { name: "Wrap Error", inside: &clierror{message: "error", details: "details", hints: []string{"hint1", "hint2"}}, @@ -54,7 +60,7 @@ func Test_Wrap(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := Wrap(tt.inside, tt.outside...) - assert.Equal(t, tt.want, err.Error()) + assert.Equal(t, tt.want, err.String()) }) } } diff --git a/internal/cmd/access/access.go b/internal/cmd/access/access.go index b8e071127..b9eb29bdc 100644 --- a/internal/cmd/access/access.go +++ b/internal/cmd/access/access.go @@ -2,13 +2,13 @@ package access import ( "fmt" - "github.com/kyma-project/cli.v3/internal/clierror" - "k8s.io/apimachinery/pkg/api/errors" + "github.com/kyma-project/cli.v3/internal/clierror" "github.com/kyma-project/cli.v3/internal/cmdcommon" "github.com/spf13/cobra" v1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" + "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/tools/clientcmd/api" @@ -34,11 +34,11 @@ func NewAccessCMD(kymaConfig *cmdcommon.KymaConfig) *cobra.Command { Use: "access", Short: "Enrich kubeconfig with access", Long: "Enrich kubeconfig with Service Account based token and certificate", - PreRunE: func(_ *cobra.Command, args []string) error { - return cfg.KubeClientConfig.Complete() + PreRun: func(_ *cobra.Command, args []string) { + clierror.Check(cfg.KubeClientConfig.Complete()) }, - RunE: func(cmd *cobra.Command, args []string) error { - return runAccess(&cfg) + Run: func(cmd *cobra.Command, args []string) { + clierror.Check(runAccess(&cfg)) }, } @@ -55,7 +55,7 @@ func NewAccessCMD(kymaConfig *cmdcommon.KymaConfig) *cobra.Command { return cmd } -func runAccess(cfg *accessConfig) error { +func runAccess(cfg *accessConfig) clierror.Error { // Create objects err := createObjects(cfg) if err != nil { diff --git a/internal/cmd/hana/check.go b/internal/cmd/hana/check.go index d29a1f8a6..23c38a190 100644 --- a/internal/cmd/hana/check.go +++ b/internal/cmd/hana/check.go @@ -31,11 +31,11 @@ func NewHanaCheckCMD(kymaConfig *cmdcommon.KymaConfig) *cobra.Command { Use: "check", Short: "Check if the Hana instance is provisioned.", Long: "Use this command to check if the Hana instance is provisioned on the SAP Kyma platform.", - PreRunE: func(_ *cobra.Command, args []string) error { - return config.KubeClientConfig.Complete() + PreRun: func(_ *cobra.Command, args []string) { + clierror.Check(config.KubeClientConfig.Complete()) }, - RunE: func(_ *cobra.Command, _ []string) error { - return runCheck(&config) + Run: func(_ *cobra.Command, _ []string) { + clierror.Check(runCheck(&config)) }, } @@ -50,14 +50,14 @@ func NewHanaCheckCMD(kymaConfig *cmdcommon.KymaConfig) *cobra.Command { } var ( - checkCommands = []func(config *hanaCheckConfig) error{ + checkCommands = []func(config *hanaCheckConfig) clierror.Error{ checkHanaInstance, checkHanaBinding, checkHanaBindingUrl, } ) -func runCheck(config *hanaCheckConfig) error { +func runCheck(config *hanaCheckConfig) clierror.Error { fmt.Printf("Checking Hana (%s/%s).\n", config.namespace, config.name) for _, command := range checkCommands { @@ -71,23 +71,23 @@ func runCheck(config *hanaCheckConfig) error { return nil } -func checkHanaInstance(config *hanaCheckConfig) error { +func checkHanaInstance(config *hanaCheckConfig) clierror.Error { u, err := kube.GetServiceInstance(config.KubeClient, config.Ctx, config.namespace, config.name) return handleCheckResponse(u, err, "Hana instance", config.namespace, config.name) } -func checkHanaBinding(config *hanaCheckConfig) error { +func checkHanaBinding(config *hanaCheckConfig) clierror.Error { u, err := kube.GetServiceBinding(config.KubeClient, config.Ctx, config.namespace, config.name) return handleCheckResponse(u, err, "Hana binding", config.namespace, config.name) } -func checkHanaBindingUrl(config *hanaCheckConfig) error { +func checkHanaBindingUrl(config *hanaCheckConfig) clierror.Error { urlName := hanaBindingUrlName(config.name) u, err := kube.GetServiceBinding(config.KubeClient, config.Ctx, config.namespace, urlName) return handleCheckResponse(u, err, "Hana URL binding", config.namespace, urlName) } -func handleCheckResponse(u *unstructured.Unstructured, err error, printedName, namespace, name string) error { +func handleCheckResponse(u *unstructured.Unstructured, err error, printedName, namespace, name string) clierror.Error { if err != nil { return clierror.Wrap(err, clierror.Message("failed to get resource data"), diff --git a/internal/cmd/hana/credentials.go b/internal/cmd/hana/credentials.go index b6f23a9d9..84922bb1b 100644 --- a/internal/cmd/hana/credentials.go +++ b/internal/cmd/hana/credentials.go @@ -34,11 +34,11 @@ func NewHanaCredentialsCMD(kymaConfig *cmdcommon.KymaConfig) *cobra.Command { Use: "credentials", Short: "Print credentials of the Hana instance.", Long: "Use this command to print credentials of the Hana instance on the SAP Kyma platform.", - PreRunE: func(_ *cobra.Command, args []string) error { - return config.KubeClientConfig.Complete() + PreRun: func(_ *cobra.Command, args []string) { + clierror.Check(config.KubeClientConfig.Complete()) }, - RunE: func(_ *cobra.Command, _ []string) error { - return runCredentials(&config) + Run: func(_ *cobra.Command, _ []string) { + clierror.Check(runCredentials(&config)) }, } @@ -56,7 +56,7 @@ func NewHanaCredentialsCMD(kymaConfig *cmdcommon.KymaConfig) *cobra.Command { return cmd } -func runCredentials(config *hanaCredentialsConfig) error { +func runCredentials(config *hanaCredentialsConfig) clierror.Error { fmt.Printf("Getting Hana credentials (%s/%s).\n", config.namespace, config.name) credentials, err := getHanaCredentials(config) @@ -77,7 +77,7 @@ func printCredentials(config *hanaCredentialsConfig, credentials credentials) { } } -func getHanaCredentials(config *hanaCredentialsConfig) (credentials, error) { +func getHanaCredentials(config *hanaCredentialsConfig) (credentials, clierror.Error) { secret, err := config.KubeClient.Static().CoreV1().Secrets(config.namespace).Get(config.Ctx, config.name, metav1.GetOptions{}) if err != nil { return handleGetHanaCredentialsError(err) @@ -88,7 +88,7 @@ func getHanaCredentials(config *hanaCredentialsConfig) (credentials, error) { }, nil } -func handleGetHanaCredentialsError(err error) (credentials, error) { +func handleGetHanaCredentialsError(err error) (credentials, clierror.Error) { hints := []string{ "Make sure that Hana is run and ready to use. You can use command 'kyma hana check'.", } diff --git a/internal/cmd/hana/delete.go b/internal/cmd/hana/delete.go index 9219fb52a..a1c0f14e0 100644 --- a/internal/cmd/hana/delete.go +++ b/internal/cmd/hana/delete.go @@ -29,11 +29,11 @@ func NewHanaDeleteCMD(kymaConfig *cmdcommon.KymaConfig) *cobra.Command { Use: "delete", Short: "Delete a Hana instance on the Kyma.", Long: "Use this command to delete a Hana instance on the SAP Kyma platform.", - PreRunE: func(_ *cobra.Command, args []string) error { - return config.KubeClientConfig.Complete() + PreRun: func(_ *cobra.Command, args []string) { + clierror.Check(config.KubeClientConfig.Complete()) }, - RunE: func(_ *cobra.Command, _ []string) error { - return runDelete(&config) + Run: func(_ *cobra.Command, _ []string) { + clierror.Check(runDelete(&config)) }, } @@ -48,14 +48,14 @@ func NewHanaDeleteCMD(kymaConfig *cmdcommon.KymaConfig) *cobra.Command { } var ( - deleteCommands = []func(*hanaDeleteConfig) error{ + deleteCommands = []func(*hanaDeleteConfig) clierror.Error{ deleteHanaBinding, deleteHanaBindingUrl, deleteHanaInstance, } ) -func runDelete(config *hanaDeleteConfig) error { +func runDelete(config *hanaDeleteConfig) clierror.Error { fmt.Printf("Deleting Hana (%s/%s).\n", config.namespace, config.name) for _, command := range deleteCommands { @@ -68,21 +68,21 @@ func runDelete(config *hanaDeleteConfig) error { return nil } -func deleteHanaInstance(config *hanaDeleteConfig) error { +func deleteHanaInstance(config *hanaDeleteConfig) clierror.Error { err := config.KubeClient.Dynamic().Resource(operator.GVRServiceInstance). Namespace(config.namespace). Delete(config.Ctx, config.name, metav1.DeleteOptions{}) return handleDeleteResponse(err, "Hana instance", config.namespace, config.name) } -func deleteHanaBinding(config *hanaDeleteConfig) error { +func deleteHanaBinding(config *hanaDeleteConfig) clierror.Error { err := config.KubeClient.Dynamic().Resource(operator.GVRServiceBinding). Namespace(config.namespace). Delete(config.Ctx, config.name, metav1.DeleteOptions{}) return handleDeleteResponse(err, "Hana binding", config.namespace, config.name) } -func deleteHanaBindingUrl(config *hanaDeleteConfig) error { +func deleteHanaBindingUrl(config *hanaDeleteConfig) clierror.Error { urlName := hanaBindingUrlName(config.name) err := config.KubeClient.Dynamic().Resource(operator.GVRServiceBinding). Namespace(config.namespace). @@ -90,7 +90,7 @@ func deleteHanaBindingUrl(config *hanaDeleteConfig) error { return handleDeleteResponse(err, "Hana URL binding", config.namespace, urlName) } -func handleDeleteResponse(err error, printedName, namespace, name string) error { +func handleDeleteResponse(err error, printedName, namespace, name string) clierror.Error { if err == nil { fmt.Printf("Deleted %s (%s/%s).\n", printedName, namespace, name) return nil diff --git a/internal/cmd/hana/map.go b/internal/cmd/hana/map.go index 15560a921..6303511ec 100644 --- a/internal/cmd/hana/map.go +++ b/internal/cmd/hana/map.go @@ -29,11 +29,11 @@ func NewMapHanaCMD(kymaConfig *cmdcommon.KymaConfig) *cobra.Command { Use: "map", Short: "Map the Hana instance to the Kyma cluster.", Long: "Use this command to map the Hana instance to the Kyma cluster.", - PreRunE: func(_ *cobra.Command, args []string) error { - return config.KubeClientConfig.Complete() + PreRun: func(_ *cobra.Command, args []string) { + clierror.Check(config.KubeClientConfig.Complete()) }, - RunE: func(_ *cobra.Command, _ []string) error { - return runMap(&config) + Run: func(_ *cobra.Command, _ []string) { + clierror.Check(runMap(&config)) }, } @@ -49,14 +49,14 @@ func NewMapHanaCMD(kymaConfig *cmdcommon.KymaConfig) *cobra.Command { } var ( - mapCommands = []func(config *hanaCheckConfig) error{ + mapCommands = []func(config *hanaCheckConfig) clierror.Error{ createHanaAPIInstanceIfNeeded, createHanaAPIBindingIfNeeded, createHanaInstanceMapping, } ) -func runMap(config *hanaCheckConfig) error { +func runMap(config *hanaCheckConfig) clierror.Error { for _, command := range mapCommands { err := command(config) if err != nil { @@ -69,7 +69,7 @@ func runMap(config *hanaCheckConfig) error { return nil } -func createHanaAPIInstanceIfNeeded(config *hanaCheckConfig) error { +func createHanaAPIInstanceIfNeeded(config *hanaCheckConfig) clierror.Error { // check if instance exists, skip API instance creation if it does instance, err := kube.GetServiceInstance(config.KubeClient, config.Ctx, config.namespace, hanaBindingAPIName(config.name)) if err == nil && instance != nil { @@ -79,7 +79,7 @@ func createHanaAPIInstanceIfNeeded(config *hanaCheckConfig) error { return createHanaAPIInstance(config) } -func createHanaAPIBindingIfNeeded(config *hanaCheckConfig) error { +func createHanaAPIBindingIfNeeded(config *hanaCheckConfig) clierror.Error { //check if binding exists, skip API binding creation if it does instance, err := kube.GetServiceBinding(config.KubeClient, config.Ctx, config.namespace, hanaBindingAPIName(config.name)) if err == nil && instance != nil { @@ -91,7 +91,7 @@ func createHanaAPIBindingIfNeeded(config *hanaCheckConfig) error { } -func createHanaAPIInstance(config *hanaCheckConfig) error { +func createHanaAPIInstance(config *hanaCheckConfig) clierror.Error { data, err := hanaAPIInstance(config) if err != nil { return clierror.Wrap(err, clierror.Message("failed to create Hana API instance object")) @@ -103,7 +103,7 @@ func createHanaAPIInstance(config *hanaCheckConfig) error { return handleProvisionResponse(err, "Hana API instance", config.namespace, hanaBindingAPIName(config.name)) } -func createHanaAPIBinding(config *hanaCheckConfig) error { +func createHanaAPIBinding(config *hanaCheckConfig) clierror.Error { data, err := hanaAPIBinding(config) if err != nil { return clierror.Wrap(err, clierror.Message("failed to create Hana API binding object")) @@ -164,7 +164,7 @@ func hanaBindingAPIName(name string) string { return fmt.Sprintf("%s-api", name) } -func createHanaInstanceMapping(config *hanaCheckConfig) error { +func createHanaInstanceMapping(config *hanaCheckConfig) clierror.Error { clusterID, err := getClusterID(config) if err != nil { return err @@ -190,7 +190,7 @@ func createHanaInstanceMapping(config *hanaCheckConfig) error { return hanaInstanceMapping(baseurl, clusterID, hanaID, token.AccessToken) } -func getClusterID(config *hanaCheckConfig) (string, error) { +func getClusterID(config *hanaCheckConfig) (string, clierror.Error) { cm, err := config.KubeClient.Static().CoreV1().ConfigMaps("kyma-system").Get(config.Ctx, "sap-btp-operator-config", metav1.GetOptions{}) if err != nil { return "", clierror.Wrap(err, clierror.Message("failed to get cluster ID")) @@ -198,7 +198,7 @@ func getClusterID(config *hanaCheckConfig) (string, error) { return cm.Data["CLUSTER_ID"], nil } -func getHanaID(config *hanaCheckConfig) (string, error) { +func getHanaID(config *hanaCheckConfig) (string, clierror.Error) { // wait for until Hana instance is ready, for default setting it should take 5 minutes fmt.Print("waiting for Hana instance to be ready... ") instanceReadyCheck := kube.IsInstanceReady(config.KubeClient, config.Ctx, config.namespace, config.name) @@ -226,7 +226,7 @@ func getHanaID(config *hanaCheckConfig) (string, error) { return status.InstanceID, nil } -func readHanaAPISecret(config *hanaCheckConfig) (string, *auth.UAA, error) { +func readHanaAPISecret(config *hanaCheckConfig) (string, *auth.UAA, clierror.Error) { fmt.Print("waiting for Hana API instance to be ready... ") instanceReadyCheck := kube.IsInstanceReady(config.KubeClient, config.Ctx, config.namespace, hanaBindingAPIName(config.name)) err := wait.PollUntilContextTimeout(config.Ctx, 5*time.Second, 2*time.Minute, true, instanceReadyCheck) @@ -262,7 +262,7 @@ func readHanaAPISecret(config *hanaCheckConfig) (string, *auth.UAA, error) { return string(baseURL), uaa, nil } -func hanaInstanceMapping(baseURL, clusterID, hanaID, token string) error { +func hanaInstanceMapping(baseURL, clusterID, hanaID, token string) clierror.Error { client := &http.Client{} requestData := HanaMapping{ @@ -288,7 +288,7 @@ func hanaInstanceMapping(baseURL, clusterID, hanaID, token string) error { // server sends status Created when mapping is created, and 200 if it already exists if resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusOK { - return clierror.Wrap(fmt.Errorf("status code: %d", resp.StatusCode), clierror.Message("failed to create mapping")) + return clierror.Wrap(fmt.Sprintf("status code: %d", resp.StatusCode), clierror.Message("failed to create mapping")) } return nil diff --git a/internal/cmd/hana/provision.go b/internal/cmd/hana/provision.go index 2af09ed23..819e8ea0c 100644 --- a/internal/cmd/hana/provision.go +++ b/internal/cmd/hana/provision.go @@ -33,11 +33,11 @@ func NewHanaProvisionCMD(kymaConfig *cmdcommon.KymaConfig) *cobra.Command { Use: "provision", Short: "Provisions a Hana instance on the Kyma.", Long: "Use this command to provision a Hana instance on the SAP Kyma platform.", - PreRunE: func(_ *cobra.Command, args []string) error { - return config.KubeClientConfig.Complete() + PreRun: func(_ *cobra.Command, args []string) { + clierror.Check(config.KubeClientConfig.Complete()) }, - RunE: func(_ *cobra.Command, _ []string) error { - return runProvision(&config) + Run: func(_ *cobra.Command, _ []string) { + clierror.Check(runProvision(&config)) }, } @@ -56,14 +56,14 @@ func NewHanaProvisionCMD(kymaConfig *cmdcommon.KymaConfig) *cobra.Command { } var ( - provisionCommands = []func(*hanaProvisionConfig) error{ + provisionCommands = []func(*hanaProvisionConfig) clierror.Error{ createHanaInstance, createHanaBinding, createHanaBindingUrl, } ) -func runProvision(config *hanaProvisionConfig) error { +func runProvision(config *hanaProvisionConfig) clierror.Error { fmt.Printf("Provisioning Hana (%s/%s).\n", config.namespace, config.name) for _, command := range provisionCommands { @@ -77,28 +77,28 @@ func runProvision(config *hanaProvisionConfig) error { return nil } -func createHanaInstance(config *hanaProvisionConfig) error { +func createHanaInstance(config *hanaProvisionConfig) clierror.Error { _, err := config.KubeClient.Dynamic().Resource(operator.GVRServiceInstance). Namespace(config.namespace). Create(config.Ctx, hanaInstance(config), metav1.CreateOptions{}) return handleProvisionResponse(err, "Hana instance", config.namespace, config.name) } -func createHanaBinding(config *hanaProvisionConfig) error { +func createHanaBinding(config *hanaProvisionConfig) clierror.Error { _, err := config.KubeClient.Dynamic().Resource(operator.GVRServiceBinding). Namespace(config.namespace). Create(config.Ctx, hanaBinding(config), metav1.CreateOptions{}) return handleProvisionResponse(err, "Hana binding", config.namespace, config.name) } -func createHanaBindingUrl(config *hanaProvisionConfig) error { +func createHanaBindingUrl(config *hanaProvisionConfig) clierror.Error { _, err := config.KubeClient.Dynamic().Resource(operator.GVRServiceBinding). Namespace(config.namespace). Create(config.Ctx, hanaBindingUrl(config), metav1.CreateOptions{}) return handleProvisionResponse(err, "Hana URL binding", config.namespace, hanaBindingUrlName(config.name)) } -func handleProvisionResponse(err error, printedName, namespace, name string) error { +func handleProvisionResponse(err error, printedName, namespace, name string) clierror.Error { if err == nil { fmt.Printf("Created %s (%s/%s).\n", printedName, namespace, name) return nil diff --git a/internal/cmd/imageimport/imageimport.go b/internal/cmd/imageimport/imageimport.go index 55a632dfe..bcb7f9b77 100644 --- a/internal/cmd/imageimport/imageimport.go +++ b/internal/cmd/imageimport/imageimport.go @@ -29,14 +29,12 @@ func NewImportCMD(kymaConfig *cmdcommon.KymaConfig) *cobra.Command { Long: `Import image from daemon to in-cluster registry.`, Args: cobra.ExactArgs(1), - PreRunE: func(_ *cobra.Command, args []string) error { - if err := config.complete(args); err != nil { - return err - } - return config.validate() + PreRun: func(_ *cobra.Command, args []string) { + clierror.Check(config.complete(args)) + clierror.Check(config.validate()) }, - RunE: func(_ *cobra.Command, args []string) error { - return runImageImport(&config) + Run: func(_ *cobra.Command, args []string) { + clierror.Check(runImageImport(&config)) }, } @@ -45,22 +43,22 @@ func NewImportCMD(kymaConfig *cmdcommon.KymaConfig) *cobra.Command { return cmd } -func (pc *provisionConfig) validate() error { +func (pc *provisionConfig) validate() clierror.Error { imageElems := strings.Split(pc.image, ":") if len(imageElems) != 2 { - return fmt.Errorf("image '%s' not in expected format 'image:tag'", pc.image) + return clierror.New(clierror.MessageF("image '%s' not in expected format 'image:tag'", pc.image)) } return nil } -func (pc *provisionConfig) complete(args []string) error { +func (pc *provisionConfig) complete(args []string) clierror.Error { pc.image = args[0] return pc.KubeClientConfig.Complete() } -func runImageImport(config *provisionConfig) error { +func runImageImport(config *provisionConfig) clierror.Error { // TODO: Add "serverless is not installed" error message registryConfig, err := registry.GetConfig(config.Ctx, config.KubeClient) if err != nil { diff --git a/internal/cmd/provision/provision.go b/internal/cmd/provision/provision.go index b5d59ba7c..09b3cef25 100644 --- a/internal/cmd/provision/provision.go +++ b/internal/cmd/provision/provision.go @@ -27,8 +27,8 @@ func NewProvisionCMD() *cobra.Command { Short: "Provisions a Kyma cluster on the BTP.", Long: `Use this command to provision a Kyma environment on the SAP BTP platform. `, - RunE: func(_ *cobra.Command, _ []string) error { - return runProvision(&config) + Run: func(_ *cobra.Command, _ []string) { + clierror.Check(runProvision(&config)) }, } @@ -47,7 +47,7 @@ func NewProvisionCMD() *cobra.Command { return cmd } -func runProvision(config *provisionConfig) error { +func runProvision(config *provisionConfig) clierror.Error { // TODO: is the credentials a good name for this field? it contains much more than credentials only credentials, err := auth.LoadCISCredentials(config.credentialsPath) if err != nil { @@ -62,7 +62,7 @@ func runProvision(config *provisionConfig) error { ) if err != nil { var hints []string - if strings.Contains(err.Error(), "Internal Server Error") { + if strings.Contains(err.String(), "Internal Server Error") { hints = append(hints, "check if CIS grant type is set to client credentials") } diff --git a/internal/cmd/referenceinstance/referenceinstance.go b/internal/cmd/referenceinstance/referenceinstance.go index d7f4ce594..c5a9c892a 100644 --- a/internal/cmd/referenceinstance/referenceinstance.go +++ b/internal/cmd/referenceinstance/referenceinstance.go @@ -2,6 +2,7 @@ package referenceinstance import ( "github.com/kyma-project/cli.v3/internal/btp/operator" + "github.com/kyma-project/cli.v3/internal/clierror" "github.com/kyma-project/cli.v3/internal/cmdcommon" "github.com/kyma-project/cli.v3/internal/kube" "github.com/spf13/cobra" @@ -32,8 +33,8 @@ func NewReferenceInstanceCMD(kymaConfig *cmdcommon.KymaConfig) *cobra.Command { Short: "Add an instance reference to a shared service instance.", Long: `Use this command to add an instance reference to a shared service instance on the SAP Kyma platform. `, - PreRunE: func(_ *cobra.Command, _ []string) error { - return config.KubeClientConfig.Complete() + PreRun: func(_ *cobra.Command, _ []string) { + clierror.Check(config.KubeClientConfig.Complete()) }, RunE: func(_ *cobra.Command, _ []string) error { return runReferenceInstance(config) diff --git a/internal/cmdcommon/kubeconfig.go b/internal/cmdcommon/kubeconfig.go index 389de3981..573d9ead3 100644 --- a/internal/cmdcommon/kubeconfig.go +++ b/internal/cmdcommon/kubeconfig.go @@ -1,6 +1,7 @@ package cmdcommon import ( + "github.com/kyma-project/cli.v3/internal/clierror" "github.com/kyma-project/cli.v3/internal/kube" "github.com/spf13/cobra" ) @@ -15,8 +16,8 @@ func (kcc *KubeClientConfig) AddFlag(cmd *cobra.Command) { cmd.Flags().StringVar(&kcc.Kubeconfig, "kubeconfig", "", "Path to the Kyma kubecongig file.") } -func (kcc *KubeClientConfig) Complete() error { - var err error +func (kcc *KubeClientConfig) Complete() clierror.Error { + var err clierror.Error kcc.KubeClient, err = kube.NewClient(kcc.Kubeconfig) return err diff --git a/internal/kube/client.go b/internal/kube/client.go index c2d55d0f0..ad684e301 100644 --- a/internal/kube/client.go +++ b/internal/kube/client.go @@ -25,7 +25,7 @@ type client struct { restClient *rest.RESTClient } -func NewClient(kubeconfig string) (Client, error) { +func NewClient(kubeconfig string) (Client, clierror.Error) { client, err := newClient(kubeconfig) if err != nil { return nil, clierror.Wrap(err, diff --git a/internal/kube/service.go b/internal/kube/service.go index 403fd5328..3c62a436d 100644 --- a/internal/kube/service.go +++ b/internal/kube/service.go @@ -2,9 +2,9 @@ package kube import ( "context" + "errors" "github.com/kyma-project/cli.v3/internal/btp/operator" - "github.com/kyma-project/cli.v3/internal/clierror" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -25,7 +25,7 @@ type Status struct { func GetServiceStatus(u *unstructured.Unstructured) (Status, error) { instance := somethingWithStatus{} if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, &instance); err != nil { - return Status{}, clierror.Wrap(err, clierror.Message("failed to read resource data")) + return Status{}, errors.New("failed to read resource data") } return instance.Status, nil @@ -93,7 +93,7 @@ func isResourceReady(instance *unstructured.Unstructured) (bool, error) { } failedMessage := GetConditionMessage(status.Conditions, "Failed") - return false, clierror.New(clierror.Message(failedMessage)) + return false, errors.New(failedMessage) } ready, err := IsReady(instance) diff --git a/internal/registry/config.go b/internal/registry/config.go index 5c6fdfc46..3fa8274b1 100644 --- a/internal/registry/config.go +++ b/internal/registry/config.go @@ -20,16 +20,16 @@ type RegistryConfig struct { PodMeta *RegistryPodMeta } -func GetConfig(ctx context.Context, client kube.Client) (*RegistryConfig, error) { +func GetConfig(ctx context.Context, client kube.Client) (*RegistryConfig, clierror.Error) { config, err := getConfig(ctx, client) if err != nil { - return nil, clierror.Wrap(err, &clierror.Error{ - Message: "failed to load in-cluster registry configuration", - Hints: []string{ + return nil, clierror.Wrap(err, + clierror.Message("failed to load in-cluster registry configuration"), + clierror.Hints( "make sure cluster is available and properly configured", "make sure the Docker Registry is installed and in Ready/Warning state.", - }, - }) + ), + ) } return config, nil diff --git a/internal/registry/config_test.go b/internal/registry/config_test.go index c61dda84d..d2ed40899 100644 --- a/internal/registry/config_test.go +++ b/internal/registry/config_test.go @@ -54,7 +54,7 @@ func TestGetConfig(t *testing.T) { config, err := GetConfig(context.Background(), kubeClient) // then - require.NoError(t, err) + require.Nil(t, err) require.Equal(t, expectedRegistryConfig, config) }) } diff --git a/internal/registry/image.go b/internal/registry/image.go index d43f032aa..5bc42c61b 100644 --- a/internal/registry/image.go +++ b/internal/registry/image.go @@ -32,7 +32,7 @@ type utils struct { remoteWrite func(ref name.Reference, img v1.Image, options ...remote.Option) error } -func ImportImage(ctx context.Context, imageName string, opts ImportOptions) (string, error) { +func ImportImage(ctx context.Context, imageName string, opts ImportOptions) (string, clierror.Error) { return importImage(ctx, imageName, opts, utils{ daemonImage: daemon.Image, portforwardNewDial: portforward.NewDialFor, @@ -40,23 +40,21 @@ func ImportImage(ctx context.Context, imageName string, opts ImportOptions) (str }) } -func importImage(ctx context.Context, imageName string, opts ImportOptions, utils utils) (string, error) { +func importImage(ctx context.Context, imageName string, opts ImportOptions, utils utils) (string, clierror.Error) { localImage, err := imageFromInternalRegistry(ctx, imageName, utils) if err != nil { - return "", clierror.Wrap(err, &clierror.Error{ - Message: "failed to load image from local docker daemon", - Hints: []string{ + return "", clierror.Wrap(err, + clierror.Message("failed to load image from local docker daemon"), + clierror.Hints( "make sure docker daemon is running", "make sure the image exists in the local docker daemon", - }, - }) + ), + ) } conn, err := utils.portforwardNewDial(opts.ClusterAPIRestConfig, opts.RegistryPodName, opts.RegistryPodNamespace) if err != nil { - return "", clierror.Wrap(err, &clierror.Error{ - Message: "failed to create registry portforward connection", - }) + return "", clierror.Wrap(err, clierror.Message("failed to create registry portforward connection")) } defer conn.Close() @@ -65,9 +63,7 @@ func importImage(ctx context.Context, imageName string, opts ImportOptions, util pushedImage, err := imageToInClusterRegistry(ctx, localImage, transport, opts.RegistryAuth, opts.RegistryPullHost, imageName, utils) if err != nil { - return "", clierror.Wrap(err, &clierror.Error{ - Message: "failed to push image to the in-cluster registry", - }) + return "", clierror.Wrap(err, clierror.Message("failed to push image to the in-cluster registry")) } return pushedImage, nil diff --git a/internal/registry/image_test.go b/internal/registry/image_test.go index 5f1b0c46d..469fa6a04 100644 --- a/internal/registry/image_test.go +++ b/internal/registry/image_test.go @@ -28,7 +28,7 @@ func Test_importImage(t *testing.T) { name string args args want string - wantErr error + wantErr clierror.Error }{ { name: "import image", @@ -79,22 +79,20 @@ func Test_importImage(t *testing.T) { args: args{ imageName: ":::::::::", }, - wantErr: &clierror.Error{ - Message: "failed to load image from local docker daemon", - Details: "repository can only contain the characters `abcdefghijklmnopqrstuvwxyz0123456789_-./`: ::::::::", - Hints: []string{"make sure docker daemon is running", "make sure the image exists in the local docker daemon"}, - }, + wantErr: clierror.Wrap("repository can only contain the characters `abcdefghijklmnopqrstuvwxyz0123456789_-./`: ::::::::", + clierror.Message("failed to load image from local docker daemon"), + clierror.Hints("make sure docker daemon is running", "make sure the image exists in the local docker daemon"), + ), }, { name: "image contains registry address error", args: args{ imageName: "gcr.io/test:image", }, - wantErr: &clierror.Error{ - Message: "failed to load image from local docker daemon", - Details: "image 'gcr.io/test:image' can't contain registry 'gcr.io' address", - Hints: []string{"make sure docker daemon is running", "make sure the image exists in the local docker daemon"}, - }, + wantErr: clierror.Wrap("image 'gcr.io/test:image' can't contain registry 'gcr.io' address", + clierror.Message("failed to load image from local docker daemon"), + clierror.Hints("make sure docker daemon is running", "make sure the image exists in the local docker daemon"), + ), }, { name: "get image from local daemon error", @@ -107,11 +105,10 @@ func Test_importImage(t *testing.T) { }, }, }, - wantErr: &clierror.Error{ - Message: "failed to load image from local docker daemon", - Details: "test-error", - Hints: []string{"make sure docker daemon is running", "make sure the image exists in the local docker daemon"}, - }, + wantErr: clierror.Wrap("test-error", + clierror.Message("failed to load image from local docker daemon"), + clierror.Hints("make sure docker daemon is running", "make sure the image exists in the local docker daemon"), + ), }, { name: "create new portforward dial error", @@ -127,10 +124,9 @@ func Test_importImage(t *testing.T) { }, }, }, - wantErr: &clierror.Error{ - Message: "failed to create registry portforward connection", - Details: "test-error", - }, + wantErr: clierror.Wrap("test-error", + clierror.Message("failed to create registry portforward connection"), + ), }, { name: "wrong PullHost format", @@ -151,10 +147,8 @@ func Test_importImage(t *testing.T) { }, }, }, - wantErr: &clierror.Error{ - Message: "failed to push image to the in-cluster registry", - Details: "registries must be valid RFC 3986 URI authorities: < >", - }, + wantErr: clierror.Wrap("registries must be valid RFC 3986 URI authorities: < >", + clierror.Message("failed to push image to the in-cluster registry")), }, { name: "write image to in-cluster registry error", @@ -186,10 +180,9 @@ func Test_importImage(t *testing.T) { }, }, }, - wantErr: &clierror.Error{ - Message: "failed to push image to the in-cluster registry", - Details: "test error", - }, + wantErr: clierror.Wrap("test error", + clierror.Message("failed to push image to the in-cluster registry"), + ), }, } for _, tt := range tests {