mirror of
https://github.com/niusmallnan/steve.git
synced 2025-06-28 07:26:57 +00:00
Drop non-preferred version and version in the schema id
This commit is contained in:
parent
39ae8e9c14
commit
0ad6c2aba1
@ -1,6 +1,8 @@
|
|||||||
package attributes
|
package attributes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/rancher/steve/pkg/schemaserver/types"
|
"github.com/rancher/steve/pkg/schemaserver/types"
|
||||||
"github.com/rancher/wrangler/pkg/data/convert"
|
"github.com/rancher/wrangler/pkg/data/convert"
|
||||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
@ -75,6 +77,14 @@ func SetGVK(s *types.APISchema, gvk schema.GroupVersionKind) {
|
|||||||
SetKind(s, gvk.Kind)
|
SetKind(s, gvk.Kind)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Table(s *types.APISchema) bool {
|
||||||
|
return str(s, "table") != "false"
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetTable(s *types.APISchema, value bool) {
|
||||||
|
setVal(s, "table", fmt.Sprint(value))
|
||||||
|
}
|
||||||
|
|
||||||
func GVR(s *types.APISchema) schema.GroupVersionResource {
|
func GVR(s *types.APISchema) schema.GroupVersionResource {
|
||||||
return schema.GroupVersionResource{
|
return schema.GroupVersionResource{
|
||||||
Group: Group(s),
|
Group: Group(s),
|
||||||
|
@ -89,20 +89,40 @@ func (p *Factory) AdminClient(ctx *types.APIRequest, s *types.APISchema, namespa
|
|||||||
return newClient(ctx, p.clientCfg, s, namespace, false)
|
return newClient(ctx, p.clientCfg, s, namespace, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Factory) ClientForWatch(ctx *types.APIRequest, s *types.APISchema, namespace string) (dynamic.ResourceInterface, error) {
|
||||||
|
return newClient(ctx, p.watchClientCfg, s, namespace, p.impersonate)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Factory) AdminClientForWatch(ctx *types.APIRequest, s *types.APISchema, namespace string) (dynamic.ResourceInterface, error) {
|
||||||
|
return newClient(ctx, p.watchClientCfg, s, namespace, false)
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Factory) TableClient(ctx *types.APIRequest, s *types.APISchema, namespace string) (dynamic.ResourceInterface, error) {
|
func (p *Factory) TableClient(ctx *types.APIRequest, s *types.APISchema, namespace string) (dynamic.ResourceInterface, error) {
|
||||||
return newClient(ctx, p.tableClientCfg, s, namespace, p.impersonate)
|
if attributes.Table(s) {
|
||||||
|
return newClient(ctx, p.tableClientCfg, s, namespace, p.impersonate)
|
||||||
|
}
|
||||||
|
return p.Client(ctx, s, namespace)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Factory) TableAdminClient(ctx *types.APIRequest, s *types.APISchema, namespace string) (dynamic.ResourceInterface, error) {
|
func (p *Factory) TableAdminClient(ctx *types.APIRequest, s *types.APISchema, namespace string) (dynamic.ResourceInterface, error) {
|
||||||
return newClient(ctx, p.tableClientCfg, s, namespace, false)
|
if attributes.Table(s) {
|
||||||
|
return newClient(ctx, p.tableClientCfg, s, namespace, false)
|
||||||
|
}
|
||||||
|
return p.AdminClient(ctx, s, namespace)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Factory) TableClientForWatch(ctx *types.APIRequest, s *types.APISchema, namespace string) (dynamic.ResourceInterface, error) {
|
func (p *Factory) TableClientForWatch(ctx *types.APIRequest, s *types.APISchema, namespace string) (dynamic.ResourceInterface, error) {
|
||||||
return newClient(ctx, p.tableWatchClientCfg, s, namespace, p.impersonate)
|
if attributes.Table(s) {
|
||||||
|
return newClient(ctx, p.tableWatchClientCfg, s, namespace, p.impersonate)
|
||||||
|
}
|
||||||
|
return p.ClientForWatch(ctx, s, namespace)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Factory) TableAdminClientForWatch(ctx *types.APIRequest, s *types.APISchema, namespace string) (dynamic.ResourceInterface, error) {
|
func (p *Factory) TableAdminClientForWatch(ctx *types.APIRequest, s *types.APISchema, namespace string) (dynamic.ResourceInterface, error) {
|
||||||
return newClient(ctx, p.tableWatchClientCfg, s, namespace, false)
|
if attributes.Table(s) {
|
||||||
|
return newClient(ctx, p.tableWatchClientCfg, s, namespace, false)
|
||||||
|
}
|
||||||
|
return p.AdminClientForWatch(ctx, s, namespace)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newClient(ctx *types.APIRequest, cfg *rest.Config, s *types.APISchema, namespace string, impersonate bool) (dynamic.ResourceInterface, error) {
|
func newClient(ctx *types.APIRequest, cfg *rest.Config, s *types.APISchema, namespace string, impersonate bool) (dynamic.ResourceInterface, error) {
|
||||||
|
@ -149,15 +149,26 @@ func (h *handler) refreshAll() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
filteredSchemas := map[string]*types.APISchema{}
|
filteredSchemas := map[string]*types.APISchema{}
|
||||||
for id, schema := range schemas {
|
for _, schema := range schemas {
|
||||||
if isListWatchable(schema) {
|
if isListWatchable(schema) {
|
||||||
|
if attributes.PreferredGroup(schema) != "" ||
|
||||||
|
attributes.PreferredVersion(schema) != "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if ok, err := h.allowed(schema); err != nil {
|
if ok, err := h.allowed(schema); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if !ok {
|
} else if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
filteredSchemas[id] = schema
|
|
||||||
|
gvk := attributes.GVK(schema)
|
||||||
|
if gvk.Kind != "" {
|
||||||
|
gvr := attributes.GVR(schema)
|
||||||
|
schema.ID = converter.GVKToSchemaID(gvk)
|
||||||
|
schema.PluralName = converter.GVRToPluralName(gvr)
|
||||||
|
}
|
||||||
|
filteredSchemas[schema.ID] = schema
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := h.getColumns(h.ctx, filteredSchemas); err != nil {
|
if err := h.getColumns(h.ctx, filteredSchemas); err != nil {
|
||||||
|
@ -22,6 +22,7 @@ type Factory interface {
|
|||||||
Schemas(user user.Info) (*types.APISchemas, error)
|
Schemas(user user.Info) (*types.APISchemas, error)
|
||||||
ByGVR(gvr schema.GroupVersionResource) string
|
ByGVR(gvr schema.GroupVersionResource) string
|
||||||
ByGVK(gvr schema.GroupVersionKind) string
|
ByGVK(gvr schema.GroupVersionKind) string
|
||||||
|
OnChange(ctx context.Context, cb func())
|
||||||
}
|
}
|
||||||
|
|
||||||
type Collection struct {
|
type Collection struct {
|
||||||
@ -29,6 +30,8 @@ type Collection struct {
|
|||||||
baseSchema *types.APISchemas
|
baseSchema *types.APISchemas
|
||||||
schemas map[string]*types.APISchema
|
schemas map[string]*types.APISchema
|
||||||
templates map[string]*Template
|
templates map[string]*Template
|
||||||
|
notifiers map[int]func()
|
||||||
|
notifierID int
|
||||||
byGVR map[schema.GroupVersionResource]string
|
byGVR map[schema.GroupVersionResource]string
|
||||||
byGVK map[schema.GroupVersionKind]string
|
byGVK map[schema.GroupVersionKind]string
|
||||||
cache *cache.LRUExpireCache
|
cache *cache.LRUExpireCache
|
||||||
@ -80,12 +83,28 @@ func NewCollection(ctx context.Context, baseSchema *types.APISchemas, access acc
|
|||||||
byGVR: map[schema.GroupVersionResource]string{},
|
byGVR: map[schema.GroupVersionResource]string{},
|
||||||
byGVK: map[schema.GroupVersionKind]string{},
|
byGVK: map[schema.GroupVersionKind]string{},
|
||||||
cache: cache.NewLRUExpireCache(1000),
|
cache: cache.NewLRUExpireCache(1000),
|
||||||
|
notifiers: map[int]func(){},
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
as: access,
|
as: access,
|
||||||
running: map[string]func(){},
|
running: map[string]func(){},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Collection) OnChange(ctx context.Context, cb func()) {
|
||||||
|
c.lock.Lock()
|
||||||
|
id := c.notifierID
|
||||||
|
c.notifierID++
|
||||||
|
c.notifiers[id] = cb
|
||||||
|
c.lock.Unlock()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
<-ctx.Done()
|
||||||
|
c.lock.Lock()
|
||||||
|
delete(c.notifiers, id)
|
||||||
|
c.lock.Unlock()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Collection) Reset(schemas map[string]*types.APISchema) {
|
func (c *Collection) Reset(schemas map[string]*types.APISchema) {
|
||||||
byGVK := map[schema.GroupVersionKind]string{}
|
byGVK := map[schema.GroupVersionKind]string{}
|
||||||
byGVR := map[schema.GroupVersionResource]string{}
|
byGVR := map[schema.GroupVersionResource]string{}
|
||||||
@ -112,6 +131,11 @@ func (c *Collection) Reset(schemas map[string]*types.APISchema) {
|
|||||||
c.cache.Remove(k)
|
c.cache.Remove(k)
|
||||||
}
|
}
|
||||||
c.lock.Unlock()
|
c.lock.Unlock()
|
||||||
|
c.lock.RLock()
|
||||||
|
for _, f := range c.notifiers {
|
||||||
|
f()
|
||||||
|
}
|
||||||
|
c.lock.RUnlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Collection) startStopTemplate(schemas map[string]*types.APISchema) {
|
func (c *Collection) startStopTemplate(schemas map[string]*types.APISchema) {
|
||||||
|
@ -72,7 +72,7 @@ func forVersion(crd *beta1.CustomResourceDefinition, group, version, kind string
|
|||||||
versionColumns = columns
|
versionColumns = columns
|
||||||
}
|
}
|
||||||
|
|
||||||
id := GVKToSchemaID(schema.GroupVersionKind{
|
id := gvkToSchemaID(schema.GroupVersionKind{
|
||||||
Group: group,
|
Group: group,
|
||||||
Version: version,
|
Version: version,
|
||||||
Kind: kind,
|
Kind: kind,
|
||||||
|
@ -69,17 +69,17 @@ func refresh(gv schema.GroupVersion, groupToPreferredVersion map[string]string,
|
|||||||
|
|
||||||
logrus.Infof("APIVersion %s/%s Kind %s", gvk.Group, gvk.Version, gvk.Kind)
|
logrus.Infof("APIVersion %s/%s Kind %s", gvk.Group, gvk.Version, gvk.Kind)
|
||||||
|
|
||||||
schema := schemasMap[GVKToSchemaID(gvk)]
|
schema := schemasMap[gvkToSchemaID(gvk)]
|
||||||
if schema == nil {
|
if schema == nil {
|
||||||
schema = &types.APISchema{
|
schema = &types.APISchema{
|
||||||
Schema: &schemas.Schema{
|
Schema: &schemas.Schema{
|
||||||
ID: GVKToSchemaID(gvk),
|
ID: gvkToSchemaID(gvk),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
attributes.SetGVK(schema, gvk)
|
attributes.SetGVK(schema, gvk)
|
||||||
}
|
}
|
||||||
|
|
||||||
schema.PluralName = GVRToPluralName(gvr)
|
schema.PluralName = gvrToPluralName(gvr)
|
||||||
attributes.SetAPIResource(schema, resource)
|
attributes.SetAPIResource(schema, resource)
|
||||||
if preferredVersion := groupToPreferredVersion[gv.Group]; preferredVersion != "" && preferredVersion != gv.Version {
|
if preferredVersion := groupToPreferredVersion[gv.Group]; preferredVersion != "" && preferredVersion != gv.Version {
|
||||||
attributes.SetPreferredVersion(schema, preferredVersion)
|
attributes.SetPreferredVersion(schema, preferredVersion)
|
||||||
|
@ -10,20 +10,34 @@ import (
|
|||||||
"k8s.io/client-go/discovery"
|
"k8s.io/client-go/discovery"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GVKToSchemaID(gvk schema.GroupVersionKind) string {
|
func gvkToSchemaID(gvk schema.GroupVersionKind) string {
|
||||||
if gvk.Group == "" {
|
if gvk.Group == "" {
|
||||||
return strings.ToLower(fmt.Sprintf("core.%s.%s", gvk.Version, gvk.Kind))
|
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))
|
return strings.ToLower(fmt.Sprintf("%s.%s.%s", gvk.Group, gvk.Version, gvk.Kind))
|
||||||
}
|
}
|
||||||
|
|
||||||
func GVRToPluralName(gvr schema.GroupVersionResource) string {
|
func gvrToPluralName(gvr schema.GroupVersionResource) string {
|
||||||
if gvr.Group == "" {
|
if gvr.Group == "" {
|
||||||
return fmt.Sprintf("core.%s.%s", gvr.Version, gvr.Resource)
|
return fmt.Sprintf("core.%s.%s", gvr.Version, gvr.Resource)
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("%s.%s.%s", gvr.Group, gvr.Version, gvr.Resource)
|
return fmt.Sprintf("%s.%s.%s", gvr.Group, gvr.Version, gvr.Resource)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GVKToSchemaID(gvk schema.GroupVersionKind) string {
|
||||||
|
if gvk.Group == "" {
|
||||||
|
return strings.ToLower(gvk.Kind)
|
||||||
|
}
|
||||||
|
return strings.ToLower(fmt.Sprintf("%s.%s", gvk.Group, gvk.Kind))
|
||||||
|
}
|
||||||
|
|
||||||
|
func GVRToPluralName(gvr schema.GroupVersionResource) string {
|
||||||
|
if gvr.Group == "" {
|
||||||
|
return gvr.Resource
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s.%s", gvr.Group, gvr.Resource)
|
||||||
|
}
|
||||||
|
|
||||||
func ToSchemas(crd v1beta1.CustomResourceDefinitionClient, client discovery.DiscoveryInterface) (map[string]*types.APISchema, error) {
|
func ToSchemas(crd v1beta1.CustomResourceDefinitionClient, client discovery.DiscoveryInterface) (map[string]*types.APISchema, error) {
|
||||||
result := map[string]*types.APISchema{}
|
result := map[string]*types.APISchema{}
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ func modelToSchema(modelName string, k *proto.Kind) *types.APISchema {
|
|||||||
Kind: convert.ToString(m["kind"]),
|
Kind: convert.ToString(m["kind"]),
|
||||||
}
|
}
|
||||||
|
|
||||||
s.ID = GVKToSchemaID(gvk)
|
s.ID = gvkToSchemaID(gvk)
|
||||||
attributes.SetGVK(&s, gvk)
|
attributes.SetGVK(&s, gvk)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,10 @@ func (s *Store) ByID(apiOp *types.APIRequest, schema *types.APISchema, id string
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) List(apiOp *types.APIRequest, schema *types.APISchema) (types.APIObjectList, error) {
|
func (s *Store) List(apiOp *types.APIRequest, schema *types.APISchema) (types.APIObjectList, error) {
|
||||||
schemaMap := apiOp.Schemas.Schemas
|
return FilterSchemas(apiOp, apiOp.Schemas.Schemas), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func FilterSchemas(apiOp *types.APIRequest, schemaMap map[string]*types.APISchema) types.APIObjectList {
|
||||||
schemas := types.APIObjectList{}
|
schemas := types.APIObjectList{}
|
||||||
|
|
||||||
included := map[string]bool{}
|
included := map[string]bool{}
|
||||||
@ -44,21 +47,21 @@ func (s *Store) List(apiOp *types.APIRequest, schema *types.APISchema) (types.AP
|
|||||||
}
|
}
|
||||||
|
|
||||||
if apiOp.AccessControl.CanList(apiOp, schema) == nil || apiOp.AccessControl.CanGet(apiOp, schema) == nil {
|
if apiOp.AccessControl.CanList(apiOp, schema) == nil || apiOp.AccessControl.CanGet(apiOp, schema) == nil {
|
||||||
schemas = s.addSchema(apiOp, schema, schemaMap, schemas, included)
|
schemas = addSchema(apiOp, schema, schemaMap, schemas, included)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return schemas, nil
|
return schemas
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) addSchema(apiOp *types.APIRequest, schema *types.APISchema, schemaMap map[string]*types.APISchema, schemas types.APIObjectList, included map[string]bool) types.APIObjectList {
|
func addSchema(apiOp *types.APIRequest, schema *types.APISchema, schemaMap map[string]*types.APISchema, schemas types.APIObjectList, included map[string]bool) types.APIObjectList {
|
||||||
included[schema.ID] = true
|
included[schema.ID] = true
|
||||||
schemas = s.traverseAndAdd(apiOp, schema, schemaMap, schemas, included)
|
schemas = traverseAndAdd(apiOp, schema, schemaMap, schemas, included)
|
||||||
schemas.Objects = append(schemas.Objects, toAPIObject(schema))
|
schemas.Objects = append(schemas.Objects, toAPIObject(schema))
|
||||||
return schemas
|
return schemas
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) traverseAndAdd(apiOp *types.APIRequest, schema *types.APISchema, schemaMap map[string]*types.APISchema, schemas types.APIObjectList, included map[string]bool) types.APIObjectList {
|
func traverseAndAdd(apiOp *types.APIRequest, schema *types.APISchema, schemaMap map[string]*types.APISchema, schemas types.APIObjectList, included map[string]bool) types.APIObjectList {
|
||||||
for _, field := range schema.ResourceFields {
|
for _, field := range schema.ResourceFields {
|
||||||
t := ""
|
t := ""
|
||||||
subType := field.Type
|
subType := field.Type
|
||||||
@ -68,7 +71,7 @@ func (s *Store) traverseAndAdd(apiOp *types.APIRequest, schema *types.APISchema,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if refSchema, ok := schemaMap[t]; ok && !included[t] {
|
if refSchema, ok := schemaMap[t]; ok && !included[t] {
|
||||||
schemas = s.addSchema(apiOp, refSchema, schemaMap, schemas, included)
|
schemas = addSchema(apiOp, refSchema, schemaMap, schemas, included)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,7 +82,7 @@ func (s *Store) traverseAndAdd(apiOp *types.APIRequest, schema *types.APISchema,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if refSchema, ok := schemaMap[t]; ok && !included[t] {
|
if refSchema, ok := schemaMap[t]; ok && !included[t] {
|
||||||
schemas = s.addSchema(apiOp, refSchema, schemaMap, schemas, included)
|
schemas = addSchema(apiOp, refSchema, schemaMap, schemas, included)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -91,7 +94,7 @@ func (s *Store) traverseAndAdd(apiOp *types.APIRequest, schema *types.APISchema,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if refSchema, ok := schemaMap[t]; ok && !included[t] {
|
if refSchema, ok := schemaMap[t]; ok && !included[t] {
|
||||||
schemas = s.addSchema(apiOp, refSchema, schemaMap, schemas, included)
|
schemas = addSchema(apiOp, refSchema, schemaMap, schemas, included)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@ import (
|
|||||||
"github.com/rancher/steve/pkg/attributes"
|
"github.com/rancher/steve/pkg/attributes"
|
||||||
"github.com/rancher/steve/pkg/schema"
|
"github.com/rancher/steve/pkg/schema"
|
||||||
"github.com/rancher/steve/pkg/schemaserver/types"
|
"github.com/rancher/steve/pkg/schemaserver/types"
|
||||||
runtimeschema "k8s.io/apimachinery/pkg/runtime/schema"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func k8sAPI(sf schema.Factory, apiOp *types.APIRequest) {
|
func k8sAPI(sf schema.Factory, apiOp *types.APIRequest) {
|
||||||
@ -16,11 +15,7 @@ func k8sAPI(sf schema.Factory, apiOp *types.APIRequest) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
apiOp.Name = vars["name"]
|
apiOp.Name = vars["name"]
|
||||||
apiOp.Type = sf.ByGVR(runtimeschema.GroupVersionResource{
|
apiOp.Type = vars["type"]
|
||||||
Version: vars["version"],
|
|
||||||
Group: group,
|
|
||||||
Resource: vars["resource"],
|
|
||||||
})
|
|
||||||
|
|
||||||
nOrN := vars["nameorns"]
|
nOrN := vars["nameorns"]
|
||||||
if nOrN != "" {
|
if nOrN != "" {
|
||||||
|
@ -3,21 +3,26 @@ package apigroups
|
|||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/rancher/steve/pkg/schema"
|
||||||
|
|
||||||
"github.com/rancher/steve/pkg/schemaserver/store/empty"
|
"github.com/rancher/steve/pkg/schemaserver/store/empty"
|
||||||
"github.com/rancher/steve/pkg/schemaserver/types"
|
"github.com/rancher/steve/pkg/schemaserver/types"
|
||||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/client-go/discovery"
|
"k8s.io/client-go/discovery"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Register(schemas *types.APISchemas, discovery discovery.DiscoveryInterface) {
|
func Template(discovery discovery.DiscoveryInterface) schema.Template {
|
||||||
schemas.MustImportAndCustomize(v1.APIGroup{}, func(schema *types.APISchema) {
|
return schema.Template{
|
||||||
schema.CollectionMethods = []string{http.MethodGet}
|
ID: "apigroup",
|
||||||
schema.ResourceMethods = []string{http.MethodGet}
|
Customize: func(apiSchema *types.APISchema) {
|
||||||
schema.Store = NewStore(discovery)
|
apiSchema.CollectionMethods = []string{http.MethodGet}
|
||||||
schema.Formatter = func(request *types.APIRequest, resource *types.RawResource) {
|
apiSchema.ResourceMethods = []string{http.MethodGet}
|
||||||
|
},
|
||||||
|
Formatter: func(request *types.APIRequest, resource *types.RawResource) {
|
||||||
resource.ID = resource.APIObject.Data().String("name")
|
resource.ID = resource.APIObject.Data().String("name")
|
||||||
}
|
},
|
||||||
})
|
Store: NewStore(discovery),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type Store struct {
|
type Store struct {
|
||||||
|
@ -58,6 +58,7 @@ func (d *DynamicColumns) SetColumns(schema *types.APISchema) error {
|
|||||||
|
|
||||||
obj, err := r.Do().Get()
|
obj, err := r.Do().Get()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
attributes.SetTable(schema, false)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
t, ok := obj.(*metav1.Table)
|
t, ok := obj.(*metav1.Table)
|
||||||
|
@ -14,16 +14,16 @@ import (
|
|||||||
"k8s.io/client-go/discovery"
|
"k8s.io/client-go/discovery"
|
||||||
)
|
)
|
||||||
|
|
||||||
func DefaultSchemas(baseSchema *types.APISchemas, discovery discovery.DiscoveryInterface, ccache clustercache.ClusterCache) *types.APISchemas {
|
func DefaultSchemas(baseSchema *types.APISchemas, ccache clustercache.ClusterCache) *types.APISchemas {
|
||||||
counts.Register(baseSchema, ccache)
|
counts.Register(baseSchema, ccache)
|
||||||
subscribe.Register(baseSchema)
|
subscribe.Register(baseSchema)
|
||||||
apigroups.Register(baseSchema, discovery)
|
|
||||||
apiroot.Register(baseSchema, []string{"v1"}, []string{"proxy:/apis"})
|
apiroot.Register(baseSchema, []string{"v1"}, []string{"proxy:/apis"})
|
||||||
return baseSchema
|
return baseSchema
|
||||||
}
|
}
|
||||||
|
|
||||||
func DefaultSchemaTemplates(cf *client.Factory, lookup accesscontrol.AccessSetLookup) []schema.Template {
|
func DefaultSchemaTemplates(cf *client.Factory, lookup accesscontrol.AccessSetLookup, discovery discovery.DiscoveryInterface) []schema.Template {
|
||||||
return []schema.Template{
|
return []schema.Template{
|
||||||
common.DefaultTemplate(cf, lookup),
|
common.DefaultTemplate(cf, lookup),
|
||||||
|
apigroups.Template(discovery),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
158
pkg/server/resources/schemas/template.go
Normal file
158
pkg/server/resources/schemas/template.go
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
package schemas
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/rancher/steve/pkg/schemaserver/builtin"
|
||||||
|
|
||||||
|
"github.com/rancher/steve/pkg/accesscontrol"
|
||||||
|
"github.com/rancher/steve/pkg/schema"
|
||||||
|
schemastore "github.com/rancher/steve/pkg/schemaserver/store/schema"
|
||||||
|
"github.com/rancher/steve/pkg/schemaserver/types"
|
||||||
|
"github.com/rancher/wrangler/pkg/broadcast"
|
||||||
|
"github.com/rancher/wrangler/pkg/schemas/validation"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"k8s.io/apiserver/pkg/authentication/user"
|
||||||
|
"k8s.io/apiserver/pkg/endpoints/request"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SetupWatcher(ctx context.Context, schemas *types.APISchemas, asl accesscontrol.AccessSetLookup, factory schema.Factory) {
|
||||||
|
// one instance shared with all stores
|
||||||
|
notifier := schemaChangeNotifier(ctx, factory)
|
||||||
|
|
||||||
|
schema := builtin.Schema
|
||||||
|
schema.Store = &Store{
|
||||||
|
Store: schema.Store,
|
||||||
|
asl: asl,
|
||||||
|
sf: factory,
|
||||||
|
schemaChangeNotify: notifier,
|
||||||
|
}
|
||||||
|
|
||||||
|
schemas.AddSchema(schema)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Store struct {
|
||||||
|
types.Store
|
||||||
|
|
||||||
|
asl accesscontrol.AccessSetLookup
|
||||||
|
sf schema.Factory
|
||||||
|
schemaChangeNotify func(context.Context) (chan interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) Watch(apiOp *types.APIRequest, schema *types.APISchema, w types.WatchRequest) (chan types.APIEvent, error) {
|
||||||
|
user, ok := request.UserFrom(apiOp.Request.Context())
|
||||||
|
if !ok {
|
||||||
|
return nil, validation.Unauthorized
|
||||||
|
}
|
||||||
|
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
wg.Add(2)
|
||||||
|
result := make(chan types.APIEvent)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
wg.Wait()
|
||||||
|
close(result)
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
c, err := s.schemaChangeNotify(apiOp.Context())
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
schemas, err := s.sf.Schemas(user)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("failed to generate schemas for user %v: %v", user, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for range c {
|
||||||
|
schemas = s.sendSchemas(result, apiOp, user, schemas)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
schemas, err := s.sf.Schemas(user)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("failed to generate schemas for notify user %v: %v", user, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for range s.userChangeNotify(apiOp.Context(), user) {
|
||||||
|
schemas = s.sendSchemas(result, apiOp, user, schemas)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) sendSchemas(result chan types.APIEvent, apiOp *types.APIRequest, user user.Info, oldSchemas *types.APISchemas) *types.APISchemas {
|
||||||
|
schemas, err := s.sf.Schemas(user)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("failed to get schemas for %v: %v", user, err)
|
||||||
|
return oldSchemas
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, apiObject := range schemastore.FilterSchemas(apiOp, schemas.Schemas).Objects {
|
||||||
|
result <- types.APIEvent{
|
||||||
|
Name: types.ChangeAPIEvent,
|
||||||
|
ResourceType: "schema",
|
||||||
|
Object: apiObject,
|
||||||
|
}
|
||||||
|
delete(oldSchemas.Schemas, apiObject.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
for id, oldSchema := range oldSchemas.Schemas {
|
||||||
|
result <- types.APIEvent{
|
||||||
|
Name: types.ChangeAPIEvent,
|
||||||
|
ResourceType: "schema",
|
||||||
|
Object: types.APIObject{
|
||||||
|
Type: "schema",
|
||||||
|
ID: id,
|
||||||
|
Object: oldSchema,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return schemas
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) userChangeNotify(ctx context.Context, user user.Info) chan interface{} {
|
||||||
|
as := s.asl.AccessFor(user)
|
||||||
|
result := make(chan interface{})
|
||||||
|
go func() {
|
||||||
|
defer close(result)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
case <-time.After(2 * time.Second):
|
||||||
|
}
|
||||||
|
|
||||||
|
newAS := s.asl.AccessFor(user)
|
||||||
|
if newAS.ID != as.ID {
|
||||||
|
result <- struct{}{}
|
||||||
|
as = newAS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func schemaChangeNotifier(ctx context.Context, factory schema.Factory) func(ctx context.Context) (chan interface{}, error) {
|
||||||
|
notify := make(chan interface{})
|
||||||
|
bcast := &broadcast.Broadcaster{}
|
||||||
|
factory.OnChange(ctx, func() {
|
||||||
|
select {
|
||||||
|
case notify <- struct{}{}:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return func(ctx context.Context) (chan interface{}, error) {
|
||||||
|
return bcast.Subscribe(ctx, func() (chan interface{}, error) {
|
||||||
|
return notify, nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -26,14 +26,11 @@ func Routes(h Handlers) http.Handler {
|
|||||||
m.Path("/").Handler(h.APIRoot).HeadersRegexp("Accepts", ".*json.*")
|
m.Path("/").Handler(h.APIRoot).HeadersRegexp("Accepts", ".*json.*")
|
||||||
m.Path("/{name:v1}").Handler(h.APIRoot)
|
m.Path("/{name:v1}").Handler(h.APIRoot)
|
||||||
|
|
||||||
m.Path("/v1/{group}.{version}.{resource}").Handler(h.K8sResource)
|
m.Path("/v1/{type}").Handler(h.K8sResource)
|
||||||
m.Path("/v1/{group}.{version}.{resource}/{nameorns}").Handler(h.K8sResource)
|
m.Path("/v1/{type}/{nameorns}").Handler(h.K8sResource)
|
||||||
m.Path("/v1/{group}.{version}.{resource}/{namespace}/{name}").Handler(h.K8sResource)
|
m.Path("/v1/{type}/{namespace}/{name}").Handler(h.K8sResource)
|
||||||
m.Path("/v1/{group}.{version}.{resource}/{nameorns}").Queries("action", "{action}").Handler(h.K8sResource)
|
m.Path("/v1/{type}/{nameorns}").Queries("action", "{action}").Handler(h.K8sResource)
|
||||||
m.Path("/v1/{group}.{version}.{resource}/{namespace}/{name}").Queries("action", "{action}").Handler(h.K8sResource)
|
m.Path("/v1/{type}/{namespace}/{name}").Queries("action", "{action}").Handler(h.K8sResource)
|
||||||
m.Path("/v1/{type:schemas}/{name:.*}").Handler(h.GenericResource)
|
|
||||||
m.Path("/v1/{type}").Handler(h.GenericResource)
|
|
||||||
m.Path("/v1/{type}/{name}").Handler(h.GenericResource)
|
|
||||||
m.Path("/api").Handler(h.K8sProxy) // Can't just prefix this as UI needs /apikeys path
|
m.Path("/api").Handler(h.K8sProxy) // Can't just prefix this as UI needs /apikeys path
|
||||||
m.PathPrefix("/api/").Handler(h.K8sProxy)
|
m.PathPrefix("/api/").Handler(h.K8sProxy)
|
||||||
m.PathPrefix("/apis").Handler(h.K8sProxy)
|
m.PathPrefix("/apis").Handler(h.K8sProxy)
|
||||||
|
@ -5,6 +5,8 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/rancher/steve/pkg/server/resources/schemas"
|
||||||
|
|
||||||
"github.com/rancher/dynamiclistener/server"
|
"github.com/rancher/dynamiclistener/server"
|
||||||
"github.com/rancher/steve/pkg/accesscontrol"
|
"github.com/rancher/steve/pkg/accesscontrol"
|
||||||
"github.com/rancher/steve/pkg/client"
|
"github.com/rancher/steve/pkg/client"
|
||||||
@ -66,8 +68,8 @@ func setup(ctx context.Context, server *Server) (http.Handler, *schema.Collectio
|
|||||||
|
|
||||||
ccache := clustercache.NewClusterCache(ctx, cf.MetadataClient())
|
ccache := clustercache.NewClusterCache(ctx, cf.MetadataClient())
|
||||||
|
|
||||||
server.BaseSchemas = resources.DefaultSchemas(server.BaseSchemas, server.K8s.Discovery(), ccache)
|
server.BaseSchemas = resources.DefaultSchemas(server.BaseSchemas, ccache)
|
||||||
server.SchemaTemplates = append(server.SchemaTemplates, resources.DefaultSchemaTemplates(cf, asl)...)
|
server.SchemaTemplates = append(server.SchemaTemplates, resources.DefaultSchemaTemplates(cf, asl, server.K8s.Discovery())...)
|
||||||
|
|
||||||
cols, err := common.NewDynamicColumns(server.RestConfig)
|
cols, err := common.NewDynamicColumns(server.RestConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -75,6 +77,9 @@ func setup(ctx context.Context, server *Server) (http.Handler, *schema.Collectio
|
|||||||
}
|
}
|
||||||
|
|
||||||
sf := schema.NewCollection(ctx, server.BaseSchemas, asl)
|
sf := schema.NewCollection(ctx, server.BaseSchemas, asl)
|
||||||
|
|
||||||
|
schemas.SetupWatcher(ctx, server.BaseSchemas, asl, sf)
|
||||||
|
|
||||||
sync := schemacontroller.Register(ctx,
|
sync := schemacontroller.Register(ctx,
|
||||||
cols,
|
cols,
|
||||||
server.K8s.Discovery(),
|
server.K8s.Discovery(),
|
||||||
|
Loading…
Reference in New Issue
Block a user