diff --git a/pkg/attributes/attributes.go b/pkg/attributes/attributes.go index 822aa73..f97550e 100644 --- a/pkg/attributes/attributes.go +++ b/pkg/attributes/attributes.go @@ -1,6 +1,8 @@ package attributes import ( + "fmt" + "github.com/rancher/steve/pkg/schemaserver/types" "github.com/rancher/wrangler/pkg/data/convert" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -75,6 +77,14 @@ func SetGVK(s *types.APISchema, gvk schema.GroupVersionKind) { 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 { return schema.GroupVersionResource{ Group: Group(s), diff --git a/pkg/client/factory.go b/pkg/client/factory.go index 430defe..ae4a06a 100644 --- a/pkg/client/factory.go +++ b/pkg/client/factory.go @@ -89,20 +89,40 @@ func (p *Factory) AdminClient(ctx *types.APIRequest, s *types.APISchema, namespa 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) { - 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) { - 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) { - 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) { - 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) { diff --git a/pkg/controllers/schema/schemas.go b/pkg/controllers/schema/schemas.go index 419c786..f6d5aef 100644 --- a/pkg/controllers/schema/schemas.go +++ b/pkg/controllers/schema/schemas.go @@ -149,15 +149,26 @@ func (h *handler) refreshAll() error { } filteredSchemas := map[string]*types.APISchema{} - for id, schema := range schemas { + for _, schema := range schemas { if isListWatchable(schema) { + if attributes.PreferredGroup(schema) != "" || + attributes.PreferredVersion(schema) != "" { + continue + } if ok, err := h.allowed(schema); err != nil { return err } else if !ok { 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 { diff --git a/pkg/schema/collection.go b/pkg/schema/collection.go index 2c56162..319d19c 100644 --- a/pkg/schema/collection.go +++ b/pkg/schema/collection.go @@ -22,6 +22,7 @@ type Factory interface { Schemas(user user.Info) (*types.APISchemas, error) ByGVR(gvr schema.GroupVersionResource) string ByGVK(gvr schema.GroupVersionKind) string + OnChange(ctx context.Context, cb func()) } type Collection struct { @@ -29,6 +30,8 @@ type Collection struct { baseSchema *types.APISchemas schemas map[string]*types.APISchema templates map[string]*Template + notifiers map[int]func() + notifierID int byGVR map[schema.GroupVersionResource]string byGVK map[schema.GroupVersionKind]string cache *cache.LRUExpireCache @@ -80,12 +83,28 @@ func NewCollection(ctx context.Context, baseSchema *types.APISchemas, access acc byGVR: map[schema.GroupVersionResource]string{}, byGVK: map[schema.GroupVersionKind]string{}, cache: cache.NewLRUExpireCache(1000), + notifiers: map[int]func(){}, ctx: ctx, as: access, 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) { byGVK := map[schema.GroupVersionKind]string{} byGVR := map[schema.GroupVersionResource]string{} @@ -112,6 +131,11 @@ func (c *Collection) Reset(schemas map[string]*types.APISchema) { c.cache.Remove(k) } c.lock.Unlock() + c.lock.RLock() + for _, f := range c.notifiers { + f() + } + c.lock.RUnlock() } func (c *Collection) startStopTemplate(schemas map[string]*types.APISchema) { diff --git a/pkg/schema/converter/crd.go b/pkg/schema/converter/crd.go index f66229e..e868361 100644 --- a/pkg/schema/converter/crd.go +++ b/pkg/schema/converter/crd.go @@ -72,7 +72,7 @@ func forVersion(crd *beta1.CustomResourceDefinition, group, version, kind string versionColumns = columns } - id := GVKToSchemaID(schema.GroupVersionKind{ + id := gvkToSchemaID(schema.GroupVersionKind{ Group: group, Version: version, Kind: kind, diff --git a/pkg/schema/converter/discovery.go b/pkg/schema/converter/discovery.go index 37fd817..9b541e1 100644 --- a/pkg/schema/converter/discovery.go +++ b/pkg/schema/converter/discovery.go @@ -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) - schema := schemasMap[GVKToSchemaID(gvk)] + schema := schemasMap[gvkToSchemaID(gvk)] if schema == nil { schema = &types.APISchema{ Schema: &schemas.Schema{ - ID: GVKToSchemaID(gvk), + ID: gvkToSchemaID(gvk), }, } attributes.SetGVK(schema, gvk) } - schema.PluralName = GVRToPluralName(gvr) + schema.PluralName = gvrToPluralName(gvr) attributes.SetAPIResource(schema, resource) if preferredVersion := groupToPreferredVersion[gv.Group]; preferredVersion != "" && preferredVersion != gv.Version { attributes.SetPreferredVersion(schema, preferredVersion) diff --git a/pkg/schema/converter/k8stonorman.go b/pkg/schema/converter/k8stonorman.go index f1eac87..5ee53b0 100644 --- a/pkg/schema/converter/k8stonorman.go +++ b/pkg/schema/converter/k8stonorman.go @@ -10,20 +10,34 @@ import ( "k8s.io/client-go/discovery" ) -func GVKToSchemaID(gvk schema.GroupVersionKind) string { +func gvkToSchemaID(gvk schema.GroupVersionKind) string { if gvk.Group == "" { return strings.ToLower(fmt.Sprintf("core.%s.%s", gvk.Version, gvk.Kind)) } return strings.ToLower(fmt.Sprintf("%s.%s.%s", gvk.Group, gvk.Version, gvk.Kind)) } -func GVRToPluralName(gvr schema.GroupVersionResource) string { +func gvrToPluralName(gvr schema.GroupVersionResource) string { if gvr.Group == "" { return fmt.Sprintf("core.%s.%s", gvr.Version, gvr.Resource) } return fmt.Sprintf("%s.%s.%s", gvr.Group, gvr.Version, gvr.Resource) } +func 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) { result := map[string]*types.APISchema{} diff --git a/pkg/schema/converter/openapi.go b/pkg/schema/converter/openapi.go index 6c25554..e771453 100644 --- a/pkg/schema/converter/openapi.go +++ b/pkg/schema/converter/openapi.go @@ -41,7 +41,7 @@ func modelToSchema(modelName string, k *proto.Kind) *types.APISchema { Kind: convert.ToString(m["kind"]), } - s.ID = GVKToSchemaID(gvk) + s.ID = gvkToSchemaID(gvk) attributes.SetGVK(&s, gvk) } } diff --git a/pkg/schemaserver/store/schema/schema_store.go b/pkg/schemaserver/store/schema/schema_store.go index 0cdf01a..ba14eb2 100644 --- a/pkg/schemaserver/store/schema/schema_store.go +++ b/pkg/schemaserver/store/schema/schema_store.go @@ -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) { - 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{} 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 { - 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 - schemas = s.traverseAndAdd(apiOp, schema, schemaMap, schemas, included) + schemas = traverseAndAdd(apiOp, schema, schemaMap, schemas, included) schemas.Objects = append(schemas.Objects, toAPIObject(schema)) 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 { t := "" 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] { - 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] { - 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] { - schemas = s.addSchema(apiOp, refSchema, schemaMap, schemas, included) + schemas = addSchema(apiOp, refSchema, schemaMap, schemas, included) } } } diff --git a/pkg/server/handler/handlers.go b/pkg/server/handler/handlers.go index 35d27ae..eb66bc5 100644 --- a/pkg/server/handler/handlers.go +++ b/pkg/server/handler/handlers.go @@ -5,7 +5,6 @@ import ( "github.com/rancher/steve/pkg/attributes" "github.com/rancher/steve/pkg/schema" "github.com/rancher/steve/pkg/schemaserver/types" - runtimeschema "k8s.io/apimachinery/pkg/runtime/schema" ) 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.Type = sf.ByGVR(runtimeschema.GroupVersionResource{ - Version: vars["version"], - Group: group, - Resource: vars["resource"], - }) + apiOp.Type = vars["type"] nOrN := vars["nameorns"] if nOrN != "" { diff --git a/pkg/server/resources/apigroups/apigroup.go b/pkg/server/resources/apigroups/apigroup.go index 89cd6db..5d0cc19 100644 --- a/pkg/server/resources/apigroups/apigroup.go +++ b/pkg/server/resources/apigroups/apigroup.go @@ -3,21 +3,26 @@ package apigroups import ( "net/http" + "github.com/rancher/steve/pkg/schema" + "github.com/rancher/steve/pkg/schemaserver/store/empty" "github.com/rancher/steve/pkg/schemaserver/types" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/discovery" ) -func Register(schemas *types.APISchemas, discovery discovery.DiscoveryInterface) { - schemas.MustImportAndCustomize(v1.APIGroup{}, func(schema *types.APISchema) { - schema.CollectionMethods = []string{http.MethodGet} - schema.ResourceMethods = []string{http.MethodGet} - schema.Store = NewStore(discovery) - schema.Formatter = func(request *types.APIRequest, resource *types.RawResource) { +func Template(discovery discovery.DiscoveryInterface) schema.Template { + return schema.Template{ + ID: "apigroup", + Customize: func(apiSchema *types.APISchema) { + apiSchema.CollectionMethods = []string{http.MethodGet} + apiSchema.ResourceMethods = []string{http.MethodGet} + }, + Formatter: func(request *types.APIRequest, resource *types.RawResource) { resource.ID = resource.APIObject.Data().String("name") - } - }) + }, + Store: NewStore(discovery), + } } type Store struct { diff --git a/pkg/server/resources/common/dynamiccolumns.go b/pkg/server/resources/common/dynamiccolumns.go index 15cadbf..81e8bd6 100644 --- a/pkg/server/resources/common/dynamiccolumns.go +++ b/pkg/server/resources/common/dynamiccolumns.go @@ -58,6 +58,7 @@ func (d *DynamicColumns) SetColumns(schema *types.APISchema) error { obj, err := r.Do().Get() if err != nil { + attributes.SetTable(schema, false) return nil } t, ok := obj.(*metav1.Table) diff --git a/pkg/server/resources/schema.go b/pkg/server/resources/schema.go index c9665f7..e00ae7a 100644 --- a/pkg/server/resources/schema.go +++ b/pkg/server/resources/schema.go @@ -14,16 +14,16 @@ import ( "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) subscribe.Register(baseSchema) - apigroups.Register(baseSchema, discovery) apiroot.Register(baseSchema, []string{"v1"}, []string{"proxy:/apis"}) 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{ common.DefaultTemplate(cf, lookup), + apigroups.Template(discovery), } } diff --git a/pkg/server/resources/schemas/template.go b/pkg/server/resources/schemas/template.go new file mode 100644 index 0000000..7515eea --- /dev/null +++ b/pkg/server/resources/schemas/template.go @@ -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 + }) + } +} diff --git a/pkg/server/router/router.go b/pkg/server/router/router.go index 2b72413..5155bc6 100644 --- a/pkg/server/router/router.go +++ b/pkg/server/router/router.go @@ -26,14 +26,11 @@ func Routes(h Handlers) http.Handler { m.Path("/").Handler(h.APIRoot).HeadersRegexp("Accepts", ".*json.*") m.Path("/{name:v1}").Handler(h.APIRoot) - 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/{group}.{version}.{resource}/{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:schemas}/{name:.*}").Handler(h.GenericResource) - m.Path("/v1/{type}").Handler(h.GenericResource) - m.Path("/v1/{type}/{name}").Handler(h.GenericResource) + m.Path("/v1/{type}").Handler(h.K8sResource) + m.Path("/v1/{type}/{nameorns}").Handler(h.K8sResource) + m.Path("/v1/{type}/{namespace}/{name}").Handler(h.K8sResource) + m.Path("/v1/{type}/{nameorns}").Queries("action", "{action}").Handler(h.K8sResource) + m.Path("/v1/{type}/{namespace}/{name}").Queries("action", "{action}").Handler(h.K8sResource) m.Path("/api").Handler(h.K8sProxy) // Can't just prefix this as UI needs /apikeys path m.PathPrefix("/api/").Handler(h.K8sProxy) m.PathPrefix("/apis").Handler(h.K8sProxy) diff --git a/pkg/server/server.go b/pkg/server/server.go index ad82c0d..92d42d7 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -5,6 +5,8 @@ import ( "errors" "net/http" + "github.com/rancher/steve/pkg/server/resources/schemas" + "github.com/rancher/dynamiclistener/server" "github.com/rancher/steve/pkg/accesscontrol" "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()) - server.BaseSchemas = resources.DefaultSchemas(server.BaseSchemas, server.K8s.Discovery(), ccache) - server.SchemaTemplates = append(server.SchemaTemplates, resources.DefaultSchemaTemplates(cf, asl)...) + server.BaseSchemas = resources.DefaultSchemas(server.BaseSchemas, ccache) + server.SchemaTemplates = append(server.SchemaTemplates, resources.DefaultSchemaTemplates(cf, asl, server.K8s.Discovery())...) cols, err := common.NewDynamicColumns(server.RestConfig) 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) + + schemas.SetupWatcher(ctx, server.BaseSchemas, asl, sf) + sync := schemacontroller.Register(ctx, cols, server.K8s.Discovery(),