171 lines
4.1 KiB
Go
171 lines
4.1 KiB
Go
package model
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/derailed/k9s/internal"
|
|
"github.com/derailed/k9s/internal/k8s"
|
|
"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
|
|
sel := ctx.Value(internal.KeySelection).(string)
|
|
ns, n := render.Namespaced(sel)
|
|
c.namespace = ns
|
|
o, err := c.factory.Get(ns, "v1/pods", n, 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 := k8s.NewMetricsServer(c.factory.Client().(k8s.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
|
|
}
|