1
0
mirror of https://github.com/rancher/steve.git synced 2025-04-27 11:00:48 +00:00
steve/pkg/schema/collection.go

234 lines
5.2 KiB
Go
Raw Normal View History

2019-08-13 23:36:03 +00:00
package schema
import (
"context"
2020-02-27 17:34:51 +00:00
"net/http"
2019-08-13 23:36:03 +00:00
"strings"
2020-02-08 20:03:57 +00:00
"sync"
2019-08-13 23:36:03 +00:00
apiserver "github.com/rancher/apiserver/pkg/server"
"github.com/rancher/apiserver/pkg/types"
2019-09-11 21:05:00 +00:00
"github.com/rancher/steve/pkg/accesscontrol"
"github.com/rancher/steve/pkg/attributes"
"github.com/rancher/wrangler/v3/pkg/name"
2020-02-08 20:03:57 +00:00
"github.com/sirupsen/logrus"
2019-08-13 23:36:03 +00:00
"k8s.io/apimachinery/pkg/runtime/schema"
2020-02-08 20:03:57 +00:00
"k8s.io/apimachinery/pkg/util/cache"
2020-02-27 17:34:51 +00:00
"k8s.io/apiserver/pkg/endpoints/request"
2019-08-13 23:36:03 +00:00
)
type Collection struct {
toSync int32
2020-01-31 05:37:59 +00:00
baseSchema *types.APISchemas
schemas map[string]*types.APISchema
templates map[string][]*Template
notifiers map[int]func()
notifierID int
2019-08-13 23:36:03 +00:00
byGVR map[schema.GroupVersionResource]string
2019-09-09 21:28:55 +00:00
byGVK map[schema.GroupVersionKind]string
2020-02-08 20:03:57 +00:00
cache *cache.LRUExpireCache
userCache *cache.LRUExpireCache
2020-02-08 20:03:57 +00:00
lock sync.RWMutex
2019-08-13 23:36:03 +00:00
2020-02-08 20:03:57 +00:00
ctx context.Context
running map[string]func()
as accesscontrol.AccessSetLookup
2019-08-13 23:36:03 +00:00
}
type Template struct {
2020-02-08 20:03:57 +00:00
Group string
Kind string
ID string
Customize func(*types.APISchema)
Formatter types.Formatter
Store types.Store
Start func(ctx context.Context) error
StoreFactory func(types.Store) types.Store
2019-08-13 23:36:03 +00:00
}
func WrapServer(factory Factory, server *apiserver.Server) http.Handler {
2020-02-27 17:34:51 +00:00
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
user, ok := request.UserFrom(req.Context())
if !ok {
return
}
schemas, err := factory.Schemas(user)
if err != nil {
logrus.Errorf("failed to lookup schemas for user %v: %v", user, err)
http.Error(rw, "schemas failed", http.StatusInternalServerError)
return
}
server.Handle(&types.APIRequest{
Request: req,
Response: rw,
Schemas: schemas,
})
})
}
2020-02-08 20:03:57 +00:00
func NewCollection(ctx context.Context, baseSchema *types.APISchemas, access accesscontrol.AccessSetLookup) *Collection {
2019-08-13 23:36:03 +00:00
return &Collection{
baseSchema: baseSchema,
2020-01-31 05:37:59 +00:00
schemas: map[string]*types.APISchema{},
templates: map[string][]*Template{},
2019-08-13 23:36:03 +00:00
byGVR: map[schema.GroupVersionResource]string{},
2019-09-09 21:28:55 +00:00
byGVK: map[schema.GroupVersionKind]string{},
2020-02-08 20:03:57 +00:00
cache: cache.NewLRUExpireCache(1000),
userCache: cache.NewLRUExpireCache(1000),
notifiers: map[int]func(){},
2020-02-08 20:03:57 +00:00
ctx: ctx,
2019-08-13 23:36:03 +00:00
as: access,
2020-02-08 20:03:57 +00:00
running: map[string]func(){},
2019-08-13 23:36:03 +00:00
}
}
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()
}()
}
2020-01-31 05:37:59 +00:00
func (c *Collection) Reset(schemas map[string]*types.APISchema) {
2019-09-09 21:28:55 +00:00
byGVK := map[schema.GroupVersionKind]string{}
byGVR := map[schema.GroupVersionResource]string{}
2019-08-13 23:36:03 +00:00
for _, s := range schemas {
gvr := attributes.GVR(s)
if gvr.Resource != "" {
byGVR[gvr] = s.ID
}
2019-09-09 21:28:55 +00:00
gvk := attributes.GVK(s)
if gvk.Kind != "" {
byGVK[gvk] = s.ID
2019-08-13 23:36:03 +00:00
}
2020-02-08 20:03:57 +00:00
c.applyTemplates(s)
2019-08-13 23:36:03 +00:00
}
2020-02-08 20:03:57 +00:00
c.lock.Lock()
c.startStopTemplate(schemas)
2019-08-13 23:36:03 +00:00
c.schemas = schemas
c.byGVR = byGVR
2019-09-09 21:28:55 +00:00
c.byGVK = byGVK
2020-02-08 20:03:57 +00:00
for _, k := range c.cache.Keys() {
c.cache.Remove(k)
}
c.lock.Unlock()
c.lock.RLock()
for _, f := range c.notifiers {
f()
}
c.lock.RUnlock()
2020-02-08 20:03:57 +00:00
}
func start(ctx context.Context, templates []*Template) error {
for _, template := range templates {
if template.Start == nil {
continue
}
if err := template.Start(ctx); err != nil {
return err
}
}
return nil
}
2020-02-08 20:03:57 +00:00
func (c *Collection) startStopTemplate(schemas map[string]*types.APISchema) {
for id := range schemas {
if _, ok := c.running[id]; ok {
continue
}
templates := c.templates[id]
if len(templates) == 0 {
2020-02-08 20:03:57 +00:00
continue
}
subCtx, cancel := context.WithCancel(c.ctx)
if err := start(subCtx, templates); err != nil {
2020-05-22 22:34:59 +00:00
cancel()
2020-02-08 20:03:57 +00:00
logrus.Errorf("failed to start schema template: %s", id)
continue
}
c.running[id] = cancel
}
for id, cancel := range c.running {
if _, ok := schemas[id]; !ok {
cancel()
delete(c.running, id)
}
}
2019-09-09 21:28:55 +00:00
}
2020-01-31 05:37:59 +00:00
func (c *Collection) Schema(id string) *types.APISchema {
2020-02-08 20:03:57 +00:00
c.lock.RLock()
defer c.lock.RUnlock()
2019-09-09 21:28:55 +00:00
return c.schemas[id]
}
func (c *Collection) IDs() (result []string) {
2020-02-08 20:03:57 +00:00
c.lock.RLock()
defer c.lock.RUnlock()
2019-09-09 21:28:55 +00:00
seen := map[string]bool{}
for _, id := range c.byGVR {
if seen[id] {
continue
}
seen[id] = true
result = append(result, id)
}
return
2019-08-13 23:36:03 +00:00
}
func (c *Collection) ByGVR(gvr schema.GroupVersionResource) string {
2020-02-08 20:03:57 +00:00
c.lock.RLock()
defer c.lock.RUnlock()
2019-09-09 21:28:55 +00:00
id, ok := c.byGVR[gvr]
if ok {
return id
}
gvr.Resource = name.GuessPluralName(strings.ToLower(gvr.Resource))
return c.byGVK[schema.GroupVersionKind{
Group: gvr.Group,
Version: gvr.Version,
Kind: gvr.Resource,
}]
2019-08-13 23:36:03 +00:00
}
2019-09-09 21:28:55 +00:00
func (c *Collection) ByGVK(gvk schema.GroupVersionKind) string {
2020-02-08 20:03:57 +00:00
c.lock.RLock()
defer c.lock.RUnlock()
2019-09-09 21:28:55 +00:00
2020-02-08 20:03:57 +00:00
return c.byGVK[gvk]
}
2020-07-24 08:42:00 +00:00
func (c *Collection) AddTemplate(templates ...Template) {
2020-02-08 20:03:57 +00:00
c.lock.RLock()
defer c.lock.RUnlock()
2020-07-24 08:42:00 +00:00
for i, template := range templates {
if template.Kind != "" {
c.templates[template.Group+"/"+template.Kind] = append(c.templates[template.Group+"/"+template.Kind], &templates[i])
} else if template.ID != "" {
c.templates[template.ID] = append(c.templates[template.ID], &templates[i])
2020-07-24 08:42:00 +00:00
}
if template.Kind == "" && template.Group == "" && template.ID == "" {
c.templates[""] = append(c.templates[""], &templates[i])
2020-07-24 08:42:00 +00:00
}
2019-08-13 23:36:03 +00:00
}
}