k9s/internal/resource/svc.go

184 lines
3.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package resource
import (
"sort"
"strconv"
"strings"
"github.com/derailed/k9s/internal/k8s"
"github.com/rs/zerolog/log"
v1 "k8s.io/api/core/v1"
)
const lbIPWidth = 16
// Service tracks a kubernetes resource.
type Service struct {
*Base
instance *v1.Service
}
// NewServiceList returns a new resource list.
func NewServiceList(ns string) List {
return NewServiceListWithArgs(ns, NewService())
}
// NewServiceListWithArgs returns a new resource list.
func NewServiceListWithArgs(ns string, res Resource) List {
return newList(ns, "svc", res, AllVerbsAccess|DescribeAccess)
}
// NewService instantiates a new Endpoint.
func NewService() *Service {
return NewServiceWithArgs(k8s.NewService())
}
// NewServiceWithArgs instantiates a new Endpoint.
func NewServiceWithArgs(r k8s.Res) *Service {
ep := &Service{
Base: &Base{
caller: r,
},
}
ep.creator = ep
return ep
}
// NewInstance builds a new Endpoint instance from a k8s resource.
func (*Service) NewInstance(i interface{}) Columnar {
cm := NewService()
switch i.(type) {
case *v1.Service:
cm.instance = i.(*v1.Service)
case v1.Service:
ii := i.(v1.Service)
cm.instance = &ii
default:
log.Fatal().Msgf("Unknown %#v", i)
}
cm.path = cm.namespacedName(cm.instance.ObjectMeta)
return cm
}
// Marshal resource to yaml.
// BOZO!! Why you need to fill type info??
func (r *Service) Marshal(path string) (string, error) {
ns, n := namespaced(path)
i, err := r.caller.Get(ns, n)
if err != nil {
return "", err
}
svc := i.(*v1.Service)
svc.TypeMeta.APIVersion = "v1"
svc.TypeMeta.Kind = "Service"
return r.marshalObject(svc)
}
// Header returns resource header.
func (*Service) Header(ns string) Row {
hh := Row{}
if ns == AllNamespaces {
hh = append(hh, "NAMESPACE")
}
return append(hh,
"NAME",
"TYPE",
"CLUSTER-IP",
"EXTERNAL-IP",
"PORT(S)",
"AGE",
)
}
// Fields retrieves displayable fields.
func (r *Service) Fields(ns string) Row {
ff := make(Row, 0, len(r.Header(ns)))
i := r.instance
if ns == AllNamespaces {
ff = append(ff, i.Namespace)
}
return append(ff,
i.ObjectMeta.Name,
string(i.Spec.Type),
i.Spec.ClusterIP,
r.toIPs(i.Spec.Type, getSvcExtIPS(i)),
r.toPorts(i.Spec.Ports),
toAge(i.ObjectMeta.CreationTimestamp),
)
}
// ExtFields returns extended fields in relation to headers.
func (r *Service) ExtFields() Properties {
return Properties{}
}
// Helpers...
func getSvcExtIPS(svc *v1.Service) []string {
results := []string{}
switch svc.Spec.Type {
case v1.ServiceTypeClusterIP:
fallthrough
case v1.ServiceTypeNodePort:
return svc.Spec.ExternalIPs
case v1.ServiceTypeLoadBalancer:
lbIps := lbIngressIP(svc.Status.LoadBalancer)
if len(svc.Spec.ExternalIPs) > 0 {
if len(lbIps) > 0 {
results = append(results, lbIps)
}
return append(results, svc.Spec.ExternalIPs...)
}
if len(lbIps) > 0 {
results = append(results, lbIps)
}
case v1.ServiceTypeExternalName:
results = append(results, svc.Spec.ExternalName)
}
return results
}
func lbIngressIP(s v1.LoadBalancerStatus) string {
ingress := s.Ingress
result := []string{}
for i := range ingress {
if len(ingress[i].IP) > 0 {
result = append(result, ingress[i].IP)
} else if len(ingress[i].Hostname) > 0 {
result = append(result, ingress[i].Hostname)
}
}
return strings.Join(result, ",")
}
func (*Service) toIPs(svcType v1.ServiceType, ips []string) string {
if len(ips) == 0 {
if svcType == v1.ServiceTypeLoadBalancer {
return "<pending>"
}
return MissingValue
}
sort.Strings(ips)
return strings.Join(ips, ",")
}
func (*Service) toPorts(pp []v1.ServicePort) string {
ports := make([]string, len(pp))
for i, p := range pp {
if len(p.Name) > 0 {
ports[i] = p.Name + ":"
}
ports[i] += strconv.Itoa(int(p.Port)) +
"►" +
strconv.Itoa(int(p.NodePort))
if p.Protocol != "TCP" {
ports[i] += "" + string(p.Protocol)
}
}
return strings.Join(ports, " ")
}