This commit is contained in:
Darren Shepherd
2020-01-30 22:01:21 -07:00
parent 9184741c57
commit 19c6732de0
2380 changed files with 587 additions and 759101 deletions

View File

@@ -3,20 +3,20 @@ package publicapi
import (
"net/http"
"github.com/sirupsen/logrus"
"github.com/rancher/norman/v2/pkg/api"
"github.com/rancher/norman/v2/pkg/auth"
"github.com/rancher/norman/v2/pkg/types"
"github.com/rancher/norman/v2/pkg/urlbuilder"
"github.com/rancher/steve/pkg/accesscontrol"
k8sproxy "github.com/rancher/steve/pkg/proxy"
"github.com/rancher/steve/pkg/resources/schema"
"github.com/rancher/steve/pkg/server/router"
"github.com/rancher/norman/pkg/api"
"github.com/rancher/norman/pkg/types"
"github.com/rancher/norman/pkg/urlbuilder"
"github.com/sirupsen/logrus"
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/client-go/rest"
)
func NewHandler(cfg *rest.Config, sf schema.Factory) (http.Handler, error) {
func NewHandler(cfg *rest.Config, sf schema.Factory, authMiddleware auth.Middleware, next http.Handler) (http.Handler, error) {
var (
err error
)
@@ -32,11 +32,13 @@ func NewHandler(cfg *rest.Config, sf schema.Factory) (http.Handler, error) {
return nil, err
}
w := authMiddleware.Wrap
return router.Routes(router.Handlers{
K8sResource: a.apiHandler(k8sAPI),
GenericResource: a.apiHandler(nil),
K8sProxy: proxy,
APIRoot: a.apiHandler(apiRoot),
Next: next,
K8sResource: w(a.apiHandler(k8sAPI)),
GenericResource: w(a.apiHandler(nil)),
K8sProxy: w(proxy),
APIRoot: w(a.apiHandler(apiRoot)),
}), nil
}

View File

@@ -2,9 +2,9 @@ package publicapi
import (
"github.com/gorilla/mux"
"github.com/rancher/norman/v2/pkg/types"
"github.com/rancher/steve/pkg/attributes"
"github.com/rancher/steve/pkg/resources/schema"
"github.com/rancher/norman/pkg/types"
runtimeschema "k8s.io/apimachinery/pkg/runtime/schema"
)

View File

@@ -0,0 +1,71 @@
package apigroups
import (
"net/http"
"github.com/rancher/norman/v2/pkg/data"
"github.com/rancher/norman/v2/pkg/store/empty"
"github.com/rancher/norman/v2/pkg/types"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/discovery"
)
func Register(schemas *types.Schemas, discovery discovery.DiscoveryInterface) {
schemas.MustImportAndCustomize(v1.APIGroup{}, func(schema *types.Schema) {
schema.CollectionMethods = []string{http.MethodGet}
schema.ResourceMethods = []string{http.MethodGet}
schema.Store = NewStore(discovery)
schema.Formatter = func(request *types.APIRequest, resource *types.RawResource) {
resource.ID = data.Object(resource.Values).String("name")
}
})
}
type Store struct {
empty.Store
discovery discovery.DiscoveryInterface
}
func NewStore(discovery discovery.DiscoveryInterface) types.Store {
return &Store{
Store: empty.Store{},
discovery: discovery,
}
}
func (e *Store) ByID(apiOp *types.APIRequest, schema *types.Schema, id string) (types.APIObject, error) {
groupList, err := e.discovery.ServerGroups()
if err != nil {
return types.APIObject{}, err
}
if id == "core" {
id = ""
}
for _, group := range groupList.Groups {
if group.Name == id {
return types.ToAPI(group), nil
}
}
return types.APIObject{}, nil
}
func (e *Store) List(apiOp *types.APIRequest, schema *types.Schema, opt *types.QueryOptions) (types.APIObject, error) {
groupList, err := e.discovery.ServerGroups()
if err != nil {
return types.APIObject{}, err
}
var result []interface{}
for _, item := range groupList.Groups {
if item.Name == "" {
item.Name = "core"
}
result = append(result, item)
}
return types.ToAPI(result), nil
}

View File

@@ -0,0 +1,37 @@
package common
import (
"github.com/rancher/norman/v2/pkg/types"
"github.com/rancher/steve/pkg/attributes"
"github.com/rancher/steve/pkg/table"
)
var (
NameColumn = table.Column{
Name: "Name",
Field: "metadata.name",
Type: "string",
Format: "name",
}
CreatedColumn = table.Column{
Name: "Created",
Field: "metadata.creationTimestamp",
Type: "string",
Format: "date",
}
)
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{
NameColumn,
CreatedColumn,
})
}
return nil
}

