k9s/internal/model/container.go

175 lines
4.2 KiB
Go

package model
import (
"context"
"fmt"
"github.com/derailed/k9s/internal"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/render"
"github.com/rs/zerolog/log"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
mv1beta1 "k8s.io/metrics/pkg/apis/metrics/v1beta1"
)
var _ render.ContainerWithMetrics = &ContainerWithMetrics{}
// Container represents a container model.
type Container struct {
Resource
pod *v1.Pod
}
// List returns a collection of containers
func (c *Container) List(ctx context.Context) ([]runtime.Object, error) {
c.pod = nil
path, ok := ctx.Value(internal.KeyPath).(string)
if !ok {
return nil, fmt.Errorf("no context path for %q", c.gvr)
}
ns, _ := render.Namespaced(path)
c.namespace = ns
o, err := c.factory.Get("v1/pods", path, labels.Everything())
if err != nil {
return nil, err
}
var po v1.Pod
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &po)
if err != nil {
return nil, err
}
c.pod = &po
res := make([]runtime.Object, 0, len(po.Spec.InitContainers)+len(po.Spec.Containers))
for _, co := range po.Spec.InitContainers {
res = append(res, ContainerRes{co})
}
for _, co := range po.Spec.Containers {
res = append(res, ContainerRes{co})
}
return res, nil
}
// Hydrate returns a pod as container rows.
func (c *Container) Hydrate(oo []runtime.Object, rr render.Rows, re Renderer) error {
mx := client.NewMetricsServer(c.factory.Client().(client.Connection))
mmx, err := mx.FetchPodMetrics(c.namespace, c.pod.Name)
if err != nil {
log.Warn().Err(err).Msgf("No metrics found for pod %q:%q", c.namespace, c.pod.Name)
}
var index int
for _, o := range oo {
co := o.(ContainerRes)
row, err := renderCoRow(co.Container.Name, index, coMetricsFor(co.Container, c.pod, mmx, true), re)
if err != nil {
return err
}
rr[index] = row
index++
}
return nil
}
func renderCoRow(n string, index int, pmx *ContainerWithMetrics, re Renderer) (render.Row, error) {
var row render.Row
if err := re.Render(pmx, n, &row); err != nil {
return render.Row{}, err
}
return row, nil
}
func coMetricsFor(co v1.Container, po *v1.Pod, mmx *mv1beta1.PodMetrics, isInit bool) *ContainerWithMetrics {
return &ContainerWithMetrics{
container: &co,
status: getContainerStatus(co.Name, po.Status),
metrics: containerMetrics(co.Name, mmx),
isInit: isInit,
age: po.ObjectMeta.CreationTimestamp,
}
}
func containerMetrics(n string, mx runtime.Object) *mv1beta1.ContainerMetrics {
pmx := mx.(*mv1beta1.PodMetrics)
for _, m := range pmx.Containers {
if m.Name == n {
return &m
}
}
return nil
}
// ----------------------------------------------------------------------------
func getContainerStatus(co string, status v1.PodStatus) *v1.ContainerStatus {
for _, c := range status.ContainerStatuses {
if c.Name == co {
return &c
}
}
for _, c := range status.InitContainerStatuses {
if c.Name == co {
return &c
}
}
return nil
}
// ContainerWithMetrics represents a container and its metrics.
type ContainerWithMetrics struct {
container *v1.Container
status *v1.ContainerStatus
metrics *mv1beta1.ContainerMetrics
isInit bool
age metav1.Time
}
func (c *ContainerWithMetrics) IsInit() bool {
return c.isInit
}
func (c *ContainerWithMetrics) Container() *v1.Container {
return c.container
}
func (c *ContainerWithMetrics) ContainerStatus() *v1.ContainerStatus {
return c.status
}
// Metrics returns the metrics associated with the pod.
func (c *ContainerWithMetrics) Metrics() *mv1beta1.ContainerMetrics {
return c.metrics
}
func (c *ContainerWithMetrics) Age() metav1.Time {
return c.age
}
// ----------------------------------------------------------------------------
// ContainerRes represents a container K8s resource.
type ContainerRes struct {
v1.Container
}
// GetObjectKind returns a schema object.
func (c ContainerRes) GetObjectKind() schema.ObjectKind {
return nil
}
// DeepCopyObject returns a container copy.
func (c ContainerRes) DeepCopyObject() runtime.Object {
return c
}