release v0.25.16 (#1400)
parent
df613ec88d
commit
fc8ffe5d37
2
Makefile
2
Makefile
|
|
@ -5,7 +5,7 @@ PACKAGE := github.com/derailed/$(NAME)
|
|||
GIT_REV ?= $(shell git rev-parse --short HEAD)
|
||||
SOURCE_DATE_EPOCH ?= $(shell date +%s)
|
||||
DATE ?= $(shell date -u -d @${SOURCE_DATE_EPOCH} +"%Y-%m-%dT%H:%M:%SZ")
|
||||
VERSION ?= v0.25.15
|
||||
VERSION ?= v0.25.16
|
||||
IMG_NAME := derailed/k9s
|
||||
IMAGE := ${IMG_NAME}:${VERSION}
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 146 KiB |
|
|
@ -0,0 +1,81 @@
|
|||
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/k9s.png" align="center" width="800" height="auto"/>
|
||||
|
||||
# Release v0.25.16
|
||||
|
||||
## 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!
|
||||
|
||||
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)
|
||||
|
||||
### A Word From Our Sponsors...
|
||||
|
||||
I want to recognize the following folks that have been kind enough to join our sponsorship program and opted to `pay it forward`!
|
||||
|
||||
* [Sebastian Racs](https://github.com/sebracs)
|
||||
* [Timothy C. Arland](https://github.com/tcarland)
|
||||
* [Julie Ng](https://github.com/julie-ng)
|
||||
|
||||
So if you feel K9s is helping with your productivity while administering your Kubernetes clusters, please consider pitching in as it will go a long way in ensuring a thriving environment for this repo and our k9ers community at large.
|
||||
|
||||
Also please take some time and give a huge shoot out to all the good folks below that have spent time plowing thru the code to help improve K9s for all of us!
|
||||
|
||||
Thank you!!
|
||||
|
||||
---
|
||||
|
||||
## ♫ Sounds Behind The Release ♭
|
||||
|
||||
[Blue Christmas - Fats Domino](https://www.youtube.com/watch?v=7jeo09zAskc)
|
||||
[Mele Kalikimaka - Bing Crosby](https://www.youtube.com/watch?v=hEvGKUXW0iI)
|
||||
[Cause - Rodriguez -- Spreading The Holiday Cheer! 🤨](https://www.youtube.com/watch?v=oKFkc19T3Dk)
|
||||
|
||||
---
|
||||
|
||||
## 🎅🎄 !!Merry Christmas To All!! 🎄🎅
|
||||
|
||||
I hope you will take this time of the year to relax, re-source and spend quality time with your loved ones. I know it's been a `tad rocky` of recent ;( as I've gotten seriously slammed with work in the last few months...
|
||||
The fine folks here on this channel have been nothing but kind, patient and willing to help, this humbles me! I feel truly blessed to be affiliated with our great `k9sers` community!
|
||||
Next month, we'll celebrate our anniversary as we've started out in this venture back in Jan 2019 (Yikes!) so get crack'in and iron out those bow ties already!!
|
||||
|
||||
Best wishes for great health, happiness and continued success for 2022 to you all!!
|
||||
|
||||
-Fernand
|
||||
|
||||
---
|
||||
|
||||
## A Christmas Story...
|
||||
|
||||
As of this drop, we've added a new feature to override the sort column and order for a given Kubernetes resource. This feature piggy backs of custom column views and add a new attribute namely `sortColumn`. For example say you'd like to set the default sort for pods to age descending vs name/namespace, you can now do the following in your `views.yml` file in the k9s config directory:
|
||||
|
||||
NOTE: This file is live thus you can nav to your fav resource, change the column config and view the resource columns and sort changes... Woot!!
|
||||
|
||||
```yaml
|
||||
k9s:
|
||||
views:
|
||||
v1/endpoints:
|
||||
columns:
|
||||
- NAME
|
||||
- NAMESPACE
|
||||
- ENDPOINTS
|
||||
- AGE
|
||||
v1/pods:
|
||||
sortColumn: AGE:desc # => suffix [:asc|:desc] for ascending or descending order.
|
||||
v1/services:
|
||||
...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Resolved Issues
|
||||
|
||||
* [Issue #1398](https://github.com/derailed/k9s/issues/1398) Pod logs containing brackets not in k9s logs output
|
||||
* [Issue #1397](https://github.com/derailed/k9s/issues/1397) Regression: k9s no longer starts in current context namespace since v0.25.12
|
||||
* [Issue #1358](https://github.com/derailed/k9s/issues/1358) Namespaces list is empty
|
||||
* [Issue #956](https://github.com/derailed/k9s/issues/956) Feature request : Default column sort (by resource view)
|
||||
|
||||
---
|
||||
|
||||
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/imhotep_logo.png" width="32" height="auto"/> © 2021 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0)
|
||||
29
cmd/root.go
29
cmd/root.go
|
|
@ -2,9 +2,6 @@ package cmd
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime/debug"
|
||||
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
"github.com/derailed/k9s/internal/color"
|
||||
"github.com/derailed/k9s/internal/config"
|
||||
|
|
@ -14,6 +11,8 @@ import (
|
|||
"github.com/rs/zerolog/log"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
"os"
|
||||
"runtime/debug"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -53,6 +52,17 @@ func Execute() {
|
|||
}
|
||||
|
||||
func run(cmd *cobra.Command, args []string) {
|
||||
config.EnsurePath(*k9sFlags.LogFile, config.DefaultDirMod)
|
||||
mod := os.O_CREATE | os.O_APPEND | os.O_WRONLY
|
||||
file, err := os.OpenFile(*k9sFlags.LogFile, mod, config.DefaultFileMod)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer func() {
|
||||
if file != nil {
|
||||
_ = file.Close()
|
||||
}
|
||||
}()
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
log.Error().Msgf("Boom! %v", err)
|
||||
|
|
@ -63,15 +73,6 @@ func run(cmd *cobra.Command, args []string) {
|
|||
}
|
||||
}()
|
||||
|
||||
config.EnsurePath(*k9sFlags.LogFile, config.DefaultDirMod)
|
||||
mod := os.O_CREATE | os.O_APPEND | os.O_WRONLY
|
||||
file, err := os.OpenFile(*k9sFlags.LogFile, mod, config.DefaultFileMod)
|
||||
defer func() {
|
||||
_ = file.Close()
|
||||
}()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
log.Logger = log.Output(zerolog.ConsoleWriter{Out: file})
|
||||
|
||||
zerolog.SetGlobalLevel(parseLevel(*k9sFlags.LogLevel))
|
||||
|
|
@ -136,6 +137,8 @@ func loadConfiguration() *config.Config {
|
|||
|
||||
func parseLevel(level string) zerolog.Level {
|
||||
switch level {
|
||||
case "trace":
|
||||
return zerolog.TraceLevel
|
||||
case "debug":
|
||||
return zerolog.DebugLevel
|
||||
case "warn":
|
||||
|
|
@ -161,7 +164,7 @@ func initK9sFlags() {
|
|||
k9sFlags.LogLevel,
|
||||
"logLevel", "l",
|
||||
config.DefaultLogLevel,
|
||||
"Specify a log level (info, warn, debug, error)",
|
||||
"Specify a log level (info, warn, debug, trace, error)",
|
||||
)
|
||||
rootCmd.Flags().StringVarP(
|
||||
k9sFlags.LogFile,
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ func makeSAR(ns, gvr string) *authorizationv1.SelfSubjectAccessReview {
|
|||
ResourceAttributes: &authorizationv1.ResourceAttributes{
|
||||
Namespace: ns,
|
||||
Group: res.Group,
|
||||
Version: res.Version,
|
||||
Resource: res.Resource,
|
||||
Subresource: spec.SubResource(),
|
||||
},
|
||||
|
|
@ -162,6 +163,7 @@ func (a *APIClient) CanI(ns, gvr string, verbs []string) (auth bool, err error)
|
|||
for _, v := range verbs {
|
||||
sar.Spec.ResourceAttributes.Verb = v
|
||||
resp, err := client.Create(ctx, sar, metav1.CreateOptions{})
|
||||
log.Trace().Msgf("[CAN] %s(%s) %v <<%v>>", gvr, verbs, resp, err)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msgf(" Dial Failed!")
|
||||
a.cache.Add(key, false, cacheExpiry)
|
||||
|
|
|
|||
|
|
@ -21,6 +21,9 @@ const (
|
|||
// AllNamespaces designates all namespaces.
|
||||
AllNamespaces = ""
|
||||
|
||||
// DefaultNamespace designates the default namespace.
|
||||
DefaultNamespace = "default"
|
||||
|
||||
// ClusterScope designates a resource is not namespaced.
|
||||
ClusterScope = "-"
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,9 @@ func (c *Cluster) Validate(conn client.Connection, ks KubeSettings) {
|
|||
if c.Namespace == nil {
|
||||
c.Namespace = NewNamespace()
|
||||
}
|
||||
if c.Namespace.Active == client.AllNamespaces {
|
||||
c.Namespace.Active = client.NamespaceAll
|
||||
}
|
||||
|
||||
if c.FeatureGates == nil {
|
||||
c.FeatureGates = NewFeatureGates()
|
||||
|
|
|
|||
|
|
@ -90,32 +90,24 @@ func (c *Config) Refine(flags *genericclioptions.ConfigFlags, k9sFlags *Flags, c
|
|||
return fmt.Errorf("The specified context %q does not exists in kubeconfig", c.K9s.CurrentContext)
|
||||
}
|
||||
c.K9s.CurrentCluster = context.Cluster
|
||||
c.K9s.ActivateCluster()
|
||||
c.K9s.ActivateCluster(context.Namespace)
|
||||
|
||||
var cns string
|
||||
if cl := c.K9s.ActiveCluster(); cl != nil && cl.Namespace != nil {
|
||||
cns = cl.Namespace.Active
|
||||
}
|
||||
var ns string
|
||||
var ns = client.DefaultNamespace
|
||||
if k9sFlags != nil && IsBoolSet(k9sFlags.AllNamespaces) {
|
||||
ns = client.NamespaceAll
|
||||
} else if isSet(flags.Namespace) {
|
||||
ns = *flags.Namespace
|
||||
} else if context.Namespace != "" {
|
||||
} else if isSet(flags.Context) {
|
||||
ns = context.Namespace
|
||||
if cns != "" {
|
||||
ns = cns
|
||||
}
|
||||
} else {
|
||||
ns = cns
|
||||
ns = c.K9s.ActiveCluster().Namespace.Active
|
||||
}
|
||||
|
||||
if ns != "" {
|
||||
if err := c.SetActiveNamespace(ns); err != nil {
|
||||
return err
|
||||
}
|
||||
flags.Namespace = &ns
|
||||
if err := c.SetActiveNamespace(ns); err != nil {
|
||||
return err
|
||||
}
|
||||
flags.Namespace = &ns
|
||||
|
||||
if isSet(flags.ClusterName) {
|
||||
c.K9s.CurrentCluster = *flags.ClusterName
|
||||
}
|
||||
|
|
@ -166,9 +158,6 @@ func (c *Config) ActiveNamespace() string {
|
|||
// ValidateFavorites ensure favorite ns are legit.
|
||||
func (c *Config) ValidateFavorites() {
|
||||
cl := c.K9s.ActiveCluster()
|
||||
if cl == nil {
|
||||
cl = NewCluster()
|
||||
}
|
||||
cl.Validate(c.client, c.settings)
|
||||
cl.Namespace.Validate(c.client, c.settings)
|
||||
}
|
||||
|
|
@ -176,16 +165,14 @@ func (c *Config) ValidateFavorites() {
|
|||
// FavNamespaces returns fav namespaces in the current cluster.
|
||||
func (c *Config) FavNamespaces() []string {
|
||||
cl := c.K9s.ActiveCluster()
|
||||
if cl == nil {
|
||||
return nil
|
||||
}
|
||||
return c.K9s.ActiveCluster().Namespace.Favorites
|
||||
|
||||
return cl.Namespace.Favorites
|
||||
}
|
||||
|
||||
// SetActiveNamespace set the active namespace in the current cluster.
|
||||
func (c *Config) SetActiveNamespace(ns string) error {
|
||||
if c.K9s.ActiveCluster() != nil {
|
||||
return c.K9s.ActiveCluster().Namespace.SetActive(ns, c.settings)
|
||||
if cl := c.K9s.ActiveCluster(); cl != nil {
|
||||
return cl.Namespace.SetActive(ns, c.settings)
|
||||
}
|
||||
err := errors.New("no active cluster. unable to set active namespace")
|
||||
log.Error().Err(err).Msg("SetActiveNamespace")
|
||||
|
|
@ -195,11 +182,11 @@ func (c *Config) SetActiveNamespace(ns string) error {
|
|||
|
||||
// ActiveView returns the active view in the current cluster.
|
||||
func (c *Config) ActiveView() string {
|
||||
if c.K9s.ActiveCluster() == nil {
|
||||
cl := c.K9s.ActiveCluster()
|
||||
if cl == nil {
|
||||
return defaultView
|
||||
}
|
||||
|
||||
cmd := c.K9s.ActiveCluster().View.Active
|
||||
cmd := cl.View.Active
|
||||
if c.K9s.manualCommand != nil && *c.K9s.manualCommand != "" {
|
||||
cmd = *c.K9s.manualCommand
|
||||
}
|
||||
|
|
@ -209,8 +196,7 @@ func (c *Config) ActiveView() string {
|
|||
|
||||
// SetActiveView set the currently cluster active view.
|
||||
func (c *Config) SetActiveView(view string) {
|
||||
cl := c.K9s.ActiveCluster()
|
||||
if cl != nil {
|
||||
if cl := c.K9s.ActiveCluster(); cl != nil {
|
||||
cl.View.Active = view
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ func TestConfigRefine(t *testing.T) {
|
|||
issue: false,
|
||||
context: "test1",
|
||||
cluster: "cluster1",
|
||||
namespace: "default",
|
||||
namespace: "ns1",
|
||||
},
|
||||
"overrideNS": {
|
||||
flags: &genericclioptions.ConfigFlags{
|
||||
|
|
|
|||
|
|
@ -47,11 +47,13 @@ func NewK9s() *K9s {
|
|||
}
|
||||
|
||||
// ActivateCluster initializes the active cluster is not present.
|
||||
func (k *K9s) ActivateCluster() {
|
||||
func (k *K9s) ActivateCluster(ns string) {
|
||||
if _, ok := k.Clusters[k.CurrentCluster]; ok {
|
||||
return
|
||||
}
|
||||
k.Clusters[k.CurrentCluster] = NewCluster()
|
||||
cl := NewCluster()
|
||||
cl.Namespace.Active = ns
|
||||
k.Clusters[k.CurrentCluster] = cl
|
||||
}
|
||||
|
||||
// OverrideRefreshRate set the refresh rate manually.
|
||||
|
|
@ -59,17 +61,17 @@ func (k *K9s) OverrideRefreshRate(r int) {
|
|||
k.manualRefreshRate = r
|
||||
}
|
||||
|
||||
// OverrideHeadless set the headlessness manually.
|
||||
// OverrideHeadless toggle the header manually.
|
||||
func (k *K9s) OverrideHeadless(b bool) {
|
||||
k.manualHeadless = &b
|
||||
}
|
||||
|
||||
// OverrideLogoless set the logolessness manually.
|
||||
// OverrideLogoless toggle the k9s logo manually.
|
||||
func (k *K9s) OverrideLogoless(b bool) {
|
||||
k.manualLogoless = &b
|
||||
}
|
||||
|
||||
// OverrideCrumbsless set the crumbslessness manually.
|
||||
// OverrideCrumbsless tooh the crumbslessness manually.
|
||||
func (k *K9s) OverrideCrumbsless(b bool) {
|
||||
k.manualCrumbsless = &b
|
||||
}
|
||||
|
|
@ -154,7 +156,6 @@ func (k *K9s) ActiveCluster() *Cluster {
|
|||
if k.Clusters == nil {
|
||||
k.Clusters = map[string]*Cluster{}
|
||||
}
|
||||
|
||||
if c, ok := k.Clusters[k.CurrentCluster]; ok {
|
||||
return c
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ type Namespace struct {
|
|||
func NewNamespace() *Namespace {
|
||||
return &Namespace{
|
||||
Active: defaultNS,
|
||||
Favorites: []string{"default"},
|
||||
Favorites: []string{defaultNS},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ type ViewConfigListener interface {
|
|||
|
||||
// ViewSetting represents a view configuration.
|
||||
type ViewSetting struct {
|
||||
Columns []string `yaml:"columns"`
|
||||
Columns []string `yaml:"columns"`
|
||||
SortColumn string `yaml:"sortColumn"`
|
||||
}
|
||||
|
||||
// ViewSettings represent a collection of view configurations.
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ func (o *LogOptions) ToLogItem(bytes []byte) *LogItem {
|
|||
|
||||
func (o *LogOptions) ToErrLogItem(err error) *LogItem {
|
||||
t := time.Now().UTC().Format(time.RFC3339Nano)
|
||||
item := NewLogItem([]byte(fmt.Sprintf("%s [red::b]%s\n", t, err)))
|
||||
item := NewLogItem([]byte(fmt.Sprintf("%s [orange::b]%s[::-]\n", t, err)))
|
||||
item.IsError = true
|
||||
return item
|
||||
}
|
||||
|
|
|
|||
|
|
@ -313,18 +313,14 @@ func (p *Pod) Scan(ctx context.Context, gvr, fqn string, wait bool) (Refs, error
|
|||
|
||||
func tailLogs(ctx context.Context, logger Logger, opts *LogOptions) LogChan {
|
||||
var (
|
||||
out = make(LogChan, 2)
|
||||
wg sync.WaitGroup
|
||||
out = make(LogChan, 2)
|
||||
wg sync.WaitGroup
|
||||
)
|
||||
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer func() {
|
||||
wg.Done()
|
||||
log.Debug().Msgf("<<< RETRY-TAIL DONE!!! %s", opts.Info())
|
||||
}()
|
||||
defer wg.Done()
|
||||
podOpts := opts.ToPodLogOptions()
|
||||
log.Debug().Msgf(">>> RETRY-TAIL START %s", opts.Info())
|
||||
var stream io.ReadCloser
|
||||
for r := 0; r < logRetryCount; r++ {
|
||||
var e error
|
||||
|
|
@ -345,7 +341,6 @@ func tailLogs(ctx context.Context, logger Logger, opts *LogOptions) LogChan {
|
|||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
log.Debug().Msgf("LOG CANCELED %s", opts.Info())
|
||||
return
|
||||
default:
|
||||
if e != nil {
|
||||
|
|
@ -358,7 +353,6 @@ func tailLogs(ctx context.Context, logger Logger, opts *LogOptions) LogChan {
|
|||
go func() {
|
||||
wg.Wait()
|
||||
close(out)
|
||||
log.Debug().Msgf("<<< LOG-TAILER %s DONE!!", opts.Info())
|
||||
}()
|
||||
|
||||
return out
|
||||
|
|
@ -369,7 +363,6 @@ func readLogs(ctx context.Context, wg *sync.WaitGroup, stream io.ReadCloser, out
|
|||
if err := stream.Close(); err != nil {
|
||||
log.Error().Err(err).Msgf("Fail to close stream %s", opts.Info())
|
||||
}
|
||||
log.Debug().Msgf("<<< LOG-READER EXIT!!! %s", opts.Info())
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
|
|
@ -383,11 +376,11 @@ func readLogs(ctx context.Context, wg *sync.WaitGroup, stream io.ReadCloser, out
|
|||
if errors.Is(err, io.EOF) {
|
||||
e := fmt.Errorf("Stream closed %w for %s", err, opts.Info())
|
||||
item = opts.ToErrLogItem(e)
|
||||
log.Debug().Err(e).Msg("log-reader EOF")
|
||||
log.Warn().Err(e).Msg("log-reader EOF")
|
||||
} else {
|
||||
e := fmt.Errorf("Stream canceled %w for %s", err, opts.Info())
|
||||
item = opts.ToErrLogItem(e)
|
||||
log.Debug().Err(e).Msg("log-reader canceled")
|
||||
log.Warn().Err(e).Msg("log-reader canceled")
|
||||
}
|
||||
}
|
||||
select {
|
||||
|
|
|
|||
|
|
@ -147,12 +147,15 @@ func (h Header) HasAge() bool {
|
|||
|
||||
// IsMetricsCol checks if given column index represents metrics.
|
||||
func (h Header) IsMetricsCol(col int) bool {
|
||||
if col < 0 || col >= len(h) {
|
||||
return false
|
||||
}
|
||||
return h[col].MX
|
||||
}
|
||||
|
||||
// IsTimeCol checks if given column index represents a timestamp.
|
||||
func (h Header) IsTimeCol(col int) bool {
|
||||
if col >= len(h) {
|
||||
if col < 0 || col >= len(h) {
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -191,7 +191,9 @@ func (t *Table) Update(data render.TableData, hasMetrics bool) {
|
|||
func (t *Table) doUpdate(data render.TableData) {
|
||||
if client.IsAllNamespaces(data.Namespace) {
|
||||
t.actions[KeyShiftP] = NewKeyAction("Sort Namespace", t.SortColCmd("NAMESPACE", true), false)
|
||||
t.sortCol.name = "NAMESPACE"
|
||||
} else {
|
||||
t.sortCol.name = "NAME"
|
||||
t.actions.Delete(KeyShiftP)
|
||||
}
|
||||
|
||||
|
|
@ -203,16 +205,24 @@ func (t *Table) doUpdate(data render.TableData) {
|
|||
cols = t.header.Columns(t.wide)
|
||||
}
|
||||
custData := data.Customize(cols, t.wide)
|
||||
|
||||
if (t.sortCol.name == "" || custData.Header.IndexOf(t.sortCol.name, false) == -1) && len(custData.Header) > 0 && t.sortCol.name != "NONE" {
|
||||
t.sortCol.name = custData.Header[0].Name
|
||||
if t.sortCol.name == "NAMESPACE" && !client.IsAllNamespaces(data.Namespace) {
|
||||
if idx := custData.Header.IndexOf("NAME", false); idx != -1 {
|
||||
t.sortCol.name = custData.Header[idx].Name
|
||||
if t.viewSetting != nil && t.viewSetting.SortColumn != "" {
|
||||
tokens := strings.Split(t.viewSetting.SortColumn, ":")
|
||||
if custData.Header.IndexOf(tokens[0], false) >= 0 {
|
||||
t.sortCol.name, t.sortCol.asc = tokens[0], true
|
||||
if len(tokens) == 2 && tokens[1] == "desc" {
|
||||
t.sortCol.asc = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if t.sortCol.name == "NAMESPACE" && !client.IsAllNamespaces(data.Namespace) && len(custData.Header) > 0 {
|
||||
if idx := custData.Header.IndexOf("NAME", false); idx >= 0 {
|
||||
t.sortCol.name = custData.Header[idx].Name
|
||||
} else {
|
||||
t.sortCol.name = custData.Header[0].Name
|
||||
}
|
||||
}
|
||||
|
||||
t.Clear()
|
||||
fg := t.styles.Table().Header.FgColor.Color()
|
||||
bg := t.styles.Table().Header.BgColor.Color()
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@ func (b *Browser) Start() {
|
|||
b.Table.Start()
|
||||
b.CmdBuff().AddListener(b)
|
||||
if err := b.GetModel().Watch(b.prepareContext()); err != nil {
|
||||
log.Error().Err(err).Msgf("Watcher failed for %s", b.GVR())
|
||||
b.App().Flash().Err(fmt.Errorf("Watcher failed for %s -- %w", b.GVR(), err))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -199,14 +199,17 @@ func ssh(a *App, node string) error {
|
|||
if err := launchShellPod(a, node); err != nil {
|
||||
return err
|
||||
}
|
||||
ns := a.Config.K9s.ActiveCluster().ShellPod.Namespace
|
||||
|
||||
cl := a.Config.K9s.ActiveCluster()
|
||||
ns := cl.ShellPod.Namespace
|
||||
sshIn(a, client.FQN(ns, k9sShellPodName()), k9sShell)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func sshIn(a *App, fqn, co string) {
|
||||
cfg := a.Config.K9s.ActiveCluster().ShellPod
|
||||
cl := a.Config.K9s.ActiveCluster()
|
||||
cfg := cl.ShellPod
|
||||
os, err := getPodOS(a.factory, fqn)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msgf("os detect failed")
|
||||
|
|
@ -232,12 +235,13 @@ func sshIn(a *App, fqn, co string) {
|
|||
}
|
||||
|
||||
func nukeK9sShell(a *App) error {
|
||||
cl := a.Config.K9s.CurrentCluster
|
||||
if !a.Config.K9s.Clusters[cl].FeatureGates.NodeShell {
|
||||
clName := a.Config.K9s.CurrentCluster
|
||||
if !a.Config.K9s.Clusters[clName].FeatureGates.NodeShell {
|
||||
return nil
|
||||
}
|
||||
|
||||
ns := a.Config.K9s.ActiveCluster().ShellPod.Namespace
|
||||
cl := a.Config.K9s.ActiveCluster()
|
||||
ns := cl.ShellPod.Namespace
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
|
||||
defer cancel()
|
||||
|
||||
|
|
@ -256,8 +260,9 @@ func nukeK9sShell(a *App) error {
|
|||
|
||||
func launchShellPod(a *App, node string) error {
|
||||
a.Flash().Infof("Launching node shell on %s...", node)
|
||||
ns := a.Config.K9s.ActiveCluster().ShellPod.Namespace
|
||||
spec := k9sShellPod(node, a.Config.K9s.ActiveCluster().ShellPod)
|
||||
cl := a.Config.K9s.ActiveCluster()
|
||||
ns := cl.ShellPod.Namespace
|
||||
spec := k9sShellPod(node, cl.ShellPod)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
|
|
|
|||
|
|
@ -350,6 +350,7 @@ func (l *Log) Flush(lines [][]byte) {
|
|||
if l.cancelUpdates {
|
||||
break
|
||||
}
|
||||
log.Debug().Msgf("FLUSH %q", string(lines[i]))
|
||||
_, _ = l.ansiWriter.Write(lines[i])
|
||||
}
|
||||
if l.follow {
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ func (l *Logger) Init(_ context.Context) error {
|
|||
if l.title != "" {
|
||||
l.SetBorder(true)
|
||||
}
|
||||
l.SetScrollable(true).SetWrap(true).SetRegions(true)
|
||||
l.SetScrollable(true).SetWrap(true)
|
||||
l.SetDynamicColors(true)
|
||||
l.SetHighlightColor(tcell.ColorOrange)
|
||||
l.SetTitleColor(tcell.ColorAqua)
|
||||
|
|
|
|||
Loading…
Reference in New Issue