View File

@@ -0,0 +1,33 @@
package common
import (
"github.com/rancher/norman/v2/pkg/store/proxy"
"github.com/rancher/norman/v2/pkg/types"
"github.com/rancher/norman/v2/pkg/types/convert"
"github.com/rancher/norman/v2/pkg/types/values"
"github.com/rancher/steve/pkg/resources/schema"
)
func Register(collection *schema.Collection, clientGetter proxy.ClientGetter) error {
collection.AddTemplate(&schema.Template{
Store: proxy.NewProxyStore(clientGetter),
Formatter: Formatter,
Mapper: &DefaultColumns{},
})
return nil
}
func Formatter(request *types.APIRequest, resource *types.RawResource) {
selfLink := convert.ToString(values.GetValueN(resource.Values, "metadata", "selfLink"))
if selfLink == "" {
return
}
u := request.URLBuilder.RelativeToRoot(selfLink)
resource.Links["view"] = u
if _, ok := resource.Links["update"]; !ok {
resource.Links["update"] = u
}
}

View File

@@ -0,0 +1,259 @@
package counts
import (
"net/http"
"strconv"
"sync"
schema2 "k8s.io/apimachinery/pkg/runtime/schema"
"github.com/rancher/norman/v2/pkg/store/empty"
"github.com/rancher/norman/v2/pkg/types"
"github.com/rancher/steve/pkg/accesscontrol"
"github.com/rancher/steve/pkg/attributes"
"github.com/rancher/steve/pkg/clustercache"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
)
var (
ignore = map[string]bool{
"count": true,
"schema": true,
"apiRoot": true,
}
)
func Register(schemas *types.Schemas, ccache clustercache.ClusterCache) {
schemas.MustImportAndCustomize(Count{}, func(schema *types.Schema) {
schema.CollectionMethods = []string{http.MethodGet}
schema.ResourceMethods = []string{http.MethodGet}
schema.Attributes["access"] = accesscontrol.AccessListMap{
"watch": accesscontrol.AccessList{
{
Namespace: "*",
ResourceName: "*",
},
},
}
schema.Store = &Store{
ccache: ccache,
}
})
}
type Count struct {
ID string `json:"id,omitempty"`
Counts map[string]ItemCount `json:"counts"`
}
type ItemCount struct {
Count int `json:"count,omitempty"`
Namespaces map[string]int `json:"namespaces,omitempty"`
Revision int `json:"revision,omitempty"`
}
type Store struct {
empty.Store
ccache clustercache.ClusterCache
}
func (s *Store) ByID(apiOp *types.APIRequest, schema *types.Schema, id string) (types.APIObject, error) {
c := s.getCount(apiOp)
return types.ToAPI(c), nil
}
func (s *Store) List(apiOp *types.APIRequest, schema *types.Schema, opt *types.QueryOptions) (types.APIObject, error) {
c := s.getCount(apiOp)
return types.ToAPI([]interface{}{c}), nil
}
func (s *Store) Watch(apiOp *types.APIRequest, schema *types.Schema, w types.WatchRequest) (chan types.APIEvent, error) {
var (
result = make(chan types.APIEvent, 100)
counts map[string]ItemCount
gvrToSchema = map[schema2.GroupVersionResource]*types.Schema{}
countLock sync.Mutex
)
counts = s.getCount(apiOp).Counts
for id := range counts {
schema := apiOp.Schemas.Schema(id)
if schema == nil {
continue
}
gvrToSchema[attributes.GVR(schema)] = schema
}
go func() {
<-apiOp.Context().Done()
countLock.Lock()
close(result)
result = nil
countLock.Unlock()
}()
onChange := func(add bool, gvr schema2.GroupVersionResource, _ string, obj runtime.Object) error {
countLock.Lock()
defer countLock.Unlock()
if result == nil {
return nil
}
schema := gvrToSchema[gvr]
if schema == nil {
return nil
}
apiObj := apiOp.Filter(nil, schema, types.ToAPI(obj))
if apiObj.IsNil() {
return nil
}
_, namespace, revision, ok := getInfo(obj)
if !ok {
return nil
}
itemCount := counts[schema.ID]
if revision <= itemCount.Revision {
return nil
}
if add {
itemCount.Count++
if namespace != "" {
itemCount.Namespaces[namespace]++
}
} else {
itemCount.Count--
if namespace != "" {
itemCount.Namespaces[namespace]--
}
}
counts[schema.ID] = itemCount
countsCopy := map[string]ItemCount{}
for k, v := range counts {
ns := map[string]int{}
for i, j := range v.Namespaces {
ns[i] = j
}
countsCopy[k] = ItemCount{
Count: v.Count,
Revision: v.Revision,
Namespaces: ns,
}
}
result <- types.APIEvent{
Name: "resource.change",
ResourceType: "counts",
Object: types.ToAPI(Count{
ID: "count",
Counts: countsCopy,
}),
}
return nil
}
s.ccache.OnAdd(apiOp.Context(), func(gvr schema2.GroupVersionResource, key string, obj runtime.Object) error {
return onChange(true, gvr, key, obj)
})
s.ccache.OnRemove(apiOp.Context(), func(gvr schema2.GroupVersionResource, key string, obj runtime.Object) error {
return onChange(false, gvr, key, obj)
})
return result, nil
}
func (s *Store) schemasToWatch(apiOp *types.APIRequest) (result []*types.Schema) {
for _, schema := range apiOp.Schemas.Schemas() {
if ignore[schema.ID] {
continue
}
if attributes.PreferredVersion(schema) != "" {
continue
}
if attributes.PreferredGroup(schema) != "" {
continue
}
if schema.Store == nil {
continue
}
if apiOp.AccessControl.CanList(apiOp, schema) != nil {
continue
}
if apiOp.AccessControl.CanWatch(apiOp, schema) != nil {
continue
}
result = append(result, schema)
}
return
}
func getInfo(obj interface{}) (name string, namespace string, revision int, ok bool) {
r, ok := obj.(runtime.Object)
if !ok {
return "", "", 0, false
}
meta, err := meta.Accessor(r)
if err != nil {
return "", "", 0, false
}
revision, err = strconv.Atoi(meta.GetResourceVersion())
if err != nil {
return "", "", 0, false
}
return meta.GetName(), meta.GetNamespace(), revision, true
}
func (s *Store) getCount(apiOp *types.APIRequest) Count {
counts := map[string]ItemCount{}
for _, schema := range s.schemasToWatch(apiOp) {
gvr := attributes.GVR(schema)
rev := 0
itemCount := ItemCount{
Namespaces: map[string]int{},
}
for _, obj := range s.ccache.List(gvr) {
_, ns, revision, ok := getInfo(obj)
if !ok {
continue
}
if revision > rev {
rev = revision
}
itemCount.Count++
if ns != "" {
itemCount.Namespaces[ns]++
}
}
itemCount.Revision = rev
counts[schema.ID] = itemCount
}
return Count{
ID: "count",
Counts: counts,
}
}

