Skip to content

Commit

Permalink
feat: validate cloud context on root cmd (#4033) (#4053)
Browse files Browse the repository at this point in the history
* feat: validate cloud context on root cmd

* chore: organize

* chore: organize

* chore: organize

* feat: added org-env names to context on set context

* feat: added org-env names fallback

* fix: golangci lint

* fix: codeql alert

* fix: utf character fails build

Co-authored-by: Jacek Wysocki <jacek.wysocki@gmail.com>
  • Loading branch information
povilasv and exu committed Jun 16, 2023
1 parent 65b7eaa commit 93c19e4
Show file tree
Hide file tree
Showing 21 changed files with 221 additions and 25 deletions.
5 changes: 5 additions & 0 deletions cmd/kubectl-testkube/commands/abort.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/kubeshop/testkube/cmd/kubectl-testkube/commands/common/validator"
"github.com/kubeshop/testkube/cmd/kubectl-testkube/commands/tests"
"github.com/kubeshop/testkube/cmd/kubectl-testkube/commands/testsuites"
"github.com/kubeshop/testkube/cmd/kubectl-testkube/config"
"github.com/kubeshop/testkube/pkg/ui"
)

Expand All @@ -20,6 +21,10 @@ func NewAbortCmd() *cobra.Command {
ui.PrintOnError("Displaying help", err)
},
PersistentPreRun: func(cmd *cobra.Command, args []string) {
cfg, err := config.Load()
ui.ExitOnError("loading config", err)
common.UiContextHeader(cmd, cfg)

validator.PersistentPreRunVersionCheck(cmd, common.Version)
}}

Expand Down
2 changes: 0 additions & 2 deletions cmd/kubectl-testkube/commands/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ func NewAgentCmd() *cobra.Command {
ui.Info("Build date", common.Date)

},
PersistentPreRun: func(cmd *cobra.Command, args []string) {
},
}

cmd.AddCommand(agent.NewAgentDebugCmd())
Expand Down
4 changes: 2 additions & 2 deletions cmd/kubectl-testkube/commands/cloud/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ func NewLoginCmd() *cobra.Command {
ui.ExitOnError("loading config file", err)

cfg.ContextType = config.ContextTypeCloud
cfg.CloudContext.Organization = orgID
cfg.CloudContext.Environment = envID
cfg.CloudContext.OrganizationId = orgID
cfg.CloudContext.EnvironmentId = envID

uris := opts.CloudUris
cfg.CloudContext.ApiUri = uris.Api
Expand Down
6 changes: 3 additions & 3 deletions cmd/kubectl-testkube/commands/common/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,10 @@ func GetClient(cmd *cobra.Command) (client.Client, string, error) {
}
case config.ContextTypeCloud:
clientType = string(client.ClientCloud)
options.CloudApiPathPrefix = fmt.Sprintf("/organizations/%s/environments/%s/agent", cfg.CloudContext.Organization, cfg.CloudContext.Environment)
options.CloudApiPathPrefix = fmt.Sprintf("/organizations/%s/environments/%s/agent", cfg.CloudContext.OrganizationId, cfg.CloudContext.EnvironmentId)
options.CloudApiKey = cfg.CloudContext.ApiKey
options.CloudEnvironment = cfg.CloudContext.Environment
options.CloudOrganization = cfg.CloudContext.Organization
options.CloudEnvironment = cfg.CloudContext.EnvironmentId
options.CloudOrganization = cfg.CloudContext.OrganizationId
options.ApiUri = cfg.CloudContext.ApiUri
}

Expand Down
60 changes: 58 additions & 2 deletions cmd/kubectl-testkube/commands/common/cloudcontext.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ package common

import (
"fmt"
"regexp"
"strings"

"github.com/spf13/cobra"

"github.com/kubeshop/testkube/cmd/kubectl-testkube/config"
"github.com/kubeshop/testkube/pkg/ui"
Expand All @@ -14,8 +18,8 @@ func UiPrintContext(cfg config.Data) {

if cfg.ContextType == config.ContextTypeCloud {
contextData := map[string]string{
"Organization ID": cfg.CloudContext.Organization,
"Environment ID ": cfg.CloudContext.Environment,
"Organization": cfg.CloudContext.OrganizationName + ui.DarkGray(" ("+cfg.CloudContext.OrganizationId+")"),
"Environment": cfg.CloudContext.EnvironmentName + ui.DarkGray(" ("+cfg.CloudContext.EnvironmentId+")"),
"API Key ": text.Obfuscate(cfg.CloudContext.ApiKey),
"API URI ": cfg.CloudContext.ApiUri,
"Namespace ": cfg.Namespace,
Expand All @@ -35,3 +39,55 @@ func UiPrintContext(cfg config.Data) {
})
}
}

func UiCloudContextValidationError(err error) {
ui.NL()
ui.Errf("Validating cloud context failed: %s", err.Error())
ui.NL()
ui.Info("Please set valid cloud context using `testkube set context` with valid values")
ui.NL()
ui.ShellCommand(" testkube set context -c cloud -e tkcenv_XXX -o tkcorg_XXX -k tkcapi_XXX")
ui.NL()
}

func UiContextHeader(cmd *cobra.Command, cfg config.Data) {
// only show header when output is pretty
if cmd.Flag("output") != nil && cmd.Flag("output").Value.String() != "pretty" {
return
}

header := "\n"
separator := " "

orgName := cfg.CloudContext.OrganizationName
if orgName == "" {
orgName = cfg.CloudContext.OrganizationId
}
envName := cfg.CloudContext.EnvironmentName
if envName == "" {
envName = cfg.CloudContext.EnvironmentId
}

if cfg.ContextType == config.ContextTypeCloud {
header += ui.DarkGray("Context: ") + ui.White(cfg.ContextType) + ui.DarkGray(" ("+Version+")") + separator
header += ui.DarkGray("Namespace: ") + ui.White(cfg.Namespace) + separator
header += ui.DarkGray("Org: ") + ui.White(orgName) + separator
header += ui.DarkGray("Env: ") + ui.White(envName)
} else {
header += ui.DarkGray("Context: ") + ui.White(cfg.ContextType) + ui.DarkGray(" ("+Version+")") + separator
header += ui.DarkGray("Namespace: ") + ui.White(cfg.Namespace)
}

fmt.Println(header)
fmt.Println(strings.Repeat("-", calculateStringSize(header)))
}

// calculateStringSize calculates the length of a string, excluding shell color codes.
func calculateStringSize(s string) int {
// Regular expression to match ANSI escape codes.
re := regexp.MustCompile(`\x1b[^m]*m`)
// Remove the escape codes from the string.
s = re.ReplaceAllString(s, "")
// Return the length of the string.
return len(s) - 1
}
4 changes: 2 additions & 2 deletions cmd/kubectl-testkube/commands/common/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,8 @@ func PopulateLoginDataToContext(orgID, envID, token string, options HelmOptions,
cfg.CloudContext.ApiUri = options.CloudUris.Api
}
cfg.ContextType = config.ContextTypeCloud
cfg.CloudContext.Organization = orgID
cfg.CloudContext.Environment = envID
cfg.CloudContext.OrganizationId = orgID
cfg.CloudContext.EnvironmentId = envID
cfg.CloudContext.ApiKey = token

return config.Save(cfg)
Expand Down
31 changes: 31 additions & 0 deletions cmd/kubectl-testkube/commands/common/validator/cloudcontext.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package validator

import (
"errors"

"github.com/kubeshop/testkube/cmd/kubectl-testkube/config"
)

func ValidateCloudContext(cfg config.Data) error {
if cfg.ContextType != config.ContextTypeCloud {
return nil
}

if cfg.CloudContext.ApiUri == "" {
return errors.New("please provide Testkube Cloud URI")
}

if cfg.CloudContext.ApiKey == "" {
return errors.New("please provide Testkube Cloud API token")
}

if cfg.CloudContext.EnvironmentId == "" {
return errors.New("please provide Environment")
}

if cfg.CloudContext.OrganizationId == "" {
return errors.New("please provide Organization")
}

return nil
}
25 changes: 22 additions & 3 deletions cmd/kubectl-testkube/commands/context/set.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import (
"github.com/spf13/cobra"

"github.com/kubeshop/testkube/cmd/kubectl-testkube/commands/common"
"github.com/kubeshop/testkube/cmd/kubectl-testkube/commands/common/validator"
"github.com/kubeshop/testkube/cmd/kubectl-testkube/config"
cloudclient "github.com/kubeshop/testkube/pkg/cloud/client"
"github.com/kubeshop/testkube/pkg/ui"
)

Expand Down Expand Up @@ -38,14 +40,14 @@ func NewSetContextCmd() *cobra.Command {
}

if org != "" {
cfg.CloudContext.Organization = org
cfg.CloudContext.OrganizationId = org
// reset env when the org is changed
if env == "" {
cfg.CloudContext.Environment = ""
cfg.CloudContext.EnvironmentId = ""
}
}
if env != "" {
cfg.CloudContext.Environment = env
cfg.CloudContext.EnvironmentId = env
}
if apiKey != "" {
cfg.CloudContext.ApiKey = apiKey
Expand All @@ -57,6 +59,18 @@ func NewSetContextCmd() *cobra.Command {
cfg.CloudContext.UiUri = uris.Ui
cfg.CloudContext.AgentUri = uris.Agent

orgClient := cloudclient.NewOrganizationsClient(rootDomain, cfg.CloudContext.ApiKey)
ui.ExitOnError("getting client", err)
org, err := orgClient.Get(cfg.CloudContext.OrganizationId)
ui.ExitOnError("getting organization", err)

envsClient := cloudclient.NewEnvironmentsClient(rootDomain, cfg.CloudContext.ApiKey, cfg.CloudContext.OrganizationId)
env, err := envsClient.Get(cfg.CloudContext.EnvironmentId)
ui.ExitOnError("getting environment", err)

cfg.CloudContext.OrganizationName = org.Name
cfg.CloudContext.EnvironmentName = env.Name

case config.ContextTypeKubeconfig:
// kubeconfig special use cases

Expand All @@ -71,9 +85,14 @@ func NewSetContextCmd() *cobra.Command {
err = config.Save(cfg)
ui.ExitOnError("saving config file", err)

if err = validator.ValidateCloudContext(cfg); err != nil {
common.UiCloudContextValidationError(err)
}

ui.Success("Your config was updated with new values")
ui.NL()
common.UiPrintContext(cfg)

},
}

Expand Down
5 changes: 5 additions & 0 deletions cmd/kubectl-testkube/commands/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/kubeshop/testkube/cmd/kubectl-testkube/commands/testsources"
"github.com/kubeshop/testkube/cmd/kubectl-testkube/commands/testsuites"
"github.com/kubeshop/testkube/cmd/kubectl-testkube/commands/webhooks"
"github.com/kubeshop/testkube/cmd/kubectl-testkube/config"
"github.com/kubeshop/testkube/pkg/ui"
)

Expand All @@ -26,6 +27,10 @@ func NewCreateCmd() *cobra.Command {
ui.PrintOnError("Displaying help", err)
},
PersistentPreRun: func(cmd *cobra.Command, args []string) {
cfg, err := config.Load()
ui.ExitOnError("loading config", err)
common.UiContextHeader(cmd, cfg)

if !crdOnly {
validator.PersistentPreRunVersionCheck(cmd, common.Version)
}
Expand Down
5 changes: 5 additions & 0 deletions cmd/kubectl-testkube/commands/debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"github.com/kubeshop/testkube/cmd/kubectl-testkube/commands/common"
"github.com/kubeshop/testkube/cmd/kubectl-testkube/commands/common/validator"
"github.com/kubeshop/testkube/cmd/kubectl-testkube/commands/debug"
"github.com/kubeshop/testkube/cmd/kubectl-testkube/config"
"github.com/kubeshop/testkube/pkg/ui"
)

Expand All @@ -19,6 +20,10 @@ func NewDebugCmd() *cobra.Command {
ui.PrintOnError("Displaying help", err)
},
PersistentPreRun: func(cmd *cobra.Command, args []string) {
cfg, err := config.Load()
ui.ExitOnError("loading config", err)
common.UiContextHeader(cmd, cfg)

validator.PersistentPreRunVersionCheck(cmd, common.Version)
}}

