mirror of
https://github.com/niusmallnan/steve.git
synced 2025-09-16 15:00:39 +00:00
Vendor
This commit is contained in:
121
pkg/schema/collection.go
Normal file
121
pkg/schema/collection.go
Normal file
@@ -0,0 +1,121 @@
|
||||
package schema
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/rancher/norman/v2/pkg/data"
|
||||
"github.com/rancher/norman/v2/pkg/types"
|
||||
"github.com/rancher/steve/pkg/accesscontrol"
|
||||
"github.com/rancher/steve/pkg/attributes"
|
||||
"github.com/rancher/steve/pkg/table"
|
||||
"github.com/rancher/wrangler/pkg/name"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
)
|
||||
|
||||
type Factory interface {
|
||||
Schemas(user user.Info) (*types.Schemas, error)
|
||||
ByGVR(gvr schema.GroupVersionResource) string
|
||||
ByGVK(gvr schema.GroupVersionKind) string
|
||||
}
|
||||
|
||||
type Collection struct {
|
||||
toSync int32
|
||||
baseSchema *types.Schemas
|
||||
schemas map[string]*types.Schema
|
||||
templates map[string]*Template
|
||||
byGVR map[schema.GroupVersionResource]string
|
||||
byGVK map[schema.GroupVersionKind]string
|
||||
|
||||
as *accesscontrol.AccessStore
|
||||
}
|
||||
|
||||
type Template struct {
|
||||
Group string
|
||||
Kind string
|
||||
ID string
|
||||
RegisterType interface{}
|
||||
Customize func(*types.Schema)
|
||||
Formatter types.Formatter
|
||||
Store types.Store
|
||||
StoreFactory func(types.Store) types.Store
|
||||
Mapper types.Mapper
|
||||
Columns []table.Column
|
||||
ComputedColumns func(data.Object)
|
||||
}
|
||||
|
||||
func NewCollection(baseSchema *types.Schemas, access *accesscontrol.AccessStore) *Collection {
|
||||
return &Collection{
|
||||
baseSchema: baseSchema,
|
||||
schemas: map[string]*types.Schema{},
|
||||
templates: map[string]*Template{},
|
||||
byGVR: map[schema.GroupVersionResource]string{},
|
||||
byGVK: map[schema.GroupVersionKind]string{},
|
||||
as: access,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Collection) Reset(schemas map[string]*types.Schema) {
|
||||
byGVK := map[schema.GroupVersionKind]string{}
|
||||
byGVR := map[schema.GroupVersionResource]string{}
|
||||
|
||||
for _, s := range schemas {
|
||||
gvr := attributes.GVR(s)
|
||||
if gvr.Resource != "" {
|
||||
byGVR[gvr] = s.ID
|
||||
}
|
||||
gvk := attributes.GVK(s)
|
||||
if gvk.Kind != "" {
|
||||
byGVK[gvk] = s.ID
|
||||
}
|
||||
}
|
||||
|
||||
c.schemas = schemas
|
||||
c.byGVR = byGVR
|
||||
c.byGVK = byGVK
|
||||
}
|
||||
|
||||
func (c *Collection) Schema(id string) *types.Schema {
|
||||
return c.schemas[id]
|
||||
}
|
||||
|
||||
func (c *Collection) IDs() (result []string) {
|
||||
seen := map[string]bool{}
|
||||
for _, id := range c.byGVR {
|
||||
if seen[id] {
|
||||
continue
|
||||
}
|
||||
seen[id] = true
|
||||
result = append(result, id)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Collection) ByGVR(gvr schema.GroupVersionResource) string {
|
||||
id, ok := c.byGVR[gvr]
|
||||
if ok {
|
||||
return id
|
||||
}
|
||||
gvr.Resource = name.GuessPluralName(strings.ToLower(gvr.Resource))
|
||||
return c.byGVK[schema.GroupVersionKind{
|
||||
Group: gvr.Group,
|
||||
Version: gvr.Version,
|
||||
Kind: gvr.Resource,
|
||||
}]
|
||||
}
|
||||
|
||||
func (c *Collection) ByGVK(gvk schema.GroupVersionKind) string {
|
||||
return c.byGVK[gvk]
|
||||
}
|
||||
|
||||
func (c *Collection) AddTemplate(template *Template) {
|
||||
if template.Kind != "" {
|
||||
c.templates[template.Group+"/"+template.Kind] = template
|
||||
}
|
||||
if template.ID != "" {
|
||||
c.templates[template.ID] = template
|
||||
}
|
||||
if template.Kind == "" && template.Group == "" && template.ID == "" {
|
||||
c.templates[""] = template
|
||||
}
|
||||
}
|
103
pkg/schema/converter/crd.go
Normal file
103
pkg/schema/converter/crd.go
Normal file
@@ -0,0 +1,103 @@
|
||||
package converter
|
||||
|
||||
import (
|
||||
"github.com/rancher/norman/v2/pkg/types"
|
||||
"github.com/rancher/steve/pkg/attributes"
|
||||
"github.com/rancher/steve/pkg/table"
|
||||
"github.com/rancher/wrangler-api/pkg/generated/controllers/apiextensions.k8s.io/v1beta1"
|
||||
beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
var (
|
||||
staticFields = map[string]types.Field{
|
||||
"apiVersion": {
|
||||
Type: "string",
|
||||
},
|
||||
"kind": {
|
||||
Type: "string",
|
||||
},
|
||||
"metadata": {
|
||||
Type: "io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func AddCustomResources(crd v1beta1.CustomResourceDefinitionClient, schemas map[string]*types.Schema) error {
|
||||
crds, err := crd.List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, crd := range crds.Items {
|
||||
if crd.Status.AcceptedNames.Plural == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
var columns []table.Column
|
||||
for _, col := range crd.Spec.AdditionalPrinterColumns {
|
||||
columns = append(columns, table.Column{
|
||||
Name: col.Name,
|
||||
Field: col.JSONPath,
|
||||
Type: col.Type,
|
||||
})
|
||||
}
|
||||
|
||||
group, kind := crd.Spec.Group, crd.Status.AcceptedNames.Kind
|
||||
|
||||
if crd.Spec.Version != "" {
|
||||
forVersion(&crd, group, crd.Spec.Version, kind, schemas, crd.Spec.AdditionalPrinterColumns, columns)
|
||||
}
|
||||
for _, version := range crd.Spec.Versions {
|
||||
forVersion(&crd, group, version.Name, kind, schemas, crd.Spec.AdditionalPrinterColumns, columns)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func forVersion(crd *beta1.CustomResourceDefinition, group, version, kind string, schemas map[string]*types.Schema, columnDefs []beta1.CustomResourceColumnDefinition, columns []table.Column) {
|
||||
var versionColumns []table.Column
|
||||
for _, col := range columnDefs {
|
||||
versionColumns = append(versionColumns, table.Column{
|
||||
Name: col.Name,
|
||||
Field: col.JSONPath,
|
||||
Type: col.Type,
|
||||
Format: col.Format,
|
||||
})
|
||||
}
|
||||
if len(versionColumns) == 0 {
|
||||
versionColumns = columns
|
||||
}
|
||||
|
||||
id := GVKToSchemaID(schema.GroupVersionKind{
|
||||
Group: group,
|
||||
Version: version,
|
||||
Kind: kind,
|
||||
})
|
||||
|
||||
schema := schemas[id]
|
||||
if schema == nil {
|
||||
return
|
||||
}
|
||||
if len(columns) > 0 {
|
||||
attributes.SetColumns(schema, columns)
|
||||
}
|
||||
|
||||
if crd.Spec.Validation != nil && crd.Spec.Validation.OpenAPIV3Schema != nil {
|
||||
if fieldsSchema := modelV3ToSchema(id, crd.Spec.Validation.OpenAPIV3Schema, schemas); fieldsSchema != nil {
|
||||
for k, v := range staticFields {
|
||||
fieldsSchema.ResourceFields[k] = v
|
||||
}
|
||||
for k, v := range fieldsSchema.ResourceFields {
|
||||
if schema.ResourceFields == nil {
|
||||
schema.ResourceFields = map[string]types.Field{}
|
||||
}
|
||||
if _, ok := schema.ResourceFields[k]; !ok {
|
||||
schema.ResourceFields[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
92
pkg/schema/converter/discovery.go
Normal file
92
pkg/schema/converter/discovery.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package converter
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/rancher/norman/v2/pkg/types"
|
||||
"github.com/rancher/steve/pkg/attributes"
|
||||
"github.com/rancher/wrangler/pkg/merr"
|
||||
"github.com/sirupsen/logrus"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/discovery"
|
||||
)
|
||||
|
||||
var (
|
||||
preferredGroups = map[string]string{
|
||||
"extensions": "apps",
|
||||
}
|
||||
)
|
||||
|
||||
func AddDiscovery(client discovery.DiscoveryInterface, schemas map[string]*types.Schema) error {
|
||||
logrus.Info("Refreshing all schemas")
|
||||
|
||||
groups, resourceLists, err := client.ServerGroupsAndResources()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
versions := indexVersions(groups)
|
||||
|
||||
var errs []error
|
||||
for _, resourceList := range resourceLists {
|
||||
gv, err := schema.ParseGroupVersion(resourceList.GroupVersion)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
if err := refresh(gv, versions, resourceList, schemas); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
|
||||
return merr.NewErrors(errs...)
|
||||
}
|
||||
|
||||
func indexVersions(groups []*metav1.APIGroup) map[string]string {
|
||||
result := map[string]string{}
|
||||
for _, group := range groups {
|
||||
result[group.Name] = group.PreferredVersion.Version
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func refresh(gv schema.GroupVersion, groupToPreferredVersion map[string]string, resources *metav1.APIResourceList, schemas map[string]*types.Schema) error {
|
||||
for _, resource := range resources.APIResources {
|
||||
if strings.Contains(resource.Name, "/") {
|
||||
continue
|
||||
}
|
||||
|
||||
gvk := schema.GroupVersionKind{
|
||||
Group: gv.Group,
|
||||
Version: gv.Version,
|
||||
Kind: resource.Kind,
|
||||
}
|
||||
gvr := gvk.GroupVersion().WithResource(resource.Name)
|
||||
|
||||
logrus.Infof("APIVersion %s/%s Kind %s", gvk.Group, gvk.Version, gvk.Kind)
|
||||
|
||||
schema := schemas[GVKToSchemaID(gvk)]
|
||||
if schema == nil {
|
||||
schema = &types.Schema{
|
||||
ID: GVKToSchemaID(gvk),
|
||||
Type: "schema",
|
||||
Dynamic: true,
|
||||
}
|
||||
attributes.SetGVK(schema, gvk)
|
||||
}
|
||||
|
||||
schema.PluralName = GVRToPluralName(gvr)
|
||||
attributes.SetAPIResource(schema, resource)
|
||||
if preferredVersion := groupToPreferredVersion[gv.Group]; preferredVersion != "" && preferredVersion != gv.Version {
|
||||
attributes.SetPreferredVersion(schema, preferredVersion)
|
||||
}
|
||||
if group := preferredGroups[gv.Group]; group != "" {
|
||||
attributes.SetPreferredGroup(schema, group)
|
||||
}
|
||||
|
||||
schemas[schema.ID] = schema
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
43
pkg/schema/converter/k8stonorman.go
Normal file
43
pkg/schema/converter/k8stonorman.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package converter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/rancher/norman/v2/pkg/types"
|
||||
"github.com/rancher/wrangler-api/pkg/generated/controllers/apiextensions.k8s.io/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/discovery"
|
||||
)
|
||||
|
||||
func GVKToSchemaID(gvk schema.GroupVersionKind) string {
|
||||
if gvk.Group == "" {
|
||||
return strings.ToLower(fmt.Sprintf("core.%s.%s", gvk.Version, gvk.Kind))
|
||||
}
|
||||
return strings.ToLower(fmt.Sprintf("%s.%s.%s", gvk.Group, gvk.Version, gvk.Kind))
|
||||
}
|
||||
|
||||
func GVRToPluralName(gvr schema.GroupVersionResource) string {
|
||||
if gvr.Group == "" {
|
||||
return fmt.Sprintf("core.%s.%s", gvr.Version, gvr.Resource)
|
||||
}
|
||||
return fmt.Sprintf("%s.%s.%s", gvr.Group, gvr.Version, gvr.Resource)
|
||||
}
|
||||
|
||||
func ToSchemas(crd v1beta1.CustomResourceDefinitionClient, client discovery.DiscoveryInterface) (map[string]*types.Schema, error) {
|
||||
result := map[string]*types.Schema{}
|
||||
|
||||
if err := AddOpenAPI(client, result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := AddDiscovery(client, result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := AddCustomResources(crd, result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
109
pkg/schema/converter/openapi.go
Normal file
109
pkg/schema/converter/openapi.go
Normal file
@@ -0,0 +1,109 @@
|
||||
package converter
|
||||
|
||||
import (
|
||||
"github.com/rancher/norman/v2/pkg/types"
|
||||
"github.com/rancher/norman/v2/pkg/types/convert"
|
||||
"github.com/rancher/steve/pkg/attributes"
|
||||
"github.com/sirupsen/logrus"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/discovery"
|
||||
"k8s.io/kube-openapi/pkg/util/proto"
|
||||
)
|
||||
|
||||
func modelToSchema(modelName string, k *proto.Kind) *types.Schema {
|
||||
s := types.Schema{
|
||||
ID: modelName,
|
||||
Type: "schema",
|
||||
ResourceFields: map[string]types.Field{},
|
||||
Attributes: map[string]interface{}{},
|
||||
Description: k.GetDescription(),
|
||||
Dynamic: true,
|
||||
}
|
||||
|
||||
for fieldName, schemaField := range k.Fields {
|
||||
s.ResourceFields[fieldName] = toField(schemaField)
|
||||
}
|
||||
|
||||
for _, fieldName := range k.RequiredFields {
|
||||
if f, ok := s.ResourceFields[fieldName]; ok {
|
||||
f.Required = true
|
||||
s.ResourceFields[fieldName] = f
|
||||
}
|
||||
}
|
||||
|
||||
if ms, ok := k.Extensions["x-kubernetes-group-version-kind"].([]interface{}); ok {
|
||||
for _, mv := range ms {
|
||||
if m, ok := mv.(map[interface{}]interface{}); ok {
|
||||
gvk := schema.GroupVersionKind{
|
||||
Group: convert.ToString(m["group"]),
|
||||
Version: convert.ToString(m["version"]),
|
||||
Kind: convert.ToString(m["kind"]),
|
||||
}
|
||||
|
||||
s.ID = GVKToSchemaID(gvk)
|
||||
attributes.SetGVK(&s, gvk)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &s
|
||||
}
|
||||
|
||||
func AddOpenAPI(client discovery.DiscoveryInterface, schemas map[string]*types.Schema) error {
|
||||
openapi, err := client.OpenAPISchema()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
models, err := proto.NewOpenAPIData(openapi)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, modelName := range models.ListModels() {
|
||||
model := models.LookupModel(modelName)
|
||||
if k, ok := model.(*proto.Kind); ok {
|
||||
schema := modelToSchema(modelName, k)
|
||||
schemas[schema.ID] = schema
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func toField(schema proto.Schema) types.Field {
|
||||
f := types.Field{
|
||||
Description: schema.GetDescription(),
|
||||
Nullable: true,
|
||||
Create: true,
|
||||
Update: true,
|
||||
}
|
||||
switch v := schema.(type) {
|
||||
case *proto.Array:
|
||||
f.Type = "array[" + toField(v.SubType).Type + "]"
|
||||
case *proto.Primitive:
|
||||
if v.Type == "number" || v.Type == "integer" {
|
||||
f.Type = "int"
|
||||
} else {
|
||||
f.Type = v.Type
|
||||
}
|
||||
case *proto.Map:
|
||||
f.Type = "map[" + toField(v.SubType).Type + "]"
|
||||
case *proto.Kind:
|
||||
parts := v.Path.Get()
|
||||
f.Type = parts[len(parts)-1]
|
||||
case proto.Reference:
|
||||
sub := v.SubSchema()
|
||||
if p, ok := sub.(*proto.Primitive); ok {
|
||||
f.Type = p.Type
|
||||
} else {
|
||||
f.Type = sub.GetPath().String()
|
||||
}
|
||||
case *proto.Arbitrary:
|
||||
default:
|
||||
logrus.Errorf("unknown type: %v", schema)
|
||||
f.Type = "json"
|
||||
}
|
||||
|
||||
return f
|
||||
}
|
37
pkg/schema/defaultmapper.go
Normal file
37
pkg/schema/defaultmapper.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package schema
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/rancher/norman/v2/pkg/data"
|
||||
"github.com/rancher/norman/v2/pkg/types"
|
||||
)
|
||||
|
||||
func newDefaultMapper() types.Mapper {
|
||||
return &defaultMapper{}
|
||||
}
|
||||
|
||||
type defaultMapper struct {
|
||||
types.EmptyMapper
|
||||
}
|
||||
|
||||
func (d *defaultMapper) FromInternal(data data.Object) {
|
||||
if data["kind"] != "" && data["apiVersion"] != "" {
|
||||
if t, ok := data["type"]; ok && data != nil {
|
||||
data["_type"] = t
|
||||
}
|
||||
}
|
||||
|
||||
if _, ok := data["id"]; ok || data == nil {
|
||||
return
|
||||
}
|
||||
|
||||
name := types.Name(data)
|
||||
namespace := types.Namespace(data)
|
||||
|
||||
if namespace == "" {
|
||||
data["id"] = name
|
||||
} else {
|
||||
data["id"] = fmt.Sprintf("%s/%s", namespace, name)
|
||||
}
|
||||
}
|
134
pkg/schema/factory.go
Normal file
134
pkg/schema/factory.go
Normal file
@@ -0,0 +1,134 @@
|
||||
package schema
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/rancher/norman/v2/pkg/api/builtin"
|
||||
"github.com/rancher/norman/v2/pkg/types"
|
||||
"github.com/rancher/steve/pkg/accesscontrol"
|
||||
"github.com/rancher/steve/pkg/attributes"
|
||||
"github.com/rancher/steve/pkg/table"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
)
|
||||
|
||||
func newSchemas() (*types.Schemas, error) {
|
||||
s, err := types.NewSchemas(builtin.Schemas)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.DefaultMapper = func() types.Mapper {
|
||||
return newDefaultMapper()
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (c *Collection) Schemas(user user.Info) (*types.Schemas, error) {
|
||||
access := c.as.AccessFor(user)
|
||||
return c.schemasForSubject(access)
|
||||
}
|
||||
|
||||
func (c *Collection) schemasForSubject(access *accesscontrol.AccessSet) (*types.Schemas, error) {
|
||||
result, err := newSchemas()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err := result.AddSchemas(c.baseSchema); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, template := range c.templates {
|
||||
if template.RegisterType != nil {
|
||||
s, err := result.Import(template.RegisterType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.applyTemplates(result, s)
|
||||
}
|
||||
}
|
||||
|
||||
for _, s := range c.schemas {
|
||||
gr := attributes.GR(s)
|
||||
|
||||
if gr.Resource == "" {
|
||||
if err := result.AddSchema(*s); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
verbs := attributes.Verbs(s)
|
||||
verbAccess := accesscontrol.AccessListMap{}
|
||||
|
||||
for _, verb := range verbs {
|
||||
a := access.AccessListFor(verb, gr)
|
||||
if len(a) > 0 {
|
||||
verbAccess[verb] = a
|
||||
}
|
||||
}
|
||||
|
||||
if len(verbAccess) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
s = s.DeepCopy()
|
||||
attributes.SetAccess(s, verbAccess)
|
||||
if verbAccess.AnyVerb("list", "get") {
|
||||
s.ResourceMethods = append(s.ResourceMethods, http.MethodGet)
|
||||
s.CollectionMethods = append(s.CollectionMethods, http.MethodGet)
|
||||
}
|
||||
if verbAccess.AnyVerb("delete") {
|
||||
s.ResourceMethods = append(s.ResourceMethods, http.MethodDelete)
|
||||
}
|
||||
if verbAccess.AnyVerb("update") {
|
||||
s.ResourceMethods = append(s.ResourceMethods, http.MethodPut)
|
||||
s.ResourceMethods = append(s.ResourceMethods, http.MethodPatch)
|
||||
}
|
||||
if verbAccess.AnyVerb("create") {
|
||||
s.CollectionMethods = append(s.CollectionMethods, http.MethodPost)
|
||||
}
|
||||
|
||||
c.applyTemplates(result, s)
|
||||
|
||||
if err := result.AddSchema(*s); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (c *Collection) applyTemplates(schemas *types.Schemas, schema *types.Schema) {
|
||||
templates := []*Template{
|
||||
c.templates[schema.ID],
|
||||
c.templates[fmt.Sprintf("%s/%s", attributes.Group(schema), attributes.Kind(schema))],
|
||||
c.templates[""],
|
||||
}
|
||||
|
||||
for _, t := range templates {
|
||||
if t == nil {
|
||||
continue
|
||||
}
|
||||
if t.Mapper != nil {
|
||||
schemas.AddMapper(schema.ID, t.Mapper)
|
||||
}
|
||||
if schema.Formatter == nil {
|
||||
schema.Formatter = t.Formatter
|
||||
}
|
||||
if schema.Store == nil {
|
||||
if t.StoreFactory == nil {
|
||||
schema.Store = t.Store
|
||||
} else {
|
||||
schema.Store = t.StoreFactory(templates[2].Store)
|
||||
}
|
||||
}
|
||||
if t.Customize != nil {
|
||||
t.Customize(schema)
|
||||
}
|
||||
if len(t.Columns) > 0 {
|
||||
schemas.AddMapper(schema.ID, table.NewColumns(t.ComputedColumns, t.Columns...))
|
||||
}
|
||||
}
|
||||
}
|
54
pkg/schema/table/mapper.go
Normal file
54
pkg/schema/table/mapper.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package table
|
||||
|
||||
import (
|
||||
"github.com/rancher/steve/pkg/attributes"
|
||||
types2 "github.com/rancher/steve/pkg/schemaserver/types"
|
||||
"github.com/rancher/wrangler/pkg/data"
|
||||
types "github.com/rancher/wrangler/pkg/schemas"
|
||||
"github.com/rancher/wrangler/pkg/schemas/mappers"
|
||||
)
|
||||
|
||||
type Column struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Field string `json:"field,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
Format string `json:"format,omitempty"`
|
||||
}
|
||||
|
||||
type Table struct {
|
||||
Columns []Column
|
||||
Computed func(data.Object)
|
||||
}
|
||||
|
||||
type ColumnMapper struct {
|
||||
definition Table
|
||||
mappers.EmptyMapper
|
||||
}
|
||||
|
||||
func NewColumns(computed func(data.Object), columns ...Column) *ColumnMapper {
|
||||
return &ColumnMapper{
|
||||
definition: Table{
|
||||
Columns: columns,
|
||||
Computed: computed,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (t *ColumnMapper) FromInternal(d data.Object) {
|
||||
if t.definition.Computed != nil {
|
||||
t.definition.Computed(d)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *ColumnMapper) ModifySchema(schema *types.Schema, schemas *types.Schemas) error {
|
||||
as := &types2.APISchema{
|
||||
Schema: schema,
|
||||
}
|
||||
cols := t.definition.Columns
|
||||
columnObj := attributes.Columns(as)
|
||||
if columns, ok := columnObj.([]Column); ok {
|
||||
cols = append(columns, cols...)
|
||||
}
|
||||
attributes.SetColumns(as, cols)
|
||||
return nil
|
||||
}
|
Reference in New Issue
Block a user