View File

@@ -0,0 +1,38 @@
package resources
import (
"github.com/rancher/norman/v2/pkg/store/apiroot"
"github.com/rancher/norman/v2/pkg/subscribe"
"github.com/rancher/norman/v2/pkg/types"
"github.com/rancher/steve/pkg/accesscontrol"
"github.com/rancher/steve/pkg/client"
"github.com/rancher/steve/pkg/clustercache"
"github.com/rancher/steve/pkg/resources/apigroups"
"github.com/rancher/steve/pkg/resources/common"
"github.com/rancher/steve/pkg/resources/core"
"github.com/rancher/steve/pkg/resources/counts"
"github.com/rancher/steve/pkg/resources/schema"
"k8s.io/client-go/kubernetes"
)
func SchemaFactory(
cf *client.Factory,
as *accesscontrol.AccessStore,
k8s kubernetes.Interface,
ccache clustercache.ClusterCache,
) (*schema.Collection, error) {
baseSchema := types.EmptySchemas()
collection := schema.NewCollection(baseSchema, as)
core.Register(collection)
counts.Register(baseSchema, ccache)
subscribe.Register(baseSchema)
apigroups.Register(baseSchema, k8s.Discovery())
apiroot.Register(baseSchema, []string{"v1"}, []string{"proxy:/apis"})
if err := common.Register(collection, cf); err != nil {
return nil, err
}
return collection, nil
}