K9s release v0.32.2 (#2598)
* [Maint] cleaning up * [Bug] Fix #2593 * [Bug] Fix #2582 * Release v0.32.2mine
parent
69cd0cd707
commit
ecd33ff48d
2
Makefile
2
Makefile
|
|
@ -11,7 +11,7 @@ DATE ?= $(shell TZ=UTC date -j -f "%s" ${SOURCE_DATE_EPOCH} +"%Y-%m-%dT%H:
|
||||||
else
|
else
|
||||||
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")
|
||||||
endif
|
endif
|
||||||
VERSION ?= v0.32.1
|
VERSION ?= v0.32.2
|
||||||
IMG_NAME := derailed/k9s
|
IMG_NAME := derailed/k9s
|
||||||
IMAGE := ${IMG_NAME}:${VERSION}
|
IMAGE := ${IMG_NAME}:${VERSION}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/k9s.png" align="center" width="800" height="auto"/>
|
||||||
|
|
||||||
|
# Release v0.32.2
|
||||||
|
|
||||||
|
## 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!
|
||||||
|
Also big thanks to all that have allocated their own time to help others on both slack and on this repo!!
|
||||||
|
|
||||||
|
As you may know, K9s is not pimped out by corps with deep pockets, thus 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!
|
||||||
|
|
||||||
|
Mo aftermath ;(
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Videos Are In The Can!
|
||||||
|
|
||||||
|
Please dial [K9s Channel](https://www.youtube.com/channel/UC897uwPygni4QIjkPCpgjmw) for up coming content...
|
||||||
|
|
||||||
|
* [K9s v0.31.0 Configs+Sneak peek](https://youtu.be/X3444KfjguE)
|
||||||
|
* [K9s v0.30.0 Sneak peek](https://youtu.be/mVBc1XneRJ4)
|
||||||
|
* [Vulnerability Scans](https://youtu.be/ULkl0MsaidU)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Resolved Issues
|
||||||
|
|
||||||
|
* [#2582](https://github.com/derailed/k9s/issues/2582) Slowness due to client-side throttling in v0.32.0 (Maybe??)
|
||||||
|
* [#2593](https://github.com/derailed/k9s/issues/2593) Popeye not working in 0.32.X
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/imhotep_logo.png" width="32" height="auto"/> © 2024 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
|
@ -246,7 +246,7 @@ func (a *APIClient) ValidNamespaceNames() (NamespaceNames, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ok, err := a.CanI(ClusterScope, "v1/namespaces", "", []string{ListVerb})
|
ok, err := a.CanI(ClusterScope, "v1/namespaces", "", ListAccess)
|
||||||
if !ok || err != nil {
|
if !ok || err != nil {
|
||||||
return nil, fmt.Errorf("user not authorized to list all namespaces")
|
return nil, fmt.Errorf("user not authorized to list all namespaces")
|
||||||
}
|
}
|
||||||
|
|
@ -524,12 +524,25 @@ func (a *APIClient) MXDial() (*versioned.Clientset, error) {
|
||||||
return a.getMxsClient(), err
|
return a.getMxsClient(), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *APIClient) invalidateCache() error {
|
||||||
|
dial, err := a.CachedDiscovery()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dial.Invalidate()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// SwitchContext handles kubeconfig context switches.
|
// SwitchContext handles kubeconfig context switches.
|
||||||
func (a *APIClient) SwitchContext(name string) error {
|
func (a *APIClient) SwitchContext(name string) error {
|
||||||
log.Debug().Msgf("Switching context %q", name)
|
log.Debug().Msgf("Switching context %q", name)
|
||||||
if err := a.config.SwitchContext(name); err != nil {
|
if err := a.config.SwitchContext(name); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := a.invalidateCache(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
a.reset()
|
a.reset()
|
||||||
ResetMetrics()
|
ResetMetrics()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,9 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
// PatchAccess patch a resource.
|
||||||
|
PatchAccess = []string{PatchVerb}
|
||||||
|
|
||||||
// GetAccess reads a resource.
|
// GetAccess reads a resource.
|
||||||
GetAccess = []string{GetVerb}
|
GetAccess = []string{GetVerb}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -177,7 +177,8 @@ func (a *Aliases) loadDefaultAliases() {
|
||||||
a.declare("help", "h", "?")
|
a.declare("help", "h", "?")
|
||||||
a.declare("quit", "q", "q!", "qa", "Q")
|
a.declare("quit", "q", "q!", "qa", "Q")
|
||||||
a.declare("aliases", "alias", "a")
|
a.declare("aliases", "alias", "a")
|
||||||
a.declare("popeye", "pop")
|
// !!BOZO!!
|
||||||
|
// a.declare("popeye", "pop")
|
||||||
a.declare("helm", "charts", "chart", "hm")
|
a.declare("helm", "charts", "chart", "hm")
|
||||||
a.declare("dir", "d")
|
a.declare("dir", "d")
|
||||||
a.declare("contexts", "context", "ctx")
|
a.declare("contexts", "context", "ctx")
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ package config_test
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
"slices"
|
"slices"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
|
@ -109,8 +110,8 @@ func TestAliasesLoad(t *testing.T) {
|
||||||
config.AppConfigDir = "testdata/aliases"
|
config.AppConfigDir = "testdata/aliases"
|
||||||
a := config.NewAliases()
|
a := config.NewAliases()
|
||||||
|
|
||||||
assert.Nil(t, a.Load("testdata/aliases/plain.yaml"))
|
assert.Nil(t, a.Load(path.Join(config.AppConfigDir, "plain.yaml")))
|
||||||
assert.Equal(t, 56, len(a.Alias))
|
assert.Equal(t, 54, len(a.Alias))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAliasesSave(t *testing.T) {
|
func TestAliasesSave(t *testing.T) {
|
||||||
|
|
@ -123,7 +124,7 @@ func TestAliasesSave(t *testing.T) {
|
||||||
|
|
||||||
assert.Equal(t, c, len(a.Alias))
|
assert.Equal(t, c, len(a.Alias))
|
||||||
assert.Nil(t, a.Save())
|
assert.Nil(t, a.Save())
|
||||||
assert.Nil(t, a.LoadFile("/tmp/test-aliases/aliases.yaml"))
|
assert.Nil(t, a.LoadFile(config.AppAliasesFile))
|
||||||
assert.Equal(t, c, len(a.Alias))
|
assert.Equal(t, c, len(a.Alias))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
aliases:
|
aliases:
|
||||||
dp: "apps.v1.deployments"
|
dp: "apps/v1/deployments"
|
||||||
pe: ".v1.pods"
|
pe: "v1/pods"
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,7 @@ func (d *Deployment) Restart(ctx context.Context, path string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
auth, err := d.Client().CanI(dp.Namespace, "apps/v1/deployments", dp.Name, []string{client.PatchVerb})
|
auth, err := d.Client().CanI(dp.Namespace, "apps/v1/deployments", dp.Name, client.PatchAccess)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -261,7 +261,7 @@ func (d *Deployment) GetPodSpec(path string) (*v1.PodSpec, error) {
|
||||||
// SetImages sets container images.
|
// SetImages sets container images.
|
||||||
func (d *Deployment) SetImages(ctx context.Context, path string, imageSpecs ImageSpecs) error {
|
func (d *Deployment) SetImages(ctx context.Context, path string, imageSpecs ImageSpecs) error {
|
||||||
ns, n := client.Namespaced(path)
|
ns, n := client.Namespaced(path)
|
||||||
auth, err := d.Client().CanI(ns, "apps/v1/deployments", n, []string{client.PatchVerb})
|
auth, err := d.Client().CanI(ns, "apps/v1/deployments", n, client.PatchAccess)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ func (d *DaemonSet) Restart(ctx context.Context, path string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
auth, err := d.Client().CanI(ds.Namespace, "apps/v1/daemonsets", ds.Name, []string{client.PatchVerb})
|
auth, err := d.Client().CanI(ds.Namespace, "apps/v1/daemonsets", ds.Name, client.PatchAccess)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -280,7 +280,7 @@ func (d *DaemonSet) GetPodSpec(path string) (*v1.PodSpec, error) {
|
||||||
// SetImages sets container images.
|
// SetImages sets container images.
|
||||||
func (d *DaemonSet) SetImages(ctx context.Context, path string, imageSpecs ImageSpecs) error {
|
func (d *DaemonSet) SetImages(ctx context.Context, path string, imageSpecs ImageSpecs) error {
|
||||||
ns, n := client.Namespaced(path)
|
ns, n := client.Namespaced(path)
|
||||||
auth, err := d.Client().CanI(ns, "apps/v1/daemonset", n, []string{client.PatchVerb})
|
auth, err := d.Client().CanI(ns, "apps/v1/daemonset", n, client.PatchAccess)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -248,7 +248,7 @@ func (n *Node) ensureCordoned(path string) (bool, error) {
|
||||||
// FetchNode retrieves a node.
|
// FetchNode retrieves a node.
|
||||||
func FetchNode(ctx context.Context, f Factory, path string) (*v1.Node, error) {
|
func FetchNode(ctx context.Context, f Factory, path string) (*v1.Node, error) {
|
||||||
_, n := client.Namespaced(path)
|
_, n := client.Namespaced(path)
|
||||||
auth, err := f.Client().CanI(client.ClusterScope, "v1/nodes", n, []string{"get"})
|
auth, err := f.Client().CanI(client.ClusterScope, "v1/nodes", n, client.GetAccess)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -272,7 +272,7 @@ func FetchNode(ctx context.Context, f Factory, path string) (*v1.Node, error) {
|
||||||
|
|
||||||
// FetchNodes retrieves all nodes.
|
// FetchNodes retrieves all nodes.
|
||||||
func FetchNodes(ctx context.Context, f Factory, labelsSel string) (*v1.NodeList, error) {
|
func FetchNodes(ctx context.Context, f Factory, labelsSel string) (*v1.NodeList, error) {
|
||||||
auth, err := f.Client().CanI(client.ClusterScope, "v1/nodes", "", []string{client.ListVerb})
|
auth, err := f.Client().CanI(client.ClusterScope, "v1/nodes", "", client.ListAccess)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -121,7 +121,7 @@ func (p *Pod) List(ctx context.Context, ns string) ([]runtime.Object, error) {
|
||||||
// Logs fetch container logs for a given pod and container.
|
// Logs fetch container logs for a given pod and container.
|
||||||
func (p *Pod) Logs(path string, opts *v1.PodLogOptions) (*restclient.Request, error) {
|
func (p *Pod) Logs(path string, opts *v1.PodLogOptions) (*restclient.Request, error) {
|
||||||
ns, n := client.Namespaced(path)
|
ns, n := client.Namespaced(path)
|
||||||
auth, err := p.Client().CanI(ns, "v1/pods:log", n, []string{client.GetVerb})
|
auth, err := p.Client().CanI(ns, "v1/pods:log", n, client.GetAccess)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -426,7 +426,7 @@ func (p *Pod) GetPodSpec(path string) (*v1.PodSpec, error) {
|
||||||
// SetImages sets container images.
|
// SetImages sets container images.
|
||||||
func (p *Pod) SetImages(ctx context.Context, path string, imageSpecs ImageSpecs) error {
|
func (p *Pod) SetImages(ctx context.Context, path string, imageSpecs ImageSpecs) error {
|
||||||
ns, n := client.Namespaced(path)
|
ns, n := client.Namespaced(path)
|
||||||
auth, err := p.Client().CanI(ns, "v1/pod", n, []string{client.PatchVerb})
|
auth, err := p.Client().CanI(ns, "v1/pod", n, client.PatchAccess)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,7 @@ func (p *PortForwarder) Start(path string, tt port.PortTunnel) (*portforward.Por
|
||||||
p.path, p.tunnel, p.age = path, tt, time.Now()
|
p.path, p.tunnel, p.age = path, tt, time.Now()
|
||||||
|
|
||||||
ns, n := client.Namespaced(path)
|
ns, n := client.Namespaced(path)
|
||||||
auth, err := p.Client().CanI(ns, "v1/pods", n, []string{client.GetVerb})
|
auth, err := p.Client().CanI(ns, "v1/pods", n, client.GetAccess)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,50 +21,24 @@ const (
|
||||||
crdCat = "crd"
|
crdCat = "crd"
|
||||||
k9sCat = "k9s"
|
k9sCat = "k9s"
|
||||||
helmCat = "helm"
|
helmCat = "helm"
|
||||||
|
crdGVR = "apiextensions.k8s.io/v1/customresourcedefinitions"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MetaAccess tracks resources metadata.
|
// MetaAccess tracks resources metadata.
|
||||||
var MetaAccess = NewMeta()
|
var MetaAccess = NewMeta()
|
||||||
|
|
||||||
var stdGroups = []string{
|
var stdGroups = map[string]struct{}{
|
||||||
"admissionregistration.k8s.io/v1",
|
"apps/v1": {},
|
||||||
"admissionregistration.k8s.io/v1beta1",
|
"autoscaling/v1": {},
|
||||||
"apiextensions.k8s.io/v1",
|
"autoscaling/v2": {},
|
||||||
"apiextensions.k8s.io/v1beta1",
|
"autoscaling/v2beta1": {},
|
||||||
"apiregistration.k8s.io/v1",
|
"autoscaling/v2beta2": {},
|
||||||
"apiregistration.k8s.io/v1beta1",
|
"batch/v1": {},
|
||||||
"apps/v1",
|
"batch/v1beta1": {},
|
||||||
"authentication.k8s.io/v1",
|
"extensions/v1beta1": {},
|
||||||
"authentication.k8s.io/v1beta1",
|
"policy/v1beta1": {},
|
||||||
"authorization.k8s.io/v1",
|
"policy/v1": {},
|
||||||
"authorization.k8s.io/v1beta1",
|
"v1": {},
|
||||||
"autoscaling/v1",
|
|
||||||
"autoscaling/v2beta1",
|
|
||||||
"autoscaling/v2beta2",
|
|
||||||
"batch/v1",
|
|
||||||
"batch/v1beta1",
|
|
||||||
"certificates.k8s.io/v1",
|
|
||||||
"certificates.k8s.io/v1beta1",
|
|
||||||
"coordination.k8s.io/v1",
|
|
||||||
"coordination.k8s.io/v1beta1",
|
|
||||||
"discovery.k8s.io/v1beta1",
|
|
||||||
"dynatrace.com/v1alpha1",
|
|
||||||
"events.k8s.io/v1",
|
|
||||||
"extensions/v1beta1",
|
|
||||||
"flowcontrol.apiserver.k8s.io/v1beta1",
|
|
||||||
"metrics.k8s.io/v1beta1",
|
|
||||||
"networking.k8s.io/v1",
|
|
||||||
"networking.k8s.io/v1beta1",
|
|
||||||
"node.k8s.io/v1",
|
|
||||||
"node.k8s.io/v1beta1",
|
|
||||||
"policy/v1beta1",
|
|
||||||
"rbac.authorization.k8s.io/v1",
|
|
||||||
"rbac.authorization.k8s.io/v1beta1",
|
|
||||||
"scheduling.k8s.io/v1",
|
|
||||||
"scheduling.k8s.io/v1beta1",
|
|
||||||
"storage.k8s.io/v1",
|
|
||||||
"storage.k8s.io/v1beta1",
|
|
||||||
"v1",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m ResourceMetas) clear() {
|
func (m ResourceMetas) clear() {
|
||||||
|
|
@ -372,7 +346,6 @@ func loadPreferred(f Factory, m ResourceMetas) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
dial.Invalidate()
|
|
||||||
rr, err := dial.ServerPreferredResources()
|
rr, err := dial.ServerPreferredResources()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug().Err(err).Msgf("Failed to load preferred resources")
|
log.Debug().Err(err).Msgf("Failed to load preferred resources")
|
||||||
|
|
@ -387,7 +360,7 @@ func loadPreferred(f Factory, m ResourceMetas) error {
|
||||||
if res.SingularName == "" {
|
if res.SingularName == "" {
|
||||||
res.SingularName = strings.ToLower(res.Kind)
|
res.SingularName = strings.ToLower(res.Kind)
|
||||||
}
|
}
|
||||||
if !isStandardGroup(res.Group) {
|
if !isStandardGroup(r.GroupVersion) {
|
||||||
res.Categories = append(res.Categories, crdCat)
|
res.Categories = append(res.Categories, crdCat)
|
||||||
}
|
}
|
||||||
m[gvr] = res
|
m[gvr] = res
|
||||||
|
|
@ -397,14 +370,12 @@ func loadPreferred(f Factory, m ResourceMetas) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func isStandardGroup(r string) bool {
|
func isStandardGroup(gv string) bool {
|
||||||
for _, res := range stdGroups {
|
if _, ok := stdGroups[gv]; ok {
|
||||||
if strings.Index(res, r) == 0 {
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
return strings.Contains(gv, "k8s.io")
|
||||||
}
|
}
|
||||||
|
|
||||||
var deprecatedGVRs = map[client.GVR]struct{}{
|
var deprecatedGVRs = map[client.GVR]struct{}{
|
||||||
|
|
@ -420,7 +391,6 @@ func loadCRDs(f Factory, m ResourceMetas) {
|
||||||
if f.Client() == nil || !f.Client().ConnectionOK() {
|
if f.Client() == nil || !f.Client().ConnectionOK() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const crdGVR = "apiextensions.k8s.io/v1/customresourcedefinitions"
|
|
||||||
oo, err := f.List(crdGVR, client.ClusterScope, false, labels.Everything())
|
oo, err := f.List(crdGVR, client.ClusterScope, false, labels.Everything())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn().Err(err).Msgf("Fail CRDs load")
|
log.Warn().Err(err).Msgf("Fail CRDs load")
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@ func (s *StatefulSet) Restart(ctx context.Context, path string) error {
|
||||||
s.Forwarders().Kill(client.FQN(p.Namespace, p.Name))
|
s.Forwarders().Kill(client.FQN(p.Namespace, p.Name))
|
||||||
}
|
}
|
||||||
|
|
||||||
auth, err := s.Client().CanI(sts.Namespace, "apps/v1/statefulsets", n, []string{client.PatchVerb})
|
auth, err := s.Client().CanI(sts.Namespace, "apps/v1/statefulsets", n, client.PatchAccess)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -291,7 +291,7 @@ func (s *StatefulSet) GetPodSpec(path string) (*v1.PodSpec, error) {
|
||||||
// SetImages sets container images.
|
// SetImages sets container images.
|
||||||
func (s *StatefulSet) SetImages(ctx context.Context, path string, imageSpecs ImageSpecs) error {
|
func (s *StatefulSet) SetImages(ctx context.Context, path string, imageSpecs ImageSpecs) error {
|
||||||
ns, n := client.Namespaced(path)
|
ns, n := client.Namespaced(path)
|
||||||
auth, err := s.Client().CanI(ns, "apps/v1/statefulset", n, []string{client.PatchVerb})
|
auth, err := s.Client().CanI(ns, "apps/v1/statefulset", n, client.PatchAccess)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -204,6 +204,10 @@ func (t *Table) updater(ctx context.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Table) refresh(ctx context.Context) error {
|
func (t *Table) refresh(ctx context.Context) error {
|
||||||
|
defer func(ti time.Time) {
|
||||||
|
log.Trace().Msgf("Refresh [%s](%d) %s ", t.gvr, t.data.RowCount(), time.Since(ti))
|
||||||
|
}(time.Now())
|
||||||
|
|
||||||
if !atomic.CompareAndSwapInt32(&t.inUpdate, 0, 1) {
|
if !atomic.CompareAndSwapInt32(&t.inUpdate, 0, 1) {
|
||||||
log.Debug().Msgf("Dropping update...")
|
log.Debug().Msgf("Dropping update...")
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -244,7 +244,6 @@ func (t *TableData) Reset(ns string) {
|
||||||
|
|
||||||
func (t *TableData) Reconcile(ctx context.Context, r Renderer, oo []runtime.Object) error {
|
func (t *TableData) Reconcile(ctx context.Context, r Renderer, oo []runtime.Object) error {
|
||||||
var rows Rows
|
var rows Rows
|
||||||
|
|
||||||
if len(oo) > 0 {
|
if len(oo) > 0 {
|
||||||
if r.IsGeneric() {
|
if r.IsGeneric() {
|
||||||
table, ok := oo[0].(*metav1.Table)
|
table, ok := oo[0].(*metav1.Table)
|
||||||
|
|
@ -399,6 +398,7 @@ func (t *TableData) Clone() *TableData {
|
||||||
header: t.header.Clone(),
|
header: t.header.Clone(),
|
||||||
rowEvents: t.rowEvents.Clone(),
|
rowEvents: t.rowEvents.Clone(),
|
||||||
namespace: t.namespace,
|
namespace: t.namespace,
|
||||||
|
gvr: t.gvr,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -442,7 +442,7 @@ func editRes(app *App, gvr client.GVR, path string) error {
|
||||||
if gvr.String() == "v1/namespaces" {
|
if gvr.String() == "v1/namespaces" {
|
||||||
ns = n
|
ns = n
|
||||||
}
|
}
|
||||||
if ok, err := app.Conn().CanI(ns, gvr.String(), n, []string{"patch"}); !ok || err != nil {
|
if ok, err := app.Conn().CanI(ns, gvr.String(), n, client.PatchAccess); !ok || err != nil {
|
||||||
return fmt.Errorf("current user can't edit resource %s", gvr)
|
return fmt.Errorf("current user can't edit resource %s", gvr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
name: k9s
|
name: k9s
|
||||||
base: core20
|
base: core20
|
||||||
version: 'v0.32.1'
|
version: 'v0.32.2'
|
||||||
summary: K9s is a CLI to view and manage your Kubernetes clusters.
|
summary: K9s is a CLI to view and manage your Kubernetes clusters.
|
||||||
description: |
|
description: |
|
||||||
K9s is a CLI to view and manage your Kubernetes clusters. By leveraging a terminal UI, you can easily traverse Kubernetes resources and view the state of your clusters in a single powerful session.
|
K9s is a CLI to view and manage your Kubernetes clusters. By leveraging a terminal UI, you can easily traverse Kubernetes resources and view the state of your clusters in a single powerful session.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue