diff --git a/cmd/kubectl-testkube/commands/common/helper.go b/cmd/kubectl-testkube/commands/common/helper.go index 22b6f1ad0b..8dc57d049a 100644 --- a/cmd/kubectl-testkube/commands/common/helper.go +++ b/cmd/kubectl-testkube/commands/common/helper.go @@ -813,6 +813,58 @@ func KubectlDescribeIngresses(namespace string) error { return process.ExecuteAndStreamOutput(kubectl, args...) } +func KubectlGetNamespacesHavingSecrets(secretName string) ([]string, error) { + kubectl, clierr := lookupKubectlPath() + if clierr != nil { + return nil, clierr.ActualError + } + + args := []string{ + "get", + "secret", + "-A", + } + + if ui.IsVerbose() { + ui.ShellCommand(kubectl, args...) + ui.NL() + } + + out, err := process.Execute(kubectl, args...) + if err != nil { + return nil, err + } + + nss := extractUniqueNamespaces(string(out), secretName) + return nss, nil +} + +func extractUniqueNamespaces(data string, secretName string) []string { + // Split the data into lines + lines := strings.Split(data, "\n") + + // Map to store unique namespaces + uniq := make(map[string]bool) + + for _, line := range lines { + parts := strings.Fields(line) + if len(parts) < 2 { + continue + } + if parts[1] == secretName { + uniq[parts[0]] = true + } + } + + // Convert map keys (namespaces) to a slice of strings + list := make([]string, 0, len(uniq)) + for namespace := range uniq { + list = append(list, namespace) + } + + return list +} + func KubectlGetPodEnvs(selector, namespace string) (map[string]string, error) { kubectl, clierr := lookupKubectlPath() if clierr != nil { @@ -821,7 +873,7 @@ func KubectlGetPodEnvs(selector, namespace string) (map[string]string, error) { args := []string{ "get", - "pod", + "secret", selector, "-n", namespace, "-o", `jsonpath='{range .items[*].spec.containers[*]}{"\nContainer: "}{.name}{"\n"}{range .env[*]}{.name}={.value}{"\n"}{end}{end}'`, diff --git a/cmd/kubectl-testkube/commands/diagnostics.go b/cmd/kubectl-testkube/commands/diagnostics.go index fb29375451..5c231accec 100644 --- a/cmd/kubectl-testkube/commands/diagnostics.go +++ b/cmd/kubectl-testkube/commands/diagnostics.go @@ -17,6 +17,7 @@ func NewDiagnosticsCmd() *cobra.Command { Run: NewRunDiagnosticsCmdFunc(), } + cmd.Flags().Bool("offline-override", false, "Pass License key manually (we will not try to locate it automatically)") cmd.Flags().StringP("key-override", "k", "", "Pass License key manually (we will not try to locate it automatically)") cmd.Flags().StringP("file-override", "f", "", "Pass License file manually (we will not try to locate it automatically)") diff --git a/cmd/kubectl-testkube/commands/diagnostics/install.go b/cmd/kubectl-testkube/commands/diagnostics/install.go index a1ffd6b551..14910d0d0e 100644 --- a/cmd/kubectl-testkube/commands/diagnostics/install.go +++ b/cmd/kubectl-testkube/commands/diagnostics/install.go @@ -22,9 +22,6 @@ func NewInstallCheckCmd() *cobra.Command { Run: RunInstallCheckFunc(), } - cmd.Flags().StringP("key-override", "k", "", "Pass License key manually (we will not try to locate it automatically)") - cmd.Flags().StringP("file-override", "f", "", "Pass License file manually (we will not try to locate it automatically)") - return cmd } diff --git a/cmd/kubectl-testkube/commands/diagnostics/license.go b/cmd/kubectl-testkube/commands/diagnostics/license.go index 165614c2a2..3195572084 100644 --- a/cmd/kubectl-testkube/commands/diagnostics/license.go +++ b/cmd/kubectl-testkube/commands/diagnostics/license.go @@ -3,6 +3,7 @@ package diagnostics import ( "github.com/spf13/cobra" + "github.com/kubeshop/testkube/cmd/kubectl-testkube/commands/common" "github.com/kubeshop/testkube/pkg/diagnostics" "github.com/kubeshop/testkube/pkg/diagnostics/loader" "github.com/kubeshop/testkube/pkg/diagnostics/validators/license" @@ -14,6 +15,26 @@ func RegisterLicenseValidators(cmd *cobra.Command, d diagnostics.Diagnostics) { namespace := cmd.Flag("namespace").Value.String() keyOverride := cmd.Flag("key-override").Value.String() fileOverride := cmd.Flag("file-override").Value.String() + isOfflineOverride := cmd.Flag("offline-override").Changed && cmd.Flag("offline-override").Value.String() == "true" + + // if not namespace provided load all namespaces having license file secret + if !cmd.Flag("namespace").Changed { + namespaces, err := common.KubectlGetNamespacesHavingSecrets("testkube-enterprise-license") + if err != nil { + ui.Errf("Can't check for namespaces, make sure you have valid access rights to list resources in Kubernetes") + ui.ExitOnError("error:", err) + return + } + + switch true { + case len(namespaces) == 0: + ui.Failf("Can't locate any Testkube installations please pass `--namespace` parameter") + case len(namespaces) == 1: + namespace = namespaces[0] + case len(namespaces) > 1: + namespace = ui.Select("Choose namespace to check license", namespaces) + } + } var err error l := loader.License{} @@ -28,19 +49,23 @@ func RegisterLicenseValidators(cmd *cobra.Command, d diagnostics.Diagnostics) { if fileOverride != "" && keyOverride != "" { l.EnterpriseOfflineActivation = true } + if isOfflineOverride { + l.EnterpriseOfflineActivation = isOfflineOverride + } - if fileOverride == "" || keyOverride == "" { + if keyOverride == "" || (l.EnterpriseOfflineActivation && fileOverride == "") { l, err = loader.GetLicenseConfig(namespace, "") ui.ExitOnError("loading license data", err) } // License validator - licenseGroup := d.AddValidatorGroup("license.validation", l.EnterpriseLicenseKey) if l.EnterpriseOfflineActivation { + licenseGroup := d.AddValidatorGroup("offline.license.validation", l.EnterpriseLicenseKey) licenseGroup.AddValidator(license.NewFileValidator()) licenseGroup.AddValidator(license.NewOfflineLicenseKeyValidator()) licenseGroup.AddValidator(license.NewOfflineLicenseValidator(l.EnterpriseLicenseKey, l.EnterpriseLicenseFile)) } else { + licenseGroup := d.AddValidatorGroup("online.license.validation", l.EnterpriseLicenseKey) licenseGroup.AddValidator(license.NewOnlineLicenseKeyValidator()) licenseGroup.AddValidator(license.NewKeygenShValidator()) } @@ -54,6 +79,7 @@ func NewLicenseCheckCmd() *cobra.Command { Run: RunLicenseCheckFunc(), } + cmd.Flags().Bool("offline-override", false, "Pass License key manually (we will not try to locate it automatically)") cmd.Flags().StringP("key-override", "k", "", "Pass License key manually (we will not try to locate it automatically)") cmd.Flags().StringP("file-override", "f", "", "Pass License file manually (we will not try to locate it automatically)") @@ -62,6 +88,8 @@ func NewLicenseCheckCmd() *cobra.Command { func RunLicenseCheckFunc() func(cmd *cobra.Command, args []string) { return func(cmd *cobra.Command, args []string) { + ui.H1("Check licensing issues") + d := diagnostics.New() RegisterLicenseValidators(cmd, d) diff --git a/pkg/diagnostics/validators/license/errors.go b/pkg/diagnostics/validators/license/errors.go index 41b2c25a89..82281b4577 100644 --- a/pkg/diagnostics/validators/license/errors.go +++ b/pkg/diagnostics/validators/license/errors.go @@ -14,7 +14,7 @@ var ( ErrLicenseKeyInvalidFormat = v.Err("license key invalid format", v.ErrorKindInvalidKeyContent) ErrOnlineLicenseKeyInvalidLength = v.Err("license key invalid length", v.ErrorKindInvalidKeyContent). - WithDetails("License key should be in form XXXXXX-XXXXXX-XXXXXX-XXXXXX-XXXXXX-XX - 37 chars in length"). + WithSuggestion("License key should be in form XXXXXX-XXXXXX-XXXXXX-XXXXXX-XXXXXX-XX - 37 chars in length"). WithSuggestion("Make sure license key is in valid format"). WithSuggestion("Make sure there is no whitespaces on the begining and the end of the key") @@ -34,7 +34,7 @@ var ( ErrOfflineLicenseKeyInvalidPrefix = v.Err("license key has invalid prefix", v.ErrorKindInvalidKeyContent). WithDetails("License key should start with 'key/' string"). WithSuggestion("Make sure license key is in valid format"). - WithSuggestion("Make sure you're NOT using offline keys for air-gapped (offline) installations"). + WithSuggestion("Make sure you're NOT using 'online' keys for air-gapped ('offline') installations"). WithSuggestion("Make sure there is no whitespaces on the begining and the end of the key") ErrOfflineLicensePublicKeyMissing = v.Err("public key is missing", v.ErrorKindLicenseInvalid) diff --git a/pkg/ui/select.go b/pkg/ui/select.go index ad77d6cef9..6dcb10b97a 100644 --- a/pkg/ui/select.go +++ b/pkg/ui/select.go @@ -5,6 +5,7 @@ import "github.com/pterm/pterm" func (ui *UI) Select(label string, options []string) string { val, _ := pterm.DefaultInteractiveSelect. WithOptions(options). + WithDefaultText(label). Show() ui.NL()