1
0
mirror of https://github.com/rancher/steve.git synced 2025-09-02 16:05:42 +00:00

Add column support

This commit is contained in:
Darren Shepherd
2019-08-14 11:08:34 -07:00
parent db650d6c19
commit 583309b969
23 changed files with 365 additions and 177 deletions

View File

@@ -122,3 +122,14 @@ func SetAPIResource(s *types.Schema, resource v1.APIResource) {
SetVerbs(s, resource.Verbs) SetVerbs(s, resource.Verbs)
SetNamespaced(s, resource.Namespaced) SetNamespaced(s, resource.Namespaced)
} }
func SetColumns(s *types.Schema, columns interface{}) {
if s.Attributes == nil {
s.Attributes = map[string]interface{}{}
}
s.Attributes["columns"] = columns
}
func Columns(s *types.Schema) interface{} {
return s.Attributes["columns"]
}

View File

@@ -22,6 +22,7 @@ type handler struct {
toSync int32 toSync int32
schemas *schema2.Collection schemas *schema2.Collection
client discovery.DiscoveryInterface client discovery.DiscoveryInterface
crd apiextcontrollerv1beta1.CustomResourceDefinitionClient
} }
func Register(ctx context.Context, func Register(ctx context.Context,
@@ -33,6 +34,7 @@ func Register(ctx context.Context,
h := &handler{ h := &handler{
client: discovery, client: discovery,
schemas: schemas, schemas: schemas,
crd: crd,
} }
apiService.OnChange(ctx, "schema", h.OnChangeAPIService) apiService.OnChange(ctx, "schema", h.OnChangeAPIService)
@@ -70,7 +72,7 @@ func (h *handler) refreshAll() error {
} }
logrus.Info("Refreshing all schemas") logrus.Info("Refreshing all schemas")
schemas, err := converter.ToSchemas(h.client) schemas, err := converter.ToSchemas(h.crd, h.client)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -0,0 +1,30 @@
package common
import (
"github.com/rancher/naok/pkg/attributes"
"github.com/rancher/naok/pkg/table"
"github.com/rancher/norman/pkg/types"
)
type DefaultColumns struct {
types.EmptyMapper
}
func (d *DefaultColumns) ModifySchema(schema *types.Schema, schemas *types.Schemas) error {
if attributes.Columns(schema) == nil {
attributes.SetColumns(schema, []table.Column{
{
Name: "Name",
Field: "metadata.name",
Type: "string",
},
{
Name: "Created",
Field: "metadata.creationTimestamp",
Type: "date",
},
})
}
return nil
}

View File

@@ -12,6 +12,7 @@ func Register(collection *schema.Collection, clientGetter proxy.ClientGetter) {
collection.AddTemplate(&schema.Template{ collection.AddTemplate(&schema.Template{
Store: proxy.NewProxyStore(clientGetter), Store: proxy.NewProxyStore(clientGetter),
Formatter: Formatter, Formatter: Formatter,
Mapper: &DefaultColumns{},
}) })
} }

View File

@@ -2,8 +2,8 @@ package resources
import ( import (
"github.com/rancher/naok/pkg/accesscontrol" "github.com/rancher/naok/pkg/accesscontrol"
"github.com/rancher/naok/pkg/counts"
"github.com/rancher/naok/pkg/resources/common" "github.com/rancher/naok/pkg/resources/common"
"github.com/rancher/naok/pkg/resources/counts"
"github.com/rancher/naok/pkg/resources/schema" "github.com/rancher/naok/pkg/resources/schema"
"github.com/rancher/norman/pkg/store/proxy" "github.com/rancher/norman/pkg/store/proxy"
"github.com/rancher/norman/pkg/subscribe" "github.com/rancher/norman/pkg/subscribe"

View File

@@ -26,12 +26,14 @@ type Collection struct {
} }
type Template struct { type Template struct {
Group string Group string
Kind string Kind string
ID string ID string
Formatter types.Formatter RegisterType interface{}
Store types.Store Customize func(*types.Schema)
Mapper types.Mapper Formatter types.Formatter
Store types.Store
Mapper types.Mapper
} }
func NewCollection(baseSchema *types.Schemas, access *accesscontrol.AccessStore) *Collection { func NewCollection(baseSchema *types.Schemas, access *accesscontrol.AccessStore) *Collection {

View File

@@ -0,0 +1,70 @@
package converter
import (
"github.com/rancher/naok/pkg/attributes"
"github.com/rancher/naok/pkg/table"
"github.com/rancher/norman/pkg/types"
"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"
)
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, resource := crd.Spec.Group, crd.Status.AcceptedNames.Plural
if crd.Spec.Version != "" {
forVersion(group, crd.Spec.Version, resource, schemas, crd.Spec.AdditionalPrinterColumns, columns)
}
for _, version := range crd.Spec.Versions {
forVersion(group, version.Name, resource, schemas, crd.Spec.AdditionalPrinterColumns, columns)
}
}
return nil
}
func forVersion(group, version, resource 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,
})
}
if len(versionColumns) == 0 {
versionColumns = columns
}
id := GVRToSchemaID(schema.GroupVersionResource{
Group: group,
Version: version,
Resource: resource,
})
schema := schemas[id]
if schema == nil {
return
}
attributes.SetColumns(schema, columns)
}

