parent
11c2ae2622
commit
01bdc85020
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.11
|
||||
VERSION ?= v0.25.12
|
||||
IMG_NAME := derailed/k9s
|
||||
IMAGE := ${IMG_NAME}:${VERSION}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -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
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ func TestConfigRefine(t *testing.T) {
|
|||
issue: false,
|
||||
context: "test1",
|
||||
cluster: "cluster1",
|
||||
namespace: "ns1",
|
||||
namespace: "default",
|
||||
},
|
||||
"overrideNS": {
|
||||
flags: &genericclioptions.ConfigFlags{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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"))
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue