Skip to content

Commit

Permalink
feat: added separated commands
Browse files Browse the repository at this point in the history
  • Loading branch information
exu committed Nov 21, 2024
1 parent bcdbfac commit 2afd53e
Show file tree
Hide file tree
Showing 13 changed files with 223 additions and 397 deletions.
9 changes: 3 additions & 6 deletions cmd/kubectl-testkube/commands/common/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -631,7 +631,7 @@ func KubectlPrintEvents(namespace string) error {
}

func KubectlVersion() (client string, server string, err error) {
kubectl, err := lookupKubectlPath()
kubectl, err := exec.LookPath("kubectl")
if err != nil {
return "", "", err
}
Expand All @@ -641,9 +641,6 @@ func KubectlVersion() (client string, server string, err error) {
"-o", "json",
}

ui.ShellCommand(kubectl, args...)
ui.NL()

out, eerr := process.Execute(kubectl, args...)
if eerr != nil {
return "", "", eerr
Expand Down Expand Up @@ -845,7 +842,7 @@ func KubectlGetSecret(selector, namespace string) (map[string]string, error) {
return nil, err
}

return convertJSONStringToMap(string(out))
return secretsJSONToMap(string(out))
}

func lookupKubectlPath() (string, *CLIError) {
Expand Down Expand Up @@ -1125,7 +1122,7 @@ func convertEnvToMap(input string) map[string]string {
return result
}

func convertJSONStringToMap(in string) (map[string]string, error) {
func secretsJSONToMap(in string) (map[string]string, error) {
res := map[string]string{}
in = strings.TrimLeft(in, "'")
in = strings.TrimRight(in, "'")
Expand Down
80 changes: 9 additions & 71 deletions cmd/kubectl-testkube/commands/diagnostics.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,97 +3,35 @@ package commands
import (
"github.com/spf13/cobra"

"github.com/kubeshop/testkube/cmd/kubectl-testkube/commands/common"

commands "github.com/kubeshop/testkube/cmd/kubectl-testkube/commands/diagnostics"
"github.com/kubeshop/testkube/pkg/diagnostics"
"github.com/kubeshop/testkube/pkg/diagnostics/loader"
"github.com/kubeshop/testkube/pkg/diagnostics/validators/deps"
"github.com/kubeshop/testkube/pkg/diagnostics/validators/license"
"github.com/kubeshop/testkube/pkg/ui"
)

// NewDebugCmd creates the 'testkube debug' command
func NewDiagnosticsCmd() *cobra.Command {

var validators common.CommaList
var groups common.CommaList
var key, file string

cmd := &cobra.Command{
Use: "diagnostics",
Aliases: []string{"diagnose", "diag", "di"},
Short: "Diagnoze testkube issues with ease",
Run: NewRunDiagnosticsCmdFunc(key, &validators, &groups),
Run: NewRunDiagnosticsCmdFunc(),
}

allValidatorStr := ""
allGroupsStr := ""
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)")

cmd.Flags().VarP(&validators, "commands", "s", "Comma-separated list of validators: "+allValidatorStr+", defaults to all")
cmd.Flags().VarP(&groups, "groups", "g", "Comma-separated list of groups, one of: "+allGroupsStr+", defaults to all")

cmd.Flags().StringVarP(&key, "key-override", "k", "", "Pass License key manually (we will not try to locate it automatically)")
cmd.Flags().StringVarP(&file, "file-override", "f", "", "Pass License file manually (we will not try to locate it automatically)")
cmd.AddCommand(commands.NewLicenseCheckCmd())
cmd.AddCommand(commands.NewInstallCheckCmd())

return cmd
}

func NewRunDiagnosticsCmdFunc(key string, commands, groups *common.CommaList) func(cmd *cobra.Command, args []string) {
func NewRunDiagnosticsCmdFunc() func(cmd *cobra.Command, args []string) {
return func(cmd *cobra.Command, args []string) {

// Fetch current setup:
namespace := cmd.Flag("namespace").Value.String()

keyOverride := cmd.Flag("key-override").Value.String()
fileOverride := cmd.Flag("file-override").Value.String()

l, err := loader.GetLicenseConfig(namespace)
ui.ExitOnError("loading license data", err)

if keyOverride != "" {
l.EnterpriseLicenseKey = keyOverride
}
if fileOverride != "" {
l.EnterpriseLicenseFile = fileOverride
}

// Compose diagnostics validators
d := diagnostics.New()

depsGroup := d.AddValidatorGroup("install.dependencies", nil)
depsGroup.AddValidator(deps.NewKubectlDependencyValidator())
depsGroup.AddValidator(deps.NewHelmDependencyValidator())

// License validator
licenseKeyGroup := d.AddValidatorGroup("license.key", key)
if l.EnterpriseOfflineActivation {
licenseKeyGroup.AddValidator(license.NewOfflineLicenseKeyValidator())

// for offline license also add license file validator
licenseFileGroup := d.AddValidatorGroup("license.file", l.EnterpriseLicenseFile)
licenseFileGroup.AddValidator(license.NewFileValidator())

offlineLicenseGroup := d.AddValidatorGroup("license.offline.check", l.EnterpriseLicenseFile)
offlineLicenseGroup.AddValidator(license.NewOfflineLicenseValidator(l.EnterpriseLicenseKey, l.EnterpriseLicenseFile))
} else {
licenseKeyGroup.AddValidator(license.NewOnlineLicenseKeyValidator())
}

// common validator for both key types
licenseKeyGroup.AddValidator(license.NewKeygenShValidator())

// TODO allow to run partially

// Run single "diagnostic"

// Run multiple

// Run predefined group

// Run all
err = d.Run()
d := diagnostics.New()
err := d.Run()
ui.ExitOnError("Running validations", err)

ui.NL(2)
}
}
39 changes: 39 additions & 0 deletions cmd/kubectl-testkube/commands/diagnostics/install.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package diagnostics

import (
"github.com/kubeshop/testkube/pkg/diagnostics"
"github.com/kubeshop/testkube/pkg/diagnostics/validators/deps"
"github.com/kubeshop/testkube/pkg/ui"
"github.com/spf13/cobra"
)

func RegisterInstallValidators(_ *cobra.Command, d diagnostics.Diagnostics) {
depsGroup := d.AddValidatorGroup("install.dependencies", nil)
depsGroup.AddValidator(deps.NewKubectlDependencyValidator())
depsGroup.AddValidator(deps.NewHelmDependencyValidator())
}

func NewInstallCheckCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "install",
Aliases: []string{"ins", "i"},
Short: "Diagnose pre-installation dependencies",
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
}

func RunInstallCheckFunc() func(cmd *cobra.Command, args []string) {
return func(cmd *cobra.Command, args []string) {
d := diagnostics.New()
RegisterInstallValidators(cmd, d)

err := d.Run()
ui.ExitOnError("Running validations", err)
ui.NL(2)
}
}
68 changes: 68 additions & 0 deletions cmd/kubectl-testkube/commands/diagnostics/license.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package diagnostics

import (
"github.com/kubeshop/testkube/pkg/diagnostics"
"github.com/kubeshop/testkube/pkg/diagnostics/loader"
"github.com/kubeshop/testkube/pkg/diagnostics/validators/license"
"github.com/kubeshop/testkube/pkg/ui"
"github.com/spf13/cobra"
)

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()

l, err := loader.GetLicenseConfig(namespace, "")
ui.ExitOnError("loading license data", err)

if keyOverride != "" {
l.EnterpriseLicenseKey = keyOverride
}
if fileOverride != "" {
l.EnterpriseLicenseFile = fileOverride
}

// License validator
licenseKeyGroup := d.AddValidatorGroup("license.key", l.EnterpriseLicenseKey)
if l.EnterpriseOfflineActivation {
licenseKeyGroup.AddValidator(license.NewOfflineLicenseKeyValidator())

// for offline license also add license file validator
licenseFileGroup := d.AddValidatorGroup("license.file", l.EnterpriseLicenseFile)
licenseFileGroup.AddValidator(license.NewFileValidator())

offlineLicenseGroup := d.AddValidatorGroup("license.offline.check", l.EnterpriseLicenseFile)
offlineLicenseGroup.AddValidator(license.NewOfflineLicenseValidator(l.EnterpriseLicenseKey, l.EnterpriseLicenseFile))
} else {
licenseKeyGroup.AddValidator(license.NewOnlineLicenseKeyValidator())
}

// common validator for both key types
licenseKeyGroup.AddValidator(license.NewKeygenShValidator())
}

func NewLicenseCheckCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "license",
Aliases: []string{"lic", "l"},
Short: "Diagnose license errors",
Run: RunLicenseCheckFunc(),
}

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
}

