K9s: release v0.25.12 (#1379)

* release v0.25.12

* update rel notes
mine
Fernand Galiana 2021-12-14 23:46:32 -07:00 committed by GitHub
parent 11c2ae2622
commit 01bdc85020
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 118 additions and 65 deletions

View File

@ -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.11
VERSION ?= v0.25.12
IMG_NAME := derailed/k9s
IMAGE := ${IMG_NAME}:${VERSION}

View File

@ -0,0 +1,54 @@
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/k9s_small.png" align="right" width="200" height="auto"/>
# Release v0.25.12
## 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`!
* [Joshua Kapellen](https://github.com/joshuakapellen)
* [Qdentity](https://github.com/qdentity)
* [Maxim](https://github.com/bsod90)
* [Sönke Schau](https://github.com/xgcssch)
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 ♭
* [Border Patrol - Eek A Mouse](https://www.youtube.com/watch?v=pQVNzolpoII)
* [All Mine - Portishead](https://www.youtube.com/watch?v=cuclNJiE8NY)
* [Come on up to the house - Tom Waits](https://www.youtube.com/watch?v=9XVGAatyeNk)
## Maintenance Release!
Hoy! end of year is... sucking! Feeling the burn ;( Apologies for the disruptions!!
`You're either a pigeon or... the statue!`
---
## Resolved Issues
* [Issue #1378](https://github.com/derailed/k9s/issues/1378) Regression: Namespace filters are no longer applied on startup
* [Issue #1376](https://github.com/derailed/k9s/issues/1376) Events not sorted correctly by dates
* [Issue #1375](https://github.com/derailed/k9s/issues/1375) Unable to show port forwards
* [Issue #1374](https://github.com/derailed/k9s/issues/1374) --all-namespaces does not work v0.25.10
* [Issue #1373](https://github.com/derailed/k9s/issues/1373) change namespace not possible
---
<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)

View File

@ -1,45 +1,42 @@
package cmd
import (
"testing"
"github.com/derailed/k9s/internal/config"
"github.com/stretchr/testify/assert"
"testing"
)
func Test_getScreenDumpDirForInfo(t *testing.T) {
tests := []struct {
name string
tests := map[string]struct {
k9sConfigFile string
expectedScreenDumpDir string
}{
{
name: "withK9sConfigFile",
"withK9sConfigFile": {
k9sConfigFile: "testdata/k9s.yml",
expectedScreenDumpDir: "/tmp",
},
{
name: "withEmptyK9sConfigFile",
"withEmptyK9sConfigFile": {
k9sConfigFile: "",
expectedScreenDumpDir: config.K9sDefaultScreenDumpDir,
},
{
name: "withInvalidK9sConfigFilePath",
"withInvalidK9sConfigFilePath": {
k9sConfigFile: "invalid",
expectedScreenDumpDir: config.K9sDefaultScreenDumpDir,
},
{
name: "withScreenDumpDirEmptyInK9sConfigFile",
"withScreenDumpDirEmptyInK9sConfigFile": {
k9sConfigFile: "testdata/k9s1.yml",
expectedScreenDumpDir: config.K9sDefaultScreenDumpDir,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
for k := range tests {
u := tests[k]
t.Run(k, func(t *testing.T) {
initK9sConfigFile := config.K9sConfigFile
config.K9sConfigFile = tt.k9sConfigFile
config.K9sConfigFile = u.k9sConfigFile
assert.Equal(t, tt.expectedScreenDumpDir, getScreenDumpDirForInfo())
assert.Equal(t, u.expectedScreenDumpDir, getScreenDumpDirForInfo())
config.K9sConfigFile = initK9sConfigFile
})

View File

@ -99,7 +99,10 @@ func (c *Config) Refine(flags *genericclioptions.ConfigFlags, k9sFlags *Flags, c
ns = *flags.Namespace
} else if context.Namespace != "" {
ns = context.Namespace
} else if cl := c.K9s.ActiveCluster(); cl != nil {
if cl := c.K9s.ActiveCluster(); cl != nil && cl.Namespace != nil && cl.Namespace.Active != "" {
ns = cl.Namespace.Active
}
} else if cl := c.K9s.ActiveCluster(); cl != nil && cl.Namespace != nil {
ns = cl.Namespace.Active
}

View File

@ -30,7 +30,7 @@ func TestConfigRefine(t *testing.T) {
issue: false,
context: "test1",
cluster: "cluster1",
namespace: "ns1",
namespace: "default",
},
"overrideNS": {
flags: &genericclioptions.ConfigFlags{

View File

@ -5,6 +5,7 @@ import (
"errors"
"os"
"path/filepath"
"regexp"
"strings"
"github.com/derailed/k9s/internal"
@ -15,6 +16,8 @@ import (
var (
_ Accessor = (*Benchmark)(nil)
_ Nuker = (*Benchmark)(nil)
BenchRx = regexp.MustCompile(`[:|]+`)
)
// Benchmark represents a benchmark resource.
@ -45,9 +48,10 @@ func (b *Benchmark) List(ctx context.Context, _ string) ([]runtime.Object, error
return nil, err
}
fileName := BenchRx.ReplaceAllString(strings.Replace(path, "/", "_", 1), "_")
oo := make([]runtime.Object, 0, len(ff))
for _, f := range ff {
if path != "" && !strings.HasPrefix(f.Name(), strings.Replace(path, "/", "_", 1)) {
if path != "" && !strings.HasPrefix(f.Name(), fileName) {
continue
}

View File

@ -41,7 +41,7 @@ func (p *PortForward) List(ctx context.Context, _ string) ([]runtime.Object, err
config, err := config.NewBench(benchFile)
if err != nil {
log.Debug().Msgf("No custom benchmark config file found")
log.Warn().Msgf("No custom benchmark config file found")
}
ff, cc := p.Factory.Forwarders(), config.Benchmarks.Containers

View File

@ -4,6 +4,7 @@ import (
"fmt"
"net/http"
"net/url"
"strings"
"time"
"github.com/derailed/k9s/internal/client"
@ -72,11 +73,6 @@ func (p *PortForwarder) LocalPort() string {
return p.tunnel.LocalPort
}
// Path returns the pod resource path.
func (p *PortForwarder) Path() string {
return PortForwardID(p.path, p.tunnel.Container, p.tunnel.PortMap())
}
// ID returns a pf id.
func (p *PortForwarder) ID() string {
return PortForwardID(p.path, p.tunnel.Container, p.tunnel.PortMap())
@ -117,9 +113,10 @@ func (p *PortForwarder) Start(path string, tt port.PortTunnel) (*portforward.Por
return nil, fmt.Errorf("user is not authorized to get pods")
}
podName := strings.Split(n, "|")[0]
var res Pod
res.Init(p, client.NewGVR("v1/pods"))
pod, err := res.GetInstance(path)
pod, err := res.GetInstance(client.FQN(ns, podName))
if err != nil {
return nil, err
}
@ -150,7 +147,7 @@ func (p *PortForwarder) Start(path string, tt port.PortTunnel) (*portforward.Por
req := clt.Post().
Resource("pods").
Namespace(ns).
Name(n).
Name(podName).
SubResource("portforward")
return p.forwardPorts("POST", req.URL(), tt.Address, tt.PortMap())
@ -175,6 +172,10 @@ func (p *PortForwarder) forwardPorts(method string, url *url.URL, addr, portMap
// PortForwardID computes port-forward identifier.
func PortForwardID(path, co, portMap string) string {
if strings.Contains(path, "|") {
return path + "|" + portMap
}
return path + "|" + co + "|" + portMap
}

View File

@ -4,11 +4,11 @@ import (
"bytes"
"context"
"fmt"
"github.com/derailed/k9s/internal/dao"
"io"
"net/http"
"os"
"path/filepath"
"regexp"
"sync"
"time"
@ -29,8 +29,6 @@ const (
var (
// K9sBenchDir directory to store K9s Benchmark files.
K9sBenchDir = filepath.Join(os.TempDir(), fmt.Sprintf("k9s-bench-%s", config.MustK9sUser()))
pathRx = regexp.MustCompile(`[:|]+`)
)
// Benchmark puts a workload under load.
@ -132,7 +130,7 @@ func (b *Benchmark) save(cluster string, r io.Reader) error {
}
ns, n := client.Namespaced(b.config.Name)
file := filepath.Join(dir, fmt.Sprintf(benchFmat, ns, pathRx.ReplaceAllString(n, "_"), time.Now().UnixNano()))
file := filepath.Join(dir, fmt.Sprintf(benchFmat, ns, dao.BenchRx.ReplaceAllString(n, "_"), time.Now().UnixNano()))
f, err := os.Create(file)
if err != nil {
return err

View File

@ -15,9 +15,6 @@ type Forwarder interface {
// ID returns the PF FQN.
ID() string
// Path returns a resource path.
Path() string
// Container returns a container name.
Container() string
@ -66,9 +63,9 @@ func (f PortForward) Render(o interface{}, gvr string, r *Row) error {
}
ports := strings.Split(pf.Port(), ":")
ns, n := client.Namespaced(pf.ID())
r.ID = pf.ID()
ns, n := client.Namespaced(r.ID)
r.Fields = Fields{
ns,
trimContainer(n),

View File

@ -124,7 +124,7 @@ func (c *Container) showPFCmd(evt *tcell.EventKey) *tcell.EventKey {
}
if !c.App().factory.Forwarders().IsContainerForwarded(c.GetTable().Path, path) {
c.App().Flash().Errf("no portforwards defined")
c.App().Flash().Errf("no port-forward defined")
return nil
}
pf := NewPortForward(client.NewGVR("portforwards"))
@ -174,7 +174,7 @@ func (c *Container) portFwdCmd(evt *tcell.EventKey) *tcell.EventKey {
}
if _, ok := c.App().factory.ForwarderFor(fwFQN(c.GetTable().Path, path)); ok {
c.App().Flash().Err(fmt.Errorf("A PortForward already exist on container %s", c.GetTable().Path))
c.App().Flash().Err(fmt.Errorf("A port-forward already exist on container %s", c.GetTable().Path))
return nil
}
@ -182,7 +182,7 @@ func (c *Container) portFwdCmd(evt *tcell.EventKey) *tcell.EventKey {
if !ok {
return nil
}
ShowPortForwards(c, c.GetTable().Path, ports, ann, startFwdCB)
ShowPortForwards(c, c.GetTable().Path+"|"+path, ports, ann, startFwdCB)
return nil
}
@ -232,11 +232,5 @@ func (c *Container) listForwardable(path string) (port.ContainerPortSpecs, map[s
return nil, nil, false
}
exposedPorts := port.FromContainerPorts(path, co.Ports)
if len(exposedPorts) == 0 {
c.App().Flash().Err(errors.New("Container exposes no ports"))
return nil, nil, false
}
return port.FromContainerPorts(path, co.Ports), po.Annotations, true
}

View File

@ -142,7 +142,7 @@ func asKey(key string) (tcell.Key, error) {
// FwFQN returns a fully qualified ns/name:container id.
func fwFQN(po, co string) string {
return po + ":" + co
return po + "|" + co
}
func isTCPPort(p string) bool {

View File

@ -85,7 +85,7 @@ func TestFwFQN(t *testing.T) {
uu := map[string]struct {
po, co, e string
}{
"cool": {po: "p1", co: "c1", e: "p1:c1"},
"cool": {po: "p1", co: "c1", e: "p1|c1"},
}
for k := range uu {

View File

@ -34,7 +34,7 @@ func ShowPortForwards(v ResourceViewer, path string, ports port.ContainerPortSpe
pf, err := aa.PreferredPorts(ports)
if err != nil {
log.Warn().Err(err).Msgf("unable to resolve ports")
log.Warn().Err(err).Msgf("unable to resolve ports on %s", path)
}
p1, p2 := pf.ToPortSpec(ports)

View File

@ -42,7 +42,12 @@ func (p *PortForwardExtender) portFwdCmd(evt *tcell.EventKey) *tcell.EventKey {
return evt
}
pod, err := fetchPod(p.App().factory, path)
podName, err := p.fetchPodName(path)
if err != nil {
p.App().Flash().Err(err)
return nil
}
pod, err := fetchPod(p.App().factory, podName)
if err != nil {
p.App().Flash().Err(err)
return nil
@ -52,10 +57,10 @@ func (p *PortForwardExtender) portFwdCmd(evt *tcell.EventKey) *tcell.EventKey {
return nil
}
if p.App().factory.Forwarders().IsPodForwarded(path) {
p.App().Flash().Errf("A PortForward already exist for pod %s", pod)
p.App().Flash().Errf("A PortForward already exist for pod %s", pod.Name)
return nil
}
if err := showFwdDialog(p, path, startFwdCB); err != nil {
if err := showFwdDialog(p, podName, startFwdCB); err != nil {
p.App().Flash().Err(err)
}

View File

@ -137,7 +137,7 @@ func (p *Pod) showPFCmd(evt *tcell.EventKey) *tcell.EventKey {
}
if !p.App().factory.Forwarders().IsPodForwarded(path) {
p.App().Flash().Errf("no portforwards defined")
p.App().Flash().Errf("no port-forward defined")
return nil
}
pf := NewPortForward(client.NewGVR("portforwards"))

View File

@ -267,10 +267,8 @@ func (f *Factory) ForwarderFor(path string) (Forwarder, bool) {
f.mx.RLock()
defer f.mx.RUnlock()
for k := range f.forwarders {
log.Debug().Msgf("KEY %q::%q", k, path)
}
fwd, ok := f.forwarders[path]
return fwd, ok
}

View File

@ -19,9 +19,6 @@ type Forwarder interface {
// ID returns the pf id.
ID() string
// Path returns a resource FQN.
Path() string
// Container returns a container name.
Container() string
@ -54,27 +51,32 @@ func NewForwarders() Forwarders {
// BOZO!! Review!!!
// IsPodForwarded checks if pod has a forward.
func (ff Forwarders) IsPodForwarded(path string) bool {
func (ff Forwarders) IsPodForwarded(fqn string) bool {
for k := range ff {
fqn := strings.Split(k, "|")
if fqn[0] == path {
if strings.HasPrefix(k, fqn) {
return true
}
}
return false
}
// IsContainerForwarded checks if pod has a forward.
func (ff Forwarders) IsContainerForwarded(path, co string) bool {
_, ok := ff[path+":"+co]
func (ff Forwarders) IsContainerForwarded(fqn, co string) bool {
prefix := fqn+"|"+co
for k := range ff {
if strings.HasPrefix(k, prefix) {
return true
}
}
return ok
return false
}
// DeleteAll stops and delete all port-forwards.
func (ff Forwarders) DeleteAll() {
for k, f := range ff {
log.Debug().Msgf("Deleting forwarder %s", f.Path())
log.Debug().Msgf("Deleting forwarder %s", f.ID())
f.Stop()
delete(ff, k)
}