K9s: rel v0.25.4 (#1322)

* release v0.25.4

* update docs

* update docs
mine
Fernand Galiana 2021-11-20 13:23:26 -07:00 committed by GitHub
parent f4007fa0ce
commit 165ffeab93
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 91 additions and 80 deletions

View File

@ -5,7 +5,7 @@ PACKAGE := github.com/derailed/$(NAME)
GIT_REV ?= $(shell git rev-parse --short HEAD) GIT_REV ?= $(shell git rev-parse --short HEAD)
SOURCE_DATE_EPOCH ?= $(shell date +%s) SOURCE_DATE_EPOCH ?= $(shell date +%s)
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")
VERSION ?= v0.25.2 VERSION ?= v0.25.4
IMG_NAME := derailed/k9s IMG_NAME := derailed/k9s
IMAGE := ${IMG_NAME}:${VERSION} IMAGE := ${IMG_NAME}:${VERSION}

View File

@ -278,12 +278,11 @@ K9s uses aliases to navigate most K8s resources.
## K9s Configuration ## 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 |
| Unix | macOS | Windows | |-----------------|------------------------------------|-----------------------|
|-----------------|-----------------------------|-----------------------| | `~/.config/k9s` | `~/Library/Application Support/k9s` | `%LOCALAPPDATA%\k9s` |
| `~/.config/k9s` | `~/Library/Preferences/k9s` | `%LOCALAPPDATA%\k9s` |
> NOTE: This is still in flux and will change while in pre-release stage! > NOTE: This is still in flux and will change while in pre-release stage!

View File

@ -0,0 +1,25 @@
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/k9s_small.png" align="right" width="200" height="auto"/>
# 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
---
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/imhotep_logo.png" width="32" height="auto"/> © 2020 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0)

View File

@ -4,7 +4,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"os" "os"
"path"
"path/filepath" "path/filepath"
"github.com/adrg/xdg" "github.com/adrg/xdg"
@ -60,14 +59,6 @@ func K9sHome() string {
if env := os.Getenv(K9sConfig); env != "" { if env := os.Getenv(K9sConfig); env != "" {
return 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") xdgK9sHome, err := xdg.ConfigFile("k9s")
if err != nil { if err != nil {
@ -116,6 +107,8 @@ func (c *Config) Refine(flags *genericclioptions.ConfigFlags, k9sFlags *Flags, c
ns, override = *flags.Namespace, true ns, override = *flags.Namespace, true
} else if context.Namespace != "" { } else if context.Namespace != "" {
ns = context.Namespace ns = context.Namespace
} else if cl := c.K9s.ActiveCluster(); cl != nil {
ns = cl.Namespace.Active
} }
if ns != "" { if ns != "" {

View File

@ -129,12 +129,6 @@ func (f *FishBuff) fireSuggestionChanged(ss []string) {
} else { } else {
suggest = ss[f.suggestionIndex] 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)
// }
//}
} }

View File

@ -20,6 +20,17 @@ func (c ContainerPortSpecs) Dump() string {
return strings.Join(ss, "\n") 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. // ToTunnels convert port specs to tunnels.
func (c ContainerPortSpecs) ToTunnels(address string) PortTunnels { func (c ContainerPortSpecs) ToTunnels(address string) PortTunnels {
tt := make(PortTunnels, 0, len(c)) 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 { func (c ContainerPortSpec) ToTunnel(address string) PortTunnel {
return PortTunnel{ return PortTunnel{
Address: address, Address: address,

View File

@ -127,14 +127,14 @@ func accessMode(aa []v1.PersistentVolumeAccessMode) string {
dd := accessDedup(aa) dd := accessDedup(aa)
s := make([]string, 0, len(dd)) s := make([]string, 0, len(dd))
for _, am := range dd { for _, am := range dd {
switch { switch am {
case am == v1.ReadWriteOnce: case v1.ReadWriteOnce:
s = append(s, "RWO") s = append(s, "RWO")
case am == v1.ReadOnlyMany: case v1.ReadOnlyMany:
s = append(s, "ROX") s = append(s, "ROX")
case am == v1.ReadWriteMany: case v1.ReadWriteMany:
s = append(s, "RWX") s = append(s, "RWX")
case am == v1.ReadWriteOncePod: case v1.ReadWriteOncePod:
s = append(s, "RWOP") s = append(s, "RWOP")
} }
} }

View File

@ -28,7 +28,7 @@ func ShowError(styles config.Dialog, pages *ui.Pages, msg string) {
} }
f.SetFocus(0) f.SetFocus(0)
modal := tview.NewModalForm("<error>", f) modal := tview.NewModalForm("<error>", f)
modal.SetText(cowTalk(f, msg)) modal.SetText(cowTalk(msg))
modal.SetTextColor(tcell.ColorOrangeRed) modal.SetTextColor(tcell.ColorOrangeRed)
modal.SetDoneFunc(func(int, string) { modal.SetDoneFunc(func(int, string) {
dismissError(pages) dismissError(pages)
@ -41,7 +41,7 @@ func dismissError(pages *ui.Pages) {
pages.RemovePage(confirmKey) pages.RemovePage(confirmKey)
} }
func cowTalk(f *tview.Form, says string) string { func cowTalk(says string) string {
msg := fmt.Sprintf("< Ruroh? %s >", says) msg := fmt.Sprintf("< Ruroh? %s >", says)
buff := make([]string, 0, len(cow)+3) buff := make([]string, 0, len(cow)+3)
buff = append(buff, msg) buff = append(buff, msg)

View File

@ -91,13 +91,7 @@ func (a *App) Init(version string, rate int) error {
if a.Conn() == nil { if a.Conn() == nil {
return errors.New("No client connection detected") return errors.New("No client connection detected")
} }
ns, err := a.Conn().Config().CurrentNamespaceName() ns := a.Config.ActiveNamespace()
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)
}
a.factory = watch.NewFactory(a.Conn()) a.factory = watch.NewFactory(a.Conn())
ok, err := a.isValidNS(ns) ok, err := a.isValidNS(ns)

View File

@ -127,6 +127,9 @@ func (b *Browser) SetInstance(path string) {
// Start initializes browser updates. // Start initializes browser updates.
func (b *Browser) Start() { func (b *Browser) Start() {
b.app.Config.ValidateFavorites() 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 { if err := b.app.Config.Save(); err != nil {
log.Error().Err(err).Msgf("Config Save") log.Error().Err(err).Msgf("Config Save")
} }

View File

@ -155,11 +155,8 @@ func (c *Command) defaultCmd() error {
} }
tokens := strings.Split(view, " ") tokens := strings.Split(view, " ")
cmd := view cmd := view
if len(tokens) == 1 || c.app.Conn().Config().OverrideNS { if len(tokens) == 1 {
ns, err := c.app.Conn().Config().CurrentNamespaceName() cmd = tokens[0] + " " + c.app.Config.ActiveNamespace()
if err == nil && !isContextCmd(tokens[0]) {
cmd = tokens[0] + " " + ns
}
} }
if err := c.run(cmd, "", true); err != nil { if err := c.run(cmd, "", true); err != nil {

View File

@ -374,6 +374,7 @@ func pipe(ctx context.Context, opts shellOpts, cmds ...*exec.Cmd) error {
return cmd.Start() return cmd.Start()
} }
cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
// BOZO!!
//cmd.SysProcAttr = &syscall.SysProcAttr{ //cmd.SysProcAttr = &syscall.SysProcAttr{
//// //Setpgid: true, //// //Setpgid: true,
//// //Setctty: true, //// //Setctty: true,
@ -385,6 +386,8 @@ func pipe(ctx context.Context, opts shellOpts, cmds ...*exec.Cmd) error {
err := cmd.Run() err := cmd.Run()
log.Debug().Msgf("Running Done") log.Debug().Msgf("Running Done")
return err return err
// BOZO!!
// select { // select {
// case <-ctx.Done(): // case <-ctx.Done():
// return errors.New("canceled by operator") // return errors.New("canceled by operator")

View File

@ -3,12 +3,9 @@ package view
import ( import (
"fmt" "fmt"
"math" "math"
"regexp"
"strings" "strings"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/port" "github.com/derailed/k9s/internal/port"
"github.com/derailed/k9s/internal/render"
"github.com/derailed/k9s/internal/ui" "github.com/derailed/k9s/internal/ui"
"github.com/derailed/tview" "github.com/derailed/tview"
"github.com/rs/zerolog/log" "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") coField.SetPlaceholder("Enter a container name/port")
} }
f.AddInputField("Local Port:", p2, fieldLen, nil, nil) f.AddInputField("Local Port:", p2, fieldLen, nil, nil)
poField := f.GetFormItemByLabel("Local Port:").(*tview.InputField) loField := f.GetFormItemByLabel("Local Port:").(*tview.InputField)
if poField.GetText() == "" { if loField.GetText() == "" {
poField.SetPlaceholder("Enter a local port") loField.SetPlaceholder("Enter a local port")
} }
coField.SetChangedFunc(func(s string) { coField.SetChangedFunc(func(s string) {
port := extractPort(s) port := extractPort(s)
poField.SetText(port) loField.SetText(port)
p2 = port p2 = port
}) })
f.AddInputField("Address:", address, fieldLen, nil, func(h string) { 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() { 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")) v.App().Flash().Err(fmt.Errorf("container to local port mismatch"))
return 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 { if err != nil {
v.App().Flash().Err(err) v.App().Flash().Err(err)
return return
@ -121,26 +122,11 @@ func DismissPortForwards(v ResourceViewer, p *ui.Pages) {
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Helpers... // Helpers...
func extractPort(p string) string { func extractPort(coPort string) string {
rx := regexp.MustCompile(`\A([\w|-]+)/?([\w|-]+)?:?(\d+)?(UDP)?\z`) tokens := strings.Split(coPort, "::")
mm := rx.FindStringSubmatch(p) if len(tokens) < 2 {
if len(mm) != 5 { return ""
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
} }
co, _ := client.Namespaced(tokens[0]) return tokens[1]
return co
} }

View File

@ -6,25 +6,23 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func TestExtractContainer(t *testing.T) { func TestExtractPort(t *testing.T) {
uu := map[string]struct { uu := map[string]struct {
port, e string portSpec, e string
}{ }{
"full": { "full": {
"co/port:8000", "co", portSpec: "co::8000",
e: "8000",
}, },
"unamed": { "toast": {
"co/:8000", "co", portSpec: "co:8000",
},
"protocol": {
"co/dns:53UDP", "co",
}, },
} }
for k := range uu { for k := range uu {
u := uu[k] u := uu[k]
t.Run(k, func(t *testing.T) { t.Run(k, func(t *testing.T) {
assert.Equal(t, u.e, extractContainer(u.port)) assert.Equal(t, u.e, extractPort(u.portSpec))
}) })
} }
} }

View File

@ -31,7 +31,6 @@ func (r *RestartExtender) bindKeys(aa ui.KeyActions) {
return return
} }
aa.Add(ui.KeyActions{ aa.Add(ui.KeyActions{
// BOZO!!
ui.KeyR: ui.NewKeyAction("Restart", r.restartCmd, true), ui.KeyR: ui.NewKeyAction("Restart", r.restartCmd, true),
}) })
} }