197 lines
4.4 KiB
Go
197 lines
4.4 KiB
Go
package resource
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"path"
|
|
"strings"
|
|
|
|
"github.com/derailed/k9s/internal/k8s"
|
|
"github.com/rs/zerolog/log"
|
|
yaml "gopkg.in/yaml.v2"
|
|
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
|
|
)
|
|
|
|
// Custom tracks a kubernetes resource.
|
|
type Custom struct {
|
|
*Base
|
|
// instance *unstructured.Unstructured
|
|
instance *metav1beta1.TableRow
|
|
group, version, name string
|
|
headers Row
|
|
}
|
|
|
|
// NewCustomList returns a new resource list.
|
|
func NewCustomList(ns, g, v, n string) List {
|
|
return NewCustomListWithArgs(ns, n, NewCustom(g, v, n))
|
|
}
|
|
|
|
// NewCustomListWithArgs returns a new resource list.
|
|
func NewCustomListWithArgs(ns, n string, res Resource) List {
|
|
return newList(ns, n, res, AllVerbsAccess)
|
|
}
|
|
|
|
// NewCustom instantiates a new Kubernetes Resource.
|
|
func NewCustom(g, v, n string) *Custom {
|
|
return NewCustomWithArgs(k8s.NewResource(g, v, n))
|
|
}
|
|
|
|
// NewCustomWithArgs instantiates a new Custom.
|
|
func NewCustomWithArgs(r k8s.Res) *Custom {
|
|
cr := &Custom{
|
|
Base: &Base{
|
|
caller: r,
|
|
},
|
|
}
|
|
cr.creator = cr
|
|
|
|
cr.group, cr.version, cr.name = r.(*k8s.Resource).GetInfo()
|
|
return cr
|
|
}
|
|
|
|
// NewInstance builds a new Custom instance from a k8s resource.
|
|
func (*Custom) NewInstance(i interface{}) Columnar {
|
|
cr := NewCustom("", "", "")
|
|
switch i.(type) {
|
|
case *metav1beta1.TableRow:
|
|
cr.instance = i.(*metav1beta1.TableRow)
|
|
case metav1beta1.TableRow:
|
|
t := i.(metav1beta1.TableRow)
|
|
cr.instance = &t
|
|
default:
|
|
log.Fatal().Msgf("Unknown %#v", i)
|
|
}
|
|
var obj map[string]interface{}
|
|
err := json.Unmarshal(cr.instance.Object.Raw, &obj)
|
|
if err != nil {
|
|
log.Error().Err(err)
|
|
}
|
|
meta := obj["metadata"].(map[string]interface{})
|
|
ns := ""
|
|
if n, ok := meta["namespace"]; ok {
|
|
ns = n.(string)
|
|
}
|
|
name := meta["name"].(string)
|
|
cr.path = path.Join(ns, name)
|
|
cr.group, cr.version, cr.name = obj["kind"].(string), obj["apiVersion"].(string), name
|
|
return cr
|
|
}
|
|
|
|
// Marshal resource to yaml.
|
|
func (r *Custom) Marshal(path string) (string, error) {
|
|
ns, n := namespaced(path)
|
|
i, err := r.caller.Get(ns, n)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
raw, err := yaml.Marshal(i)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return string(raw), nil
|
|
}
|
|
|
|
// List all resources
|
|
func (r *Custom) List(ns string) (Columnars, error) {
|
|
ii, err := r.caller.List(ns)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(ii) != 1 {
|
|
return Columnars{}, errors.New("no resources found")
|
|
}
|
|
|
|
table := ii[0].(*metav1beta1.Table)
|
|
r.headers = make(Row, len(table.ColumnDefinitions))
|
|
for i, h := range table.ColumnDefinitions {
|
|
r.headers[i] = h.Name
|
|
}
|
|
rows := table.Rows
|
|
cc := make(Columnars, 0, len(rows))
|
|
for i := 0; i < len(rows); i++ {
|
|
cc = append(cc, r.creator.NewInstance(rows[i]))
|
|
}
|
|
return cc, nil
|
|
}
|
|
|
|
// Header return resource header.
|
|
func (r *Custom) Header(ns string) Row {
|
|
hh := make(Row, 0, len(r.headers)+1)
|
|
if ns == AllNamespaces {
|
|
hh = append(hh, "NAMESPACE")
|
|
}
|
|
|
|
for _, h := range r.headers {
|
|
hh = append(hh, strings.ToUpper(h))
|
|
}
|
|
return hh
|
|
}
|
|
|
|
// Fields retrieves displayable fields.
|
|
func (r *Custom) Fields(ns string) Row {
|
|
ff := make(Row, 0, len(r.Header(ns)))
|
|
|
|
var obj map[string]interface{}
|
|
err := json.Unmarshal(r.instance.Object.Raw, &obj)
|
|
if err != nil {
|
|
log.Error().Err(err)
|
|
return Row{}
|
|
}
|
|
|
|
meta := obj["metadata"].(map[string]interface{})
|
|
|
|
if ns == AllNamespaces {
|
|
ff = append(ff, meta["namespace"].(string))
|
|
}
|
|
for _, c := range r.instance.Cells {
|
|
ff = append(ff, fmt.Sprintf("%v", c))
|
|
}
|
|
|
|
return ff
|
|
}
|
|
|
|
// ExtFields returns extended fields in relation to headers.
|
|
func (*Custom) ExtFields() Properties {
|
|
return Properties{}
|
|
}
|
|
|
|
func getCRDS() map[string]k8s.APIGroup {
|
|
m := map[string]k8s.APIGroup{}
|
|
list := NewCRDList("")
|
|
ll, _ := list.Resource().List("")
|
|
for _, l := range ll {
|
|
ff := l.ExtFields()
|
|
grp := k8s.APIGroup{
|
|
Resource: ff["name"].(string),
|
|
Version: ff["version"].(string),
|
|
Group: ff["group"].(string),
|
|
Kind: ff["kind"].(string),
|
|
}
|
|
if aa, ok := ff["aliases"].([]interface{}); ok {
|
|
if n, ok := ff["plural"].(string); ok {
|
|
grp.Plural = n
|
|
}
|
|
if n, ok := ff["singular"].(string); ok {
|
|
grp.Singular = n
|
|
}
|
|
aliases := make([]string, len(aa))
|
|
for i, a := range aa {
|
|
aliases[i] = a.(string)
|
|
}
|
|
grp.Aliases = aliases
|
|
} else if s, ok := ff["singular"].(string); ok {
|
|
grp.Singular = s
|
|
if p, ok := ff["plural"].(string); ok {
|
|
grp.Plural = p
|
|
}
|
|
} else if s, ok := ff["plural"].(string); ok {
|
|
grp.Plural = s
|
|
}
|
|
m[grp.Kind] = grp
|
|
}
|
|
return m
|
|
}
|