From 3efba04e31f8f0091da8101ada47e1708ac57d67 Mon Sep 17 00:00:00 2001 From: Yechezkel Rabinovich Date: Sun, 12 Apr 2026 21:27:09 +0300 Subject: [PATCH 1/5] feat: add --backend flag to skip interactive backend selection Adds a global --backend persistent flag that allows specifying the backend name directly, skipping the interactive prompt in selectBackendName(). Applies to all commands that select a backend: get-ingestion-key, get-datasources-api-key, and deploy. Validates the provided name against fetched backends and errors with available options if invalid. Closes SEN-854 --- cmd/login.go | 20 ++++++++++++++------ cmd/root.go | 4 ++++ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/cmd/login.go b/cmd/login.go index a1b57df..464d299 100644 --- a/cmd/login.go +++ b/cmd/login.go @@ -2,6 +2,7 @@ package cmd import ( "context" + "fmt" "github.com/getsentry/sentry-go" "github.com/pkg/errors" @@ -22,7 +23,8 @@ const ( ) var ( - ErrNoActiveBackends = errors.New("no active backends") + ErrNoActiveBackends = errors.New("no active backends") + ErrInvalidBackendName = errors.New("specified backend not found") ) func init() { @@ -187,13 +189,19 @@ func selectBackendName(tenantUUID string, deployFlow bool) (string, bool, error) return "", false, nil } - backendId := "" - switch len(backendsList) { - case 0: + if len(backendsList) == 0 { return "", false, ErrNoActiveBackends - case 1: + } + + backendId := "" + if flagValue := viper.GetString(BACKEND_FLAG); flagValue != "" { + if _, ok := backendNames[flagValue]; !ok { + return "", false, fmt.Errorf("%w: %q (available: %v)", ErrInvalidBackendName, flagValue, maps.Keys(backendNames)) + } + backendId = flagValue + } else if len(backendsList) == 1 { backendId = backendsList[0].Name - default: + } else { backendId = ui.GlobalWriter.SelectPrompt("Select backend:", maps.Keys(backendNames)) } diff --git a/cmd/root.go b/cmd/root.go index 36bd409..0923f80 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -33,6 +33,7 @@ const ( KUBECONTEXT_FLAG = "kube-context" HELM_RELEASE_FLAG = "release-name" CLUSTER_NAME_FLAG = "cluster-name" + BACKEND_FLAG = "backend" SKIP_CLI_UPDATE_FLAG = "skip-cli-update" INSTALLATION_ID_FLAG = "installation-id" INVALID_TOKEN_MESSAGE = "Issue with authentication - try again to copy command line and rerun" @@ -62,6 +63,9 @@ func init() { RootCmd.PersistentFlags().Bool(SKIP_CLI_UPDATE_FLAG, false, "disable automatic cli update check") viper.BindPFlag(SKIP_CLI_UPDATE_FLAG, RootCmd.PersistentFlags().Lookup(SKIP_CLI_UPDATE_FLAG)) + RootCmd.PersistentFlags().String(BACKEND_FLAG, "", "backend name to use (skips interactive selection)") + viper.BindPFlag(BACKEND_FLAG, RootCmd.PersistentFlags().Lookup(BACKEND_FLAG)) + RootCmd.PersistentFlags().String(CLUSTER_NAME_FLAG, "", "cluster name") viper.BindPFlag(CLUSTER_NAME_FLAG, RootCmd.PersistentFlags().Lookup(CLUSTER_NAME_FLAG)) From 0d28eb733e1eec480fe9f9aee3f903c148dde72c Mon Sep 17 00:00:00 2001 From: Yechezkel Rabinovich Date: Sun, 12 Apr 2026 21:29:18 +0300 Subject: [PATCH 2/5] test: add backend flag test suite matching tenant-uuid pattern --- cmd/backend_flag_test.go | 107 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 cmd/backend_flag_test.go diff --git a/cmd/backend_flag_test.go b/cmd/backend_flag_test.go new file mode 100644 index 0000000..d8bd754 --- /dev/null +++ b/cmd/backend_flag_test.go @@ -0,0 +1,107 @@ +package cmd + +import ( + "testing" + + "github.com/spf13/viper" + "github.com/stretchr/testify/suite" +) + +type BackendFlagTestSuite struct { + suite.Suite + originalBackend string +} + +func (suite *BackendFlagTestSuite) SetupTest() { + suite.originalBackend = viper.GetString(BACKEND_FLAG) +} + +func (suite *BackendFlagTestSuite) TearDownTest() { + viper.Set(BACKEND_FLAG, suite.originalBackend) +} + +func TestBackendFlagTestSuite(t *testing.T) { + suite.Run(t, &BackendFlagTestSuite{}) +} + +func (suite *BackendFlagTestSuite) TestBackendFlagExists() { + flag := RootCmd.PersistentFlags().Lookup(BACKEND_FLAG) + suite.NotNil(flag, "backend flag should be registered") + suite.Equal("", flag.DefValue, "backend flag should have empty default value") + suite.Equal("backend name to use (skips interactive selection)", flag.Usage, "backend flag should have correct usage description") +} + +func (suite *BackendFlagTestSuite) TestBackendFlagConstant() { + suite.Equal("backend", BACKEND_FLAG) +} + +func (suite *BackendFlagTestSuite) TestViperBindingForBackend() { + testBackend := "gcp-us-east1" + viper.Set(BACKEND_FLAG, testBackend) + + result := viper.GetString(BACKEND_FLAG) + suite.Equal(testBackend, result) +} + +func (suite *BackendFlagTestSuite) TestEmptyBackendFlag() { + viper.Set(BACKEND_FLAG, "") + + result := viper.GetString(BACKEND_FLAG) + suite.Equal("", result) +} + +func (suite *BackendFlagTestSuite) TestBackendFlagWithValidName() { + validBackend := "gcp-us-east1" + viper.Set(BACKEND_FLAG, validBackend) + + result := viper.GetString(BACKEND_FLAG) + suite.Equal(validBackend, result) +} + +func (suite *BackendFlagTestSuite) TestCommandsHaveAccessToBackendFlag() { + commands := []struct { + name string + cmd interface{} + }{ + {"IngestionKeyCmd", IngestionKeyCmd}, + {"getDatasourcesAPIKeyCmd", getDatasourcesAPIKeyCmd}, + {"DeployCmd", DeployCmd}, + } + + for _, tc := range commands { + suite.Run(tc.name, func() { + flag := RootCmd.PersistentFlags().Lookup(BACKEND_FLAG) + suite.NotNil(flag, "Flag should be accessible to "+tc.name) + }) + } +} + +func (suite *BackendFlagTestSuite) TestBackendFlagPattern() { + suite.Run("flag not set - should be empty", func() { + viper.Set(BACKEND_FLAG, "") + backendName := viper.GetString(BACKEND_FLAG) + + if backendName == "" { + suite.Empty(backendName) + } else { + suite.Fail("Expected empty backend name when flag not set") + } + }) + + suite.Run("flag set - should use flag value", func() { + expectedBackend := "gcp-us-east1" + viper.Set(BACKEND_FLAG, expectedBackend) + backendName := viper.GetString(BACKEND_FLAG) + + if backendName == "" { + suite.Fail("Should not be empty when flag is set") + } else { + suite.Equal(expectedBackend, backendName) + } + }) +} + +func (suite *BackendFlagTestSuite) TestErrInvalidBackendName() { + suite.NotNil(ErrInvalidBackendName) + suite.Equal("specified backend not found", ErrInvalidBackendName.Error()) +} From 5a6e04568932e5f5880f6f4e96edc0f8afa098a4 Mon Sep 17 00:00:00 2001 From: Yechezkel Rabinovich Date: Sun, 12 Apr 2026 21:39:26 +0300 Subject: [PATCH 3/5] refactor: rename to --backend-name, skip validation, default incloud --- cmd/backend_flag_test.go | 37 ++++++++++++++++--------------------- cmd/login.go | 16 +++++++--------- cmd/root.go | 6 +++--- 3 files changed, 26 insertions(+), 33 deletions(-) diff --git a/cmd/backend_flag_test.go b/cmd/backend_flag_test.go index d8bd754..2147f8f 100644 --- a/cmd/backend_flag_test.go +++ b/cmd/backend_flag_test.go @@ -13,11 +13,11 @@ type BackendFlagTestSuite struct { } func (suite *BackendFlagTestSuite) SetupTest() { - suite.originalBackend = viper.GetString(BACKEND_FLAG) + suite.originalBackend = viper.GetString(BACKEND_NAME_FLAG) } func (suite *BackendFlagTestSuite) TearDownTest() { - viper.Set(BACKEND_FLAG, suite.originalBackend) + viper.Set(BACKEND_NAME_FLAG, suite.originalBackend) } func TestBackendFlagTestSuite(t *testing.T) { @@ -25,36 +25,36 @@ func TestBackendFlagTestSuite(t *testing.T) { } func (suite *BackendFlagTestSuite) TestBackendFlagExists() { - flag := RootCmd.PersistentFlags().Lookup(BACKEND_FLAG) + flag := RootCmd.PersistentFlags().Lookup(BACKEND_NAME_FLAG) suite.NotNil(flag, "backend flag should be registered") suite.Equal("", flag.DefValue, "backend flag should have empty default value") - suite.Equal("backend name to use (skips interactive selection)", flag.Usage, "backend flag should have correct usage description") + suite.Equal("backend name to use", flag.Usage, "backend flag should have correct usage description") } func (suite *BackendFlagTestSuite) TestBackendFlagConstant() { - suite.Equal("backend", BACKEND_FLAG) + suite.Equal("backend-name", BACKEND_NAME_FLAG) } func (suite *BackendFlagTestSuite) TestViperBindingForBackend() { testBackend := "gcp-us-east1" - viper.Set(BACKEND_FLAG, testBackend) + viper.Set(BACKEND_NAME_FLAG, testBackend) - result := viper.GetString(BACKEND_FLAG) + result := viper.GetString(BACKEND_NAME_FLAG) suite.Equal(testBackend, result) } func (suite *BackendFlagTestSuite) TestEmptyBackendFlag() { - viper.Set(BACKEND_FLAG, "") + viper.Set(BACKEND_NAME_FLAG, "") - result := viper.GetString(BACKEND_FLAG) + result := viper.GetString(BACKEND_NAME_FLAG) suite.Equal("", result) } func (suite *BackendFlagTestSuite) TestBackendFlagWithValidName() { validBackend := "gcp-us-east1" - viper.Set(BACKEND_FLAG, validBackend) + viper.Set(BACKEND_NAME_FLAG, validBackend) - result := viper.GetString(BACKEND_FLAG) + result := viper.GetString(BACKEND_NAME_FLAG) suite.Equal(validBackend, result) } @@ -70,7 +70,7 @@ func (suite *BackendFlagTestSuite) TestCommandsHaveAccessToBackendFlag() { for _, tc := range commands { suite.Run(tc.name, func() { - flag := RootCmd.PersistentFlags().Lookup(BACKEND_FLAG) + flag := RootCmd.PersistentFlags().Lookup(BACKEND_NAME_FLAG) suite.NotNil(flag, "Flag should be accessible to "+tc.name) }) } @@ -78,8 +78,8 @@ func (suite *BackendFlagTestSuite) TestCommandsHaveAccessToBackendFlag() { func (suite *BackendFlagTestSuite) TestBackendFlagPattern() { suite.Run("flag not set - should be empty", func() { - viper.Set(BACKEND_FLAG, "") - backendName := viper.GetString(BACKEND_FLAG) + viper.Set(BACKEND_NAME_FLAG, "") + backendName := viper.GetString(BACKEND_NAME_FLAG) if backendName == "" { suite.Empty(backendName) @@ -90,8 +90,8 @@ func (suite *BackendFlagTestSuite) TestBackendFlagPattern() { suite.Run("flag set - should use flag value", func() { expectedBackend := "gcp-us-east1" - viper.Set(BACKEND_FLAG, expectedBackend) - backendName := viper.GetString(BACKEND_FLAG) + viper.Set(BACKEND_NAME_FLAG, expectedBackend) + backendName := viper.GetString(BACKEND_NAME_FLAG) if backendName == "" { suite.Fail("Should not be empty when flag is set") @@ -100,8 +100,3 @@ func (suite *BackendFlagTestSuite) TestBackendFlagPattern() { } }) } - -func (suite *BackendFlagTestSuite) TestErrInvalidBackendName() { - suite.NotNil(ErrInvalidBackendName) - suite.Equal("specified backend not found", ErrInvalidBackendName.Error()) -} diff --git a/cmd/login.go b/cmd/login.go index 464d299..b74fbb3 100644 --- a/cmd/login.go +++ b/cmd/login.go @@ -2,7 +2,6 @@ package cmd import ( "context" - "fmt" "github.com/getsentry/sentry-go" "github.com/pkg/errors" @@ -23,8 +22,7 @@ const ( ) var ( - ErrNoActiveBackends = errors.New("no active backends") - ErrInvalidBackendName = errors.New("specified backend not found") + ErrNoActiveBackends = errors.New("no active backends") ) func init() { @@ -162,6 +160,11 @@ func fetchApiKey(tenantUUID string) (*auth.ApiKey, error) { } func selectBackendName(tenantUUID string, deployFlow bool) (string, bool, error) { + if flagValue := viper.GetString(BACKEND_NAME_FLAG); flagValue != "" { + // non-incloud architecture is deprecated, so explicitly provided backends are always incloud + return flagValue, true, nil + } + var err error var auth0Token *auth.Auth0Token if auth0Token, err = auth.LoadAuth0Token(); err != nil { @@ -194,12 +197,7 @@ func selectBackendName(tenantUUID string, deployFlow bool) (string, bool, error) } backendId := "" - if flagValue := viper.GetString(BACKEND_FLAG); flagValue != "" { - if _, ok := backendNames[flagValue]; !ok { - return "", false, fmt.Errorf("%w: %q (available: %v)", ErrInvalidBackendName, flagValue, maps.Keys(backendNames)) - } - backendId = flagValue - } else if len(backendsList) == 1 { + if len(backendsList) == 1 { backendId = backendsList[0].Name } else { backendId = ui.GlobalWriter.SelectPrompt("Select backend:", maps.Keys(backendNames)) diff --git a/cmd/root.go b/cmd/root.go index 0923f80..c9478be 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -33,7 +33,7 @@ const ( KUBECONTEXT_FLAG = "kube-context" HELM_RELEASE_FLAG = "release-name" CLUSTER_NAME_FLAG = "cluster-name" - BACKEND_FLAG = "backend" + BACKEND_NAME_FLAG = "backend-name" SKIP_CLI_UPDATE_FLAG = "skip-cli-update" INSTALLATION_ID_FLAG = "installation-id" INVALID_TOKEN_MESSAGE = "Issue with authentication - try again to copy command line and rerun" @@ -63,8 +63,8 @@ func init() { RootCmd.PersistentFlags().Bool(SKIP_CLI_UPDATE_FLAG, false, "disable automatic cli update check") viper.BindPFlag(SKIP_CLI_UPDATE_FLAG, RootCmd.PersistentFlags().Lookup(SKIP_CLI_UPDATE_FLAG)) - RootCmd.PersistentFlags().String(BACKEND_FLAG, "", "backend name to use (skips interactive selection)") - viper.BindPFlag(BACKEND_FLAG, RootCmd.PersistentFlags().Lookup(BACKEND_FLAG)) + RootCmd.PersistentFlags().String(BACKEND_NAME_FLAG, "", "backend name to use") + viper.BindPFlag(BACKEND_NAME_FLAG, RootCmd.PersistentFlags().Lookup(BACKEND_NAME_FLAG)) RootCmd.PersistentFlags().String(CLUSTER_NAME_FLAG, "", "cluster name") viper.BindPFlag(CLUSTER_NAME_FLAG, RootCmd.PersistentFlags().Lookup(CLUSTER_NAME_FLAG)) From d85cce120645071fd8c3f73ecdc4f2699c9e511b Mon Sep 17 00:00:00 2001 From: Yechezkel Rabinovich Date: Sun, 12 Apr 2026 21:52:14 +0300 Subject: [PATCH 4/5] fix: abort on empty backend selection --- cmd/login.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmd/login.go b/cmd/login.go index b74fbb3..b907aad 100644 --- a/cmd/login.go +++ b/cmd/login.go @@ -201,6 +201,9 @@ func selectBackendName(tenantUUID string, deployFlow bool) (string, bool, error) backendId = backendsList[0].Name } else { backendId = ui.GlobalWriter.SelectPrompt("Select backend:", maps.Keys(backendNames)) + if backendId == "" { + return "", false, ErrExecutionAborted + } } return backendId, backendNames[backendId], nil From 414469bf0438171cdaf6efc259fd81f50126ff84 Mon Sep 17 00:00:00 2001 From: Yechezkel Rabinovich Date: Sun, 12 Apr 2026 21:54:28 +0300 Subject: [PATCH 5/5] refactor: simplify selectBackendName to early returns --- cmd/login.go | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/cmd/login.go b/cmd/login.go index b907aad..1447fec 100644 --- a/cmd/login.go +++ b/cmd/login.go @@ -196,14 +196,13 @@ func selectBackendName(tenantUUID string, deployFlow bool) (string, bool, error) return "", false, ErrNoActiveBackends } - backendId := "" if len(backendsList) == 1 { - backendId = backendsList[0].Name - } else { - backendId = ui.GlobalWriter.SelectPrompt("Select backend:", maps.Keys(backendNames)) - if backendId == "" { - return "", false, ErrExecutionAborted - } + return backendsList[0].Name, backendNames[backendsList[0].Name], nil + } + + backendId := ui.GlobalWriter.SelectPrompt("Select backend:", maps.Keys(backendNames)) + if backendId == "" { + return "", false, ErrExecutionAborted } return backendId, backendNames[backendId], nil