Skip to content

Commit

Permalink
K9s/release v0.32.0 (#2577)
Browse files Browse the repository at this point in the history
* [Perf] improved load perf and ui updates

* [Bug] Fix #2557

* [Maint] refactor + spring cleaning up

* [Bug] Fix #2569

* [Maint] Refactor + cleanup

* [Bug] Fix #2560

* [Maint] Refactor + cleanup

* Release v0.32.0
  • Loading branch information
derailed committed Mar 2, 2024
1 parent 82ba6f9 commit 0d16531
Show file tree
Hide file tree
Showing 235 changed files with 5,583 additions and 4,556 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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.31.9
VERSION ?= v0.32.0
IMG_NAME := derailed/k9s
IMAGE := ${IMG_NAME}:${VERSION}

Expand Down
73 changes: 73 additions & 0 deletions change_logs/release_v0.32.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/k9s.png" align="center" width="800" height="auto"/>

# Release v0.32.0

## 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!

A lot of refactors, perf improvements (crossing fingers+toes!) and general spring cleaning items in this release.
Thus I expect a bit of `disturbance in the farce` given the major code churns, so please beware!

---

## Videos Are In The Can!

Please dial [K9s Channel](https://www.youtube.com/channel/UC897uwPygni4QIjkPCpgjmw) for up coming content...

* [K9s v0.31.0 Configs+Sneak peek](https://youtu.be/X3444KfjguE)
* [K9s v0.30.0 Sneak peek](https://youtu.be/mVBc1XneRJ4)
* [Vulnerability Scans](https://youtu.be/ULkl0MsaidU)

---

## A Word From Our Sponsors...

To all the good folks below that opted to `pay it forward` and join our sponsorship program, I salute you!!

* [Justin Reid](https://github.com/jmreid)
* [Danni](https://github.com/danninov)
* [Robert Krahn](https://github.com/rksm)
* [Hao Ke](https://github.com/kehao95)
* [PH](https://github.com/raphael-com-ph)

> Sponsorship cancellations since the last release: **9!!** 🥹
---

## Resolved Issues

* [#2569](https://github.com/derailed/k9s/issues/2569) k9s panics on start if the main config file (config.yml) is owned by root
* [#2568](https://github.com/derailed/k9s/issues/2568) kube context in running k9s is no longer sticky, during kubectx context switch
* [#2560](https://github.com/derailed/k9s/issues/2560) Namespace/Settings keeps resetting
* [#2557](https://github.com/derailed/k9s/issues/2557) [Feature]: Sort CRDs by their group
* [#1462](https://github.com/derailed/k9s/issues/1462) k9s running very slowly when opening namespace with 13k pods (maybe??)

---

## Contributed PRs

Please be sure to give `Big Thanks!` and `ATTA Girls/Boys!` to all the fine contributors for making K9s better for all of us!!

* [#2564](https://github.com/derailed/k9s/pull/2564) Add everforest skins
* [#2558](https://github.com/derailed/k9s/pull/2558) feat: sort by role in node list view
* [#2554](https://github.com/derailed/k9s/pull/2554) Added context to the debug command for debug-container plugin
* [#2554](https://github.com/derailed/k9s/pull/2554) Correctly respect the KUBECACHEDIR env var
* [#2546](https://github.com/derailed/k9s/pull/2546) Use configured log fgColor to print log markers

---

<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/imhotep_logo.png" width="32" height="auto"/> © 2024 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0)
4 changes: 2 additions & 2 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ func loadConfiguration() (*config.Config, error) {
errs = errors.Join(errs, err)
}

if err := k9sCfg.Load(config.AppConfigFile); err != nil {
if err := k9sCfg.Load(config.AppConfigFile, false); err != nil {
errs = errors.Join(errs, err)
}
k9sCfg.K9s.Override(k9sFlags)
Expand All @@ -151,7 +151,7 @@ func loadConfiguration() (*config.Config, error) {
}

log.Info().Msg("✅ Kubernetes connectivity")
if err := k9sCfg.Save(); err != nil {
if err := k9sCfg.Save(false); err != nil {
log.Error().Err(err).Msg("Config save")
errs = errors.Join(errs, err)
}
Expand Down
60 changes: 14 additions & 46 deletions internal/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,64 +212,26 @@ func (a *APIClient) ServerVersion() (*version.Info, error) {
return info, nil
}

func (a *APIClient) IsValidNamespace(ns string) bool {
if IsClusterWide(ns) || ns == NotNamespaced {
return true
}

ok, err := a.CanI(ClusterScope, "v1/namespaces", "", []string{ListVerb})
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(cacheNSKey)
if !ok {
return make(NamespaceNames)
func (a *APIClient) IsValidNamespace(n string) bool {
ok, err := a.isValidNamespace(n)
if err != nil {
log.Warn().Err(err).Msgf("namespace validation failed for: %q", n)
}

return cns.(NamespaceNames)
return ok
}

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")
}

cnss := a.cachedNamespaceNames()
if _, ok := cnss[n]; ok {
return true, nil
}

dial, err := a.Dial()
if err != nil {
return false, err
}
ctx, cancel := context.WithTimeout(context.Background(), a.config.CallTimeout())
defer cancel()
_, err = dial.CoreV1().Namespaces().Get(ctx, n, metav1.GetOptions{})
nn, err := a.ValidNamespaceNames()
if err != nil {
return false, err
}
cnss[n] = struct{}{}
a.cache.Add(cacheNSKey, cnss, cacheExpiry)
_, ok := nn[n]

return true, nil
return ok, nil
}

// ValidNamespaceNames returns all available namespaces.
Expand All @@ -283,6 +245,12 @@ func (a *APIClient) ValidNamespaceNames() (NamespaceNames, error) {
return nss, nil
}
}

ok, err := a.CanI(ClusterScope, "v1/namespaces", "", []string{ListVerb})
if !ok || err != nil {
return nil, fmt.Errorf("user not authorized to list all namespaces")
}

dial, err := a.Dial()
if err != nil {
return nil, err
Expand Down
2 changes: 1 addition & 1 deletion internal/client/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const (
defaultCallTimeoutDuration time.Duration = 15 * time.Second

// UsePersistentConfig caches client config to avoid reloads.
UsePersistentConfig = false
UsePersistentConfig = true
)

// Config tracks a kubernetes configuration.
Expand Down
4 changes: 3 additions & 1 deletion internal/config/alias.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
package config

import (
"errors"
"fmt"
"io/fs"
"os"
"sync"

Expand Down Expand Up @@ -136,7 +138,7 @@ func (a *Aliases) LoadFile(path string) error {
if path == "" {
return nil
}
if _, err := os.Stat(path); os.IsNotExist(err) {
if _, err := os.Stat(path); errors.Is(err, fs.ErrNotExist) {
return nil
}

Expand Down
20 changes: 10 additions & 10 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package config
import (
"errors"
"fmt"
"io/fs"
"os"

"github.com/derailed/k9s/internal/client"
Expand Down Expand Up @@ -52,14 +53,13 @@ func (c *Config) ContextAliasesPath() string {
}

// ContextPluginsPath returns a context specific plugins file spec.
func (c *Config) ContextPluginsPath() string {
func (c *Config) ContextPluginsPath() (string, error) {
ct, err := c.K9s.ActiveContext()
if err != nil {
log.Error().Err(err).Msgf("active context load failed")
return ""
return "", err
}

return AppContextPluginsFile(ct.GetClusterName(), c.K9s.activeContextName)
return AppContextPluginsFile(ct.GetClusterName(), c.K9s.activeContextName), nil
}

// Refine the configuration based on cli args.
Expand Down Expand Up @@ -209,9 +209,9 @@ func (c *Config) Merge(c1 *Config) {
}

// Load loads K9s configuration from file.
func (c *Config) Load(path string) error {
if _, err := os.Stat(path); os.IsNotExist(err) {
if err := c.Save(); err != nil {
func (c *Config) Load(path string, force bool) error {
if _, err := os.Stat(path); errors.Is(err, fs.ErrNotExist) {
if err := c.Save(force); err != nil {
return err
}
}
Expand All @@ -234,12 +234,12 @@ func (c *Config) Load(path string) error {
}

// Save configuration to disk.
func (c *Config) Save() error {
func (c *Config) Save(force bool) error {
c.Validate()
if err := c.K9s.Save(); err != nil {
if err := c.K9s.Save(force); err != nil {
return err
}
if _, err := os.Stat(AppConfigFile); os.IsNotExist(err) {
if _, err := os.Stat(AppConfigFile); errors.Is(err, fs.ErrNotExist) {
return c.SaveFile(AppConfigFile)
}

Expand Down
32 changes: 20 additions & 12 deletions internal/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package config_test

import (
"errors"
"fmt"
"os"
"path/filepath"
Expand Down Expand Up @@ -58,7 +59,7 @@ func TestConfigSave(t *testing.T) {
c.K9s.Override(u.k9sFlags)
assert.NoError(t, c.Refine(u.flags, u.k9sFlags, client.NewConfig(u.flags)))
}
assert.NoError(t, c.Save())
assert.NoError(t, c.Save(true))
bb, err := os.ReadFile(config.AppConfigFile)
assert.NoError(t, err)
ee, err := os.ReadFile("testdata/configs/default.yaml")
Expand Down Expand Up @@ -265,16 +266,19 @@ func TestContextAliasesPath(t *testing.T) {

func TestContextPluginsPath(t *testing.T) {
uu := map[string]struct {
ct string
e string
ct, e string
err error
}{
"empty": {},
"empty": {
err: errors.New(`no context found for: ""`),
},
"happy": {
ct: "ct-1-1",
e: "/tmp/test/cl-1/ct-1-1/plugins.yaml",
},
"not-exists": {
ct: "fred",
ct: "fred",
err: errors.New(`no context found for: "fred"`),
},
}

Expand All @@ -283,7 +287,11 @@ func TestContextPluginsPath(t *testing.T) {
t.Run(k, func(t *testing.T) {
c := mock.NewMockConfig()
_, _ = c.K9s.ActivateContext(u.ct)
assert.Equal(t, u.e, c.ContextPluginsPath())
s, err := c.ContextPluginsPath()
if err != nil {
assert.Equal(t, u.err, err)
}
assert.Equal(t, u.e, s)
})
}
}
Expand All @@ -309,7 +317,7 @@ Invalid type. Expected: boolean, given: string`,
u := uu[k]
t.Run(k, func(t *testing.T) {
cfg := config.NewConfig(nil)
if err := cfg.Load(u.f); err != nil {
if err := cfg.Load(u.f, true); err != nil {
assert.Equal(t, u.err, err.Error())
}
})
Expand Down Expand Up @@ -520,14 +528,14 @@ func TestConfigValidate(t *testing.T) {
cfg := mock.NewMockConfig()
cfg.SetConnection(mock.NewMockConnection())

assert.Nil(t, cfg.Load("testdata/configs/k9s.yaml"))
assert.Nil(t, cfg.Load("testdata/configs/k9s.yaml", true))
cfg.Validate()
}

func TestConfigLoad(t *testing.T) {
cfg := mock.NewMockConfig()

assert.Nil(t, cfg.Load("testdata/configs/k9s.yaml"))
assert.Nil(t, cfg.Load("testdata/configs/k9s.yaml", true))
assert.Equal(t, 2, cfg.K9s.RefreshRate)
assert.Equal(t, int64(200), cfg.K9s.Logger.TailCount)
assert.Equal(t, 2000, cfg.K9s.Logger.BufferSize)
Expand All @@ -536,13 +544,13 @@ func TestConfigLoad(t *testing.T) {
func TestConfigLoadCrap(t *testing.T) {
cfg := mock.NewMockConfig()

assert.NotNil(t, cfg.Load("testdata/configs/k9s_not_there.yaml"))
assert.NotNil(t, cfg.Load("testdata/configs/k9s_not_there.yaml", true))
}

func TestConfigSaveFile(t *testing.T) {
cfg := mock.NewMockConfig()

assert.Nil(t, cfg.Load("testdata/configs/k9s.yaml"))
assert.Nil(t, cfg.Load("testdata/configs/k9s.yaml", true))

cfg.K9s.RefreshRate = 100
cfg.K9s.ReadOnly = true
Expand All @@ -561,7 +569,7 @@ func TestConfigSaveFile(t *testing.T) {

func TestConfigReset(t *testing.T) {
cfg := mock.NewMockConfig()
assert.Nil(t, cfg.Load("testdata/configs/k9s.yaml"))
assert.Nil(t, cfg.Load("testdata/configs/k9s.yaml", true))
cfg.Reset()
cfg.Validate()

Expand Down
9 changes: 9 additions & 0 deletions internal/config/data/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,15 @@ func NewConfig(ct *api.Context) *Config {
}
}

func (c *Config) Merge(c1 *Config) {
if c1 == nil {
return
}
if c.Context != nil && c1.Context != nil {
c.Context.merge(c1.Context)
}
}

// Validate ensures config is in norms.
func (c *Config) Validate(conn client.Connection, ks KubeSettings) {
c.mx.Lock()
Expand Down

0 comments on commit 0d16531

Please sign in to comment.