Expand Down
5 changes: 5 additions & 0 deletions cmd/kubectl-testkube/commands/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/kubeshop/testkube/cmd/kubectl-testkube/commands/testsources"
"github.com/kubeshop/testkube/cmd/kubectl-testkube/commands/testsuites"
"github.com/kubeshop/testkube/cmd/kubectl-testkube/commands/webhooks"
"github.com/kubeshop/testkube/cmd/kubectl-testkube/config"
"github.com/kubeshop/testkube/pkg/ui"
)

Expand All @@ -24,6 +25,10 @@ func NewDeleteCmd() *cobra.Command {
ui.PrintOnError("Displaying help", err)
},
PersistentPreRun: func(cmd *cobra.Command, args []string) {
cfg, err := config.Load()
ui.ExitOnError("loading config", err)
common.UiContextHeader(cmd, cfg)

validator.PersistentPreRunVersionCheck(cmd, common.Version)
}}

Expand Down
5 changes: 5 additions & 0 deletions cmd/kubectl-testkube/commands/download.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/kubeshop/testkube/cmd/kubectl-testkube/commands/common"
"github.com/kubeshop/testkube/cmd/kubectl-testkube/commands/common/validator"
"github.com/kubeshop/testkube/cmd/kubectl-testkube/commands/tests"
"github.com/kubeshop/testkube/cmd/kubectl-testkube/config"
"github.com/kubeshop/testkube/pkg/ui"
)

