K9s/release v0.31.9 (#2543)
* [Bug] fix #2535 * [Bug] fix #2532 * [Bug] fix #2536 * [Bug] fix #2533 * [Bug] fix #2538 * [Maint] cleaning up * Release notesmine
parent
207d05615a
commit
f2f4077b59
2
Makefile
2
Makefile
|
|
@ -11,7 +11,7 @@ DATE ?= $(shell TZ=UTC date -j -f "%s" ${SOURCE_DATE_EPOCH} +"%Y-%m-%dT%H:
|
|||
else
|
||||
DATE ?= $(shell date -u -d @${SOURCE_DATE_EPOCH} +"%Y-%m-%dT%H:%M:%SZ")
|
||||
endif
|
||||
VERSION ?= v0.31.8
|
||||
VERSION ?= v0.31.9
|
||||
IMG_NAME := derailed/k9s
|
||||
IMAGE := ${IMG_NAME}:${VERSION}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,98 @@
|
|||
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/k9s.png" align="center" width="800" height="auto"/>
|
||||
|
||||
# Release v0.31.9
|
||||
|
||||
## 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!
|
||||
|
||||
```text
|
||||
S .-'-.
|
||||
o __| F `\
|
||||
S `-,-`--._ `\
|
||||
[] .->' X `|-'
|
||||
`=/ (__/_ /
|
||||
\_, ` _)
|
||||
`----; |
|
||||
```
|
||||
|
||||
⛔️ WE HAVE A PIPER DOWN! I REPEAT PIPER IS DOWN!! ⛔️
|
||||
|
||||
Popeye is undergoing heavy surgery at the moment so I had to break the bridge.
|
||||
If you dig Popeye please run the binary separately for the time being.
|
||||
I'll post another message here once the spinach formula upgrade is successful!
|
||||
|
||||
Also please make sure to add the gory details to issues ie relevant configs, debug logs, etc...
|
||||
Comments like: `same here!` or `me to!` doesn't really cut it for us to zero in ;(
|
||||
|
||||
Everyone has slightly different settings/platforms so every little bits of info helps with the resolves even if seemingly irrelevant.
|
||||
|
||||
Thank you all for pitching in and helping flesh out issues!!
|
||||
|
||||
---
|
||||
|
||||
## 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)
|
||||
|
||||
---
|
||||
|
||||
## ♫ Sounds Behind The Release ♭
|
||||
|
||||
Ushered or Taylored out?
|
||||
|
||||
* [Rough God Goes Riding - Van Morrison](https://www.youtube.com/watch?v=-kGrwRlJxcM)
|
||||
* [Walk On - John Hiatt](https://www.youtube.com/watch?v=YVdMyeTQCkw)
|
||||
* [On The Beach - Neil Young](https://www.youtube.com/watch?v=KBVde75e4sU)
|
||||
|
||||
---
|
||||
|
||||
## A Word From Our Sponsors...
|
||||
|
||||
To all the good folks below that opted to `pay it forward` and join our sponsorship program, I salute you!!
|
||||
|
||||
* [Francis Lalonde](https://github.com/f-lalonde)
|
||||
* [e-conomic a/s](https://github.com/e-conomic)
|
||||
|
||||
> Sponsorship cancellations since the last release: **2!** 🥹
|
||||
|
||||
---
|
||||
|
||||
## Resolved Issues
|
||||
|
||||
* [#2540](https://github.com/derailed/k9s/issues/2540) Option --write not functional
|
||||
* [#2538](https://github.com/derailed/k9s/issues/2538) Opening screen dumps (sd) in K9s results in Failed to launch editor error message
|
||||
* [#2536](https://github.com/derailed/k9s/issues/2536) Recent namespaces are lost when changing context
|
||||
* [#2535](https://github.com/derailed/k9s/issues/2535) Namespaced configmap edit fails for user with RoleBinding to a role that allows it
|
||||
* [#2532](https://github.com/derailed/k9s/issues/2532) Sporadic crashes (Maybe??)
|
||||
|
||||
---
|
||||
|
||||
## Contributed PRs
|
||||
|
||||
Please be sure to give `Big Thanks!` and `ATTA Girls/Boys!` to all the fine contributors for making K9s better for all of us!!
|
||||
|
||||
* [#2541](https://github.com/derailed/k9s/pull/2541) Add Rose Pine moon and dawn variants to skins
|
||||
* [#2531](https://github.com/derailed/k9s/pull/2531) fix the --write flag
|
||||
* [#2516](https://github.com/derailed/k9s/pull/2516) Added defaultsToFullScreen flag for Live/Details view,logs
|
||||
|
||||
---
|
||||
|
||||
<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)
|
||||
|
|
@ -84,7 +84,7 @@ func (a *APIClient) ConnectionOK() bool {
|
|||
return a.connOK
|
||||
}
|
||||
|
||||
func makeSAR(ns, gvr string) *authorizationv1.SelfSubjectAccessReview {
|
||||
func makeSAR(ns, gvr, name string) *authorizationv1.SelfSubjectAccessReview {
|
||||
if ns == ClusterScope {
|
||||
ns = BlankNamespace
|
||||
}
|
||||
|
|
@ -98,13 +98,14 @@ func makeSAR(ns, gvr string) *authorizationv1.SelfSubjectAccessReview {
|
|||
Version: res.Version,
|
||||
Resource: res.Resource,
|
||||
Subresource: spec.SubResource(),
|
||||
Name: name,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func makeCacheKey(ns, gvr string, vv []string) string {
|
||||
return ns + ":" + gvr + "::" + strings.Join(vv, ",")
|
||||
func makeCacheKey(ns, gvr, n string, vv []string) string {
|
||||
return ns + ":" + gvr + ":" + n + "::" + strings.Join(vv, ",")
|
||||
}
|
||||
|
||||
// ActiveContext returns the current context name.
|
||||
|
|
@ -142,14 +143,14 @@ func (a *APIClient) clearCache() {
|
|||
}
|
||||
|
||||
// CanI checks if user has access to a certain resource.
|
||||
func (a *APIClient) CanI(ns, gvr string, verbs []string) (auth bool, err error) {
|
||||
func (a *APIClient) CanI(ns, gvr, name string, verbs []string) (auth bool, err error) {
|
||||
if !a.getConnOK() {
|
||||
return false, errors.New("ACCESS -- No API server connection")
|
||||
}
|
||||
if IsClusterWide(ns) {
|
||||
ns = BlankNamespace
|
||||
}
|
||||
key := makeCacheKey(ns, gvr, verbs)
|
||||
key := makeCacheKey(ns, gvr, name, verbs)
|
||||
if v, ok := a.cache.Get(key); ok {
|
||||
if auth, ok = v.(bool); ok {
|
||||
return auth, nil
|
||||
|
|
@ -160,7 +161,7 @@ func (a *APIClient) CanI(ns, gvr string, verbs []string) (auth bool, err error)
|
|||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
client, sar := dial.AuthorizationV1().SelfSubjectAccessReviews(), makeSAR(ns, gvr)
|
||||
client, sar := dial.AuthorizationV1().SelfSubjectAccessReviews(), makeSAR(ns, gvr, name)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), a.config.CallTimeout())
|
||||
defer cancel()
|
||||
|
|
@ -215,7 +216,7 @@ func (a *APIClient) IsValidNamespace(ns string) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
ok, err := a.CanI(ClusterScope, "v1/namespaces", []string{ListVerb})
|
||||
ok, err := a.CanI(ClusterScope, "v1/namespaces", "", []string{ListVerb})
|
||||
if ok && err == nil {
|
||||
nn, _ := a.ValidNamespaceNames()
|
||||
_, ok = nn[ns]
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ func TestMakeSAR(t *testing.T) {
|
|||
for k := range uu {
|
||||
u := uu[k]
|
||||
t.Run(k, func(t *testing.T) {
|
||||
assert.Equal(t, u.sar, makeSAR(u.ns, u.gvr.String()))
|
||||
assert.Equal(t, u.sar, makeSAR(u.ns, u.gvr.String(), ""))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ func (m *MetricsServer) checkAccess(ns, gvr, msg string) error {
|
|||
return errors.New("no metrics-server detected on cluster")
|
||||
}
|
||||
|
||||
auth, err := m.CanI(ns, gvr, ListAccess)
|
||||
auth, err := m.CanI(ns, gvr, "", ListAccess)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ type PodsMetricsMap map[string]*mv1beta1.PodMetrics
|
|||
// Authorizer checks what a user can or cannot do to a resource.
|
||||
type Authorizer interface {
|
||||
// CanI returns true if the user can use these actions for a given resource.
|
||||
CanI(ns, gvr string, verbs []string) (bool, error)
|
||||
CanI(ns, gvr, n string, verbs []string) (bool, error)
|
||||
}
|
||||
|
||||
// Connection represents a Kubernetes apiserver connection.
|
||||
|
|
|
|||
|
|
@ -33,6 +33,20 @@ func NewAliases() *Aliases {
|
|||
}
|
||||
}
|
||||
|
||||
func (a *Aliases) AliasesFor(s string) []string {
|
||||
aa := make([]string, 0, 10)
|
||||
|
||||
a.mx.RLock()
|
||||
defer a.mx.RUnlock()
|
||||
for k, v := range a.Alias {
|
||||
if v == s {
|
||||
aa = append(aa, k)
|
||||
}
|
||||
}
|
||||
|
||||
return aa
|
||||
}
|
||||
|
||||
// Keys returns all aliases keys.
|
||||
func (a *Aliases) Keys() []string {
|
||||
a.mx.RLock()
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ func NewMockConnectionWithContext(ct string) mockConnection {
|
|||
return mockConnection{ct: ct}
|
||||
}
|
||||
|
||||
func (m mockConnection) CanI(ns, gvr string, verbs []string) (bool, error) {
|
||||
func (m mockConnection) CanI(ns, gvr, n string, verbs []string) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
func (m mockConnection) Config() *client.Config {
|
||||
|
|
|
|||
|
|
@ -37,6 +37,10 @@ func NewAlias(f Factory) *Alias {
|
|||
return &a
|
||||
}
|
||||
|
||||
func (a *Alias) AliasesFor(s string) []string {
|
||||
return a.Aliases.AliasesFor(s)
|
||||
}
|
||||
|
||||
// Check verifies an alias is defined for this command.
|
||||
func (a *Alias) Check(cmd string) (string, bool) {
|
||||
return a.Aliases.Get(cmd)
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ func (c *conn) SupportsRes(grp string, versions []string) (string, bool, error)
|
|||
}
|
||||
func (c *conn) ServerVersion() (*version.Info, error) { return nil, nil }
|
||||
func (c *conn) CurrentNamespaceName() (string, error) { return "", nil }
|
||||
func (c *conn) CanI(ns, gvr string, verbs []string) (bool, error) { return true, nil }
|
||||
func (c *conn) CanI(ns, gvr, n string, verbs []string) (bool, error) { return true, nil }
|
||||
func (c *conn) ActiveContext() string { return "" }
|
||||
func (c *conn) ActiveNamespace() string { return "" }
|
||||
func (c *conn) IsValidNamespace(string) bool { return true }
|
||||
|
|
|
|||
|
|
@ -47,8 +47,8 @@ func (c *CronJob) ListImages(ctx context.Context, fqn string) ([]string, error)
|
|||
|
||||
// Run a CronJob.
|
||||
func (c *CronJob) Run(path string) error {
|
||||
ns, _ := client.Namespaced(path)
|
||||
auth, err := c.Client().CanI(ns, jobGVR, []string{client.GetVerb, client.CreateVerb})
|
||||
ns, n := client.Namespaced(path)
|
||||
auth, err := c.Client().CanI(ns, jobGVR, n, []string{client.GetVerb, client.CreateVerb})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -144,7 +144,7 @@ func (c *CronJob) GetInstance(fqn string) (*batchv1.CronJob, error) {
|
|||
// ToggleSuspend toggles suspend/resume on a CronJob.
|
||||
func (c *CronJob) ToggleSuspend(ctx context.Context, path string) error {
|
||||
ns, n := client.Namespaced(path)
|
||||
auth, err := c.Client().CanI(ns, c.GVR(), []string{client.GetVerb, client.UpdateVerb})
|
||||
auth, err := c.Client().CanI(ns, c.GVR(), n, []string{client.GetVerb, client.UpdateVerb})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ func (d *Deployment) IsHappy(dp appsv1.Deployment) bool {
|
|||
// Scale a Deployment.
|
||||
func (d *Deployment) Scale(ctx context.Context, path string, replicas int32) error {
|
||||
ns, n := client.Namespaced(path)
|
||||
auth, err := d.Client().CanI(ns, "apps/v1/deployments:scale", []string{client.GetVerb, client.UpdateVerb})
|
||||
auth, err := d.Client().CanI(ns, "apps/v1/deployments:scale", n, []string{client.GetVerb, client.UpdateVerb})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -91,7 +91,7 @@ func (d *Deployment) Restart(ctx context.Context, path string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
auth, err := d.Client().CanI(dp.Namespace, "apps/v1/deployments", []string{client.PatchVerb})
|
||||
auth, err := d.Client().CanI(dp.Namespace, "apps/v1/deployments", dp.Name, []string{client.PatchVerb})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -266,7 +266,7 @@ func (d *Deployment) GetPodSpec(path string) (*v1.PodSpec, error) {
|
|||
// SetImages sets container images.
|
||||
func (d *Deployment) SetImages(ctx context.Context, path string, imageSpecs ImageSpecs) error {
|
||||
ns, n := client.Namespaced(path)
|
||||
auth, err := d.Client().CanI(ns, "apps/v1/deployments", []string{client.PatchVerb})
|
||||
auth, err := d.Client().CanI(ns, "apps/v1/deployments", n, []string{client.PatchVerb})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ func (d *DaemonSet) Restart(ctx context.Context, path string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
auth, err := d.Client().CanI(ds.Namespace, "apps/v1/daemonsets", []string{client.PatchVerb})
|
||||
auth, err := d.Client().CanI(ds.Namespace, "apps/v1/daemonsets", ds.Name, []string{client.PatchVerb})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -285,7 +285,7 @@ func (d *DaemonSet) GetPodSpec(path string) (*v1.PodSpec, error) {
|
|||
// SetImages sets container images.
|
||||
func (d *DaemonSet) SetImages(ctx context.Context, path string, imageSpecs ImageSpecs) error {
|
||||
ns, n := client.Namespaced(path)
|
||||
auth, err := d.Client().CanI(ns, "apps/v1/daemonset", []string{client.PatchVerb})
|
||||
auth, err := d.Client().CanI(ns, "apps/v1/daemonset", n, []string{client.PatchVerb})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ func (g *Generic) ToYAML(path string, showManaged bool) (string, error) {
|
|||
// Delete deletes a resource.
|
||||
func (g *Generic) Delete(ctx context.Context, path string, propagation *metav1.DeletionPropagation, grace Grace) error {
|
||||
ns, n := client.Namespaced(path)
|
||||
auth, err := g.Client().CanI(ns, g.gvrStr(), []string{client.DeleteVerb})
|
||||
auth, err := g.Client().CanI(ns, g.gvrStr(), n, []string{client.DeleteVerb})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ func (n *Node) Drain(path string, opts DrainOptions, w io.Writer) error {
|
|||
dd, errs := h.GetPodsForDeletion(path)
|
||||
if len(errs) != 0 {
|
||||
for _, e := range errs {
|
||||
if _, err := h.ErrOut.Write([]byte(e.Error() + "\n")); err != nil {
|
||||
if _, err := h.ErrOut.Write([]byte(fmt.Sprintf("[%s] %s\n", path, e.Error()))); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
@ -247,7 +247,8 @@ func (n *Node) ensureCordoned(path string) (bool, error) {
|
|||
|
||||
// FetchNode retrieves a node.
|
||||
func FetchNode(ctx context.Context, f Factory, path string) (*v1.Node, error) {
|
||||
auth, err := f.Client().CanI(client.ClusterScope, "v1/nodes", []string{"get"})
|
||||
_, n := client.Namespaced(path)
|
||||
auth, err := f.Client().CanI(client.ClusterScope, "v1/nodes", n, []string{"get"})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -271,7 +272,7 @@ func FetchNode(ctx context.Context, f Factory, path string) (*v1.Node, error) {
|
|||
|
||||
// FetchNodes retrieves all nodes.
|
||||
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", "", []string{client.ListVerb})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -132,8 +132,8 @@ func (p *Pod) List(ctx context.Context, ns string) ([]runtime.Object, error) {
|
|||
|
||||
// Logs fetch container logs for a given pod and container.
|
||||
func (p *Pod) Logs(path string, opts *v1.PodLogOptions) (*restclient.Request, error) {
|
||||
ns, _ := client.Namespaced(path)
|
||||
auth, err := p.Client().CanI(ns, "v1/pods:log", []string{client.GetVerb})
|
||||
ns, n := client.Namespaced(path)
|
||||
auth, err := p.Client().CanI(ns, "v1/pods:log", n, []string{client.GetVerb})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -141,7 +141,6 @@ func (p *Pod) Logs(path string, opts *v1.PodLogOptions) (*restclient.Request, er
|
|||
return nil, fmt.Errorf("user is not authorized to view pod logs")
|
||||
}
|
||||
|
||||
ns, n := client.Namespaced(path)
|
||||
dial, err := p.Client().DialLogs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -457,7 +456,7 @@ func (p *Pod) GetPodSpec(path string) (*v1.PodSpec, error) {
|
|||
// SetImages sets container images.
|
||||
func (p *Pod) SetImages(ctx context.Context, path string, imageSpecs ImageSpecs) error {
|
||||
ns, n := client.Namespaced(path)
|
||||
auth, err := p.Client().CanI(ns, "v1/pod", []string{client.PatchVerb})
|
||||
auth, err := p.Client().CanI(ns, "v1/pod", n, []string{client.PatchVerb})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,140 +3,141 @@
|
|||
|
||||
package dao
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"time"
|
||||
// !!BOZO!!
|
||||
// import (
|
||||
// "bytes"
|
||||
// "context"
|
||||
// "encoding/json"
|
||||
// "errors"
|
||||
// "fmt"
|
||||
// "os"
|
||||
// "path/filepath"
|
||||
// "sort"
|
||||
// "time"
|
||||
|
||||
"github.com/derailed/k9s/internal"
|
||||
"github.com/derailed/k9s/internal/client"
|
||||
cfg "github.com/derailed/k9s/internal/config"
|
||||
"github.com/derailed/k9s/internal/render"
|
||||
"github.com/derailed/popeye/pkg"
|
||||
"github.com/derailed/popeye/pkg/config"
|
||||
"github.com/derailed/popeye/types"
|
||||
"github.com/rs/zerolog/log"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
// "github.com/derailed/k9s/internal"
|
||||
// "github.com/derailed/k9s/internal/client"
|
||||
// cfg "github.com/derailed/k9s/internal/config"
|
||||
// "github.com/derailed/k9s/internal/render"
|
||||
// "github.com/derailed/popeye/pkg"
|
||||
// "github.com/derailed/popeye/pkg/config"
|
||||
// "github.com/derailed/popeye/types"
|
||||
// "github.com/rs/zerolog/log"
|
||||
// "k8s.io/apimachinery/pkg/runtime"
|
||||
// )
|
||||
|
||||
var _ Accessor = (*Popeye)(nil)
|
||||
// var _ Accessor = (*Popeye)(nil)
|
||||
|
||||
// Popeye tracks cluster sanitization.
|
||||
type Popeye struct {
|
||||
NonResource
|
||||
}
|
||||
// // Popeye tracks cluster sanitization.
|
||||
// type Popeye struct {
|
||||
// NonResource
|
||||
// }
|
||||
|
||||
// NewPopeye returns a new set of aliases.
|
||||
func NewPopeye(f Factory) *Popeye {
|
||||
a := Popeye{}
|
||||
a.Init(f, client.NewGVR("popeye"))
|
||||
// // NewPopeye returns a new set of aliases.
|
||||
// func NewPopeye(f Factory) *Popeye {
|
||||
// a := Popeye{}
|
||||
// a.Init(f, client.NewGVR("popeye"))
|
||||
|
||||
return &a
|
||||
}
|
||||
// return &a
|
||||
// }
|
||||
|
||||
type readWriteCloser struct {
|
||||
*bytes.Buffer
|
||||
}
|
||||
// type readWriteCloser struct {
|
||||
// *bytes.Buffer
|
||||
// }
|
||||
|
||||
// Close close read stream.
|
||||
func (readWriteCloser) Close() error {
|
||||
return nil
|
||||
}
|
||||
// // Close close read stream.
|
||||
// func (readWriteCloser) Close() error {
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// List returns a collection of aliases.
|
||||
func (p *Popeye) List(ctx context.Context, ns string) ([]runtime.Object, error) {
|
||||
defer func(t time.Time) {
|
||||
log.Debug().Msgf("Popeye -- Elapsed %v", time.Since(t))
|
||||
if err := recover(); err != nil {
|
||||
log.Debug().Msgf("POPEYE DIED!")
|
||||
}
|
||||
}(time.Now())
|
||||
// // List returns a collection of aliases.
|
||||
// func (p *Popeye) List(ctx context.Context, ns string) ([]runtime.Object, error) {
|
||||
// defer func(t time.Time) {
|
||||
// log.Debug().Msgf("Popeye -- Elapsed %v", time.Since(t))
|
||||
// if err := recover(); err != nil {
|
||||
// log.Debug().Msgf("POPEYE DIED!")
|
||||
// }
|
||||
// }(time.Now())
|
||||
|
||||
flags, js := config.NewFlags(), "json"
|
||||
flags.Output = &js
|
||||
flags.ActiveNamespace = &ns
|
||||
// flags, js := config.NewFlags(), "json"
|
||||
// flags.Output = &js
|
||||
// flags.ActiveNamespace = &ns
|
||||
|
||||
if report, ok := ctx.Value(internal.KeyPath).(string); ok && report != "" {
|
||||
ns, n := client.Namespaced(report)
|
||||
sections := []string{n}
|
||||
flags.Sections = §ions
|
||||
flags.ActiveNamespace = &ns
|
||||
}
|
||||
spinach := filepath.Join(cfg.AppConfigDir, "spinach.yaml")
|
||||
if c, err := p.getFactory().Client().Config().CurrentContextName(); err == nil {
|
||||
spinach = filepath.Join(cfg.AppConfigDir, fmt.Sprintf("%s_spinach.yaml", c))
|
||||
}
|
||||
if _, err := os.Stat(spinach); err == nil {
|
||||
flags.Spinach = &spinach
|
||||
}
|
||||
// if report, ok := ctx.Value(internal.KeyPath).(string); ok && report != "" {
|
||||
// ns, n := client.Namespaced(report)
|
||||
// sections := []string{n}
|
||||
// flags.Sections = §ions
|
||||
// flags.ActiveNamespace = &ns
|
||||
// }
|
||||
// spinach := filepath.Join(cfg.AppConfigDir, "spinach.yaml")
|
||||
// if c, err := p.getFactory().Client().Config().CurrentContextName(); err == nil {
|
||||
// spinach = filepath.Join(cfg.AppConfigDir, fmt.Sprintf("%s_spinach.yaml", c))
|
||||
// }
|
||||
// if _, err := os.Stat(spinach); err == nil {
|
||||
// flags.Spinach = &spinach
|
||||
// }
|
||||
|
||||
popeye, err := pkg.NewPopeye(flags, &log.Logger)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
popeye.SetFactory(newPopeyeFactory(p.Factory))
|
||||
if err = popeye.Init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// popeye, err := pkg.NewPopeye(flags, &log.Logger)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// popeye.SetFactory(newPopeyeFactory(p.Factory))
|
||||
// if err = popeye.Init(); err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
buff := readWriteCloser{Buffer: bytes.NewBufferString("")}
|
||||
popeye.SetOutputTarget(buff)
|
||||
if _, _, err = popeye.Sanitize(); err != nil {
|
||||
log.Error().Err(err).Msgf("BOOM %#v", *flags.Sections)
|
||||
return nil, err
|
||||
}
|
||||
// buff := readWriteCloser{Buffer: bytes.NewBufferString("")}
|
||||
// popeye.SetOutputTarget(buff)
|
||||
// if _, _, err = popeye.Sanitize(); err != nil {
|
||||
// log.Error().Err(err).Msgf("BOOM %#v", *flags.Sections)
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
var b render.Builder
|
||||
if err = json.Unmarshal(buff.Bytes(), &b); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// var b render.Builder
|
||||
// if err = json.Unmarshal(buff.Bytes(), &b); err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
oo := make([]runtime.Object, 0, len(b.Report.Sections))
|
||||
sort.Sort(b.Report.Sections)
|
||||
for _, s := range b.Report.Sections {
|
||||
s.Tally.Count = len(s.Outcome)
|
||||
if s.Tally.Sum() > 0 {
|
||||
oo = append(oo, s)
|
||||
}
|
||||
}
|
||||
// oo := make([]runtime.Object, 0, len(b.Report.Sections))
|
||||
// sort.Sort(b.Report.Sections)
|
||||
// for _, s := range b.Report.Sections {
|
||||
// s.Tally.Count = len(s.Outcome)
|
||||
// if s.Tally.Sum() > 0 {
|
||||
// oo = append(oo, s)
|
||||
// }
|
||||
// }
|
||||
|
||||
return oo, nil
|
||||
}
|
||||
// return oo, nil
|
||||
// }
|
||||
|
||||
// Get retrieves a resource.
|
||||
func (p *Popeye) Get(_ context.Context, _ string) (runtime.Object, error) {
|
||||
return nil, errors.New("NYI!!")
|
||||
}
|
||||
// // Get retrieves a resource.
|
||||
// func (p *Popeye) Get(_ context.Context, _ string) (runtime.Object, error) {
|
||||
// return nil, errors.New("NYI!!")
|
||||
// }
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Helpers...
|
||||
// // ----------------------------------------------------------------------------
|
||||
// // Helpers...
|
||||
|
||||
type popFactory struct {
|
||||
Factory
|
||||
}
|
||||
// type popFactory struct {
|
||||
// Factory
|
||||
// }
|
||||
|
||||
var _ types.Factory = (*popFactory)(nil)
|
||||
// var _ types.Factory = (*popFactory)(nil)
|
||||
|
||||
func newPopeyeFactory(f Factory) *popFactory {
|
||||
return &popFactory{Factory: f}
|
||||
}
|
||||
// func newPopeyeFactory(f Factory) *popFactory {
|
||||
// return &popFactory{Factory: f}
|
||||
// }
|
||||
|
||||
func (p *popFactory) Client() types.Connection {
|
||||
return &popeyeConnection{Connection: p.Factory.Client()}
|
||||
}
|
||||
// func (p *popFactory) Client() types.Connection {
|
||||
// return &popeyeConnection{Connection: p.Factory.Client()}
|
||||
// }
|
||||
|
||||
type popeyeConnection struct {
|
||||
client.Connection
|
||||
}
|
||||
// type popeyeConnection struct {
|
||||
// client.Connection
|
||||
// }
|
||||
|
||||
var _ types.Connection = (*popeyeConnection)(nil)
|
||||
// var _ types.Connection = (*popeyeConnection)(nil)
|
||||
|
||||
func (c *popeyeConnection) Config() types.Config {
|
||||
return c.Connection.Config()
|
||||
}
|
||||
// func (c *popeyeConnection) Config() types.Config {
|
||||
// return c.Connection.Config()
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
ns, n := client.Namespaced(path)
|
||||
auth, err := p.Client().CanI(ns, "v1/pods", []string{client.GetVerb})
|
||||
auth, err := p.Client().CanI(ns, "v1/pods", n, []string{client.GetVerb})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -136,7 +136,7 @@ func (p *PortForwarder) Start(path string, tt port.PortTunnel) (*portforward.Por
|
|||
return nil, fmt.Errorf("unable to forward port because pod is not running. Current status=%v", pod.Status.Phase)
|
||||
}
|
||||
|
||||
auth, err = p.Client().CanI(ns, "v1/pods:portforward", []string{client.CreateVerb})
|
||||
auth, err = p.Client().CanI(ns, "v1/pods:portforward", "", []string{client.CreateVerb})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -107,7 +107,8 @@ func AccessorFor(f Factory, gvr client.GVR) (Accessor, error) {
|
|||
client.NewGVR("batch/v1beta1/cronjobs"): &CronJob{},
|
||||
client.NewGVR("batch/v1/jobs"): &Job{},
|
||||
client.NewGVR("v1/namespaces"): &Namespace{},
|
||||
client.NewGVR("popeye"): &Popeye{},
|
||||
// !!BOZO!!
|
||||
//client.NewGVR("popeye"): &Popeye{},
|
||||
client.NewGVR("helm"): &HelmChart{},
|
||||
client.NewGVR("helm-history"): &HelmHistory{},
|
||||
client.NewGVR("dir"): &Dir{},
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ func (s *StatefulSet) IsHappy(sts appsv1.StatefulSet) bool {
|
|||
// Scale a StatefulSet.
|
||||
func (s *StatefulSet) Scale(ctx context.Context, path string, replicas int32) error {
|
||||
ns, n := client.Namespaced(path)
|
||||
auth, err := s.Client().CanI(ns, "apps/v1/statefulsets:scale", []string{client.GetVerb, client.UpdateVerb})
|
||||
auth, err := s.Client().CanI(ns, "apps/v1/statefulsets:scale", n, []string{client.GetVerb, client.UpdateVerb})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -87,7 +87,7 @@ func (s *StatefulSet) Restart(ctx context.Context, path string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
ns, _ := client.Namespaced(path)
|
||||
ns, n := client.Namespaced(path)
|
||||
pp, err := podsFromSelector(s.Factory, ns, sts.Spec.Selector.MatchLabels)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -96,7 +96,7 @@ func (s *StatefulSet) Restart(ctx context.Context, path string) error {
|
|||
s.Forwarders().Kill(client.FQN(p.Namespace, p.Name))
|
||||
}
|
||||
|
||||
auth, err := s.Client().CanI(sts.Namespace, "apps/v1/statefulsets", []string{client.PatchVerb})
|
||||
auth, err := s.Client().CanI(sts.Namespace, "apps/v1/statefulsets", n, []string{client.PatchVerb})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -296,7 +296,7 @@ func (s *StatefulSet) GetPodSpec(path string) (*v1.PodSpec, error) {
|
|||
// SetImages sets container images.
|
||||
func (s *StatefulSet) SetImages(ctx context.Context, path string, imageSpecs ImageSpecs) error {
|
||||
ns, n := client.Namespaced(path)
|
||||
auth, err := s.Client().CanI(ns, "apps/v1/statefulset", []string{client.PatchVerb})
|
||||
auth, err := s.Client().CanI(ns, "apps/v1/statefulset", n, []string{client.PatchVerb})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ type Workload struct {
|
|||
func (w *Workload) Delete(ctx context.Context, path string, propagation *metav1.DeletionPropagation, grace Grace) error {
|
||||
gvr, _ := ctx.Value(internal.KeyGVR).(client.GVR)
|
||||
ns, n := client.Namespaced(path)
|
||||
auth, err := w.Client().CanI(ns, gvr.String(), []string{client.DeleteVerb})
|
||||
auth, err := w.Client().CanI(ns, gvr.String(), n, []string{client.DeleteVerb})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,14 +82,15 @@ var Registry = map[string]ResourceMeta{
|
|||
DAO: &dao.Alias{},
|
||||
Renderer: &render.Alias{},
|
||||
},
|
||||
"popeye": {
|
||||
DAO: &dao.Popeye{},
|
||||
Renderer: &render.Popeye{},
|
||||
},
|
||||
"sanitizer": {
|
||||
DAO: &dao.Popeye{},
|
||||
TreeRenderer: &xray.Section{},
|
||||
},
|
||||
// !!BOZO!!
|
||||
//"popeye": {
|
||||
// DAO: &dao.Popeye{},
|
||||
// Renderer: &render.Popeye{},
|
||||
//},
|
||||
//"sanitizer": {
|
||||
// DAO: &dao.Popeye{},
|
||||
// TreeRenderer: &xray.Section{},
|
||||
//},
|
||||
|
||||
// Core...
|
||||
"v1/endpoints": {
|
||||
|
|
|
|||
|
|
@ -74,8 +74,8 @@ func (s *StatusIndicator) ClusterInfoChanged(prev, cur model.ClusterMeta) {
|
|||
s.SetPermanent(fmt.Sprintf(
|
||||
statusIndicatorFmt,
|
||||
cur.K9sVer,
|
||||
cur.Context,
|
||||
cur.Cluster,
|
||||
cur.User,
|
||||
cur.K8sVer,
|
||||
AsPercDelta(prev.Cpu, cur.Cpu),
|
||||
AsPercDelta(prev.Cpu, cur.Mem),
|
||||
|
|
|
|||
|
|
@ -75,12 +75,12 @@ func hotKeyActions(r Runner, aa ui.KeyActions) error {
|
|||
errs = errors.Join(errs, err)
|
||||
continue
|
||||
}
|
||||
_, ok := aa[key]
|
||||
if ok && !hk.Override {
|
||||
errs = errors.Join(errs, fmt.Errorf("duplicated hotkeys found for %q in %q", hk.ShortCut, k))
|
||||
if _, ok := aa[key]; ok {
|
||||
if !hk.Override {
|
||||
errs = errors.Join(errs, fmt.Errorf("duplicate hotkey found for %q in %q", hk.ShortCut, k))
|
||||
continue
|
||||
} else if ok && hk.Override == true {
|
||||
log.Info().Msgf("Action %q has been overrided by hotkey in %q", hk.ShortCut, k)
|
||||
}
|
||||
log.Info().Msgf("Action %q has been overridden by hotkey in %q", hk.ShortCut, k)
|
||||
}
|
||||
|
||||
command, err := r.EnvFn()().Substitute(hk.Command)
|
||||
|
|
@ -127,18 +127,18 @@ func pluginActions(r Runner, aa ui.KeyActions) error {
|
|||
continue
|
||||
}
|
||||
key, err := asKey(plugin.ShortCut)
|
||||
|
||||
if err != nil {
|
||||
errs = errors.Join(errs, err)
|
||||
continue
|
||||
}
|
||||
_, ok := aa[key]
|
||||
if ok && !plugin.Override {
|
||||
errs = errors.Join(errs, fmt.Errorf("duplicated plugin key found for %q in %q", plugin.ShortCut, k))
|
||||
if _, ok := aa[key]; ok {
|
||||
if !plugin.Override {
|
||||
errs = errors.Join(errs, fmt.Errorf("duplicate plugin key found for %q in %q", plugin.ShortCut, k))
|
||||
continue
|
||||
} else if ok && plugin.Override == true {
|
||||
log.Info().Msgf("Action %q has been overrided by plugin in %q", plugin.ShortCut, k)
|
||||
}
|
||||
log.Info().Msgf("Action %q has been overridden by plugin in %q", plugin.ShortCut, k)
|
||||
}
|
||||
|
||||
aa[key] = ui.NewKeyActionWithOpts(
|
||||
plugin.Description,
|
||||
pluginAction(r, plugin),
|
||||
|
|
|
|||
|
|
@ -485,6 +485,8 @@ func (a *App) switchContext(ci *cmd.Interpreter, force bool) error {
|
|||
}
|
||||
if err := a.Config.Save(); err != nil {
|
||||
log.Error().Err(err).Msg("config save failed!")
|
||||
} else {
|
||||
log.Debug().Msgf("Saved context config for: %q", name)
|
||||
}
|
||||
a.initFactory(ns)
|
||||
if err := a.command.Reset(a.Config.ContextAliasesPath(), true); err != nil {
|
||||
|
|
|
|||
|
|
@ -398,7 +398,7 @@ func editRes(app *App, gvr client.GVR, path string) error {
|
|||
if gvr.String() == "v1/namespaces" {
|
||||
ns = n
|
||||
}
|
||||
if ok, err := app.Conn().CanI(ns, gvr.String(), []string{"patch"}); !ok || err != nil {
|
||||
if ok, err := app.Conn().CanI(ns, gvr.String(), n, []string{"patch"}); !ok || err != nil {
|
||||
return fmt.Errorf("current user can't edit resource %s", gvr)
|
||||
}
|
||||
|
||||
|
|
@ -423,7 +423,7 @@ func (b *Browser) switchNamespaceCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
}
|
||||
ns := b.namespaces[i]
|
||||
|
||||
auth, err := b.App().factory.Client().CanI(ns, b.GVR().String(), client.ListAccess)
|
||||
auth, err := b.App().factory.Client().CanI(ns, b.GVR().String(), "", client.ListAccess)
|
||||
if !auth {
|
||||
if err == nil {
|
||||
err = fmt.Errorf("current user can't access namespace %s", ns)
|
||||
|
|
@ -556,7 +556,7 @@ func (b *Browser) simpleDelete(selections []string, msg string) {
|
|||
dialog.ShowConfirm(b.app.Styles.Dialog(), b.app.Content.Pages, "Confirm Delete", msg, func() {
|
||||
b.ShowDeleted()
|
||||
if len(selections) > 1 {
|
||||
b.app.Flash().Infof("Delete %d marked %s", len(selections), b.GVR())
|
||||
b.app.Flash().Infof("Delete %d marked %s", len(selections), b.GVR().R())
|
||||
} else {
|
||||
b.app.Flash().Infof("Delete resource %s %s", b.GVR(), selections[0])
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ func (c *ClusterInfo) StylesChanged(s *config.Styles) {
|
|||
func (c *ClusterInfo) hasMetrics() bool {
|
||||
mx := c.app.Conn().HasMetrics()
|
||||
if mx {
|
||||
auth, err := c.app.Conn().CanI("", "metrics.k8s.io/v1beta1/nodes", client.ListAccess)
|
||||
auth, err := c.app.Conn().CanI("", "metrics.k8s.io/v1beta1/nodes", "", client.ListAccess)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msgf("No nodes metrics access")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,14 +39,7 @@ func NewCommand(app *App) *Command {
|
|||
|
||||
// AliasesFor gather all known aliases for a given resource.
|
||||
func (c *Command) AliasesFor(s string) []string {
|
||||
aa := make([]string, 0, 10)
|
||||
for k, v := range c.alias.Alias {
|
||||
if v == s {
|
||||
aa = append(aa, k)
|
||||
}
|
||||
}
|
||||
|
||||
return aa
|
||||
return c.alias.AliasesFor(s)
|
||||
}
|
||||
|
||||
// Init initializes the command.
|
||||
|
|
@ -163,6 +156,13 @@ func (c *Command) run(p *cmd.Interpreter, fqn string, clearStack bool) error {
|
|||
}
|
||||
|
||||
if context, ok := p.HasContext(); ok {
|
||||
if context != c.app.Config.ActiveContextName() {
|
||||
if err := c.app.Config.Save(); err != nil {
|
||||
log.Error().Err(err).Msg("config save failed!")
|
||||
} else {
|
||||
log.Debug().Msgf("Saved context config for: %q", context)
|
||||
}
|
||||
}
|
||||
res, err := dao.AccessorFor(c.app.factory, client.NewGVR("contexts"))
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
package view
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
|
|
@ -15,10 +16,10 @@ import (
|
|||
const drainKey = "drain"
|
||||
|
||||
// DrainFunc represents a drain callback function.
|
||||
type DrainFunc func(v ResourceViewer, path string, opts dao.DrainOptions)
|
||||
type DrainFunc func(v ResourceViewer, sels []string, opts dao.DrainOptions)
|
||||
|
||||
// ShowDrain pops a node drain dialog.
|
||||
func ShowDrain(view ResourceViewer, path string, opts dao.DrainOptions, okFn DrainFunc) {
|
||||
func ShowDrain(view ResourceViewer, sels []string, opts dao.DrainOptions, okFn DrainFunc) {
|
||||
styles := view.App().Styles
|
||||
|
||||
f := tview.NewForm()
|
||||
|
|
@ -63,10 +64,17 @@ func ShowDrain(view ResourceViewer, path string, opts dao.DrainOptions, okFn Dra
|
|||
})
|
||||
f.AddButton("OK", func() {
|
||||
DismissDrain(view, pages)
|
||||
okFn(view, path, opts)
|
||||
okFn(view, sels, opts)
|
||||
})
|
||||
|
||||
modal := tview.NewModalForm("<Drain>", f)
|
||||
path := "Drain "
|
||||
if len(sels) == 1 {
|
||||
path += sels[0]
|
||||
} else {
|
||||
path += fmt.Sprintf("(%d) nodes", len(sels))
|
||||
}
|
||||
path += "?"
|
||||
modal.SetText(path)
|
||||
modal.SetDoneFunc(func(_ int, b string) {
|
||||
DismissDrain(view, pages)
|
||||
|
|
|
|||
|
|
@ -123,12 +123,11 @@ func edit(a *App, opts shellOpts) bool {
|
|||
)
|
||||
for _, e := range editorEnvVars {
|
||||
env := os.Getenv(e)
|
||||
if env != "" {
|
||||
if env == "" {
|
||||
continue
|
||||
}
|
||||
bin, err = exec.LookPath(env)
|
||||
if err != nil {
|
||||
continue
|
||||
if bin, err = exec.LookPath(env); err == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
if bin == "" {
|
||||
|
|
|
|||
|
|
@ -96,8 +96,8 @@ func (n *Node) showPods(a *App, _ ui.Tabular, _ client.GVR, path string) {
|
|||
}
|
||||
|
||||
func (n *Node) drainCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||
path := n.GetTable().GetSelectedItem()
|
||||
if path == "" {
|
||||
sels := n.GetTable().GetSelectedItems()
|
||||
if len(sels) == 0 {
|
||||
return evt
|
||||
}
|
||||
|
||||
|
|
@ -105,12 +105,12 @@ func (n *Node) drainCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|||
GracePeriodSeconds: -1,
|
||||
Timeout: 5 * time.Second,
|
||||
}
|
||||
ShowDrain(n, path, opts, drainNode)
|
||||
ShowDrain(n, sels, opts, drainNode)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func drainNode(v ResourceViewer, path string, opts dao.DrainOptions) {
|
||||
func drainNode(v ResourceViewer, sels []string, opts dao.DrainOptions) {
|
||||
res, err := dao.AccessorFor(v.App().factory, v.GVR())
|
||||
if err != nil {
|
||||
v.App().Flash().Err(err)
|
||||
|
|
@ -125,14 +125,14 @@ func drainNode(v ResourceViewer, path string, opts dao.DrainOptions) {
|
|||
v.Stop()
|
||||
defer v.Start()
|
||||
{
|
||||
d := NewDetails(v.App(), "Drain Progress", path, contentYAML, true)
|
||||
d := NewDetails(v.App(), "Drain Progress", "nodes", contentYAML, true)
|
||||
if err := v.App().inject(d, false); err != nil {
|
||||
v.App().Flash().Err(err)
|
||||
}
|
||||
|
||||
if err := m.Drain(path, opts, d.GetWriter()); err != nil {
|
||||
for _, sel := range sels {
|
||||
if err := m.Drain(sel, opts, d.GetWriter()); err != nil {
|
||||
v.App().Flash().Err(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
v.Refresh()
|
||||
}
|
||||
|
|
@ -140,8 +140,8 @@ func drainNode(v ResourceViewer, path string, opts dao.DrainOptions) {
|
|||
|
||||
func (n *Node) toggleCordonCmd(cordon bool) func(evt *tcell.EventKey) *tcell.EventKey {
|
||||
return func(evt *tcell.EventKey) *tcell.EventKey {
|
||||
path := n.GetTable().GetSelectedItem()
|
||||
if path == "" {
|
||||
sels := n.GetTable().GetSelectedItems()
|
||||
if len(sels) == 0 {
|
||||
return evt
|
||||
}
|
||||
|
||||
|
|
@ -151,7 +151,11 @@ func (n *Node) toggleCordonCmd(cordon bool) func(evt *tcell.EventKey) *tcell.Eve
|
|||
} else {
|
||||
title, msg = title+"Uncordon", "Uncordon "
|
||||
}
|
||||
msg += path + "?"
|
||||
if len(sels) == 1 {
|
||||
msg += sels[0] + "?"
|
||||
} else {
|
||||
msg += fmt.Sprintf("(%d) marked %s?", len(sels), n.GVR().R())
|
||||
}
|
||||
dialog.ShowConfirm(n.App().Styles.Dialog(), n.App().Content.Pages, title, msg, func() {
|
||||
res, err := dao.AccessorFor(n.App().factory, n.GVR())
|
||||
if err != nil {
|
||||
|
|
@ -163,9 +167,11 @@ func (n *Node) toggleCordonCmd(cordon bool) func(evt *tcell.EventKey) *tcell.Eve
|
|||
n.App().Flash().Err(fmt.Errorf("expecting a maintainer for %q", n.GVR()))
|
||||
return
|
||||
}
|
||||
if err := m.ToggleCordon(path, cordon); err != nil {
|
||||
for _, s := range sels {
|
||||
if err := m.ToggleCordon(s, cordon); err != nil {
|
||||
n.App().Flash().Err(err)
|
||||
}
|
||||
}
|
||||
n.Refresh()
|
||||
}, func() {})
|
||||
|
||||
|
|
|
|||
|
|
@ -191,7 +191,7 @@ func (f *Factory) isClusterWide() bool {
|
|||
|
||||
// CanForResource return an informer is user has access.
|
||||
func (f *Factory) CanForResource(ns, gvr string, verbs []string) (informers.GenericInformer, error) {
|
||||
auth, err := f.Client().CanI(ns, gvr, verbs)
|
||||
auth, err := f.Client().CanI(ns, gvr, "", verbs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
name: k9s
|
||||
base: core20
|
||||
version: 'v0.31.8'
|
||||
version: 'v0.31.9'
|
||||
summary: K9s is a CLI to view and manage your Kubernetes clusters.
|
||||
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.
|
||||
|
|
|
|||
Loading…
Reference in New Issue