1
0
mirror of https://github.com/rancher/steve.git synced 2025-07-05 11:06:37 +00:00
steve/pkg/resources/virtual/common/common.go

117 lines
3.5 KiB
Go
Raw Normal View History

// Package common provides cache.TransformFunc's which are common to all types
package common
import (
"fmt"
"strings"
"github.com/rancher/steve/pkg/summarycache"
"github.com/rancher/wrangler/v3/pkg/data"
wranglerSummary "github.com/rancher/wrangler/v3/pkg/summary"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/tools/cache"
)
// SummaryCache provides an interface to get a summary/relationships for an object. Implemented by the summaryCache
// struct from pkg/summarycache
type SummaryCache interface {
SummaryAndRelationship(runtime.Object) (*wranglerSummary.SummarizedObject, []summarycache.Relationship)
}
// DefaultFields produces a VirtualTransformFunc through GetTransform() that applies to all k8s types
type DefaultFields struct {
Cache SummaryCache
}
// GetTransform produces the default transformation func
func (d *DefaultFields) GetTransform() cache.TransformFunc {
return d.transform
}
// transform implements virtual.VirtualTransformFunc, and adds reserved fields/summary
func (d *DefaultFields) transform(obj any) (any, error) {
raw, isSignal, err := getUnstructured(obj)
if isSignal {
return obj, nil
}
if err != nil {
return nil, err
}
raw = addIDField(raw)
raw, err = addSummaryFields(raw, d.Cache)
if err != nil {
return nil, fmt.Errorf("unable to add summary fields: %w", err)
}
return raw, nil
}
// addSummaryFields adds the virtual fields for object state.
func addSummaryFields(raw *unstructured.Unstructured, cache SummaryCache) (*unstructured.Unstructured, error) {
s, relationships := cache.SummaryAndRelationship(raw)
if s != nil {
data.PutValue(raw.Object, map[string]interface{}{
"name": s.State,
"error": s.Error,
"transitioning": s.Transitioning,
"message": strings.Join(s.Message, ":"),
}, "metadata", "state")
}
var rels []any
for _, relationship := range relationships {
rel, err := toMap(relationship)
if err != nil {
return nil, fmt.Errorf("unable to convert relationship to map: %w", err)
}
rels = append(rels, rel)
}
data.PutValue(raw.Object, rels, "metadata", "relationships")
normalizeConditions(raw)
return raw, nil
}
// addIDField adds the ID field based on namespace/name, and moves the current id field to _id if present
func addIDField(raw *unstructured.Unstructured) *unstructured.Unstructured {
objectID := raw.GetName()
namespace := raw.GetNamespace()
if namespace != "" {
objectID = fmt.Sprintf("%s/%s", namespace, objectID)
}
currentIDValue, ok := raw.Object["id"]
if ok {
raw.Object["_id"] = currentIDValue
}
raw.Object["id"] = objectID
return raw
}
func normalizeConditions(raw *unstructured.Unstructured) {
var (
obj data.Object
newConditions []any
)
obj = raw.Object
for _, condition := range obj.Slice("status", "conditions") {
var summary wranglerSummary.Summary
for _, summarizer := range wranglerSummary.ConditionSummarizers {
summary = summarizer(obj, []wranglerSummary.Condition{{Object: condition}}, summary)
}
condition.Set("error", summary.Error)
condition.Set("transitioning", summary.Transitioning)
if condition.String("lastUpdateTime") == "" {
condition.Set("lastUpdateTime", condition.String("lastTransitionTime"))
}
// needs to be reconverted back to a map[string]any or we can have encoding problems with unregistered types
var mapCondition map[string]any = condition
newConditions = append(newConditions, mapCondition)
}
if len(newConditions) > 0 {
obj.SetNested(newConditions, "status", "conditions")
}
}