View File

@@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"github.com/rancher/norman/pkg/types" "github.com/rancher/norman/pkg/types"
"github.com/rancher/wrangler-api/pkg/generated/controllers/apiextensions.k8s.io/v1beta1"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/discovery" "k8s.io/client-go/discovery"
) )
@@ -15,14 +16,14 @@ func gvkToSchemaID(gvk schema.GroupVersionKind) string {
return fmt.Sprintf("%s.%s.%s", gvk.Group, gvk.Version, gvk.Kind) return fmt.Sprintf("%s.%s.%s", gvk.Group, gvk.Version, gvk.Kind)
} }
func GVRToSchemaID(gvk schema.GroupVersionResource) string { func GVRToSchemaID(gvr schema.GroupVersionResource) string {
if gvk.Group == "" { if gvr.Group == "" {
return fmt.Sprintf("core.%s.%s", gvk.Version, gvk.Resource) return fmt.Sprintf("core.%s.%s", gvr.Version, gvr.Resource)
} }
return fmt.Sprintf("%s.%s.%s", gvk.Group, gvk.Version, gvk.Resource) return fmt.Sprintf("%s.%s.%s", gvr.Group, gvr.Version, gvr.Resource)
} }
func ToSchemas(client discovery.DiscoveryInterface) (map[string]*types.Schema, error) { func ToSchemas(crd v1beta1.CustomResourceDefinitionClient, client discovery.DiscoveryInterface) (map[string]*types.Schema, error) {
result := map[string]*types.Schema{} result := map[string]*types.Schema{}
if err := AddOpenAPI(client, result); err != nil { if err := AddOpenAPI(client, result); err != nil {
@@ -33,5 +34,9 @@ func ToSchemas(client discovery.DiscoveryInterface) (map[string]*types.Schema, e
return nil, err return nil, err
} }
if err := AddCustomResources(crd, result); err != nil {
return nil, err
}
return result, nil return result, nil
} }

View File

@@ -3,9 +3,8 @@ package schema
import ( import (
"fmt" "fmt"
"github.com/rancher/norman/pkg/data"
"github.com/rancher/norman/pkg/types" "github.com/rancher/norman/pkg/types"
"github.com/rancher/norman/pkg/types/convert"
"github.com/rancher/norman/pkg/types/values"
) )
func newDefaultMapper() types.Mapper { func newDefaultMapper() types.Mapper {
@@ -16,7 +15,7 @@ type defaultMapper struct {
types.EmptyMapper types.EmptyMapper
} }
func (d *defaultMapper) FromInternal(data map[string]interface{}) { func (d *defaultMapper) FromInternal(data data.Object) {
if data["kind"] != "" && data["apiVersion"] != "" { if data["kind"] != "" && data["apiVersion"] != "" {
if t, ok := data["type"]; ok && data != nil { if t, ok := data["type"]; ok && data != nil {
data["_type"] = t data["_type"] = t
@@ -27,8 +26,8 @@ func (d *defaultMapper) FromInternal(data map[string]interface{}) {
return return
} }
name := convert.ToString(values.GetValueN(data, "metadata", "name")) name := types.Name(data)
namespace := convert.ToString(values.GetValueN(data, "metadata", "namespace")) namespace := types.Namespace(data)
if namespace == "" { if namespace == "" {
data["id"] = name data["id"] = name

View File

@@ -38,6 +38,16 @@ func (c *Collection) schemasForSubject(subjectKey string, access *accesscontrol.
return nil, err 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 { for _, s := range c.schemas {
gr := attributes.GR(s) gr := attributes.GR(s)
@@ -78,7 +88,7 @@ func (c *Collection) schemasForSubject(subjectKey string, access *accesscontrol.
s.CollectionMethods = append(s.CollectionMethods, http.MethodPost) s.CollectionMethods = append(s.CollectionMethods, http.MethodPost)
} }
c.applyTemplates(s) c.applyTemplates(result, s)
if err := result.AddSchema(*s); err != nil { if err := result.AddSchema(*s); err != nil {
return nil, err return nil, err
@@ -88,7 +98,7 @@ func (c *Collection) schemasForSubject(subjectKey string, access *accesscontrol.
return result, nil return result, nil
} }
func (c *Collection) applyTemplates(schema *types.Schema) { func (c *Collection) applyTemplates(schemas *types.Schemas, schema *types.Schema) {
templates := []*Template{ templates := []*Template{
c.templates[schema.ID], c.templates[schema.ID],
c.templates[fmt.Sprintf("%s/%s", attributes.Group(schema), attributes.Kind(schema))], c.templates[fmt.Sprintf("%s/%s", attributes.Group(schema), attributes.Kind(schema))],
@@ -99,8 +109,8 @@ func (c *Collection) applyTemplates(schema *types.Schema) {
if t == nil { if t == nil {
continue continue
} }
if schema.Mapper == nil { if t.Mapper != nil {
schema.Mapper = t.Mapper schemas.AddMapper(schema.ID, t.Mapper)
} }
if schema.Formatter == nil { if schema.Formatter == nil {
schema.Formatter = t.Formatter schema.Formatter = t.Formatter
@@ -108,5 +118,8 @@ func (c *Collection) applyTemplates(schema *types.Schema) {
if schema.Store == nil { if schema.Store == nil {
schema.Store = t.Store schema.Store = t.Store
} }
if t.Customize != nil {
t.Customize(schema)
}
} }
} }

View File

@@ -1,14 +1,14 @@
package server package publicapi
import ( import (
"net/http" "net/http"
"strings" "strings"
"github.com/gorilla/mux"
"github.com/rancher/naok/pkg/accesscontrol" "github.com/rancher/naok/pkg/accesscontrol"
"github.com/rancher/naok/pkg/attributes" "github.com/rancher/naok/pkg/attributes"
k8sproxy "github.com/rancher/naok/pkg/proxy" k8sproxy "github.com/rancher/naok/pkg/proxy"
"github.com/rancher/naok/pkg/resources/schema" "github.com/rancher/naok/pkg/resources/schema"
"github.com/rancher/naok/pkg/server/router"
"github.com/rancher/norman/pkg/api" "github.com/rancher/norman/pkg/api"
"github.com/rancher/norman/pkg/types" "github.com/rancher/norman/pkg/types"
"github.com/rancher/norman/pkg/urlbuilder" "github.com/rancher/norman/pkg/urlbuilder"
@@ -16,29 +16,30 @@ import (
"k8s.io/client-go/rest" "k8s.io/client-go/rest"
) )
func newAPIServer(cfg *rest.Config, sf schema.Factory) (http.Handler, error) { func NewHandler(cfg *rest.Config, sf schema.Factory) (http.Handler, error) {
var ( var (
err error err error
) )
a := &apiServer{ a := &apiServer{
Router: mux.NewRouter(),
sf: sf, sf: sf,
server: api.NewAPIServer(), server: api.DefaultAPIServer(),
} }
a.server.AccessControl = accesscontrol.NewAccessControl()
a.Router.NotFoundHandler, err = k8sproxy.Handler("/", cfg) proxy, err := k8sproxy.Handler("/", cfg)
if err != nil { if err != nil {
return nil, err return nil, err
} }
a.Router.StrictSlash(true) return router.Routes(router.Handlers{
a.server.AccessControl = accesscontrol.NewAccessControl() K8sResource: a.apiHandler(k8sAPI),
return a, a.routes() GenericResource: a.apiHandler(nil),
K8sProxy: proxy,
}), nil
} }
type apiServer struct { type apiServer struct {
*mux.Router
sf schema.Factory sf schema.Factory
server *api.Server server *api.Server
} }
@@ -77,3 +78,21 @@ func (a *apiServer) Schema(base string, schema *types.Schema) string {
} }
return urlbuilder.ConstructBasicURL(base, "v1", strings.ToLower(schema.ID)) return urlbuilder.ConstructBasicURL(base, "v1", strings.ToLower(schema.ID))
} }
type APIFunc func(schema.Factory, *types.APIRequest)
func (a *apiServer) apiHandler(apiFunc APIFunc) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
a.api(rw, req, apiFunc)
})
}
func (a *apiServer) api(rw http.ResponseWriter, req *http.Request, apiFunc APIFunc) {
apiOp, ok := a.common(rw, req)
if ok {
if apiFunc != nil {
apiFunc(a.sf, apiOp)
}
a.server.Handle(apiOp)
}
}

View File

@@ -0,0 +1,38 @@
package publicapi
import (
"github.com/gorilla/mux"
"github.com/rancher/naok/pkg/attributes"
"github.com/rancher/naok/pkg/resources/schema"
"github.com/rancher/norman/pkg/types"
runtimeschema "k8s.io/apimachinery/pkg/runtime/schema"
)
func k8sAPI(sf schema.Factory, apiOp *types.APIRequest) {
vars := mux.Vars(apiOp.Request)
group := vars["group"]
if group == "core" {
group = ""
}
apiOp.Name = vars["name"]
apiOp.Type = sf.ByGVR(runtimeschema.GroupVersionResource{
Version: vars["version"],
Group: group,
Resource: vars["resource"],
})
nOrN := vars["nameorns"]
if nOrN != "" {
schema := apiOp.Schemas.Schema(apiOp.Type)
if attributes.Namespaced(schema) {
vars["namespace"] = nOrN
} else {
vars["name"] = nOrN
}
}
if namespace := vars["namespace"]; namespace != "" {
apiOp.Namespaces = []string{namespace}
}
}

View File

@@ -0,0 +1,29 @@
package router
import (
"net/http"
"github.com/gorilla/mux"
)
type Handlers struct {
K8sResource http.Handler
GenericResource http.Handler
K8sProxy http.Handler
}
func Routes(h Handlers) http.Handler {
m := mux.NewRouter()
m.UseEncodedPath()
m.StrictSlash(true)
m.NotFoundHandler = h.K8sProxy
m.Path("/v1/{type:schemas}/{name:.*}").Handler(h.GenericResource)
m.Path("/v1/{group}.{version}.{resource}").Handler(h.K8sResource)
m.Path("/v1/{group}.{version}.{resource}/{nameorns}").Handler(h.K8sResource)
m.Path("/v1/{group}.{version}.{resource}/{namespace}/{name}").Handler(h.K8sResource)
m.Path("/v1/{type}").Handler(h.GenericResource)
m.Path("/v1/{type}/{name}").Handler(h.GenericResource)
return m
}

View File

@@ -1,68 +0,0 @@
package server
import (
"net/http"
"github.com/gorilla/mux"
"github.com/rancher/naok/pkg/attributes"
"github.com/rancher/norman/pkg/types"
"k8s.io/apimachinery/pkg/runtime/schema"
)
type APIFunc func(*types.APIRequest)
func (a *apiServer) routes() error {
a.Path("/v1/{type:schemas}/{name:.*}").Handler(a.handle(nil))
a.Path("/v1/{group}.{version}.{resource}").Handler(a.handle(a.k8sAPI))
a.Path("/v1/{group}.{version}.{resource}/{nameorns}").Handler(a.handle(a.k8sAPI))
a.Path("/v1/{group}.{version}.{resource}/{namespace}/{name}").Handler(a.handle(a.k8sAPI))
a.Path("/v1/{type}").Handler(a.handle(nil))
a.Path("/v1/{type}/{name}").Handler(a.handle(nil))
return nil
}
func (a *apiServer) handle(apiFunc APIFunc) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
a.api(rw, req, apiFunc)
})
}
func (a *apiServer) api(rw http.ResponseWriter, req *http.Request, apiFunc APIFunc) {
apiOp, ok := a.common(rw, req)
if ok {
if apiFunc != nil {
apiFunc(apiOp)
}
a.server.Handle(apiOp)
}
}
func (a *apiServer) k8sAPI(apiOp *types.APIRequest) {
vars := mux.Vars(apiOp.Request)
group := vars["group"]
if group == "core" {
group = ""
}
apiOp.Name = vars["name"]
apiOp.Type = a.sf.ByGVR(schema.GroupVersionResource{
Version: vars["version"],
Group: group,
Resource: vars["resource"],
})
nOrN := vars["nameorns"]
if nOrN != "" {
schema := apiOp.Schemas.Schema(apiOp.Type)
if attributes.Namespaced(schema) {
vars["namespace"] = nOrN
} else {
vars["name"] = nOrN
}
}
if namespace := vars["namespace"]; namespace != "" {
apiOp.Namespaces = []string{namespace}
}
}

View File

@@ -4,12 +4,11 @@ import (
"context" "context"
"net/http" "net/http"
"github.com/rancher/naok/pkg/resources"
"github.com/rancher/naok/pkg/controllers/schema"
"github.com/rancher/naok/pkg/accesscontrol" "github.com/rancher/naok/pkg/accesscontrol"
"github.com/rancher/naok/pkg/client" "github.com/rancher/naok/pkg/client"
"github.com/rancher/naok/pkg/controllers/schema"
"github.com/rancher/naok/pkg/resources"
"github.com/rancher/naok/pkg/server/publicapi"
"github.com/rancher/wrangler-api/pkg/generated/controllers/apiextensions.k8s.io" "github.com/rancher/wrangler-api/pkg/generated/controllers/apiextensions.k8s.io"
"github.com/rancher/wrangler-api/pkg/generated/controllers/apiregistration.k8s.io" "github.com/rancher/wrangler-api/pkg/generated/controllers/apiregistration.k8s.io"
rbaccontroller "github.com/rancher/wrangler-api/pkg/generated/controllers/rbac" rbaccontroller "github.com/rancher/wrangler-api/pkg/generated/controllers/rbac"
@@ -17,7 +16,6 @@ import (
"github.com/rancher/wrangler/pkg/start" "github.com/rancher/wrangler/pkg/start"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
) )
type Config struct { type Config struct {
@@ -52,7 +50,21 @@ func Run(ctx context.Context, cfg Config) error {
return err return err
} }
starter, err := startAPI(ctx, cfg.ListenAddress, restConfig, k8s, crd, api, rbac) cf, err := client.NewFactory(restConfig)
if err != nil {
return err
}
sf := resources.SchemaFactory(cf,
accesscontrol.NewAccessStore(rbac.Rbac().V1()))
schema.Register(ctx,
k8s.Discovery(),
crd.Apiextensions().V1beta1().CustomResourceDefinition(),
api.Apiregistration().V1().APIService(),
sf)
handler, err := publicapi.NewHandler(restConfig, sf)
if err != nil { if err != nil {
return err return err
} }
@@ -61,37 +73,6 @@ func Run(ctx context.Context, cfg Config) error {
return err return err
} }
if err := starter(); err != nil { logrus.Infof("listening on %s", cfg.ListenAddress)
return err return http.ListenAndServe(cfg.ListenAddress, handler)
}
<-ctx.Done()
return nil
}
func startAPI(ctx context.Context, listenAddress string, restConfig *rest.Config, k8s *kubernetes.Clientset, crd *apiextensions.Factory,
api *apiregistration.Factory, rbac *rbaccontroller.Factory) (func() error, error) {
cf, err := client.NewFactory(restConfig)
if err != nil {
return nil, err
}
as := accesscontrol.NewAccessStore(rbac.Rbac().V1())
sf := resources.SchemaFactory(cf, as)
schema.Register(ctx,
k8s.Discovery(),
crd.Apiextensions().V1beta1().CustomResourceDefinition(),
api.Apiregistration().V1().APIService(),
sf)
return func() error {
handler, err := newAPIServer(restConfig, sf)
if err != nil {
return err
}
logrus.Infof("listening on %s", listenAddress)
return http.ListenAndServe(listenAddress, handler)
}, nil
} }

35
pkg/table/mapper.go Normal file
View File

@@ -0,0 +1,35 @@
package table
import (
"github.com/rancher/naok/pkg/attributes"
"github.com/rancher/norman/pkg/data"
"github.com/rancher/norman/pkg/types"
)
type Column struct {
Name string `json:"name,omitempty"`
Field string `json:"field,omitempty"`
Type string `json:"type,omitempty"`
}
type Table struct {
Columns []Column
Computed func(data.Object)
}
type ColumnMapper struct {
definition Table
types.EmptyMapper
}
func (t *ColumnMapper) FromInternal(d data.Object) {
d.Map("metadata").Set("columns", t.definition.Columns)
if t.definition.Computed != nil {
t.definition.Computed(d)
}
}
func (t *ColumnMapper) ModifySchema(schema *types.Schema, schemas *types.Schemas) error {
attributes.SetColumns(schema, t.definition.Columns)
return nil
}

View File

@@ -51,7 +51,7 @@ type Defaults struct {
ErrorHandler types.ErrorHandler ErrorHandler types.ErrorHandler
} }
func NewAPIServer() *Server { func DefaultAPIServer() *Server {
s := &Server{ s := &Server{
DefaultNamespace: "default", DefaultNamespace: "default",
Schemas: types.EmptySchemas(), Schemas: types.EmptySchemas(),

View File

@@ -19,7 +19,29 @@ func (o Object) Map(names ...string) Object {
return Object(m) return Object(m)
} }
func (o Object) Slice(names ...string) (result []Object) {
v := values.GetValueN(o, names...)
for _, item := range convert.ToInterfaceSlice(v) {
result = append(result, Object(convert.ToMapInterface(item)))
}
return
}
func (o Object) Values() (result []Object) {
for k := range o {
result = append(result, o.Map(k))
}
return
}
func (o Object) String(names ...string) string { func (o Object) String(names ...string) string {
v := values.GetValueN(o, names...) v := values.GetValueN(o, names...)
return convert.ToString(v) return convert.ToString(v)
} }
func (o Object) Set(key string, obj interface{}) {
if o == nil {
return
}
o[key] = obj
}

View File

@@ -1,6 +1,9 @@
package proxy package proxy
import "github.com/rancher/norman/pkg/types" import (
"github.com/rancher/norman/pkg/data"
"github.com/rancher/norman/pkg/types"
)
type AddAPIVersionKind struct { type AddAPIVersionKind struct {
APIVersion string APIVersion string
@@ -8,24 +11,21 @@ type AddAPIVersionKind struct {
Next types.Mapper Next types.Mapper
} }
func (d AddAPIVersionKind) FromInternal(data map[string]interface{}) { func (d AddAPIVersionKind) FromInternal(data data.Object) {
if d.Next != nil { if d.Next != nil {
d.Next.FromInternal(data) d.Next.FromInternal(data)
} }
} }
func (d AddAPIVersionKind) ToInternal(data map[string]interface{}) error { func (d AddAPIVersionKind) ToInternal(data data.Object) error {
if d.Next != nil { if d.Next != nil {
if err := d.Next.ToInternal(data); err != nil { if err := d.Next.ToInternal(data); err != nil {
return err return err
} }
} }
if data == nil { data.Set("apiVersion", d.APIVersion)
return nil data.Set("kind", d.Kind)
}
data["apiVersion"] = d.APIVersion
data["kind"] = d.Kind
return nil return nil
} }

View File

@@ -1,23 +1,23 @@
package types package types
import ( import (
"github.com/rancher/norman/pkg/types/convert" "github.com/rancher/norman/pkg/data"
"github.com/rancher/norman/pkg/types/definition" "github.com/rancher/norman/pkg/types/definition"
) )
type Mapper interface { type Mapper interface {
FromInternal(data map[string]interface{}) FromInternal(data data.Object)
ToInternal(data map[string]interface{}) error ToInternal(data data.Object) error
ModifySchema(schema *Schema, schemas *Schemas) error ModifySchema(schema *Schema, schemas *Schemas) error
} }
type EmptyMapper struct { type EmptyMapper struct {
} }
func (e *EmptyMapper) FromInternal(data map[string]interface{}) { func (e *EmptyMapper) FromInternal(data data.Object) {
} }
func (e *EmptyMapper) ToInternal(data map[string]interface{}) error { func (e *EmptyMapper) ToInternal(data data.Object) error {
return nil return nil
} }
@@ -27,13 +27,13 @@ func (e *EmptyMapper) ModifySchema(schema *Schema, schemas *Schemas) error {
type Mappers []Mapper type Mappers []Mapper
func (m Mappers) FromInternal(data map[string]interface{}) { func (m Mappers) FromInternal(data data.Object) {
for _, mapper := range m { for _, mapper := range m {
mapper.FromInternal(data) mapper.FromInternal(data)
} }
} }
func (m Mappers) ToInternal(data map[string]interface{}) error { func (m Mappers) ToInternal(data data.Object) error {
var errors []error var errors []error
for i := len(m) - 1; i >= 0; i-- { for i := len(m) - 1; i >= 0; i-- {
errors = append(errors, m[i].ToInternal(data)) errors = append(errors, m[i].ToInternal(data))
@@ -59,23 +59,20 @@ type typeMapper struct {
subMapSchemas map[string]*Schema subMapSchemas map[string]*Schema
} }
func (t *typeMapper) FromInternal(data map[string]interface{}) { func (t *typeMapper) FromInternal(data data.Object) {
for fieldName, schema := range t.subSchemas { for fieldName, schema := range t.subSchemas {
if schema.Mapper == nil { if schema.Mapper == nil {
continue continue
} }
fieldData, _ := data[fieldName].(map[string]interface{}) schema.Mapper.FromInternal(data.Map(fieldName))
schema.Mapper.FromInternal(fieldData)
} }
for fieldName, schema := range t.subMapSchemas { for fieldName, schema := range t.subMapSchemas {
if schema.Mapper == nil { if schema.Mapper == nil {
continue continue
} }
datas, _ := data[fieldName].(map[string]interface{}) for _, fieldData := range data.Map(fieldName).Values() {
for _, fieldData := range datas { schema.Mapper.FromInternal(fieldData)
mapFieldData, _ := fieldData.(map[string]interface{})
schema.Mapper.FromInternal(mapFieldData)
} }
} }
@@ -83,17 +80,15 @@ func (t *typeMapper) FromInternal(data map[string]interface{}) {
if schema.Mapper == nil { if schema.Mapper == nil {
continue continue
} }
datas, _ := data[fieldName].([]interface{}) for _, fieldData := range data.Slice(fieldName) {
for _, fieldData := range datas { schema.Mapper.FromInternal(fieldData)
mapFieldData, _ := fieldData.(map[string]interface{})
schema.Mapper.FromInternal(mapFieldData)
} }
} }
Mappers(t.Mappers).FromInternal(data) Mappers(t.Mappers).FromInternal(data)
} }
func (t *typeMapper) ToInternal(data map[string]interface{}) error { func (t *typeMapper) ToInternal(data data.Object) error {
errors := Errors{} errors := Errors{}
errors.Add(Mappers(t.Mappers).ToInternal(data)) errors.Add(Mappers(t.Mappers).ToInternal(data))
@@ -101,9 +96,8 @@ func (t *typeMapper) ToInternal(data map[string]interface{}) error {
if schema.Mapper == nil { if schema.Mapper == nil {
continue continue
} }
datas, _ := data[fieldName].([]interface{}) for _, fieldData := range data.Slice(fieldName) {
for _, fieldData := range datas { errors.Add(schema.Mapper.ToInternal(fieldData))
errors.Add(schema.Mapper.ToInternal(convert.ToMapInterface(fieldData)))
} }
} }
@@ -111,9 +105,8 @@ func (t *typeMapper) ToInternal(data map[string]interface{}) error {
if schema.Mapper == nil { if schema.Mapper == nil {
continue continue
} }
datas, _ := data[fieldName].(map[string]interface{}) for _, fieldData := range data.Slice(fieldName) {
for _, fieldData := range datas { errors.Add(schema.Mapper.ToInternal(fieldData))
errors.Add(schema.Mapper.ToInternal(convert.ToMapInterface(fieldData)))
} }
} }
@@ -121,8 +114,7 @@ func (t *typeMapper) ToInternal(data map[string]interface{}) error {
if schema.Mapper == nil { if schema.Mapper == nil {
continue continue
} }
fieldData, _ := data[fieldName].(map[string]interface{}) errors.Add(schema.Mapper.ToInternal(data.Map(fieldName)))
errors.Add(schema.Mapper.ToInternal(fieldData))
} }
return errors.Err() return errors.Err()

View File

@@ -348,3 +348,10 @@ func APIChan(c <-chan APIEvent, f func(APIEvent) APIEvent) chan APIEvent {
}() }()
return result return result
} }
func FormatterChain(formatter Formatter, next Formatter) Formatter {
return func(request *APIRequest, resource *RawResource) {
formatter(request, resource)
next(request, resource)
}
}

4
vendor/modules.txt vendored
View File

@@ -42,16 +42,16 @@ github.com/pkg/errors
github.com/rancher/norman/pkg/authorization github.com/rancher/norman/pkg/authorization
github.com/rancher/norman/pkg/types github.com/rancher/norman/pkg/types
github.com/rancher/norman/pkg/types/convert github.com/rancher/norman/pkg/types/convert
github.com/rancher/norman/pkg/store/empty
github.com/rancher/norman/pkg/store/proxy github.com/rancher/norman/pkg/store/proxy
github.com/rancher/norman/pkg/subscribe github.com/rancher/norman/pkg/subscribe
github.com/rancher/norman/pkg/types/values github.com/rancher/norman/pkg/types/values
github.com/rancher/norman/pkg/store/empty
github.com/rancher/norman/pkg/api/builtin github.com/rancher/norman/pkg/api/builtin
github.com/rancher/norman/pkg/data
github.com/rancher/norman/pkg/api github.com/rancher/norman/pkg/api
github.com/rancher/norman/pkg/urlbuilder github.com/rancher/norman/pkg/urlbuilder
github.com/rancher/norman/pkg/httperror github.com/rancher/norman/pkg/httperror
github.com/rancher/norman/pkg/types/slice github.com/rancher/norman/pkg/types/slice
github.com/rancher/norman/pkg/data
github.com/rancher/norman/pkg/types/definition github.com/rancher/norman/pkg/types/definition
github.com/rancher/norman/pkg/types/convert/merge github.com/rancher/norman/pkg/types/convert/merge
github.com/rancher/norman/pkg/api/writer github.com/rancher/norman/pkg/api/writer