parent
cbe13e8c63
commit
6a43167f1a
2
Makefile
2
Makefile
|
|
@ -11,7 +11,7 @@ DATE ?= $(shell TZ=UTC date -j -f "%s" ${SOURCE_DATE_EPOCH} +"%Y-%m-%dT%H:
|
|||
else
|
||||
DATE ?= $(shell date -u -d @${SOURCE_DATE_EPOCH} +"%Y-%m-%dT%H:%M:%SZ")
|
||||
endif
|
||||
VERSION ?= v0.30.5
|
||||
VERSION ?= v0.30.6
|
||||
IMG_NAME := derailed/k9s
|
||||
IMAGE := ${IMG_NAME}:${VERSION}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/k9s-xmas.png" align="center" width="800" height="auto"/>
|
||||
|
||||
# Release v0.30.6
|
||||
|
||||
## Notes
|
||||
|
||||
Thank you to all that contributed with flushing out issues and enhancements for K9s!
|
||||
I'll try to mark some of these issues as fixed. But if you don't mind grab the latest rev
|
||||
and see if we're happier with some of the fixes!
|
||||
If you've filed an issue please help me verify and close.
|
||||
|
||||
Your support, kindness and awesome suggestions to make K9s better are, as ever, very much noted and appreciated!
|
||||
Also big thanks to all that have allocated their own time to help others on both slack and on this repo!!
|
||||
|
||||
As you may know, K9s is not pimped out by corps with deep pockets, thus if you feel K9s is helping your Kubernetes journey,
|
||||
please consider joining our [sponsorship program](https://github.com/sponsors/derailed) and/or make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer)
|
||||
|
||||
On Slack? Please join us [K9slackers](https://join.slack.com/t/k9sers/shared_invite/enQtOTA5MDEyNzI5MTU0LWQ1ZGI3MzliYzZhZWEyNzYxYzA3NjE0YTk1YmFmNzViZjIyNzhkZGI0MmJjYzhlNjdlMGJhYzE2ZGU1NjkyNTM)
|
||||
|
||||
## 🎄 Maintenance Release! 🎄
|
||||
|
||||
Thank you all for pitching in and helping flesh out issues!!
|
||||
|
||||
---
|
||||
|
||||
## Videos Are In The Can!
|
||||
|
||||
Please dial [K9s Channel](https://www.youtube.com/channel/UC897uwPygni4QIjkPCpgjmw) for up coming content...
|
||||
|
||||
* [K9s v0.30.0 Sneak peek](https://youtu.be/mVBc1XneRJ4)
|
||||
* [Vulnerability Scans](https://youtu.be/ULkl0MsaidU)
|
||||
|
||||
---
|
||||
|
||||
## Resolved Issues
|
||||
|
||||
* [#2401](https://github.com/derailed/k9s/issues/2401) Context completion broken with mixed case context names
|
||||
* [#2400](https://github.com/derailed/k9s/issues/2400) Panic on start if dns lookup fails
|
||||
* [#2387](https://github.com/derailed/k9s/issues/2387) Invalid namespace xxx - with feelings??
|
||||
|
||||
---
|
||||
|
||||
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/imhotep_logo.png" width="32" height="auto"/> © 2023 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0)
|
||||
|
|
@ -93,7 +93,7 @@ func run(cmd *cobra.Command, args []string) error {
|
|||
|
||||
cfg, err := loadConfiguration()
|
||||
if err != nil {
|
||||
return err
|
||||
log.Error().Err(err).Msgf("load configuration failed")
|
||||
}
|
||||
app := view.NewApp(cfg)
|
||||
if err := app.Init(version, *k9sFlags.RefreshRate); err != nil {
|
||||
|
|
@ -120,12 +120,11 @@ func loadConfiguration() (*config.Config, error) {
|
|||
k9sCfg.K9s.Override(k9sFlags)
|
||||
if err := k9sCfg.Refine(k8sFlags, k9sFlags, k8sCfg); err != nil {
|
||||
log.Error().Err(err).Msgf("refine failed")
|
||||
return nil, err
|
||||
}
|
||||
conn, err := client.InitConnection(k8sCfg)
|
||||
k9sCfg.SetConnection(conn)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msgf("failed to connect to context %q", k9sCfg.K9s.ActiveContextName())
|
||||
return nil, err
|
||||
return k9sCfg, err
|
||||
}
|
||||
// Try to access server version if that fail. Connectivity issue?
|
||||
if !conn.CheckConnectivity() {
|
||||
|
|
@ -134,7 +133,6 @@ func loadConfiguration() (*config.Config, error) {
|
|||
if !conn.ConnectionOK() {
|
||||
return nil, fmt.Errorf("k8s connection failed for context: %s", k9sCfg.K9s.ActiveContextName())
|
||||
}
|
||||
k9sCfg.SetConnection(conn)
|
||||
|
||||
log.Info().Msg("✅ Kubernetes connectivity")
|
||||
if err := k9sCfg.Save(); err != nil {
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ const (
|
|||
cacheExpiry = 5 * time.Minute
|
||||
cacheMXAPIKey = "metricsAPI"
|
||||
serverVersion = "serverVersion"
|
||||
cacheNSKey = "validNamespaces"
|
||||
)
|
||||
|
||||
var supportedMetricsAPIVersions = []string{"v1beta1"}
|
||||
|
|
@ -213,29 +214,28 @@ func (a *APIClient) ServerVersion() (*version.Info, error) {
|
|||
}
|
||||
|
||||
func (a *APIClient) IsValidNamespace(ns string) bool {
|
||||
if IsAllNamespace(ns) {
|
||||
if IsClusterWide(ns) || ns == NotNamespaced {
|
||||
return true
|
||||
}
|
||||
|
||||
ok, err := a.CanI(ClusterScope, "v1/namespaces", []string{ListVerb})
|
||||
if !ok || err != nil {
|
||||
cool, err := a.isValidNamespace(ns)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msgf("unable to assert valid namespace")
|
||||
}
|
||||
return cool
|
||||
}
|
||||
nn, err := a.ValidNamespaceNames()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if ok && err == nil {
|
||||
nn, _ := a.ValidNamespaceNames()
|
||||
_, ok = nn[ns]
|
||||
|
||||
return ok
|
||||
}
|
||||
|
||||
ok, err = a.isValidNamespace(ns)
|
||||
if ok && err == nil {
|
||||
return ok
|
||||
}
|
||||
log.Warn().Err(err).Msgf("namespace validation failed for: %q", ns)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (a *APIClient) cachedNamespaceNames() NamespaceNames {
|
||||
cns, ok := a.cache.Get("validNamespaces")
|
||||
cns, ok := a.cache.Get(cacheNSKey)
|
||||
if !ok {
|
||||
return make(NamespaceNames)
|
||||
}
|
||||
|
|
@ -244,6 +244,10 @@ func (a *APIClient) cachedNamespaceNames() NamespaceNames {
|
|||
}
|
||||
|
||||
func (a *APIClient) isValidNamespace(n string) (bool, error) {
|
||||
if IsClusterWide(n) || n == NotNamespaced {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if a == nil {
|
||||
return false, errors.New("invalid client")
|
||||
}
|
||||
|
|
@ -264,7 +268,7 @@ func (a *APIClient) isValidNamespace(n string) (bool, error) {
|
|||
return false, err
|
||||
}
|
||||
cnss[n] = struct{}{}
|
||||
a.cache.Add("validNamespaces", cnss, cacheExpiry)
|
||||
a.cache.Add(cacheNSKey, cnss, cacheExpiry)
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
|
@ -275,7 +279,7 @@ func (a *APIClient) ValidNamespaceNames() (NamespaceNames, error) {
|
|||
return nil, fmt.Errorf("validNamespaces: no available client found")
|
||||
}
|
||||
|
||||
if nn, ok := a.cache.Get("validNamespaces"); ok {
|
||||
if nn, ok := a.cache.Get(cacheNSKey); ok {
|
||||
if nss, ok := nn.(NamespaceNames); ok {
|
||||
return nss, nil
|
||||
}
|
||||
|
|
@ -294,7 +298,7 @@ func (a *APIClient) ValidNamespaceNames() (NamespaceNames, error) {
|
|||
for _, n := range nn.Items {
|
||||
nns[n.Name] = struct{}{}
|
||||
}
|
||||
a.cache.Add("validNamespaces", nns, cacheExpiry)
|
||||
a.cache.Add(cacheNSKey, nns, cacheExpiry)
|
||||
|
||||
return nns, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,8 +8,145 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
authorizationv1 "k8s.io/api/authorization/v1"
|
||||
)
|
||||
|
||||
func TestMakeSAR(t *testing.T) {
|
||||
uu := map[string]struct {
|
||||
ns string
|
||||
gvr GVR
|
||||
sar *authorizationv1.SelfSubjectAccessReview
|
||||
}{
|
||||
"all-pods": {
|
||||
ns: NamespaceAll,
|
||||
gvr: NewGVR("v1/pods"),
|
||||
sar: &authorizationv1.SelfSubjectAccessReview{
|
||||
Spec: authorizationv1.SelfSubjectAccessReviewSpec{
|
||||
ResourceAttributes: &authorizationv1.ResourceAttributes{
|
||||
Namespace: NamespaceAll,
|
||||
Version: "v1",
|
||||
Resource: "pods",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"ns-pods": {
|
||||
ns: "fred",
|
||||
gvr: NewGVR("v1/pods"),
|
||||
sar: &authorizationv1.SelfSubjectAccessReview{
|
||||
Spec: authorizationv1.SelfSubjectAccessReviewSpec{
|
||||
ResourceAttributes: &authorizationv1.ResourceAttributes{
|
||||
Namespace: "fred",
|
||||
Version: "v1",
|
||||
Resource: "pods",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"clusterscope-ns": {
|
||||
ns: ClusterScope,
|
||||
gvr: NewGVR("v1/namespaces"),
|
||||
sar: &authorizationv1.SelfSubjectAccessReview{
|
||||
Spec: authorizationv1.SelfSubjectAccessReviewSpec{
|
||||
ResourceAttributes: &authorizationv1.ResourceAttributes{
|
||||
Version: "v1",
|
||||
Resource: "namespaces",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"subres-pods": {
|
||||
ns: "fred",
|
||||
gvr: NewGVR("v1/pods:logs"),
|
||||
sar: &authorizationv1.SelfSubjectAccessReview{
|
||||
Spec: authorizationv1.SelfSubjectAccessReviewSpec{
|
||||
ResourceAttributes: &authorizationv1.ResourceAttributes{
|
||||
Namespace: "fred",
|
||||
Version: "v1",
|
||||
Resource: "pods",
|
||||
Subresource: "logs",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for k := range uu {
|
||||
u := uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
assert.Equal(t, u.sar, makeSAR(u.ns, u.gvr.String()))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsValidNamespace(t *testing.T) {
|
||||
c := NewTestAPIClient()
|
||||
|
||||
uu := map[string]struct {
|
||||
ns string
|
||||
cache NamespaceNames
|
||||
ok bool
|
||||
}{
|
||||
"all-ns": {
|
||||
ns: NamespaceAll,
|
||||
cache: NamespaceNames{
|
||||
DefaultNamespace: {},
|
||||
},
|
||||
ok: true,
|
||||
},
|
||||
"blank-ns": {
|
||||
ns: BlankNamespace,
|
||||
cache: NamespaceNames{
|
||||
DefaultNamespace: {},
|
||||
},
|
||||
ok: true,
|
||||
},
|
||||
"cluster-ns": {
|
||||
ns: ClusterScope,
|
||||
cache: NamespaceNames{
|
||||
DefaultNamespace: {},
|
||||
},
|
||||
ok: true,
|
||||
},
|
||||
"no-ns": {
|
||||
ns: NotNamespaced,
|
||||
cache: NamespaceNames{
|
||||
DefaultNamespace: {},
|
||||
},
|
||||
ok: true,
|
||||
},
|
||||
"default-ns": {
|
||||
ns: DefaultNamespace,
|
||||
cache: NamespaceNames{
|
||||
DefaultNamespace: {},
|
||||
},
|
||||
ok: true,
|
||||
},
|
||||
"valid-ns": {
|
||||
ns: "fred",
|
||||
cache: NamespaceNames{
|
||||
"fred": {},
|
||||
},
|
||||
ok: true,
|
||||
},
|
||||
"invalid-ns": {
|
||||
ns: "fred",
|
||||
cache: NamespaceNames{
|
||||
DefaultNamespace: {},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
expiry := 1 * time.Millisecond
|
||||
for k := range uu {
|
||||
u := uu[k]
|
||||
c.cache.Add("validNamespaces", u.cache, expiry)
|
||||
t.Run(k, func(t *testing.T) {
|
||||
assert.Equal(t, u.ok, c.IsValidNamespace(u.ns))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckCacheBool(t *testing.T) {
|
||||
c := NewTestAPIClient()
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import (
|
|||
"errors"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/rs/zerolog"
|
||||
|
|
@ -18,6 +19,31 @@ func init() {
|
|||
zerolog.SetGlobalLevel(zerolog.FatalLevel)
|
||||
}
|
||||
|
||||
func TestCallTimeout(t *testing.T) {
|
||||
uu := map[string]struct {
|
||||
t string
|
||||
e time.Duration
|
||||
}{
|
||||
"custom": {
|
||||
t: "1m",
|
||||
e: 1 * time.Minute,
|
||||
},
|
||||
"default": {
|
||||
e: 15 * time.Second,
|
||||
},
|
||||
}
|
||||
|
||||
for k := range uu {
|
||||
u := uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
flags := genericclioptions.NewConfigFlags(false)
|
||||
flags.Timeout = &u.t
|
||||
cfg := client.NewConfig(flags)
|
||||
assert.Equal(t, u.e, cfg.CallTimeout())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigCurrentContext(t *testing.T) {
|
||||
var kubeConfig = "./testdata/config"
|
||||
|
||||
|
|
|
|||
|
|
@ -8,8 +8,193 @@ import (
|
|||
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/stretchr/testify/assert"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func TestMetaFQN(t *testing.T) {
|
||||
uu := map[string]struct {
|
||||
meta metav1.ObjectMeta
|
||||
e string
|
||||
}{
|
||||
"empty": {
|
||||
e: "-/",
|
||||
},
|
||||
"full": {
|
||||
meta: metav1.ObjectMeta{Name: "blee", Namespace: "ns1"},
|
||||
e: "ns1/blee",
|
||||
},
|
||||
"no-ns": {
|
||||
meta: metav1.ObjectMeta{Name: "blee"},
|
||||
e: "-/blee",
|
||||
},
|
||||
}
|
||||
|
||||
for k := range uu {
|
||||
u := uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
assert.Equal(t, u.e, client.MetaFQN(u.meta))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCoFQN(t *testing.T) {
|
||||
uu := map[string]struct {
|
||||
meta metav1.ObjectMeta
|
||||
co string
|
||||
e string
|
||||
}{
|
||||
"empty": {
|
||||
e: "-/:",
|
||||
},
|
||||
"full": {
|
||||
meta: metav1.ObjectMeta{Name: "blee", Namespace: "ns1"},
|
||||
co: "fred",
|
||||
e: "ns1/blee:fred",
|
||||
},
|
||||
"no-co": {
|
||||
meta: metav1.ObjectMeta{Name: "blee", Namespace: "ns1"},
|
||||
e: "ns1/blee:",
|
||||
},
|
||||
}
|
||||
|
||||
for k := range uu {
|
||||
u := uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
assert.Equal(t, u.e, client.CoFQN(u.meta, u.co))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsClusterScoped(t *testing.T) {
|
||||
uu := map[string]struct {
|
||||
ns string
|
||||
e bool
|
||||
}{
|
||||
"empty": {},
|
||||
"all": {
|
||||
ns: client.NamespaceAll,
|
||||
},
|
||||
"none": {
|
||||
ns: client.BlankNamespace,
|
||||
},
|
||||
"custom": {
|
||||
ns: "fred",
|
||||
},
|
||||
"scoped": {
|
||||
ns: "-",
|
||||
e: true,
|
||||
},
|
||||
}
|
||||
|
||||
for k := range uu {
|
||||
u := uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
assert.Equal(t, u.e, client.IsClusterScoped(u.ns))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsNamespaced(t *testing.T) {
|
||||
uu := map[string]struct {
|
||||
ns string
|
||||
e bool
|
||||
}{
|
||||
"empty": {},
|
||||
"all": {
|
||||
ns: client.NamespaceAll,
|
||||
},
|
||||
"none": {
|
||||
ns: client.BlankNamespace,
|
||||
},
|
||||
"custom": {
|
||||
ns: "fred",
|
||||
e: true,
|
||||
},
|
||||
}
|
||||
|
||||
for k := range uu {
|
||||
u := uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
assert.Equal(t, u.e, client.IsNamespaced(u.ns))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsAllNamespaces(t *testing.T) {
|
||||
uu := map[string]struct {
|
||||
ns string
|
||||
e bool
|
||||
}{
|
||||
"empty": {
|
||||
e: true,
|
||||
},
|
||||
"all": {
|
||||
ns: client.NamespaceAll,
|
||||
e: true,
|
||||
},
|
||||
"none": {
|
||||
ns: client.BlankNamespace,
|
||||
e: true,
|
||||
},
|
||||
"custom": {
|
||||
ns: "fred",
|
||||
},
|
||||
}
|
||||
|
||||
for k := range uu {
|
||||
u := uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
assert.Equal(t, u.e, client.IsAllNamespaces(u.ns))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsAllNamespace(t *testing.T) {
|
||||
uu := map[string]struct {
|
||||
ns string
|
||||
e bool
|
||||
}{
|
||||
"empty": {},
|
||||
"all": {
|
||||
ns: client.NamespaceAll,
|
||||
e: true,
|
||||
},
|
||||
"custom": {
|
||||
ns: "fred",
|
||||
},
|
||||
}
|
||||
|
||||
for k := range uu {
|
||||
u := uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
assert.Equal(t, u.e, client.IsAllNamespace(u.ns))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCleanseNamespace(t *testing.T) {
|
||||
uu := map[string]struct {
|
||||
ns, e string
|
||||
}{
|
||||
"empty": {},
|
||||
"all": {
|
||||
ns: client.NamespaceAll,
|
||||
e: client.BlankNamespace,
|
||||
},
|
||||
"custom": {
|
||||
ns: "fred",
|
||||
e: "fred",
|
||||
},
|
||||
}
|
||||
|
||||
for k := range uu {
|
||||
u := uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
assert.Equal(t, u.e, client.CleanseNamespace(u.ns))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNamespaced(t *testing.T) {
|
||||
uu := []struct {
|
||||
p, ns, n string
|
||||
|
|
|
|||
|
|
@ -40,18 +40,11 @@ func NewActiveNamespace(n string) *Namespace {
|
|||
|
||||
// Validate validates a namespace is setup correctly.
|
||||
func (n *Namespace) Validate(c client.Connection, ks KubeSettings) {
|
||||
if n.Active == client.BlankNamespace || c == nil {
|
||||
n.Active = client.DefaultNamespace
|
||||
}
|
||||
if c == nil {
|
||||
if c == nil || !c.IsValidNamespace(n.Active) {
|
||||
return
|
||||
}
|
||||
if !n.isAllNamespaces() && !c.IsValidNamespace(n.Active) {
|
||||
log.Error().Msgf("[Config] Validation failed active namespace %q does not exists. Resetting to default ns", n.Active)
|
||||
n.Active = client.DefaultNamespace
|
||||
}
|
||||
for _, ns := range n.Favorites {
|
||||
if ns != client.NamespaceAll && !c.IsValidNamespace(ns) {
|
||||
if !c.IsValidNamespace(ns) {
|
||||
log.Debug().Msgf("[Namespace] Invalid favorite found '%s' - %t", ns, n.isAllNamespaces())
|
||||
n.rmFavNS(ns)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -184,9 +184,9 @@ func (a *App) suggestCommand() model.SuggestionFunc {
|
|||
return a.cmdHistory.List()
|
||||
}
|
||||
|
||||
s = strings.ToLower(s)
|
||||
ls := strings.ToLower(s)
|
||||
for _, k := range a.command.alias.Aliases.Keys() {
|
||||
if suggest, ok := cmd.ShouldAddSuggest(s, k); ok {
|
||||
if suggest, ok := cmd.ShouldAddSuggest(ls, k); ok {
|
||||
entries = append(entries, suggest)
|
||||
}
|
||||
}
|
||||
|
|
@ -195,12 +195,10 @@ func (a *App) suggestCommand() model.SuggestionFunc {
|
|||
if err != nil {
|
||||
log.Error().Err(err).Msg("failed to list namespaces")
|
||||
}
|
||||
|
||||
entries = append(entries, cmd.SuggestSubCommand(s, namespaceNames, contextNames)...)
|
||||
if len(entries) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
entries.Sort()
|
||||
return
|
||||
}
|
||||
|
|
@ -214,11 +212,11 @@ func (a *App) contextNames() ([]string, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
contextNames := make([]string, 0, len(contexts))
|
||||
for ctxName := range contexts {
|
||||
contextNames = append(contextNames, ctxName)
|
||||
}
|
||||
|
||||
return contextNames, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -94,6 +94,7 @@ func SuggestSubCommand(command string, namespaces client.NamespaceNames, context
|
|||
}
|
||||
|
||||
func completeNS(s string, nn client.NamespaceNames) []string {
|
||||
s = strings.ToLower(s)
|
||||
var suggests []string
|
||||
if suggest, ok := ShouldAddSuggest(s, client.NamespaceAll); ok {
|
||||
suggests = append(suggests, suggest)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
name: k9s
|
||||
base: core20
|
||||
version: 'v0.30.5'
|
||||
version: 'v0.30.6'
|
||||
summary: K9s is a CLI to view and manage your Kubernetes clusters.
|
||||
description: |
|
||||
K9s is a CLI to view and manage your Kubernetes clusters. By leveraging a terminal UI, you can easily traverse Kubernetes resources and view the state of your clusters in a single powerful session.
|
||||
|
|
|
|||
Loading…
Reference in New Issue