155 lines
3.3 KiB
Go
155 lines
3.3 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 *metav1beta1.TableRow
|
|
group, version, name string
|
|
headers Row
|
|
}
|
|
|
|
// NewCustomList returns a new resource list.
|
|
func NewCustomList(c k8s.Connection, ns, group, version, name string) List {
|
|
if !c.IsNamespaced(name) {
|
|
ns = NotNamespaced
|
|
}
|
|
return NewList(
|
|
ns,
|
|
name,
|
|
NewCustom(c, group, version, name), AllVerbsAccess,
|
|
)
|
|
}
|
|
|
|
// NewCustom instantiates a new Kubernetes Resource.
|
|
func NewCustom(c k8s.Connection, group, version, name string) *Custom {
|
|
cr := &Custom{Base: &Base{Connection: c, Resource: k8s.NewResource(c, group, version, name)}}
|
|
cr.Factory = cr
|
|
cr.group, cr.version, cr.name = cr.Resource.(*k8s.Resource).GetInfo()
|
|
|
|
return cr
|
|
}
|
|
|
|
// New builds a new Custom instance from a k8s resource.
|
|
func (r *Custom) New(i interface{}) Columnar {
|
|
cr := NewCustom(r.Connection, "", "", "")
|
|
switch instance := i.(type) {
|
|
case *metav1beta1.TableRow:
|
|
cr.instance = instance
|
|
case metav1beta1.TableRow:
|
|
cr.instance = &instance
|
|
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.Resource.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.Resource.List(ns)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(ii) == 0 {
|
|
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.New(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{})
|
|
rns, ok := meta["namespace"].(string)
|
|
|
|
if ns == AllNamespaces {
|
|
if ok {
|
|
ff = append(ff, rns)
|
|
}
|
|
}
|
|
|
|
for _, c := range r.instance.Cells {
|
|
ff = append(ff, fmt.Sprintf("%v", c))
|
|
}
|
|
|
|
return ff
|
|
}
|