128 lines
3.2 KiB
Go
128 lines
3.2 KiB
Go
// SPDX-License-Identifier: Apache-2.0
|
|
// Copyright Authors of K9s
|
|
|
|
package view
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/derailed/k9s/internal"
|
|
"github.com/derailed/k9s/internal/client"
|
|
"github.com/derailed/k9s/internal/dao"
|
|
"github.com/derailed/k9s/internal/render"
|
|
"github.com/derailed/k9s/internal/ui"
|
|
"github.com/derailed/k9s/internal/ui/dialog"
|
|
"github.com/derailed/tcell/v2"
|
|
"github.com/go-errors/errors"
|
|
"github.com/rs/zerolog/log"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
)
|
|
|
|
// OwnerExtender adds owner actions to a given viewer.
|
|
type OwnerExtender struct {
|
|
ResourceViewer
|
|
}
|
|
|
|
// NewOwnerExtender returns a new extender.
|
|
func NewOwnerExtender(r ResourceViewer) ResourceViewer {
|
|
v := &OwnerExtender{ResourceViewer: r}
|
|
v.AddBindKeysFn(v.bindKeys)
|
|
|
|
return v
|
|
}
|
|
|
|
func (v *OwnerExtender) bindKeys(aa *ui.KeyActions) {
|
|
aa.Add(ui.KeyShiftJ, ui.NewKeyAction("Jump Owner", v.ownerCmd, true))
|
|
}
|
|
|
|
func (v *OwnerExtender) ownerCmd(evt *tcell.EventKey) *tcell.EventKey {
|
|
path := v.GetTable().GetSelectedItem()
|
|
if path == "" {
|
|
return evt
|
|
}
|
|
|
|
if err := v.findOwnerFor(path); err != nil {
|
|
log.Warn().Msgf("Unable to jump to the owner of resource %q: %s", path, err)
|
|
v.App().Flash().Warnf("Unable to jump owner: %s", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (v *OwnerExtender) findOwnerFor(path string) error {
|
|
res, err := dao.AccessorFor(v.App().factory, v.GVR())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
o, err := res.Get(v.defaultCtx(), path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
u, ok := v.asUnstructuredObject(o)
|
|
if !ok {
|
|
return errors.Errorf("unsupported object type: %t", o)
|
|
}
|
|
|
|
ns, _ := client.Namespaced(path)
|
|
ownerReferences := u.GetOwnerReferences()
|
|
if len(ownerReferences) == 1 {
|
|
return v.jumpOwner(ns, ownerReferences[0])
|
|
} else if len(ownerReferences) > 1 {
|
|
owners := make([]string, 0, len(ownerReferences))
|
|
for idx, ownerRef := range ownerReferences {
|
|
owners = append(owners, fmt.Sprintf("%d: %s", idx, ownerRef.Kind))
|
|
}
|
|
|
|
dialog.ShowSelection(v.App().Styles.Dialog(), v.App().Content.Pages, "Jump To", owners, func(index int) {
|
|
if index >= 0 {
|
|
err = v.jumpOwner(ns, ownerReferences[index])
|
|
}
|
|
})
|
|
return err
|
|
}
|
|
|
|
return errors.Errorf("no owner found")
|
|
}
|
|
|
|
func (v *OwnerExtender) jumpOwner(ns string, owner metav1.OwnerReference) error {
|
|
gv, err := schema.ParseGroupVersion(owner.APIVersion)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
gvr, namespaced, found := dao.MetaAccess.GVK2GVR(gv, owner.Kind)
|
|
if !found {
|
|
return errors.Errorf("unsupported GVK: %s/%s", owner.APIVersion, owner.Kind)
|
|
}
|
|
|
|
var ownerFQN string
|
|
if namespaced {
|
|
ownerFQN = client.FQN(ns, owner.Name)
|
|
} else {
|
|
ownerFQN = owner.Name
|
|
}
|
|
|
|
v.App().gotoResource(gvr.String(), ownerFQN, false, true)
|
|
return nil
|
|
}
|
|
|
|
func (v *OwnerExtender) defaultCtx() context.Context {
|
|
return context.WithValue(context.Background(), internal.KeyFactory, v.App().factory)
|
|
}
|
|
|
|
func (v *OwnerExtender) asUnstructuredObject(o runtime.Object) (*unstructured.Unstructured, bool) {
|
|
switch v := o.(type) {
|
|
case *unstructured.Unstructured:
|
|
return v, true
|
|
case *render.PodWithMetrics:
|
|
return v.Raw, true
|
|
default:
|
|
return nil, false
|
|
}
|
|
}
|