From d4a1e56bc81d4daf259fbda9af10cce2be6de699 Mon Sep 17 00:00:00 2001 From: Sebastian Choren Date: Fri, 9 Feb 2024 20:23:22 -0300 Subject: [PATCH 1/6] fix commands --- cli/cmd/middleware.go | 58 ++++++++++++++++++++++++++++++- cli/config/configurator.go | 6 +++- cli/pkg/resourcemanager/client.go | 15 +++++--- 3 files changed, 72 insertions(+), 7 deletions(-) diff --git a/cli/cmd/middleware.go b/cli/cmd/middleware.go index 473ebffb0a..c0c3c7a237 100644 --- a/cli/cmd/middleware.go +++ b/cli/cmd/middleware.go @@ -1,13 +1,20 @@ package cmd import ( + "context" "errors" "fmt" "os" + "os/exec" + "strings" + "syscall" + "github.com/kubeshop/tracetest/cli/config" "github.com/kubeshop/tracetest/cli/pkg/resourcemanager" + "github.com/kubeshop/tracetest/cli/ui" "github.com/spf13/cobra" + "github.com/spf13/pflag" ) type RunFn func(cmd *cobra.Command, args []string) (string, error) @@ -19,7 +26,7 @@ func WithResultHandler(runFn RunFn) CobraRunFn { res, err := runFn(cmd, args) if err != nil { - OnError(err) + handleError(err, cmd, args) return } @@ -29,6 +36,55 @@ func WithResultHandler(runFn RunFn) CobraRunFn { } } +func handleError(err error, cmd *cobra.Command, args []string) { + reqErr := resourcemanager.RequestError{} + if errors.As(err, &reqErr) && reqErr.IsAuthError { + handleAuthError(cmd, args) + } else { + OnError(err) + } +} + +func handleAuthError(cmd *cobra.Command, args []string) { + ui.DefaultUI.Warning("Your authentication token has expired, please log in again.") + configurator. + WithOnFinish(func(ctx context.Context, _ config.Config) { + retryCommand(cmd, args) + }). + ExecuteUserLogin(context.Background(), cliConfig) +} + +func retryCommand(cmd *cobra.Command, args []string) { + cmdLine := buildCmdLine(cmd, args) + execCmd := exec.Command("sh", "-c", cmdLine) + execCmd.Stdout = os.Stdout + execCmd.Stderr = os.Stderr + err := execCmd.Run() + + if err != nil { + exitWithCmdStatus(err) + } else { + os.Exit(0) + } +} + +func buildCmdLine(cmd *cobra.Command, args []string) string { + cmdLine := append([]string{cmd.CommandPath()}, args...) + cmd.Flags().VisitAll(func(flag *pflag.Flag) { + if flag.Changed { + cmdLine = append(cmdLine, fmt.Sprintf("--%s=%s", flag.Name, flag.Value.String())) + } + }) + return strings.Join(cmdLine, " ") +} + +func exitWithCmdStatus(err error) { + if exitError, ok := err.(*exec.ExitError); ok { + ws := exitError.Sys().(syscall.WaitStatus) + os.Exit(ws.ExitStatus()) + } +} + type errorMessageRenderer interface { Render() } diff --git a/cli/config/configurator.go b/cli/config/configurator.go index aa695bf224..3656b04a66 100644 --- a/cli/config/configurator.go +++ b/cli/config/configurator.go @@ -187,6 +187,10 @@ func (c Configurator) handleOAuth(ctx context.Context, cfg Config, prev *Config, return cfg, nil } + return c.ExecuteUserLogin(ctx, cfg) +} + +func (c Configurator) ExecuteUserLogin(ctx context.Context, cfg Config) (Config, error) { oauthServer := oauth.NewOAuthServer(cfg.OAuthEndpoint(), cfg.UIEndpoint) err := oauthServer.WithOnSuccess(c.onOAuthSuccess(ctx, cfg)). WithOnFailure(c.onOAuthFailure). @@ -195,7 +199,7 @@ func (c Configurator) handleOAuth(ctx context.Context, cfg Config, prev *Config, return Config{}, err } - return cfg, nil + return cfg, err } func (c Configurator) exchangeToken(cfg Config, token string) (Config, error) { diff --git a/cli/pkg/resourcemanager/client.go b/cli/pkg/resourcemanager/client.go index 0f3ba2c900..5e66d42d81 100644 --- a/cli/pkg/resourcemanager/client.go +++ b/cli/pkg/resourcemanager/client.go @@ -134,8 +134,9 @@ var ErrNotFound = RequestError{ } type RequestError struct { - Code int `json:"code"` - Message string `json:"error"` + Code int `json:"code"` + Message string `json:"error"` + IsAuthError bool `json:"isAuthError"` } type alternateRequestError struct { @@ -152,6 +153,10 @@ func (e RequestError) Is(target error) bool { return ok && t.Code == e.Code } +func isAuthError(resp *http.Response) bool { + return resp.StatusCode == http.StatusUnauthorized +} + func isSuccessResponse(resp *http.Response) bool { // successfull http status codes are 2xx return resp.StatusCode >= 200 && resp.StatusCode < 300 @@ -188,9 +193,9 @@ func parseRequestError(resp *http.Response, format Format) error { if err != nil { return fmt.Errorf("cannot parse response body: %w", err) } - return RequestError{ - Code: alternateReqError.Status, - Message: alternateReqError.Detail, + Code: alternateReqError.Status, + Message: alternateReqError.Detail, + IsAuthError: isAuthError(resp), } } From b8719728e62667a948e882d46d84d311bed50682 Mon Sep 17 00:00:00 2001 From: Sebastian Choren Date: Wed, 14 Feb 2024 20:45:46 -0300 Subject: [PATCH 2/6] pass cmd in context --- cli/cmd/configure_cmd.go | 3 +- cli/cmd/dashboard_cmd.go | 3 +- cli/cmd/middleware.go | 51 +++++++++++++++++++++++++--------- cli/cmd/resource_apply_cmd.go | 3 +- cli/cmd/resource_delete_cmd.go | 3 +- cli/cmd/resource_export_cmd.go | 3 +- cli/cmd/resource_get_cmd.go | 3 +- cli/cmd/resource_list_cmd.go | 3 +- cli/cmd/resource_run_cmd.go | 3 +- cli/cmd/start_cmd.go | 4 +-- cli/cmd/version_cmd.go | 4 ++- 11 files changed, 51 insertions(+), 32 deletions(-) diff --git a/cli/cmd/configure_cmd.go b/cli/cmd/configure_cmd.go index ef9d8582f0..934df57c1e 100644 --- a/cli/cmd/configure_cmd.go +++ b/cli/cmd/configure_cmd.go @@ -21,8 +21,7 @@ var configureCmd = &cobra.Command{ Short: "Configure your tracetest CLI", Long: "Configure your tracetest CLI", PreRun: setupLogger, - Run: WithResultHandler(WithParamsHandler(configParams)(func(cmd *cobra.Command, _ []string) (string, error) { - ctx := context.Background() + Run: WithResultHandler(WithParamsHandler(configParams)(func(ctx context.Context, cmd *cobra.Command, _ []string) (string, error) { flags := agentConfig.Flags{ CI: configParams.CI, } diff --git a/cli/cmd/dashboard_cmd.go b/cli/cmd/dashboard_cmd.go index a1598a2188..c2f50e94b6 100644 --- a/cli/cmd/dashboard_cmd.go +++ b/cli/cmd/dashboard_cmd.go @@ -1,6 +1,7 @@ package cmd import ( + "context" "fmt" "github.com/kubeshop/tracetest/cli/ui" @@ -13,7 +14,7 @@ var dashboardCmd = &cobra.Command{ Short: "Opens the Tracetest Dashboard URL", Long: "Opens the Tracetest Dashboard URL", PreRun: setupCommand(), - Run: WithResultHandler(func(_ *cobra.Command, _ []string) (string, error) { + Run: WithResultHandler(func(_ context.Context, _ *cobra.Command, _ []string) (string, error) { if cliConfig.IsEmpty() { return "", fmt.Errorf("missing Tracetest endpoint configuration") } diff --git a/cli/cmd/middleware.go b/cli/cmd/middleware.go index c0c3c7a237..71b71a8942 100644 --- a/cli/cmd/middleware.go +++ b/cli/cmd/middleware.go @@ -17,16 +17,38 @@ import ( "github.com/spf13/pflag" ) -type RunFn func(cmd *cobra.Command, args []string) (string, error) +type RunFn func(ctx context.Context, cmd *cobra.Command, args []string) (string, error) type CobraRunFn func(cmd *cobra.Command, args []string) type MiddlewareWrapper func(RunFn) RunFn +type keyCmd struct{} + +var cmdKey keyCmd + +func ContextWithCmd(cmd *cobra.Command, args []string) context.Context { + return context.WithValue( + context.Background(), + cmdKey, + buildCmdLine(cmd, args), + ) +} + +func ContextGetCmd(ctx context.Context) string { + v := ctx.Value(cmdKey) + if v == nil { + return "" + } + return v.(string) +} + func WithResultHandler(runFn RunFn) CobraRunFn { return func(cmd *cobra.Command, args []string) { - res, err := runFn(cmd, args) + ctx := ContextWithCmd(cmd, args) + + res, err := runFn(ctx, cmd, args) if err != nil { - handleError(err, cmd, args) + handleError(ctx, err) return } @@ -36,26 +58,29 @@ func WithResultHandler(runFn RunFn) CobraRunFn { } } -func handleError(err error, cmd *cobra.Command, args []string) { +func handleError(ctx context.Context, err error) { reqErr := resourcemanager.RequestError{} if errors.As(err, &reqErr) && reqErr.IsAuthError { - handleAuthError(cmd, args) + handleAuthError(ctx) } else { OnError(err) } } -func handleAuthError(cmd *cobra.Command, args []string) { +func handleAuthError(ctx context.Context) { ui.DefaultUI.Warning("Your authentication token has expired, please log in again.") configurator. WithOnFinish(func(ctx context.Context, _ config.Config) { - retryCommand(cmd, args) + retryCommand(ctx) }). - ExecuteUserLogin(context.Background(), cliConfig) + ExecuteUserLogin(ctx, cliConfig) } -func retryCommand(cmd *cobra.Command, args []string) { - cmdLine := buildCmdLine(cmd, args) +func retryCommand(ctx context.Context) { + cmdLine := ContextGetCmd(ctx) + if cmdLine == "" { + os.Exit(1) + } execCmd := exec.Command("sh", "-c", cmdLine) execCmd.Stdout = os.Stdout execCmd.Stderr = os.Stderr @@ -64,7 +89,7 @@ func retryCommand(cmd *cobra.Command, args []string) { if err != nil { exitWithCmdStatus(err) } else { - os.Exit(0) + os.Exit(1) } } @@ -122,7 +147,7 @@ func handleErrorMessage(err error) string { func WithParamsHandler(validators ...Validator) MiddlewareWrapper { return func(runFn RunFn) RunFn { - return func(cmd *cobra.Command, args []string) (string, error) { + return func(ctx context.Context, cmd *cobra.Command, args []string) (string, error) { errors := make([]error, 0) for _, validator := range validators { @@ -138,7 +163,7 @@ func WithParamsHandler(validators ...Validator) MiddlewareWrapper { return "", fmt.Errorf(errorText) } - return runFn(cmd, args) + return runFn(ctx, cmd, args) } } } diff --git a/cli/cmd/resource_apply_cmd.go b/cli/cmd/resource_apply_cmd.go index fa91fb884f..2f9e902499 100644 --- a/cli/cmd/resource_apply_cmd.go +++ b/cli/cmd/resource_apply_cmd.go @@ -21,9 +21,8 @@ func init() { Short: "Apply resources", Long: "Apply (create/update) resources to your Tracetest server", PreRun: setupCommand(), - Run: WithResourceMiddleware(func(_ *cobra.Command, args []string) (string, error) { + Run: WithResourceMiddleware(func(ctx context.Context, _ *cobra.Command, args []string) (string, error) { resourceType := resourceParams.ResourceName - ctx := context.Background() resourceClient, err := resources.Get(resourceType) if err != nil { diff --git a/cli/cmd/resource_delete_cmd.go b/cli/cmd/resource_delete_cmd.go index 13fd1e1ee5..ab291527a6 100644 --- a/cli/cmd/resource_delete_cmd.go +++ b/cli/cmd/resource_delete_cmd.go @@ -21,9 +21,8 @@ func init() { Short: "Delete resources", Long: "Delete resources from your Tracetest server", PreRun: setupCommand(), - Run: WithResourceMiddleware(func(_ *cobra.Command, args []string) (string, error) { + Run: WithResourceMiddleware(func(ctx context.Context, _ *cobra.Command, args []string) (string, error) { resourceType := resourceParams.ResourceName - ctx := context.Background() resourceClient, err := resources.Get(resourceType) if err != nil { diff --git a/cli/cmd/resource_export_cmd.go b/cli/cmd/resource_export_cmd.go index cba48c3d7e..a9d33cefeb 100644 --- a/cli/cmd/resource_export_cmd.go +++ b/cli/cmd/resource_export_cmd.go @@ -21,9 +21,8 @@ func init() { Long: "Export a resource from your Tracetest server", Short: "Export resource", PreRun: setupCommand(), - Run: WithResourceMiddleware(func(_ *cobra.Command, args []string) (string, error) { + Run: WithResourceMiddleware(func(ctx context.Context, _ *cobra.Command, args []string) (string, error) { resourceType := resourceParams.ResourceName - ctx := context.Background() resourceClient, err := resources.Get(resourceType) if err != nil { diff --git a/cli/cmd/resource_get_cmd.go b/cli/cmd/resource_get_cmd.go index b10dece6d5..09760770f4 100644 --- a/cli/cmd/resource_get_cmd.go +++ b/cli/cmd/resource_get_cmd.go @@ -20,9 +20,8 @@ func init() { Short: "Get resource", Long: "Get a resource from your Tracetest server", PreRun: setupCommand(), - Run: WithResourceMiddleware(func(_ *cobra.Command, args []string) (string, error) { + Run: WithResourceMiddleware(func(ctx context.Context, _ *cobra.Command, args []string) (string, error) { resourceType := resourceParams.ResourceName - ctx := context.Background() resourceClient, err := resources.Get(resourceType) if err != nil { diff --git a/cli/cmd/resource_list_cmd.go b/cli/cmd/resource_list_cmd.go index 59f6bcb7d1..a2536b36b2 100644 --- a/cli/cmd/resource_list_cmd.go +++ b/cli/cmd/resource_list_cmd.go @@ -19,9 +19,8 @@ func init() { Short: "List resources", Long: "List resources from your Tracetest server", PreRun: setupCommand(), - Run: WithResourceMiddleware(func(_ *cobra.Command, args []string) (string, error) { + Run: WithResourceMiddleware(func(ctx context.Context, _ *cobra.Command, args []string) (string, error) { resourceType := resourceParams.ResourceName - ctx := context.Background() resourceClient, err := resources.Get(resourceType) if err != nil { diff --git a/cli/cmd/resource_run_cmd.go b/cli/cmd/resource_run_cmd.go index 39e3c1ffbe..1eb2022b4a 100644 --- a/cli/cmd/resource_run_cmd.go +++ b/cli/cmd/resource_run_cmd.go @@ -24,8 +24,7 @@ func init() { Short: "run resources", Long: "run resources", PreRun: setupCommand(WithOptionalResourceName()), - Run: WithResourceMiddleware(func(_ *cobra.Command, args []string) (string, error) { - ctx := context.Background() + Run: WithResourceMiddleware(func(ctx context.Context, _ *cobra.Command, args []string) (string, error) { resourceType, err := getResourceType(runParams, resourceParams) if err != nil { return "", err diff --git a/cli/cmd/start_cmd.go b/cli/cmd/start_cmd.go index d32095ee02..00d2d912a1 100644 --- a/cli/cmd/start_cmd.go +++ b/cli/cmd/start_cmd.go @@ -24,9 +24,7 @@ var startCmd = &cobra.Command{ Short: "Start Tracetest", Long: "Start using Tracetest", PreRun: setupCommand(SkipConfigValidation(), SkipVersionMismatchCheck()), - Run: WithResultHandler((func(_ *cobra.Command, _ []string) (string, error) { - ctx := context.Background() - + Run: WithResultHandler((func(ctx context.Context, _ *cobra.Command, _ []string) (string, error) { flags := agentConfig.Flags{ OrganizationID: saveParams.organizationID, EnvironmentID: saveParams.environmentID, diff --git a/cli/cmd/version_cmd.go b/cli/cmd/version_cmd.go index 2df624019d..04026d6903 100644 --- a/cli/cmd/version_cmd.go +++ b/cli/cmd/version_cmd.go @@ -1,6 +1,8 @@ package cmd import ( + "context" + "github.com/spf13/cobra" ) @@ -10,7 +12,7 @@ var versionCmd = &cobra.Command{ Short: "Display this CLI tool version", Long: "Display this CLI tool version", PreRun: setupCommand(), - Run: WithResultHandler(func(_ *cobra.Command, _ []string) (string, error) { + Run: WithResultHandler(func(_ context.Context, _ *cobra.Command, _ []string) (string, error) { return versionText, nil }), PostRun: teardownCommand, From 779c947bdc8db97044702374c3517013dba42481 Mon Sep 17 00:00:00 2001 From: Sebastian Choren Date: Wed, 14 Feb 2024 21:48:12 -0300 Subject: [PATCH 3/6] fix start --- cli/cmd/middleware.go | 107 +++++++++++++++++++------------------ cli/cmd/root.go | 12 +++-- cli/cmd/start_cmd.go | 15 +++++- cli/config/config.go | 44 +++++++++++++-- cli/config/configurator.go | 25 ++++++--- 5 files changed, 136 insertions(+), 67 deletions(-) diff --git a/cli/cmd/middleware.go b/cli/cmd/middleware.go index 71b71a8942..08e66159a7 100644 --- a/cli/cmd/middleware.go +++ b/cli/cmd/middleware.go @@ -5,45 +5,63 @@ import ( "errors" "fmt" "os" - "os/exec" - "strings" - "syscall" "github.com/kubeshop/tracetest/cli/config" "github.com/kubeshop/tracetest/cli/pkg/resourcemanager" "github.com/kubeshop/tracetest/cli/ui" "github.com/spf13/cobra" - "github.com/spf13/pflag" ) type RunFn func(ctx context.Context, cmd *cobra.Command, args []string) (string, error) type CobraRunFn func(cmd *cobra.Command, args []string) type MiddlewareWrapper func(RunFn) RunFn -type keyCmd struct{} +// type keyCmd struct{} -var cmdKey keyCmd +// var cmdKey keyCmd -func ContextWithCmd(cmd *cobra.Command, args []string) context.Context { - return context.WithValue( - context.Background(), - cmdKey, - buildCmdLine(cmd, args), - ) -} +// func ContextWithCmd(cmd *cobra.Command, args []string) context.Context { +// return context.WithValue( +// cmd.Context(), +// cmdKey, +// buildCmdLine(cmd, args), +// ) +// } + +// func ContextGetCmd(ctx context.Context) string { +// v := ctx.Value(cmdKey) +// if v == nil { +// return "" +// } +// return v.(string) +// } + +func rootCtx(cmd *cobra.Command) context.Context { + if cmd == nil { + return nil + } -func ContextGetCmd(ctx context.Context) string { - v := ctx.Value(cmdKey) - if v == nil { - return "" + var ( + ctx = cmd.Context() + p = cmd.Parent() + ) + if cmd.Parent() == nil { + return ctx } - return v.(string) + for { + ctx = p.Context() + p = p.Parent() + if p == nil { + break + } + } + return ctx } func WithResultHandler(runFn RunFn) CobraRunFn { return func(cmd *cobra.Command, args []string) { - ctx := ContextWithCmd(cmd, args) + ctx := rootCtx(cmd) res, err := runFn(ctx, cmd, args) @@ -77,38 +95,25 @@ func handleAuthError(ctx context.Context) { } func retryCommand(ctx context.Context) { - cmdLine := ContextGetCmd(ctx) - if cmdLine == "" { - os.Exit(1) - } - execCmd := exec.Command("sh", "-c", cmdLine) - execCmd.Stdout = os.Stdout - execCmd.Stderr = os.Stderr - err := execCmd.Run() - - if err != nil { - exitWithCmdStatus(err) - } else { - os.Exit(1) - } -} - -func buildCmdLine(cmd *cobra.Command, args []string) string { - cmdLine := append([]string{cmd.CommandPath()}, args...) - cmd.Flags().VisitAll(func(flag *pflag.Flag) { - if flag.Changed { - cmdLine = append(cmdLine, fmt.Sprintf("--%s=%s", flag.Name, flag.Value.String())) - } - }) - return strings.Join(cmdLine, " ") -} - -func exitWithCmdStatus(err error) { - if exitError, ok := err.(*exec.ExitError); ok { - ws := exitError.Sys().(syscall.WaitStatus) - os.Exit(ws.ExitStatus()) - } -} + handleRootExecErr(rootCmd.ExecuteContext(ctx)) +} + +// func buildCmdLine(cmd *cobra.Command, args []string) string { +// cmdLine := append([]string{cmd.CommandPath()}, args...) +// cmd.Flags().VisitAll(func(flag *pflag.Flag) { +// if flag.Changed { +// cmdLine = append(cmdLine, fmt.Sprintf("--%s=%s", flag.Name, flag.Value.String())) +// } +// }) +// return strings.Join(cmdLine, " ") +// } + +// func exitWithCmdStatus(err error) { +// if exitError, ok := err.(*exec.ExitError); ok { +// ws := exitError.Sys().(syscall.WaitStatus) +// os.Exit(ws.ExitStatus()) +// } +// } type errorMessageRenderer interface { Render() diff --git a/cli/cmd/root.go b/cli/cmd/root.go index 72a1c3fc40..118a753e67 100644 --- a/cli/cmd/root.go +++ b/cli/cmd/root.go @@ -31,10 +31,16 @@ var rootCmd = &cobra.Command{ } func Execute() { - if err := rootCmd.Execute(); err != nil { - fmt.Fprintln(os.Stderr, err) - ExitCLI(1) + handleRootExecErr(rootCmd.Execute()) +} + +func handleRootExecErr(err error) { + if err == nil { + return } + + fmt.Fprintln(os.Stderr, err) + ExitCLI(1) } func ExitCLI(errorCode int) { diff --git a/cli/cmd/start_cmd.go b/cli/cmd/start_cmd.go index 00d2d912a1..955ff48f07 100644 --- a/cli/cmd/start_cmd.go +++ b/cli/cmd/start_cmd.go @@ -4,14 +4,16 @@ import ( "context" "os" + "github.com/davecgh/go-spew/spew" agentConfig "github.com/kubeshop/tracetest/agent/config" "github.com/kubeshop/tracetest/agent/runner" "github.com/kubeshop/tracetest/agent/ui" + "github.com/kubeshop/tracetest/cli/config" "github.com/spf13/cobra" ) var ( - agentRunner = runner.NewRunner(configurator, resources, ui.DefaultUI) + agentRunner = runner.NewRunner(configurator.WithErrorHandler(handleError), resources, ui.DefaultUI) defaultToken = os.Getenv("TRACETEST_TOKEN") defaultEndpoint = os.Getenv("TRACETEST_SERVER_URL") defaultAPIKey = os.Getenv("TRACETEST_API_KEY") @@ -35,6 +37,17 @@ var startCmd = &cobra.Command{ LogLevel: saveParams.logLevel, } + // override organization and environment id from context. + // this happens when auto rerunning the cmd after relogin + if orgID := config.ContextGetOrganizationID(ctx); orgID != "" { + flags.OrganizationID = orgID + } + if envID := config.ContextGetEnvironmentID(ctx); envID != "" { + flags.EnvironmentID = envID + } + + spew.Dump(flags) + cfg, err := agentConfig.LoadConfig() if err != nil { return "", err diff --git a/cli/config/config.go b/cli/config/config.go index 86f1a77343..d95d5fe146 100644 --- a/cli/config/config.go +++ b/cli/config/config.go @@ -1,6 +1,7 @@ package config import ( + "context" "encoding/json" "fmt" "os" @@ -143,15 +144,45 @@ func ParseServerURL(serverURL string) (scheme, endpoint, serverPath string, err return url.Scheme, url.Host, url.Path, nil } -func Save(config Config) error { +type orgIDKeyType struct{} +type envIDKeyType struct{} + +var orgIDKey = orgIDKeyType{} +var envIDKey = envIDKeyType{} + +func ContextWithOrganizationID(ctx context.Context, orgID string) context.Context { + return context.WithValue(ctx, orgIDKey, orgID) +} + +func ContextWithEnvironmentID(ctx context.Context, envID string) context.Context { + return context.WithValue(ctx, envIDKey, envID) +} + +func ContextGetOrganizationID(ctx context.Context) string { + v := ctx.Value(orgIDKey) + if v == nil { + return "" + } + return v.(string) +} + +func ContextGetEnvironmentID(ctx context.Context) string { + v := ctx.Value(envIDKey) + if v == nil { + return "" + } + return v.(string) +} + +func Save(ctx context.Context, config Config) (context.Context, error) { configPath, err := GetConfigurationPath() if err != nil { - return fmt.Errorf("could not get configuration path: %w", err) + return ctx, fmt.Errorf("could not get configuration path: %w", err) } configYml, err := yaml.Marshal(config) if err != nil { - return fmt.Errorf("could not marshal configuration into yml: %w", err) + return ctx, fmt.Errorf("could not marshal configuration into yml: %w", err) } if _, err := os.Stat(configPath); os.IsNotExist(err) { @@ -159,10 +190,13 @@ func Save(config Config) error { } err = os.WriteFile(configPath, configYml, 0755) if err != nil { - return fmt.Errorf("could not write file: %w", err) + return ctx, fmt.Errorf("could not write file: %w", err) } - return nil + ctx = ContextWithOrganizationID(ctx, config.OrganizationID) + ctx = ContextWithEnvironmentID(ctx, config.EnvironmentID) + + return ctx, nil } func GetConfigurationPath() (string, error) { diff --git a/cli/config/configurator.go b/cli/config/configurator.go index 3656b04a66..a74f73719b 100644 --- a/cli/config/configurator.go +++ b/cli/config/configurator.go @@ -20,6 +20,7 @@ type Configurator struct { resources *resourcemanager.Registry ui cliUI.UI onFinish onFinishFn + errorHandlerFn errorHandlerFn flags agentConfig.Flags finalServerURL string } @@ -34,6 +35,9 @@ func NewConfigurator(resources *resourcemanager.Registry) Configurator { ui.Success("Successfully configured Tracetest CLI") ui.Finish() }, + errorHandlerFn: func(ctx context.Context, err error) { + ui.Exit(err.Error()) + }, flags: agentConfig.Flags{}, } } @@ -43,6 +47,13 @@ func (c Configurator) WithOnFinish(onFinish onFinishFn) Configurator { return c } +type errorHandlerFn func(ctx context.Context, err error) + +func (c Configurator) WithErrorHandler(fn errorHandlerFn) Configurator { + c.errorHandlerFn = fn + return c +} + func (c Configurator) Start(ctx context.Context, prev *Config, flags agentConfig.Flags) error { c.flags = flags serverURL, err := c.getServerURL(prev, flags) @@ -67,7 +78,7 @@ func (c Configurator) Start(ctx context.Context, prev *Config, flags agentConfig } if flags.CI { - err = Save(cfg) + _, err = Save(ctx, cfg) if err != nil { return err } @@ -143,7 +154,7 @@ func (c Configurator) populateConfigWithVersionInfo(ctx context.Context, cfg Con serverType := version.GetType() if serverType == "oss" { - err := Save(cfg) + _, err = Save(ctx, cfg) if err != nil { return Config{}, fmt.Errorf("could not save configuration: %w", err), false } @@ -249,7 +260,7 @@ func (c Configurator) onOAuthSuccess(ctx context.Context, cfg Config) func(token } func (c Configurator) onOAuthFailure(err error) { - c.ui.Exit(err.Error()) + c.errorHandlerFn(context.Background(), err) } func (c Configurator) ShowOrganizationSelector(ctx context.Context, cfg Config, flags agentConfig.Flags) { @@ -257,7 +268,7 @@ func (c Configurator) ShowOrganizationSelector(ctx context.Context, cfg Config, if cfg.OrganizationID == "" && flags.AgentApiKey == "" { orgID, err := c.organizationSelector(ctx, cfg) if err != nil { - c.ui.Exit(err.Error()) + c.errorHandlerFn(ctx, err) return } @@ -268,16 +279,16 @@ func (c Configurator) ShowOrganizationSelector(ctx context.Context, cfg Config, if cfg.EnvironmentID == "" && flags.AgentApiKey == "" { envID, err := c.environmentSelector(ctx, cfg) if err != nil { - c.ui.Exit(err.Error()) + c.errorHandlerFn(ctx, err) return } cfg.EnvironmentID = envID } - err := Save(cfg) + ctx, err := Save(ctx, cfg) if err != nil { - c.ui.Exit(err.Error()) + c.errorHandlerFn(ctx, err) return } From af11c4334deb32657c605f508a25e7bb89c031fe Mon Sep 17 00:00:00 2001 From: Sebastian Choren Date: Wed, 14 Feb 2024 21:49:26 -0300 Subject: [PATCH 4/6] cleanup --- cli/cmd/middleware.go | 40 +++------------------------------------- 1 file changed, 3 insertions(+), 37 deletions(-) diff --git a/cli/cmd/middleware.go b/cli/cmd/middleware.go index 08e66159a7..d738ecd357 100644 --- a/cli/cmd/middleware.go +++ b/cli/cmd/middleware.go @@ -17,27 +17,9 @@ type RunFn func(ctx context.Context, cmd *cobra.Command, args []string) (string, type CobraRunFn func(cmd *cobra.Command, args []string) type MiddlewareWrapper func(RunFn) RunFn -// type keyCmd struct{} - -// var cmdKey keyCmd - -// func ContextWithCmd(cmd *cobra.Command, args []string) context.Context { -// return context.WithValue( -// cmd.Context(), -// cmdKey, -// buildCmdLine(cmd, args), -// ) -// } - -// func ContextGetCmd(ctx context.Context) string { -// v := ctx.Value(cmdKey) -// if v == nil { -// return "" -// } -// return v.(string) -// } - func rootCtx(cmd *cobra.Command) context.Context { + // cobra does not correctly progpagate rootcmd context to sub commands, + // so we need to manually traverse the command tree to find the root context if cmd == nil { return nil } @@ -61,6 +43,7 @@ func rootCtx(cmd *cobra.Command) context.Context { func WithResultHandler(runFn RunFn) CobraRunFn { return func(cmd *cobra.Command, args []string) { + // we need the root cmd context in case of an error caused rerun ctx := rootCtx(cmd) res, err := runFn(ctx, cmd, args) @@ -98,23 +81,6 @@ func retryCommand(ctx context.Context) { handleRootExecErr(rootCmd.ExecuteContext(ctx)) } -// func buildCmdLine(cmd *cobra.Command, args []string) string { -// cmdLine := append([]string{cmd.CommandPath()}, args...) -// cmd.Flags().VisitAll(func(flag *pflag.Flag) { -// if flag.Changed { -// cmdLine = append(cmdLine, fmt.Sprintf("--%s=%s", flag.Name, flag.Value.String())) -// } -// }) -// return strings.Join(cmdLine, " ") -// } - -// func exitWithCmdStatus(err error) { -// if exitError, ok := err.(*exec.ExitError); ok { -// ws := exitError.Sys().(syscall.WaitStatus) -// os.Exit(ws.ExitStatus()) -// } -// } - type errorMessageRenderer interface { Render() } From 1aaef288a76573876c89ec3afd2475598ad3e1e9 Mon Sep 17 00:00:00 2001 From: Sebastian Choren Date: Thu, 15 Feb 2024 19:41:56 -0300 Subject: [PATCH 5/6] fix exit --- cli/cmd/root.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/cmd/root.go b/cli/cmd/root.go index 118a753e67..b73ad1d65f 100644 --- a/cli/cmd/root.go +++ b/cli/cmd/root.go @@ -36,7 +36,7 @@ func Execute() { func handleRootExecErr(err error) { if err == nil { - return + ExitCLI(0) } fmt.Fprintln(os.Stderr, err) From b47522767cb7e297e64111cfbdd2750abd64731a Mon Sep 17 00:00:00 2001 From: Sebastian Choren Date: Thu, 15 Feb 2024 19:44:00 -0300 Subject: [PATCH 6/6] update deps --- go.mod | 4 ++-- go.sum | 7 ------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index e481c05224..7ca83b22e2 100644 --- a/go.mod +++ b/go.mod @@ -25,6 +25,7 @@ require ( github.com/fluidtruck/deepcopy v1.0.0 github.com/fsnotify/fsnotify v1.6.0 github.com/fullstorydev/grpcurl v1.8.6 + github.com/gdamore/tcell/v2 v2.7.0 github.com/goccy/go-yaml v1.11.0 github.com/gogo/protobuf v1.3.2 github.com/golang-jwt/jwt v3.2.1+incompatible @@ -49,6 +50,7 @@ require ( github.com/pkg/errors v0.9.1 github.com/prometheus/prometheus v1.8.2-0.20211217191541-41f1a8125e66 github.com/pterm/pterm v0.12.69 + github.com/rivo/tview v0.0.0-20240122063236-8526c9fe1b54 github.com/segmentio/analytics-go/v3 v3.2.1 github.com/spf13/cobra v1.7.0 github.com/spf13/pflag v1.0.5 @@ -113,7 +115,6 @@ require ( github.com/fatih/color v1.13.0 // indirect github.com/felixge/httpsnoop v1.0.2 // indirect github.com/gdamore/encoding v1.0.0 // indirect - github.com/gdamore/tcell/v2 v2.7.0 // indirect github.com/go-logr/logr v1.2.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-redis/redis/v7 v7.4.1 // indirect @@ -162,7 +163,6 @@ require ( github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect - github.com/rivo/tview v0.0.0-20240122063236-8526c9fe1b54 // indirect github.com/rivo/uniseg v0.4.4 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/segmentio/backo-go v1.0.0 // indirect diff --git a/go.sum b/go.sum index 890bb6d9ef..f4e712bba7 100644 --- a/go.sum +++ b/go.sum @@ -633,8 +633,6 @@ github.com/gabriel-vasile/mimetype v1.4.0/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmx github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= -github.com/gdamore/tcell/v2 v2.6.1-0.20231203215052-2917c3801e73 h1:SeDV6ZUSVlTAUUPdMzPXgMyj96z+whQJRRUff8dIeic= -github.com/gdamore/tcell/v2 v2.6.1-0.20231203215052-2917c3801e73/go.mod h1:pwzJMyH4Hd0AZMJkWQ+/g01dDvYWEvmJuaiRU71Xl8k= github.com/gdamore/tcell/v2 v2.7.0 h1:I5LiGTQuwrysAt1KS9wg1yFfOI3arI3ucFrxtd/xqaA= github.com/gdamore/tcell/v2 v2.7.0/go.mod h1:hl/KtAANGBecfIPxk+FzKvThTqI84oplgbPEmVX60b8= github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= @@ -1345,7 +1343,6 @@ github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= @@ -2408,8 +2405,6 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= @@ -2419,7 +2414,6 @@ golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuX golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo= golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -2436,7 +2430,6 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=