Fix lint build (#3066)

* clean up

* fix bug computing resource limits

* update rs rendering to match kubectl

* display ctx view if all hell fails

* clean ups and refactors

* fix linter issues
mine
Fernand Galiana 2025-01-19 09:47:29 -07:00 committed by GitHub
parent 4642077975
commit d72467a582
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 58 additions and 44 deletions

View File

@ -1088,12 +1088,12 @@ to make this project a reality!
## Meet The Core Team! ## Meet The Core Team!
If you have chops in GO and K8s and would like to offer your time to help maintain and enhance this project, please reach out to me.
* [Fernand Galiana](https://github.com/derailed) * [Fernand Galiana](https://github.com/derailed)
* <img src="assets/mail.png" width="16" height="auto" alt="email"/> fernand@imhotep.io * <img src="assets/mail.png" width="16" height="auto" alt="email"/> fernand@imhotep.io
* <img src="assets/twitter.png" width="16" height="auto" alt="twitter"/> [@kitesurfer](https://twitter.com/kitesurfer?lang=en) * <img src="assets/twitter.png" width="16" height="auto" alt="twitter"/> [@kitesurfer](https://twitter.com/kitesurfer?lang=en)
* [Aleksei Romanenko](https://github.com/slimus)
We always enjoy hearing from folks who benefit from our work! We always enjoy hearing from folks who benefit from our work!
## Contributions Guideline ## Contributions Guideline

View File

@ -545,16 +545,13 @@ func (a *APIClient) SwitchContext(name string) error {
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()
// Need reload to pick up any kubeconfig changes. // Need reload to pick up any kubeconfig changes.
a.config = NewConfig(a.config.flags) a.config = NewConfig(a.config.flags)
return nil return a.invalidateCache()
} }
func (a *APIClient) reset() { func (a *APIClient) reset() {

View File

@ -63,7 +63,7 @@ func TestHelperInList(t *testing.T) {
func TestEnsureDirPathNone(t *testing.T) { func TestEnsureDirPathNone(t *testing.T) {
var mod os.FileMode = 0744 var mod os.FileMode = 0744
dir := filepath.Join("/tmp", "fred") dir := filepath.Join("/tmp", "k9s-test")
os.Remove(dir) os.Remove(dir)
path := filepath.Join(dir, "duh.yaml") path := filepath.Join(dir, "duh.yaml")

View File

@ -1,3 +1,6 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Authors of K9s
package data package data
// Proxy tracks a context's proxy configuration. // Proxy tracks a context's proxy configuration.

View File

@ -13,7 +13,8 @@ import (
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
) )
func isBoolSet(b *bool) bool { // IsBoolSet checks if a bool ptr is set.
func IsBoolSet(b *bool) bool {
return b != nil && *b return b != nil && *b
} }
@ -70,8 +71,3 @@ func MustK9sUser() string {
} }
return usr.Username return usr.Username
} }
// IsBoolSet checks if a bool prt is set.
func IsBoolSet(b *bool) bool {
return b != nil && *b
}

View File

@ -292,7 +292,7 @@ func (k *K9s) Override(k9sFlags *Flags) {
// IsHeadless returns headless setting. // IsHeadless returns headless setting.
func (k *K9s) IsHeadless() bool { func (k *K9s) IsHeadless() bool {
if isBoolSet(k.manualHeadless) { if IsBoolSet(k.manualHeadless) {
return true return true
} }
@ -301,7 +301,7 @@ func (k *K9s) IsHeadless() bool {
// IsLogoless returns logoless setting. // IsLogoless returns logoless setting.
func (k *K9s) IsLogoless() bool { func (k *K9s) IsLogoless() bool {
if isBoolSet(k.manualLogoless) { if IsBoolSet(k.manualLogoless) {
return true return true
} }
@ -310,7 +310,7 @@ func (k *K9s) IsLogoless() bool {
// IsCrumbsless returns crumbsless setting. // IsCrumbsless returns crumbsless setting.
func (k *K9s) IsCrumbsless() bool { func (k *K9s) IsCrumbsless() bool {
if isBoolSet(k.manualCrumbsless) { if IsBoolSet(k.manualCrumbsless) {
return true return true
} }

View File

@ -116,7 +116,7 @@ func getContainerStatus(kind string, name string, status v1.PodStatus) *v1.Conta
func (c *Container) fetchPod(fqn string) (*v1.Pod, error) { func (c *Container) fetchPod(fqn string) (*v1.Pod, error) {
o, err := c.getFactory().Get("v1/pods", fqn, true, labels.Everything()) o, err := c.getFactory().Get("v1/pods", fqn, true, labels.Everything())
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("failed to locate pod %q: %w", fqn, err)
} }
var po v1.Pod var po v1.Pod
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &po) err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &po)

View File

@ -43,15 +43,12 @@ func (g *Generic) List(ctx context.Context, ns string) ([]runtime.Object, error)
ns = client.BlankNamespace ns = client.BlankNamespace
} }
var (
ll *unstructured.UnstructuredList
err error
)
dial, err := g.dynClient() dial, err := g.dynClient()
if err != nil { if err != nil {
return nil, err return nil, err
} }
var ll *unstructured.UnstructuredList
if client.IsClusterScoped(ns) { if client.IsClusterScoped(ns) {
ll, err = dial.List(ctx, metav1.ListOptions{LabelSelector: labelSel}) ll, err = dial.List(ctx, metav1.ListOptions{LabelSelector: labelSel})
} else { } else {
@ -71,12 +68,13 @@ func (g *Generic) List(ctx context.Context, ns string) ([]runtime.Object, error)
// Get returns a given resource. // Get returns a given resource.
func (g *Generic) Get(ctx context.Context, path string) (runtime.Object, error) { func (g *Generic) Get(ctx context.Context, path string) (runtime.Object, error) {
var opts metav1.GetOptions
ns, n := client.Namespaced(path) ns, n := client.Namespaced(path)
dial, err := g.dynClient() dial, err := g.dynClient()
if err != nil { if err != nil {
return nil, err return nil, err
} }
var opts metav1.GetOptions
if client.IsClusterScoped(ns) { if client.IsClusterScoped(ns) {
return dial.Get(ctx, n, opts) return dial.Get(ctx, n, opts)
} }

View File

@ -257,7 +257,7 @@ func FetchNode(ctx context.Context, f Factory, path string) (*v1.Node, error) {
return nil, fmt.Errorf("user is not authorized to list nodes") return nil, fmt.Errorf("user is not authorized to list nodes")
} }
o, err := f.Get("v1/nodes", client.FQN(client.ClusterScope, path), false, labels.Everything()) o, err := f.Get("v1/nodes", client.FQN(client.ClusterScope, path), true, labels.Everything())
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -424,7 +424,8 @@ func loadCRDs(f Factory, m ResourceMetas) {
var meta metav1.APIResource var meta metav1.APIResource
meta.Kind = crd.Spec.Names.Kind meta.Kind = crd.Spec.Names.Kind
meta.Group = crd.Spec.Group meta.Group = crd.Spec.Group
meta.Name = crd.Name meta.Name = strings.TrimSuffix(crd.Name, "."+meta.Group)
meta.SingularName = crd.Spec.Names.Singular meta.SingularName = crd.Spec.Names.Singular
meta.ShortNames = crd.Spec.Names.ShortNames meta.ShortNames = crd.Spec.Names.ShortNames
meta.Namespaced = crd.Spec.Scope == apiext.NamespaceScoped meta.Namespaced = crd.Spec.Scope == apiext.NamespaceScoped
@ -435,11 +436,6 @@ func loadCRDs(f Factory, m ResourceMetas) {
} }
} }
// meta, errs := extractMeta(o)
// if len(errs) > 0 {
// log.Error().Err(errs[0]).Msgf("Fail to extract CRD meta (%d) errors", len(errs))
// continue
// }
meta.Categories = append(meta.Categories, crdCat) meta.Categories = append(meta.Categories, crdCat)
gvr := client.NewGVRFromMeta(meta) gvr := client.NewGVRFromMeta(meta)
m[gvr] = meta m[gvr] = meta

View File

@ -138,8 +138,8 @@ func (p Pod) Render(o interface{}, ns string, row *model1.Row) error {
} }
c, r := gatherCoMX(&po.Spec, ccmx) c, r := gatherCoMX(&po.Spec, ccmx)
phase := p.Phase(&po) phase := p.Phase(&po)
row.ID = client.MetaFQN(po.ObjectMeta)
row.ID = client.MetaFQN(po.ObjectMeta)
row.Fields = model1.Fields{ row.Fields = model1.Fields{
po.Namespace, po.Namespace,
po.Name, po.Name,
@ -254,7 +254,7 @@ func cosLimits(cc []v1.Container) (resource.Quantity, resource.Quantity) {
for _, c := range cc { for _, c := range cc {
limits := c.Resources.Limits limits := c.Resources.Limits
if len(limits) == 0 { if len(limits) == 0 {
return resource.Quantity{}, resource.Quantity{} continue
} }
if limits.Cpu() != nil { if limits.Cpu() != nil {
cpu.Add(*limits.Cpu()) cpu.Add(*limits.Cpu())
@ -263,6 +263,7 @@ func cosLimits(cc []v1.Container) (resource.Quantity, resource.Quantity) {
mem.Add(*limits.Memory()) mem.Add(*limits.Memory())
} }
} }
return *cpu, *mem return *cpu, *mem
} }

View File

@ -6,6 +6,7 @@ package render
import ( import (
"fmt" "fmt"
"strconv" "strconv"
"strings"
"github.com/derailed/k9s/internal/client" "github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/model1" "github.com/derailed/k9s/internal/model1"
@ -34,7 +35,9 @@ func (ReplicaSet) Header(ns string) model1.Header {
model1.HeaderColumn{Name: "DESIRED", Align: tview.AlignRight}, model1.HeaderColumn{Name: "DESIRED", Align: tview.AlignRight},
model1.HeaderColumn{Name: "CURRENT", Align: tview.AlignRight}, model1.HeaderColumn{Name: "CURRENT", Align: tview.AlignRight},
model1.HeaderColumn{Name: "READY", Align: tview.AlignRight}, model1.HeaderColumn{Name: "READY", Align: tview.AlignRight},
model1.HeaderColumn{Name: "LABELS", Wide: true}, model1.HeaderColumn{Name: "CONTAINERS", Wide: true},
model1.HeaderColumn{Name: "IMAGES", Wide: true},
model1.HeaderColumn{Name: "SELECTOR", Wide: true},
model1.HeaderColumn{Name: "VALID", Wide: true}, model1.HeaderColumn{Name: "VALID", Wide: true},
model1.HeaderColumn{Name: "AGE", Time: true}, model1.HeaderColumn{Name: "AGE", Time: true},
} }
@ -46,12 +49,21 @@ func (r ReplicaSet) Render(o interface{}, ns string, row *model1.Row) error {
if !ok { if !ok {
return fmt.Errorf("expected ReplicaSet, but got %T", o) return fmt.Errorf("expected ReplicaSet, but got %T", o)
} }
var rs appsv1.ReplicaSet var rs appsv1.ReplicaSet
err := runtime.DefaultUnstructuredConverter.FromUnstructured(raw.Object, &rs) err := runtime.DefaultUnstructuredConverter.FromUnstructured(raw.Object, &rs)
if err != nil { if err != nil {
return err return err
} }
var (
cc = rs.Spec.Template.Spec.Containers
cos, imgs = make([]string, 0, len(cc)), make([]string, 0, len(cc))
)
for _, co := range cc {
cos, imgs = append(cos, co.Name), append(imgs, co.Image)
}
row.ID = client.MetaFQN(rs.ObjectMeta) row.ID = client.MetaFQN(rs.ObjectMeta)
row.Fields = model1.Fields{ row.Fields = model1.Fields{
rs.Namespace, rs.Namespace,
@ -60,6 +72,8 @@ func (r ReplicaSet) Render(o interface{}, ns string, row *model1.Row) error {
strconv.Itoa(int(*rs.Spec.Replicas)), strconv.Itoa(int(*rs.Spec.Replicas)),
strconv.Itoa(int(rs.Status.Replicas)), strconv.Itoa(int(rs.Status.Replicas)),
strconv.Itoa(int(rs.Status.ReadyReplicas)), strconv.Itoa(int(rs.Status.ReadyReplicas)),
strings.Join(cos, ","),
strings.Join(imgs, ","),
mapToStr(rs.Labels), mapToStr(rs.Labels),
AsStatus(r.diagnose(rs)), AsStatus(r.diagnose(rs)),
ToAge(rs.GetCreationTimestamp()), ToAge(rs.GetCreationTimestamp()),

View File

@ -530,7 +530,7 @@ func (a *App) Run() error {
}) })
}() }()
if err := a.command.defaultCmd(); err != nil { if err := a.command.defaultCmd(true); err != nil {
return err return err
} }
a.SetRunning(true) a.SetRunning(true)

View File

@ -209,19 +209,24 @@ func (c *Command) run(p *cmd.Interpreter, fqn string, clearStack bool) error {
return c.exec(p, gvr, co, clearStack) return c.exec(p, gvr, co, clearStack)
} }
func (c *Command) defaultCmd() error { func (c *Command) defaultCmd(isRoot bool) error {
if c.app.Conn() == nil || !c.app.Conn().ConnectionOK() { if c.app.Conn() == nil || !c.app.Conn().ConnectionOK() {
return c.run(cmd.NewInterpreter("context"), "", true) return c.run(cmd.NewInterpreter("context"), "", true)
} }
defCmd := "pod"
if isRoot {
defCmd = "ctx"
}
p := cmd.NewInterpreter(c.app.Config.ActiveView()) p := cmd.NewInterpreter(c.app.Config.ActiveView())
if p.IsBlank() { if p.IsBlank() {
return c.run(p.Reset("pod"), "", true) return c.run(p.Reset(defCmd), "", true)
} }
if err := c.run(p, "", true); err != nil { if err := c.run(p, "", true); err != nil {
log.Error().Err(err).Msgf("Default run command failed %q", p.GetLine()) p = p.Reset(defCmd)
return c.run(p.Reset("pod"), "", true) log.Error().Err(fmt.Errorf("Command failed. Using default command: %s", p.GetLine()))
return c.run(p, "", true)
} }
return nil return nil

View File

@ -393,6 +393,7 @@ func (l *Log) toggleAllContainers(evt *tcell.EventKey) *tcell.EventKey {
func (l *Log) filterCmd(evt *tcell.EventKey) *tcell.EventKey { func (l *Log) filterCmd(evt *tcell.EventKey) *tcell.EventKey {
if !l.logs.cmdBuff.IsActive() { if !l.logs.cmdBuff.IsActive() {
fmt.Fprintln(l.ansiWriter)
return evt return evt
} }
@ -451,7 +452,7 @@ func (l *Log) clearCmd(*tcell.EventKey) *tcell.EventKey {
func (l *Log) markCmd(*tcell.EventKey) *tcell.EventKey { func (l *Log) markCmd(*tcell.EventKey) *tcell.EventKey {
_, _, w, _ := l.GetRect() _, _, w, _ := l.GetRect()
fmt.Fprintf(l.ansiWriter, "\n[%s:-:b]%s[-:-:-]", l.app.Styles.Views().Log.FgColor.String(), strings.Repeat("", w-4)) fmt.Fprintf(l.ansiWriter, "\n[%s:-:b]%s[-:-:-]", l.app.Styles.Views().Log.FgColor.String(), strings.Repeat("-", w-4))
l.follow = true l.follow = true
return nil return nil

View File

@ -20,8 +20,6 @@ import (
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
) )
const promptPage = "prompt"
// PortForward presents active portforward viewer. // PortForward presents active portforward viewer.
type PortForward struct { type PortForward struct {
ResourceViewer ResourceViewer

View File

@ -21,7 +21,7 @@ import (
const ( const (
defaultResync = 10 * time.Minute defaultResync = 10 * time.Minute
defaultWaitTime = 250 * time.Millisecond defaultWaitTime = 500 * time.Millisecond
) )
// Factory tracks various resource informers. // Factory tracks various resource informers.
@ -72,13 +72,13 @@ func (f *Factory) Terminate() {
// List returns a resource collection. // List returns a resource collection.
func (f *Factory) List(gvr, ns string, wait bool, labels labels.Selector) ([]runtime.Object, error) { func (f *Factory) List(gvr, ns string, wait bool, labels labels.Selector) ([]runtime.Object, error) {
if client.IsAllNamespace(ns) {
ns = client.BlankNamespace
}
inf, err := f.CanForResource(ns, gvr, client.ListAccess) inf, err := f.CanForResource(ns, gvr, client.ListAccess)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if client.IsAllNamespace(ns) {
ns = client.BlankNamespace
}
var oo []runtime.Object var oo []runtime.Object
if client.IsClusterScoped(ns) { if client.IsClusterScoped(ns) {
@ -110,6 +110,10 @@ func (f *Factory) HasSynced(gvr, ns string) (bool, error) {
// Get retrieves a given resource. // Get retrieves a given resource.
func (f *Factory) Get(gvr, fqn string, wait bool, sel labels.Selector) (runtime.Object, error) { func (f *Factory) Get(gvr, fqn string, wait bool, sel labels.Selector) (runtime.Object, error) {
ns, n := namespaced(fqn) ns, n := namespaced(fqn)
if client.IsAllNamespace(ns) {
ns = client.BlankNamespace
}
inf, err := f.CanForResource(ns, gvr, []string{client.GetVerb}) inf, err := f.CanForResource(ns, gvr, []string{client.GetVerb})
if err != nil { if err != nil {
return nil, err return nil, err
@ -128,6 +132,7 @@ func (f *Factory) Get(gvr, fqn string, wait bool, sel labels.Selector) (runtime.
if client.IsClusterScoped(ns) { if client.IsClusterScoped(ns) {
return inf.Lister().Get(n) return inf.Lister().Get(n)
} }
return inf.Lister().ByNamespace(ns).Get(n) return inf.Lister().ByNamespace(ns).Get(n)
} }