k9s/internal/model/reconcile.go

103 lines
2.7 KiB
Go

package model
import (
"context"
"fmt"
"time"
"github.com/derailed/k9s/internal"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/render"
"github.com/rs/zerolog/log"
)
// Reconcile previous vs current state and emits delta events.
func Reconcile(ctx context.Context, table render.TableData, gvr client.GVR) (render.TableData, error) {
defer func(t time.Time) {
log.Debug().Msgf("RECONCILE elapsed: %v", time.Since(t))
}(time.Now())
path, ok := ctx.Value(internal.KeyPath).(string)
if !ok {
return table, fmt.Errorf("no path specified for %s", gvr)
}
log.Debug().Msgf("Reconcile %q in ns %q with path %q", gvr, table.Namespace, path)
factory, ok := ctx.Value(internal.KeyFactory).(Factory)
if !ok {
return table, fmt.Errorf("no factory found for %s", gvr)
}
m, ok := Registry[string(gvr)]
if !ok {
log.Warn().Msgf("Resource %s not found in registry. Going generic!", gvr)
m = ResourceMeta{
Model: &Generic{},
Renderer: &render.Generic{},
}
}
if m.Model == nil {
m.Model = &Resource{}
}
m.Model.Init(table.Namespace, string(gvr), factory)
table.Header = m.Renderer.Header(table.Namespace)
oo, err := m.Model.List(ctx)
if err != nil {
return table, err
}
log.Debug().Msgf("Model returned [%d] items", len(oo))
rows := make(render.Rows, len(oo))
if err := m.Model.Hydrate(oo, rows, m.Renderer); err != nil {
return table, err
}
update(&table, rows)
log.Debug().Msgf("Table returned [%d] events", len(table.RowEvents))
return table, nil
}
func update(table *render.TableData, rows render.Rows) {
cacheEmpty := len(table.RowEvents) == 0
kk := make([]string, 0, len(rows))
var blankDelta render.DeltaRow
for _, row := range rows {
kk = append(kk, row.ID)
if cacheEmpty {
table.RowEvents = append(table.RowEvents, render.NewRowEvent(render.EventAdd, row))
continue
}
if index, ok := table.RowEvents.FindIndex(row.ID); ok {
delta := render.NewDeltaRow(table.RowEvents[index].Row, row, table.Header.HasAge())
if delta.IsBlank() {
table.RowEvents[index].Kind, table.RowEvents[index].Deltas = render.EventUnchanged, blankDelta
} else {
table.RowEvents[index] = render.NewDeltaRowEvent(row, delta)
}
continue
}
table.RowEvents = append(table.RowEvents, render.NewRowEvent(render.EventAdd, row))
}
if cacheEmpty {
return
}
ensureDeletes(table, kk)
}
// EnsureDeletes delete items in cache that are no longer valid.
func ensureDeletes(table *render.TableData, newKeys []string) {
for _, re := range table.RowEvents {
var found bool
for i, key := range newKeys {
if key == re.Row.ID {
found = true
newKeys = append(newKeys[:i], newKeys[i+1:]...)
break
}
}
if !found {
table.RowEvents = table.RowEvents.Delete(re.Row.ID)
}
}
}