Expand All @@ -32,6 +33,10 @@ func NewDownloadCmd() *cobra.Command {
ui.PrintOnError("Displaying help", err)
},
PersistentPreRun: func(cmd *cobra.Command, args []string) {
cfg, err := config.Load()
ui.ExitOnError("loading config", err)
common.UiContextHeader(cmd, cfg)

validator.PersistentPreRunVersionCheck(cmd, common.Version)
}}

Expand Down
6 changes: 6 additions & 0 deletions cmd/kubectl-testkube/commands/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/kubeshop/testkube/cmd/kubectl-testkube/commands/testsources"
"github.com/kubeshop/testkube/cmd/kubectl-testkube/commands/testsuites"
"github.com/kubeshop/testkube/cmd/kubectl-testkube/commands/webhooks"
"github.com/kubeshop/testkube/cmd/kubectl-testkube/config"
"github.com/kubeshop/testkube/pkg/ui"
)

Expand All @@ -27,7 +28,12 @@ func NewGetCmd() *cobra.Command {
ui.PrintOnError("Displaying help", err)
},
PersistentPreRun: func(cmd *cobra.Command, args []string) {
cfg, err := config.Load()
ui.ExitOnError("loading config", err)
common.UiContextHeader(cmd, cfg)

validator.PersistentPreRunVersionCheck(cmd, common.Version)

}}

cmd.AddCommand(tests.NewGetTestsCmd())
Expand Down
16 changes: 16 additions & 0 deletions cmd/kubectl-testkube/commands/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"github.com/kubeshop/testkube/cmd/kubectl-testkube/commands/cloud"
"github.com/kubeshop/testkube/cmd/kubectl-testkube/commands/common"
"github.com/kubeshop/testkube/cmd/kubectl-testkube/commands/common/validator"
"github.com/kubeshop/testkube/cmd/kubectl-testkube/config"
"github.com/kubeshop/testkube/pkg/telemetry"
"github.com/kubeshop/testkube/pkg/ui"
Expand Down Expand Up @@ -62,6 +63,20 @@ var RootCmd = &cobra.Command{
Short: "Testkube entrypoint for kubectl plugin",
PersistentPreRun: func(cmd *cobra.Command, args []string) {
ui.SetVerbose(verbose)

cfg, err := config.Load()
ui.ExitOnError("loading config", err)

common.UiContextHeader(cmd, cfg)

// don't validate context before set
if cmd.Name() == "context" {
return
}

if err = validator.ValidateCloudContext(cfg); err != nil {
common.UiCloudContextValidationError(err)
}
},

PersistentPostRun: func(cmd *cobra.Command, args []string) {
Expand Down Expand Up @@ -148,6 +163,7 @@ func Execute() {
RootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "", false, "show additional debug messages")
RootCmd.PersistentFlags().StringVarP(&apiURI, "api-uri", "a", apiURI, "api uri, default value read from config if set")
RootCmd.PersistentFlags().BoolVarP(&oauthEnabled, "oauth-enabled", "", cfg.OAuth2Data.Enabled, "enable oauth")

if err := RootCmd.Execute(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
Expand Down
5 changes: 5 additions & 0 deletions cmd/kubectl-testkube/commands/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/kubeshop/testkube/cmd/kubectl-testkube/commands/common/validator"
"github.com/kubeshop/testkube/cmd/kubectl-testkube/commands/tests"
"github.com/kubeshop/testkube/cmd/kubectl-testkube/commands/testsuites"
"github.com/kubeshop/testkube/cmd/kubectl-testkube/config"
"github.com/kubeshop/testkube/pkg/ui"
)

Expand All @@ -21,6 +22,10 @@ func NewRunCmd() *cobra.Command {
ui.PrintOnError("Displaying help", err)
},
PersistentPreRun: func(cmd *cobra.Command, args []string) {
cfg, err := config.Load()
ui.ExitOnError("loading config", err)
common.UiContextHeader(cmd, cfg)

validator.PersistentPreRunVersionCheck(cmd, common.Version)
}}

Expand Down
Loading

0 comments on commit 93c19e4

Please sign in to comment.