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:
@@ -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"]
|
||||||
|
}
|
||||||
|
@@ -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
|
||||||
}
|
}
|
||||||
|
30
pkg/resources/common/defaultcolumns.go
Normal file
30
pkg/resources/common/defaultcolumns.go
Normal 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
|
||||||
|
}
|
@@ -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{},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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"
|
||||||
|
@@ -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 {
|
||||||
|
70
pkg/resources/schema/converter/crd.go
Normal file
70
pkg/resources/schema/converter/crd.go
Normal 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)
|
||||||
|
}
|
@@ -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
|
||||||
}
|
}
|
||||||
|
@@ -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
|
||||||
|
@@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
38
pkg/server/publicapi/handlers.go
Normal file
38
pkg/server/publicapi/handlers.go
Normal 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}
|
||||||
|
}
|
||||||
|
}
|
29
pkg/server/router/router.go
Normal file
29
pkg/server/router/router.go
Normal 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
|
||||||
|
}
|
@@ -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}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -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
35
pkg/table/mapper.go
Normal 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
|
||||||
|
}
|
2
vendor/github.com/rancher/norman/pkg/api/server.go
generated
vendored
2
vendor/github.com/rancher/norman/pkg/api/server.go
generated
vendored
@@ -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(),
|
||||||
|
22
vendor/github.com/rancher/norman/pkg/data/data.go
generated
vendored
22
vendor/github.com/rancher/norman/pkg/data/data.go
generated
vendored
@@ -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
|
||||||
|
}
|
||||||
|
16
vendor/github.com/rancher/norman/pkg/store/proxy/mapper.go
generated
vendored
16
vendor/github.com/rancher/norman/pkg/store/proxy/mapper.go
generated
vendored
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
46
vendor/github.com/rancher/norman/pkg/types/mapper.go
generated
vendored
46
vendor/github.com/rancher/norman/pkg/types/mapper.go
generated
vendored
@@ -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()
|
||||||
|
7
vendor/github.com/rancher/norman/pkg/types/server_types.go
generated
vendored
7
vendor/github.com/rancher/norman/pkg/types/server_types.go
generated
vendored
@@ -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
4
vendor/modules.txt
vendored
@@ -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
|
||||||
|
Reference in New Issue
Block a user