diff --git a/Makefile b/Makefile index a71a4564..65b99d45 100644 --- a/Makefile +++ b/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.2 +VERSION ?= v0.25.4 IMG_NAME := derailed/k9s IMAGE := ${IMG_NAME}:${VERSION} diff --git a/README.md b/README.md index 728e7268..b568683b 100644 --- a/README.md +++ b/README.md @@ -278,12 +278,11 @@ K9s uses aliases to navigate most K8s resources. ## K9s Configuration - K9s keeps its configurations inside of a `k9s` directory and the location depends on your operating system. K9s leverages [XDG](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html) to load its various configurations files. For information on the default locations for your OS please see [this link](https://github.com/adrg/xdg/blob/master/README.md). If you are still confused a quick `k9s info` will reveal where k9s is loading its configurations from. if `XDG_CONFIG_HOME` is unset, then K9s will default to look for its configuration in `$HOME/.config/k9s`. Alternatively, you can set `K9SCONFIG` to tell K9s the directory location to pull its configurations from. + K9s keeps its configurations inside of a `k9s` directory and the location depends on your operating system. K9s leverages [XDG](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html) to load its various configurations files. For information on the default locations for your OS please see [this link](https://github.com/adrg/xdg/blob/master/README.md). If you are still confused a quick `k9s info` will reveal where k9s is loading its configurations from. Alternatively, you can set `K9SCONFIG` to tell K9s the directory location to pull its configurations from. - - | Unix | macOS | Windows | - |-----------------|-----------------------------|-----------------------| - | `~/.config/k9s` | `~/Library/Preferences/k9s` | `%LOCALAPPDATA%\k9s` | + | Unix | macOS | Windows | + |-----------------|------------------------------------|-----------------------| + | `~/.config/k9s` | `~/Library/Application Support/k9s` | `%LOCALAPPDATA%\k9s` | > NOTE: This is still in flux and will change while in pre-release stage! diff --git a/change_logs/release_v0.25.4.md b/change_logs/release_v0.25.4.md new file mode 100644 index 00000000..f4b6e902 --- /dev/null +++ b/change_logs/release_v0.25.4.md @@ -0,0 +1,25 @@ + + +# Release v0.25.4 + +## 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) + +## Maintenance Release! + +--- + +## Resolved Issues + +* [Issue #1319](https://github.com/derailed/k9s/issues/1319) Namespace filters are no longer applied on startup +* [Issue #1317](https://github.com/derailed/k9s/issues/1317) port forwarding broke with multiple exposed ports +* [Issue #1316](https://github.com/derailed/k9s/issues/1316) Configuration for macOS is using wrong path + +--- + + © 2020 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0) diff --git a/internal/config/config.go b/internal/config/config.go index 10a52708..f4b04a26 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -4,7 +4,6 @@ import ( "errors" "fmt" "os" - "path" "path/filepath" "github.com/adrg/xdg" @@ -60,14 +59,6 @@ func K9sHome() string { if env := os.Getenv(K9sConfig); env != "" { return env } - if env := os.Getenv("XDG_CONFIG_HOME"); env == "" { - dir, err := os.UserHomeDir() - if err != nil { - log.Error().Err(err).Msgf("user home dir") - return "" - } - return path.Join(dir, ".config", "k9s") - } xdgK9sHome, err := xdg.ConfigFile("k9s") if err != nil { @@ -116,6 +107,8 @@ func (c *Config) Refine(flags *genericclioptions.ConfigFlags, k9sFlags *Flags, c ns, override = *flags.Namespace, true } else if context.Namespace != "" { ns = context.Namespace + } else if cl := c.K9s.ActiveCluster(); cl != nil { + ns = cl.Namespace.Active } if ns != "" { diff --git a/internal/model/fish_buff.go b/internal/model/fish_buff.go index 5d02cbff..ab92f4de 100644 --- a/internal/model/fish_buff.go +++ b/internal/model/fish_buff.go @@ -129,12 +129,6 @@ func (f *FishBuff) fireSuggestionChanged(ss []string) { } else { suggest = ss[f.suggestionIndex] } - f.SetText(f.GetText(), suggest) + f.SetText(f.GetText(), suggest) - // BOZO!! - //for _, l := range f.listeners { - // if listener, ok := l.(SuggestionListener); ok { - // listener.SuggestionChanged(text, sug) - // } - //} } diff --git a/internal/port/co_portspec.go b/internal/port/co_portspec.go index 3328f14b..41b8ce52 100644 --- a/internal/port/co_portspec.go +++ b/internal/port/co_portspec.go @@ -20,6 +20,17 @@ func (c ContainerPortSpecs) Dump() string { return strings.Join(ss, "\n") } +// InSpecs checks if given port matches a spec. +func (c ContainerPortSpecs) MatchSpec(s string) bool { + for _, spec := range c { + if spec.MatchSpec(s) { + return true + } + } + + return false +} + // ToTunnels convert port specs to tunnels. func (c ContainerPortSpecs) ToTunnels(address string) PortTunnels { tt := make(PortTunnels, 0, len(c)) @@ -97,6 +108,15 @@ func NewPortSpec(co, portName string, port int32) ContainerPortSpec { } } +func (c ContainerPortSpec) MatchSpec(s string) bool { + tokens := strings.Split(s, "::") + if len(tokens) < 2 { + return false + } + + return tokens[0] == c.Container && tokens[1] == c.PortNum +} + func (c ContainerPortSpec) ToTunnel(address string) PortTunnel { return PortTunnel{ Address: address, diff --git a/internal/render/pv.go b/internal/render/pv.go index d50dbee2..a66012ea 100644 --- a/internal/render/pv.go +++ b/internal/render/pv.go @@ -127,14 +127,14 @@ func accessMode(aa []v1.PersistentVolumeAccessMode) string { dd := accessDedup(aa) s := make([]string, 0, len(dd)) for _, am := range dd { - switch { - case am == v1.ReadWriteOnce: + switch am { + case v1.ReadWriteOnce: s = append(s, "RWO") - case am == v1.ReadOnlyMany: + case v1.ReadOnlyMany: s = append(s, "ROX") - case am == v1.ReadWriteMany: + case v1.ReadWriteMany: s = append(s, "RWX") - case am == v1.ReadWriteOncePod: + case v1.ReadWriteOncePod: s = append(s, "RWOP") } } diff --git a/internal/ui/dialog/error.go b/internal/ui/dialog/error.go index ad539dbd..c26baa34 100644 --- a/internal/ui/dialog/error.go +++ b/internal/ui/dialog/error.go @@ -28,7 +28,7 @@ func ShowError(styles config.Dialog, pages *ui.Pages, msg string) { } f.SetFocus(0) modal := tview.NewModalForm("", f) - modal.SetText(cowTalk(f, msg)) + modal.SetText(cowTalk(msg)) modal.SetTextColor(tcell.ColorOrangeRed) modal.SetDoneFunc(func(int, string) { dismissError(pages) @@ -41,7 +41,7 @@ func dismissError(pages *ui.Pages) { pages.RemovePage(confirmKey) } -func cowTalk(f *tview.Form, says string) string { +func cowTalk(says string) string { msg := fmt.Sprintf("< Ruroh? %s >", says) buff := make([]string, 0, len(cow)+3) buff = append(buff, msg) diff --git a/internal/view/app.go b/internal/view/app.go index dd10867b..db825f03 100644 --- a/internal/view/app.go +++ b/internal/view/app.go @@ -91,13 +91,7 @@ func (a *App) Init(version string, rate int) error { if a.Conn() == nil { return errors.New("No client connection detected") } - ns, err := a.Conn().Config().CurrentNamespaceName() - log.Debug().Msgf("CURRENT-NS %q -- %v", ns, err) - if err != nil { - log.Info().Msg("No namespace specified using cluster default namespace") - } else if err = a.Config.SetActiveNamespace(ns); err != nil { - log.Error().Err(err).Msgf("Fail to set active namespace to %q", ns) - } + ns := a.Config.ActiveNamespace() a.factory = watch.NewFactory(a.Conn()) ok, err := a.isValidNS(ns) diff --git a/internal/view/browser.go b/internal/view/browser.go index eb8fb0b4..1874beb2 100644 --- a/internal/view/browser.go +++ b/internal/view/browser.go @@ -127,6 +127,9 @@ func (b *Browser) SetInstance(path string) { // Start initializes browser updates. func (b *Browser) Start() { b.app.Config.ValidateFavorites() + if err := b.app.switchNS(b.GetModel().GetNamespace()); err != nil { + log.Error().Err(err).Msgf("ns switch failed") + } if err := b.app.Config.Save(); err != nil { log.Error().Err(err).Msgf("Config Save") } diff --git a/internal/view/command.go b/internal/view/command.go index 96724b0b..448d3a43 100644 --- a/internal/view/command.go +++ b/internal/view/command.go @@ -155,11 +155,8 @@ func (c *Command) defaultCmd() error { } tokens := strings.Split(view, " ") cmd := view - if len(tokens) == 1 || c.app.Conn().Config().OverrideNS { - ns, err := c.app.Conn().Config().CurrentNamespaceName() - if err == nil && !isContextCmd(tokens[0]) { - cmd = tokens[0] + " " + ns - } + if len(tokens) == 1 { + cmd = tokens[0] + " " + c.app.Config.ActiveNamespace() } if err := c.run(cmd, "", true); err != nil { diff --git a/internal/view/exec.go b/internal/view/exec.go index 82ba29e2..07adfd11 100644 --- a/internal/view/exec.go +++ b/internal/view/exec.go @@ -374,6 +374,7 @@ func pipe(ctx context.Context, opts shellOpts, cmds ...*exec.Cmd) error { return cmd.Start() } cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr + // BOZO!! //cmd.SysProcAttr = &syscall.SysProcAttr{ //// //Setpgid: true, //// //Setctty: true, @@ -385,6 +386,8 @@ func pipe(ctx context.Context, opts shellOpts, cmds ...*exec.Cmd) error { err := cmd.Run() log.Debug().Msgf("Running Done") return err + + // BOZO!! // select { // case <-ctx.Done(): // return errors.New("canceled by operator") diff --git a/internal/view/pf_dialog.go b/internal/view/pf_dialog.go index 89921c33..b3119e62 100644 --- a/internal/view/pf_dialog.go +++ b/internal/view/pf_dialog.go @@ -3,12 +3,9 @@ package view import ( "fmt" "math" - "regexp" "strings" - "github.com/derailed/k9s/internal/client" "github.com/derailed/k9s/internal/port" - "github.com/derailed/k9s/internal/render" "github.com/derailed/k9s/internal/ui" "github.com/derailed/tview" "github.com/rs/zerolog/log" @@ -47,13 +44,13 @@ func ShowPortForwards(v ResourceViewer, path string, ports port.ContainerPortSpe coField.SetPlaceholder("Enter a container name/port") } f.AddInputField("Local Port:", p2, fieldLen, nil, nil) - poField := f.GetFormItemByLabel("Local Port:").(*tview.InputField) - if poField.GetText() == "" { - poField.SetPlaceholder("Enter a local port") + loField := f.GetFormItemByLabel("Local Port:").(*tview.InputField) + if loField.GetText() == "" { + loField.SetPlaceholder("Enter a local port") } coField.SetChangedFunc(func(s string) { port := extractPort(s) - poField.SetText(port) + loField.SetText(port) p2 = port }) f.AddInputField("Address:", address, fieldLen, nil, func(h string) { @@ -69,11 +66,15 @@ func ShowPortForwards(v ResourceViewer, path string, ports port.ContainerPortSpe } f.AddButton("OK", func() { - if coField.GetText() == "" || poField.GetText() == "" { + if coField.GetText() == "" || loField.GetText() == "" { v.App().Flash().Err(fmt.Errorf("container to local port mismatch")) return } - tt, err := port.ToTunnels(address, coField.GetText(), poField.GetText()) + if !ports.MatchSpec(coField.GetText()) { + v.App().Flash().Err(fmt.Errorf("invalid container port")) + return + } + tt, err := port.ToTunnels(address, coField.GetText(), loField.GetText()) if err != nil { v.App().Flash().Err(err) return @@ -121,26 +122,11 @@ func DismissPortForwards(v ResourceViewer, p *ui.Pages) { // ---------------------------------------------------------------------------- // Helpers... -func extractPort(p string) string { - rx := regexp.MustCompile(`\A([\w|-]+)/?([\w|-]+)?:?(\d+)?(╱UDP)?\z`) - mm := rx.FindStringSubmatch(p) - if len(mm) != 5 { - return p - } - for i := 3; i > 0; i-- { - if mm[i] != "" { - return mm[i] - } - } - return p -} - -func extractContainer(p string) string { - tokens := strings.Split(p, ":") - if len(tokens) != 2 { - return render.NAValue +func extractPort(coPort string) string { + tokens := strings.Split(coPort, "::") + if len(tokens) < 2 { + return "" } - co, _ := client.Namespaced(tokens[0]) - return co + return tokens[1] } diff --git a/internal/view/pf_dialog_test.go b/internal/view/pf_dialog_test.go index 284af6b1..e59ccaec 100644 --- a/internal/view/pf_dialog_test.go +++ b/internal/view/pf_dialog_test.go @@ -6,25 +6,23 @@ import ( "github.com/stretchr/testify/assert" ) -func TestExtractContainer(t *testing.T) { +func TestExtractPort(t *testing.T) { uu := map[string]struct { - port, e string + portSpec, e string }{ "full": { - "co/port:8000", "co", + portSpec: "co::8000", + e: "8000", }, - "unamed": { - "co/:8000", "co", - }, - "protocol": { - "co/dns:53╱UDP", "co", + "toast": { + portSpec: "co:8000", }, } for k := range uu { u := uu[k] t.Run(k, func(t *testing.T) { - assert.Equal(t, u.e, extractContainer(u.port)) + assert.Equal(t, u.e, extractPort(u.portSpec)) }) } } diff --git a/internal/view/restart_extender.go b/internal/view/restart_extender.go index c1d5a25f..aa153780 100644 --- a/internal/view/restart_extender.go +++ b/internal/view/restart_extender.go @@ -31,7 +31,6 @@ func (r *RestartExtender) bindKeys(aa ui.KeyActions) { return } aa.Add(ui.KeyActions{ - // BOZO!! ui.KeyR: ui.NewKeyAction("Restart", r.restartCmd, true), }) }