shell autocomplete for k8s flags (#2477)

mine
Jayson Wang 2024-01-17 08:26:00 +08:00 committed by GitHub
parent 22db95e6e4
commit 66cb682aab
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 65 additions and 2 deletions

View File

@ -4,11 +4,14 @@
package cmd package cmd
import ( import (
"errors"
"fmt" "fmt"
"os" "os"
"runtime/debug" "runtime/debug"
"strings"
"github.com/derailed/k9s/internal/config/data" "github.com/derailed/k9s/internal/config/data"
"k8s.io/client-go/tools/clientcmd/api"
"github.com/derailed/k9s/internal/client" "github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/color" "github.com/derailed/k9s/internal/color"
@ -44,11 +47,19 @@ var (
out = colorable.NewColorableStdout() out = colorable.NewColorableStdout()
) )
type FlagError struct{ err error }
func (e *FlagError) Error() string { return e.err.Error() }
func init() { func init() {
if err := config.InitLogLoc(); err != nil { if err := config.InitLogLoc(); err != nil {
fmt.Printf("Fail to init k9s logs location %s\n", err) fmt.Printf("Fail to init k9s logs location %s\n", err)
} }
rootCmd.SetFlagErrorFunc(func(command *cobra.Command, err error) error {
return &FlagError{err: err}
})
rootCmd.AddCommand(versionCmd(), infoCmd()) rootCmd.AddCommand(versionCmd(), infoCmd())
initK9sFlags() initK9sFlags()
initK8sFlags() initK8sFlags()
@ -57,8 +68,11 @@ func init() {
// Execute root command. // Execute root command.
func Execute() { func Execute() {
if err := rootCmd.Execute(); err != nil { if err := rootCmd.Execute(); err != nil {
var flagError *FlagError
if !errors.As(err, &flagError) {
panic(err) panic(err)
} }
}
} }
func run(cmd *cobra.Command, args []string) error { func run(cmd *cobra.Command, args []string) error {
@ -281,6 +295,7 @@ func initK8sFlags() {
initAsFlags() initAsFlags()
initCertFlags() initCertFlags()
initK8sFlagCompletion()
} }
func initAsFlags() { func initAsFlags() {
@ -335,3 +350,51 @@ func initCertFlags() {
"Bearer token for authentication to the API server", "Bearer token for authentication to the API server",
) )
} }
func initK8sFlagCompletion() {
_ = rootCmd.RegisterFlagCompletionFunc("context", k8sFlagCompletionFunc(func(cfg *api.Config) map[string]*api.Context {
return cfg.Contexts
}))
_ = rootCmd.RegisterFlagCompletionFunc("cluster", k8sFlagCompletionFunc(func(cfg *api.Config) map[string]*api.Cluster {
return cfg.Clusters
}))
_ = rootCmd.RegisterFlagCompletionFunc("user", k8sFlagCompletionFunc(func(cfg *api.Config) map[string]*api.AuthInfo {
return cfg.AuthInfos
}))
_ = rootCmd.RegisterFlagCompletionFunc("namespace", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
conn, err := client.InitConnection(client.NewConfig(k8sFlags))
if err != nil {
return nil, cobra.ShellCompDirectiveError
}
nss, err := conn.ValidNamespaceNames()
if err != nil {
return nil, cobra.ShellCompDirectiveError
}
return filterFlagCompletions(nss, toComplete)
})
}
func k8sFlagCompletionFunc[T any](picker func(cfg *api.Config) map[string]T) func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) {
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
k8sCfg, err := client.NewConfig(k8sFlags).RawConfig()
if err != nil {
return nil, cobra.ShellCompDirectiveError
}
return filterFlagCompletions(picker(&k8sCfg), toComplete)
}
}
func filterFlagCompletions[T any](m map[string]T, toComplete string) ([]string, cobra.ShellCompDirective) {
var completions []string
for name := range m {
if strings.HasPrefix(name, toComplete) {
completions = append(completions, name)
}
}
return completions, cobra.ShellCompDirectiveNoFileComp
}

View File

@ -71,7 +71,7 @@ func InitConnection(config *Config) (*APIClient, error) {
if err != nil { if err != nil {
log.Error().Err(err).Msgf("Fail to locate metrics-server") log.Error().Err(err).Msgf("Fail to locate metrics-server")
} }
if errors.Is(err, noMetricServerErr) || errors.Is(err, metricsUnsupportedErr) { if err == nil || errors.Is(err, noMetricServerErr) || errors.Is(err, metricsUnsupportedErr) {
return &a, nil return &a, nil
} }
a.connOK = false a.connOK = false