1
0
mirror of https://github.com/rancher/steve.git synced 2025-04-27 19:05:09 +00:00
steve/pkg/stores/sqlproxy/tablelistconvert/client.go
Silvio Moioli 7a84620e8b
SQLite backed cache (#223)
This uses SQLite-backed informers provided by Lasso with https://github.com/rancher/lasso/pull/65 to implement Steve API (/v1/) functionality.

This new functionality is available behind a feature flag to be specified at Steve startup

See https://confluence.suse.com/pages/viewpage.action?pageId=1359086083 

Co-authored-by: Ricardo Weir <ricardo.weir@suse.com>
Co-authored-by: Michael Bolot <michael.bolot@suse.com>
Co-authored-by: Silvio Moioli <silvio@moioli.net>
Signed-off-by: Silvio Moioli <silvio@moioli.net>
2024-06-05 16:17:12 +02:00

134 lines
3.1 KiB
Go

/*
Package tablelistconvert provides a client that will use a table client but convert *UnstructuredList and *Unstructured objects
returned by ByID and List to resemble those returned by non-table clients while preserving some table-related data.
*/
package tablelistconvert
import (
"context"
"fmt"
"github.com/rancher/wrangler/v2/pkg/data"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
k8sWatch "k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/dynamic"
)
type Client struct {
dynamic.ResourceInterface
}
var _ dynamic.ResourceInterface = (*Client)(nil)
type tableConvertWatch struct {
done chan struct{}
events chan k8sWatch.Event
k8sWatch.Interface
}
// List will return an *UnstructuredList that contains Items instead of just using the Object field to store a table as
// Table Clients do. The items will preserve values for columns in the form of metadata.fields.
func (c *Client) List(ctx context.Context, opts metav1.ListOptions) (*unstructured.UnstructuredList, error) {
list, err := c.ResourceInterface.List(ctx, opts)
if err != nil {
return nil, err
}
tableToList(list)
return list, nil
}
func (c *Client) Watch(ctx context.Context, opts metav1.ListOptions) (k8sWatch.Interface, error) {
w, err := c.ResourceInterface.Watch(ctx, opts)
if err != nil {
return nil, err
}
events := make(chan k8sWatch.Event)
done := make(chan struct{})
eventWatch := &tableConvertWatch{done: done, events: events, Interface: w}
eventWatch.feed()
return eventWatch, nil
}
func (w *tableConvertWatch) feed() {
tableEvents := w.Interface.ResultChan()
go func() {
for {
select {
case e, ok := <-tableEvents:
if !ok {
close(w.events)
return
}
if unstr, ok := e.Object.(*unstructured.Unstructured); ok {
rowToObject(unstr)
w.events <- e
}
case <-w.done:
close(w.events)
return
}
}
}()
}
func (w *tableConvertWatch) ResultChan() <-chan k8sWatch.Event {
return w.events
}
func (w *tableConvertWatch) Stop() {
fmt.Println("stop")
close(w.done)
w.Interface.Stop()
}
func rowToObject(obj *unstructured.Unstructured) {
if obj == nil {
return
}
if obj.Object["kind"] != "Table" ||
(obj.Object["apiVersion"] != "meta.k8s.io/v1" &&
obj.Object["apiVersion"] != "meta.k8s.io/v1beta1") {
return
}
items := tableToObjects(obj.Object)
if len(items) == 1 {
obj.Object = items[0].Object
}
}
func tableToList(obj *unstructured.UnstructuredList) {
if obj.Object["kind"] != "Table" ||
(obj.Object["apiVersion"] != "meta.k8s.io/v1" &&
obj.Object["apiVersion"] != "meta.k8s.io/v1beta1") {
return
}
obj.Items = tableToObjects(obj.Object)
}
func tableToObjects(obj map[string]interface{}) []unstructured.Unstructured {
var result []unstructured.Unstructured
rows, _ := obj["rows"].([]interface{})
for _, row := range rows {
m, ok := row.(map[string]interface{})
if !ok {
continue
}
cells := m["cells"]
object, ok := m["object"].(map[string]interface{})
if !ok {
continue
}
data.PutValue(object, cells, "metadata", "fields")
result = append(result, unstructured.Unstructured{
Object: object,
})
}
return result
}