func RunLicenseCheckFunc() func(cmd *cobra.Command, args []string) {
return func(cmd *cobra.Command, args []string) {
d := diagnostics.New()
RegisterLicenseValidators(cmd, d)

err := d.Run()
ui.ExitOnError("Running validations", err)
}
}
37 changes: 32 additions & 5 deletions pkg/diagnostics/loader/license_loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,13 @@ type License struct {
EnterpriseLicenseName string `envconfig:"ENTERPRISE_LICENSE_NAME"`
}

func GetLicenseConfig(namespace string) (l License, err error) {
const DefaultSecretName = "testkube-enterprise-license"

func GetLicenseConfig(namespace, secretName string) (l License, err error) {
if secretName == "" {
secretName = DefaultSecretName
}

// get control plane api pod envs
envs, err := common.KubectlGetPodEnvs("-l app.kubernetes.io/name=testkube-cloud-api", namespace)
if err != nil {
Expand All @@ -27,6 +33,10 @@ func GetLicenseConfig(namespace string) (l License, err error) {
l.EnterpriseOfflineActivation = true
}

if f, ok := envs["ENTERPRISE_LICENSE_FILE"]; ok && f != "" {
l.EnterpriseLicenseFile = f
}

if k, ok := envs["ENTERPRISE_LICENSE_KEY_PATH"]; ok && k != "" {
l.EnterpriseLicenseKeyPath = k
}
Expand All @@ -37,14 +47,31 @@ func GetLicenseConfig(namespace string) (l License, err error) {

if k, ok := envs["ENTERPRISE_LICENSE_KEY"]; ok && k != "" {
l.EnterpriseLicenseKey = k
} else {
}

// try to load from secret - there is no easy way of just stream the key content
secrets, err := common.KubectlGetSecret(secretName, namespace)
ui.WarnOnError("getting secrets from pods", err)

const (
keySecretKeyName = "LICENSE_KEY"
fileSecretKeyName = "license.lic"
)

// If no direct key value provided try to figure out it from secret mapped as file
if l.EnterpriseLicenseKey == "" {
// try to load from secret - there is no easy way of just stream the key content
secrets, err := common.KubectlGetSecret("testkube-enterprise-license", namespace)
ui.ExitOnError("getting secrets from pods", err)
if k, ok := secrets["LICENSE_KEY"]; ok {
if k, ok := secrets[keySecretKeyName]; ok {
l.EnterpriseLicenseKey = k
}
}

// If no direct file value provided try to figure out it from secret mapped as file
if l.EnterpriseLicenseFile == "" {
if k, ok := secrets[fileSecretKeyName]; ok {
l.EnterpriseLicenseFile = k
}
}

return l, err
}
1 change: 0 additions & 1 deletion pkg/diagnostics/renderer/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ func (r CLIRenderer) RenderResult(res validators.ValidationResult) {
for _, s := range err.Suggestions {
ui.Printf(" * %s\n", ui.LightBlue(s))
}
ui.NL()
}
if err.DocsURI != "" {
ui.Printf(" For more details follow docs: [%s]\n", ui.Yellow(err.DocsURI))
Expand Down
3 changes: 3 additions & 0 deletions pkg/diagnostics/validators/deps/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ package deps
import v "github.com/kubeshop/testkube/pkg/diagnostics/validators"

var (
ErrKubernetesInvalidVersion = v.Err("Your Kubernetes cluster has older version than Testkube require to run correctly", v.ErrorKindFileNotFound).
WithSuggestion("Consider upgrading Kubernetes to recent version").
WithSuggestion("Please follow your provider upgrading instructions")
ErrKubectlInvalidVersion = v.Err("kubectl has older version than required", v.ErrorKindFileNotFound).
WithSuggestion("Consider upgrading kubectl to recent version").
WithDocsURI("https://kubernetes.io/docs/tasks/tools")
Expand Down
21 changes: 14 additions & 7 deletions pkg/diagnostics/validators/deps/kuebctl_binary.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import (
const MinimalKubectlVersion = "v1."

type KubectlDependencyValidator struct {
MinimalKubectlVersion string
RequiredKubectlVersion string
RequiredKubernetesVersion string
}

func (v KubectlDependencyValidator) Validate(subject any) (r validators.ValidationResult) {
Expand All @@ -21,27 +22,33 @@ func (v KubectlDependencyValidator) Validate(subject any) (r validators.Validati
return r.WithError(ErrKubectlFileNotFound)
}

clientVersion, _, err := common.KubectlVersion()
clientVersion, kubernetesVersion, err := common.KubectlVersion()
if err != nil {
return r.WithStdError(err)
}

fmt.Printf("%+v\n", clientVersion)

ok, err := semver.Lt(clientVersion, v.MinimalKubectlVersion)
ok, err := semver.Lte(v.RequiredKubectlVersion, clientVersion)
if err != nil {
return r.WithStdError(err)
}
if !ok {
return r.WithError(ErrKubectlInvalidVersion.WithDetails(fmt.Sprintf("We need at least version %s, but your is %s, please consider upgrading", v.RequiredKubectlVersion, clientVersion)))
}

ok, err = semver.Lte(v.RequiredKubernetesVersion, kubernetesVersion)
if err != nil {
return r.WithStdError(err)
}
if !ok {
return r.WithError(ErrKubectlInvalidVersion.WithDetails(fmt.Sprintf("We need version %s but your is %s, consider upgrading", clientVersion, v.MinimalKubectlVersion)))
return r.WithError(ErrKubernetesInvalidVersion.WithDetails(fmt.Sprintf("We need at least version %s, but your is %s, please consider upgrading", v.RequiredKubectlVersion, kubernetesVersion)))
}

return r.WithValidStatus()
}

func NewKubectlDependencyValidator() KubectlDependencyValidator {
return KubectlDependencyValidator{
MinimalKubectlVersion: "1.31.3",
RequiredKubectlVersion: validators.RequiredKubectlVersion,
RequiredKubernetesVersion: validators.RequiredKubernetesVersion,
}
}
4 changes: 2 additions & 2 deletions pkg/diagnostics/validators/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ func (e Error) Error() string {
s += e.Message
}
if e.Details != "" {
s += " " + e.Details
s += " - " + e.Details
}
return e.Message
return s
}

func (e Error) WithSuggestion(s string) Error {
Expand Down
Loading

0 comments on commit 2afd53e

Please sign in to comment.