parent
1c19ef6ad6
commit
d0f874e01a
2
Makefile
2
Makefile
|
|
@ -11,7 +11,7 @@ DATE ?= $(shell TZ=UTC date -j -f "%s" ${SOURCE_DATE_EPOCH} +"%Y-%m-%dT%H:
|
||||||
else
|
else
|
||||||
DATE ?= $(shell date -u -d @${SOURCE_DATE_EPOCH} +"%Y-%m-%dT%H:%M:%SZ")
|
DATE ?= $(shell date -u -d @${SOURCE_DATE_EPOCH} +"%Y-%m-%dT%H:%M:%SZ")
|
||||||
endif
|
endif
|
||||||
VERSION ?= v0.30.7
|
VERSION ?= v0.30.8
|
||||||
IMG_NAME := derailed/k9s
|
IMG_NAME := derailed/k9s
|
||||||
IMAGE := ${IMG_NAME}:${VERSION}
|
IMAGE := ${IMG_NAME}:${VERSION}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/k9s.png" align="center" width="800" height="auto"/>
|
||||||
|
|
||||||
|
# Release v0.30.8
|
||||||
|
|
||||||
|
## 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
|
||||||
|
|
||||||
|
* [#2423](https://github.com/derailed/k9s/issues/2423) CPU and MEM counters of AKS clusters show not available
|
||||||
|
* [#2418](https://github.com/derailed/k9s/issues/2418) Boom! runtime error: invalid memory address or nil pointer dereference
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 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!!
|
||||||
|
|
||||||
|
* [#2424](https://github.com/derailed/k9s/pull/2424) fix the check for whether the cluster supports metrics
|
||||||
|
|
||||||
|
<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)
|
||||||
|
|
@ -54,7 +54,7 @@ func (c *Context) Validate(conn client.Connection, ks KubeSettings) {
|
||||||
if c.Namespace == nil {
|
if c.Namespace == nil {
|
||||||
c.Namespace = NewNamespace()
|
c.Namespace = NewNamespace()
|
||||||
}
|
}
|
||||||
c.Namespace.Validate(conn, ks)
|
c.Namespace.Validate(conn)
|
||||||
|
|
||||||
if c.View == nil {
|
if c.View == nil {
|
||||||
c.View = NewView()
|
c.View = NewView()
|
||||||
|
|
|
||||||
|
|
@ -22,10 +22,7 @@ type Namespace struct {
|
||||||
|
|
||||||
// NewNamespace create a new namespace configuration.
|
// NewNamespace create a new namespace configuration.
|
||||||
func NewNamespace() *Namespace {
|
func NewNamespace() *Namespace {
|
||||||
return &Namespace{
|
return NewActiveNamespace(client.DefaultNamespace)
|
||||||
Active: client.DefaultNamespace,
|
|
||||||
Favorites: []string{client.DefaultNamespace},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewActiveNamespace(n string) *Namespace {
|
func NewActiveNamespace(n string) *Namespace {
|
||||||
|
|
@ -39,7 +36,7 @@ func NewActiveNamespace(n string) *Namespace {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates a namespace is setup correctly.
|
// Validate validates a namespace is setup correctly.
|
||||||
func (n *Namespace) Validate(c client.Connection, ks KubeSettings) {
|
func (n *Namespace) Validate(c client.Connection) {
|
||||||
if c == nil || !c.IsValidNamespace(n.Active) {
|
if c == nil || !c.IsValidNamespace(n.Active) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -56,7 +53,11 @@ func (n *Namespace) SetActive(ns string, ks KubeSettings) error {
|
||||||
if ns == client.BlankNamespace {
|
if ns == client.BlankNamespace {
|
||||||
ns = client.NamespaceAll
|
ns = client.NamespaceAll
|
||||||
}
|
}
|
||||||
|
if n == nil {
|
||||||
|
n = NewActiveNamespace(ns)
|
||||||
|
} else {
|
||||||
n.Active = ns
|
n.Active = ns
|
||||||
|
}
|
||||||
if ns != "" && !n.LockFavorites {
|
if ns != "" && !n.LockFavorites {
|
||||||
n.addFavNS(ns)
|
n.addFavNS(ns)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ import (
|
||||||
|
|
||||||
func TestNSValidate(t *testing.T) {
|
func TestNSValidate(t *testing.T) {
|
||||||
ns := data.NewNamespace()
|
ns := data.NewNamespace()
|
||||||
ns.Validate(mock.NewMockConnection(), mock.NewMockKubeSettings(makeFlags("cl-1", "ct-1")))
|
ns.Validate(mock.NewMockConnection())
|
||||||
|
|
||||||
assert.Equal(t, "default", ns.Active)
|
assert.Equal(t, "default", ns.Active)
|
||||||
assert.Equal(t, []string{"default"}, ns.Favorites)
|
assert.Equal(t, []string{"default"}, ns.Favorites)
|
||||||
|
|
@ -21,7 +21,7 @@ func TestNSValidate(t *testing.T) {
|
||||||
|
|
||||||
func TestNSValidateMissing(t *testing.T) {
|
func TestNSValidateMissing(t *testing.T) {
|
||||||
ns := data.NewNamespace()
|
ns := data.NewNamespace()
|
||||||
ns.Validate(mock.NewMockConnection(), mock.NewMockKubeSettings(makeFlags("cl-1", "ct-1")))
|
ns.Validate(mock.NewMockConnection())
|
||||||
|
|
||||||
assert.Equal(t, "default", ns.Active)
|
assert.Equal(t, "default", ns.Active)
|
||||||
assert.Equal(t, []string{"default"}, ns.Favorites)
|
assert.Equal(t, []string{"default"}, ns.Favorites)
|
||||||
|
|
@ -29,7 +29,7 @@ func TestNSValidateMissing(t *testing.T) {
|
||||||
|
|
||||||
func TestNSValidateNoNS(t *testing.T) {
|
func TestNSValidateNoNS(t *testing.T) {
|
||||||
ns := data.NewNamespace()
|
ns := data.NewNamespace()
|
||||||
ns.Validate(mock.NewMockConnection(), mock.NewMockKubeSettings(makeFlags("cl-1", "ct-1")))
|
ns.Validate(mock.NewMockConnection())
|
||||||
|
|
||||||
assert.Equal(t, "default", ns.Active)
|
assert.Equal(t, "default", ns.Active)
|
||||||
assert.Equal(t, []string{"default"}, ns.Favorites)
|
assert.Equal(t, []string{"default"}, ns.Favorites)
|
||||||
|
|
@ -61,7 +61,7 @@ func TestNSSetActive(t *testing.T) {
|
||||||
func TestNSValidateRmFavs(t *testing.T) {
|
func TestNSValidateRmFavs(t *testing.T) {
|
||||||
ns := data.NewNamespace()
|
ns := data.NewNamespace()
|
||||||
ns.Favorites = []string{"default", "fred"}
|
ns.Favorites = []string{"default", "fred"}
|
||||||
ns.Validate(mock.NewMockConnection(), mock.NewMockKubeSettings(makeFlags("cl-1", "ct-1")))
|
ns.Validate(mock.NewMockConnection())
|
||||||
|
|
||||||
assert.Equal(t, []string{"default", "fred"}, ns.Favorites)
|
assert.Equal(t, []string{"default", "fred"}, ns.Favorites)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -163,6 +163,13 @@ func (k *K9s) ActiveContextName() string {
|
||||||
// ActiveContext returns the currently active context.
|
// ActiveContext returns the currently active context.
|
||||||
func (k *K9s) ActiveContext() (*data.Context, error) {
|
func (k *K9s) ActiveContext() (*data.Context, error) {
|
||||||
if k.activeConfig != nil {
|
if k.activeConfig != nil {
|
||||||
|
if k.activeConfig.Context == nil {
|
||||||
|
ct, err := k.ks.CurrentContext()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
k.activeConfig.Context = data.NewContextFromConfig(ct)
|
||||||
|
}
|
||||||
return k.activeConfig.Context, nil
|
return k.activeConfig.Context, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -187,6 +194,7 @@ func (k *K9s) ActivateContext(n string) (*data.Context, error) {
|
||||||
}
|
}
|
||||||
// If the context specifies a default namespace, use it!
|
// If the context specifies a default namespace, use it!
|
||||||
if k.conn != nil {
|
if k.conn != nil {
|
||||||
|
k.Validate(k.conn, k.ks)
|
||||||
if ns := k.conn.ActiveNamespace(); ns != client.BlankNamespace {
|
if ns := k.conn.ActiveNamespace(); ns != client.BlankNamespace {
|
||||||
k.activeConfig.Context.Namespace.Active = ns
|
k.activeConfig.Context.Namespace.Active = ns
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -328,17 +336,17 @@ func (k *K9s) Validate(c client.Connection, ks data.KubeSettings) {
|
||||||
if k.ShellPod == nil {
|
if k.ShellPod == nil {
|
||||||
k.ShellPod = NewShellPod()
|
k.ShellPod = NewShellPod()
|
||||||
}
|
}
|
||||||
k.ShellPod.Validate(c, ks)
|
k.ShellPod.Validate()
|
||||||
|
|
||||||
if k.Logger == nil {
|
if k.Logger == nil {
|
||||||
k.Logger = NewLogger()
|
k.Logger = NewLogger()
|
||||||
} else {
|
} else {
|
||||||
k.Logger.Validate(c, ks)
|
k.Logger.Validate()
|
||||||
}
|
}
|
||||||
if k.Thresholds == nil {
|
if k.Thresholds == nil {
|
||||||
k.Thresholds = NewThreshold()
|
k.Thresholds = NewThreshold()
|
||||||
}
|
}
|
||||||
k.Thresholds.Validate(c, ks)
|
k.Thresholds.Validate()
|
||||||
|
|
||||||
if k.activeConfig != nil {
|
if k.activeConfig != nil {
|
||||||
k.activeConfig.Validate(c, ks)
|
k.activeConfig.Validate(c, ks)
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,6 @@
|
||||||
|
|
||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/derailed/k9s/internal/client"
|
|
||||||
"github.com/derailed/k9s/internal/config/data"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// DefaultLoggerTailCount tracks default log tail size.
|
// DefaultLoggerTailCount tracks default log tail size.
|
||||||
DefaultLoggerTailCount = 100
|
DefaultLoggerTailCount = 100
|
||||||
|
|
@ -39,7 +34,7 @@ func NewLogger() *Logger {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate checks thresholds and make sure we're cool. If not use defaults.
|
// Validate checks thresholds and make sure we're cool. If not use defaults.
|
||||||
func (l *Logger) Validate(_ client.Connection, _ data.KubeSettings) {
|
func (l *Logger) Validate() {
|
||||||
if l.TailCount <= 0 {
|
if l.TailCount <= 0 {
|
||||||
l.TailCount = DefaultLoggerTailCount
|
l.TailCount = DefaultLoggerTailCount
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import (
|
||||||
|
|
||||||
func TestNewLogger(t *testing.T) {
|
func TestNewLogger(t *testing.T) {
|
||||||
l := config.NewLogger()
|
l := config.NewLogger()
|
||||||
l.Validate(nil, nil)
|
l.Validate()
|
||||||
|
|
||||||
assert.Equal(t, int64(100), l.TailCount)
|
assert.Equal(t, int64(100), l.TailCount)
|
||||||
assert.Equal(t, 5000, l.BufferSize)
|
assert.Equal(t, 5000, l.BufferSize)
|
||||||
|
|
@ -20,7 +20,7 @@ func TestNewLogger(t *testing.T) {
|
||||||
|
|
||||||
func TestLoggerValidate(t *testing.T) {
|
func TestLoggerValidate(t *testing.T) {
|
||||||
var l config.Logger
|
var l config.Logger
|
||||||
l.Validate(nil, nil)
|
l.Validate()
|
||||||
|
|
||||||
assert.Equal(t, int64(100), l.TailCount)
|
assert.Equal(t, int64(100), l.TailCount)
|
||||||
assert.Equal(t, 5000, l.BufferSize)
|
assert.Equal(t, 5000, l.BufferSize)
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,6 @@
|
||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/derailed/k9s/internal/client"
|
|
||||||
"github.com/derailed/k9s/internal/config/data"
|
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -37,7 +35,7 @@ func NewShellPod() *ShellPod {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the configuration.
|
// Validate validates the configuration.
|
||||||
func (s *ShellPod) Validate(client.Connection, data.KubeSettings) {
|
func (s *ShellPod) Validate() {
|
||||||
if s.Image == "" {
|
if s.Image == "" {
|
||||||
s.Image = defaultDockerShellImage
|
s.Image = defaultDockerShellImage
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -445,10 +445,6 @@ func (s *Styles) Reset() {
|
||||||
s.K9s = newStyle()
|
s.K9s = newStyle()
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultSkin loads the default skin.
|
|
||||||
func (s *Styles) DefaultSkin() {
|
|
||||||
}
|
|
||||||
|
|
||||||
// FgColor returns the foreground color.
|
// FgColor returns the foreground color.
|
||||||
func (s *Styles) FgColor() tcell.Color {
|
func (s *Styles) FgColor() tcell.Color {
|
||||||
return s.Body().FgColor.Color()
|
return s.Body().FgColor.Color()
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,6 @@
|
||||||
|
|
||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/derailed/k9s/internal/client"
|
|
||||||
"github.com/derailed/k9s/internal/config/data"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// SeverityLow tracks low severity.
|
// SeverityLow tracks low severity.
|
||||||
SeverityLow SeverityLevel = iota
|
SeverityLow SeverityLevel = iota
|
||||||
|
|
@ -66,7 +61,7 @@ func NewThreshold() Threshold {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate a namespace is setup correctly.
|
// Validate a namespace is setup correctly.
|
||||||
func (t Threshold) Validate(c client.Connection, ks data.KubeSettings) {
|
func (t Threshold) Validate() {
|
||||||
for _, k := range []string{"cpu", "memory"} {
|
for _, k := range []string{"cpu", "memory"} {
|
||||||
v, ok := t[k]
|
v, ok := t[k]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|
|
||||||
|
|
@ -211,12 +211,12 @@ func (c *Configurator) activeConfig() (cluster string, context string, ok bool)
|
||||||
func (c *Configurator) RefreshStyles() {
|
func (c *Configurator) RefreshStyles() {
|
||||||
if c.Styles == nil {
|
if c.Styles == nil {
|
||||||
c.Styles = config.NewStyles()
|
c.Styles = config.NewStyles()
|
||||||
} else {
|
|
||||||
c.Styles.Reset()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cl, ct, ok := c.activeConfig()
|
cl, ct, ok := c.activeConfig()
|
||||||
if !ok {
|
if !ok {
|
||||||
|
log.Debug().Msgf("No custom skin found. Using stock skin")
|
||||||
|
c.updateStyles("")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -228,7 +228,7 @@ func (c *Configurator) RefreshStyles() {
|
||||||
|
|
||||||
skin, ok := c.activeSkin()
|
skin, ok := c.activeSkin()
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Debug().Msgf("No custom skin found. Loading default")
|
log.Debug().Msgf("No custom skin found. Using stock skin")
|
||||||
c.updateStyles("")
|
c.updateStyles("")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -248,9 +248,6 @@ func (c *Configurator) RefreshStyles() {
|
||||||
|
|
||||||
func (c *Configurator) updateStyles(f string) {
|
func (c *Configurator) updateStyles(f string) {
|
||||||
c.skinFile = f
|
c.skinFile = f
|
||||||
if !c.HasSkin() {
|
|
||||||
c.Styles.DefaultSkin()
|
|
||||||
}
|
|
||||||
c.Styles.Update()
|
c.Styles.Update()
|
||||||
|
|
||||||
render.ModColor = c.Styles.Frame().Status.ModifyColor.Color()
|
render.ModColor = c.Styles.Frame().Status.ModifyColor.Color()
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,8 @@ const (
|
||||||
bannerFmt = "<<K9s-Shell>> Pod: %s | Container: %s \n"
|
bannerFmt = "<<K9s-Shell>> Pod: %s | Container: %s \n"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var editorEnvVars = []string{"KUBE_EDITOR", "K9S_EDITOR", "EDITOR"}
|
||||||
|
|
||||||
type shellOpts struct {
|
type shellOpts struct {
|
||||||
clear, background bool
|
clear, background bool
|
||||||
pipes []string
|
pipes []string
|
||||||
|
|
@ -115,13 +117,23 @@ func run(a *App, opts shellOpts) (bool, chan error, chan string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func edit(a *App, opts shellOpts) bool {
|
func edit(a *App, opts shellOpts) bool {
|
||||||
bin, err := exec.LookPath(os.Getenv("K9S_EDITOR"))
|
var (
|
||||||
if err != nil {
|
bin string
|
||||||
bin, err = exec.LookPath(os.Getenv("EDITOR"))
|
err error
|
||||||
if err != nil {
|
)
|
||||||
log.Error().Err(err).Msgf("K9S_EDITOR|EDITOR not set")
|
for _, e := range editorEnvVars {
|
||||||
return false
|
env := os.Getenv(e)
|
||||||
|
if env != "" {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
bin, err = exec.LookPath(env)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if bin == "" {
|
||||||
|
a.Flash().Errf("You must set at least one of those env vars: %s", strings.Join(editorEnvVars, "|"))
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
opts.binary, opts.background = bin, false
|
opts.binary, opts.background = bin, false
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
name: k9s
|
name: k9s
|
||||||
base: core20
|
base: core20
|
||||||
version: 'v0.30.7'
|
version: 'v0.30.8'
|
||||||
summary: K9s is a CLI to view and manage your Kubernetes clusters.
|
summary: K9s is a CLI to view and manage your Kubernetes clusters.
|
||||||
description: |
|
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.
|
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