From 2eb87bffc15af459b32f78eb791a32cfc46f1235 Mon Sep 17 00:00:00 2001 From: Darren Shepherd Date: Fri, 5 Jun 2020 20:45:26 -0700 Subject: [PATCH] Add fields to cluster --- pkg/server/resources/clusters/clusters.go | 154 +++++++++++++++------- pkg/server/resources/schema.go | 7 +- pkg/server/server.go | 6 +- pkg/server/store/switchstore/store.go | 59 +++++++++ 4 files changed, 171 insertions(+), 55 deletions(-) create mode 100644 pkg/server/store/switchstore/store.go diff --git a/pkg/server/resources/clusters/clusters.go b/pkg/server/resources/clusters/clusters.go index 7c3c46a..e2a0135 100644 --- a/pkg/server/resources/clusters/clusters.go +++ b/pkg/server/resources/clusters/clusters.go @@ -3,14 +3,21 @@ package clusters import ( "context" "net/http" + "time" "github.com/rancher/steve/pkg/clustercache" "github.com/rancher/steve/pkg/schemaserver/store/empty" "github.com/rancher/steve/pkg/schemaserver/types" "github.com/rancher/steve/pkg/server/store/proxy" + "github.com/rancher/steve/pkg/server/store/switchschema" + "github.com/rancher/steve/pkg/server/store/switchstore" + "github.com/rancher/wrangler/pkg/data" "github.com/rancher/wrangler/pkg/schemas/validation" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/version" + "k8s.io/client-go/discovery" ) const ( @@ -31,15 +38,22 @@ var ( } ) -type Cluster struct { -} +func Register(ctx context.Context, schemas *types.APISchemas, cg proxy.ClientGetter, cluster clustercache.ClusterCache) error { + k8s, err := cg.AdminK8sInterface() + if err != nil { + return err + } -func Register(ctx context.Context, schemas *types.APISchemas, cg proxy.ClientGetter, cluster clustercache.ClusterCache) { shell := &shell{ cg: cg, namespace: "dashboard-shells", } + picker := &picker{ + start: time.Now(), + discovery: k8s.Discovery(), + } + cluster.OnAdd(ctx, shell.PurgeOldShell) cluster.OnChange(ctx, func(gvr schema.GroupVersionResource, key string, obj, oldObj runtime.Object) error { return shell.PurgeOldShell(gvr, key, obj) @@ -47,74 +61,112 @@ func Register(ctx context.Context, schemas *types.APISchemas, cg proxy.ClientGet schemas.MustImportAndCustomize(Cluster{}, func(schema *types.APISchema) { schema.CollectionMethods = []string{http.MethodGet} schema.ResourceMethods = []string{http.MethodGet} - schema.Store = &Store{} - + schema.Formatter = Format + schema.Store = &switchstore.Store{ + Picker: picker.Picker, + } schema.LinkHandlers = map[string]http.Handler{ "shell": shell, } - - schema.Formatter = func(request *types.APIRequest, resource *types.RawResource) { - resource.Links["api"] = request.URLBuilder.RelativeToRoot("/k8s/clusters/" + resource.ID) - } }) + + return nil +} + +type Cluster struct { + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec ClusterSpec `json:"spec"` + Status ClusterStatus `json:"status"` +} + +type ClusterSpec struct { + DisplayName string `json:"displayName"` +} + +type ClusterStatus struct { + Driver string `json:"driver"` + Version *version.Info `json:"version,omitempty"` +} + +func Format(request *types.APIRequest, resource *types.RawResource) { + copy := [][]string{ + {"spec", "displayName"}, + {"metadata", "creationTimestamp"}, + {"status", "driver"}, + {"status", "version"}, + } + + from := resource.APIObject.Data() + to := data.New() + + for _, keys := range copy { + to.SetNested(data.GetValueN(from, keys...), keys...) + } + + resource.APIObject.Object = to + resource.Links["api"] = request.URLBuilder.RelativeToRoot("/k8s/clusters/" + resource.ID) } type Store struct { empty.Store + + start time.Time + discovery discovery.DiscoveryInterface } -func toClusterList(obj types.APIObjectList, err error) (types.APIObjectList, error) { - for i := range obj.Objects { - obj.Objects[i], _ = toCluster(obj.Objects[i], err) +type picker struct { + start time.Time + discovery discovery.DiscoveryInterface +} + +func (p *picker) Picker(apiOp *types.APIRequest, schema *types.APISchema, verb, id string) (types.Store, error) { + clusters := apiOp.Schemas.LookupSchema(rancherCluster) + if clusters == nil { + return &Store{ + start: p.start, + discovery: p.discovery, + }, nil } - return obj, err -} - -func toCluster(obj types.APIObject, err error) (types.APIObject, error) { - return types.APIObject{ - Type: "cluster", - ID: obj.ID, - Object: &Cluster{}, - }, err + return &switchschema.Store{ + Schema: clusters, + }, nil } func (s *Store) ByID(apiOp *types.APIRequest, schema *types.APISchema, id string) (types.APIObject, error) { - clusters := apiOp.Schemas.LookupSchema(rancherCluster) - if clusters == nil { - if id == localID { - return local, nil - } - return types.APIObject{}, validation.NotFound + if id == localID { + return s.newLocal(), nil } - return toCluster(clusters.Store.ByID(apiOp, clusters, id)) + return types.APIObject{}, validation.NotFound } func (s *Store) List(apiOp *types.APIRequest, schema *types.APISchema) (types.APIObjectList, error) { - clusters := apiOp.Schemas.LookupSchema(rancherCluster) - if clusters == nil { - return localList, nil - } - return toClusterList(clusters.Store.List(apiOp, clusters)) + return types.APIObjectList{ + Objects: []types.APIObject{ + s.newLocal(), + }, + }, nil } -func (s *Store) Watch(apiOp *types.APIRequest, schema *types.APISchema, w types.WatchRequest) (chan types.APIEvent, error) { - clusters := apiOp.Schemas.LookupSchema(rancherCluster) - if clusters == nil { - return nil, nil +func (s *Store) newLocal() types.APIObject { + cluster := &Cluster{ + ObjectMeta: metav1.ObjectMeta{ + CreationTimestamp: metav1.NewTime(s.start), + }, + Spec: ClusterSpec{ + DisplayName: "Remote", + }, + Status: ClusterStatus{ + Driver: "remote", + }, } - target, err := clusters.Store.Watch(apiOp, clusters, w) - if err != nil { - return nil, err + version, err := s.discovery.ServerVersion() + if err == nil { + cluster.Status.Version = version + } + return types.APIObject{ + Type: "cluster", + ID: localID, + Object: cluster, } - - result := make(chan types.APIEvent) - go func() { - defer close(result) - for event := range target { - event.Object, _ = toCluster(event.Object, nil) - result <- event - } - }() - - return result, nil } diff --git a/pkg/server/resources/schema.go b/pkg/server/resources/schema.go index 6edb84e..c9ad44b 100644 --- a/pkg/server/resources/schema.go +++ b/pkg/server/resources/schema.go @@ -20,14 +20,15 @@ import ( "k8s.io/client-go/discovery" ) -func DefaultSchemas(ctx context.Context, baseSchema *types.APISchemas, ccache clustercache.ClusterCache, cg proxy.ClientGetter) *types.APISchemas { +func DefaultSchemas(ctx context.Context, baseSchema *types.APISchemas, ccache clustercache.ClusterCache, cg proxy.ClientGetter) (*types.APISchemas, error) { counts.Register(baseSchema, ccache) subscribe.Register(baseSchema) apiroot.Register(baseSchema, []string{"v1"}, []string{"proxy:/apis"}) userpreferences.Register(baseSchema, cg) - clusters.Register(ctx, baseSchema, cg, ccache) helm.Register(baseSchema) - return baseSchema + + err := clusters.Register(ctx, baseSchema, cg, ccache) + return baseSchema, err } func DefaultSchemaTemplates(cf *client.Factory, lookup accesscontrol.AccessSetLookup, discovery discovery.DiscoveryInterface) []schema.Template { diff --git a/pkg/server/server.go b/pkg/server/server.go index ec936b6..2777897 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -67,7 +67,11 @@ func setup(ctx context.Context, server *Server) (http.Handler, *schema.Collectio ccache := clustercache.NewClusterCache(ctx, cf.DynamicClient()) - server.BaseSchemas = resources.DefaultSchemas(ctx, server.BaseSchemas, ccache, cf) + server.BaseSchemas, err = resources.DefaultSchemas(ctx, server.BaseSchemas, ccache, cf) + if err != nil { + return nil, nil, err + } + server.SchemaTemplates = append(server.SchemaTemplates, resources.DefaultSchemaTemplates(cf, asl, server.K8s.Discovery())...) cols, err := common.NewDynamicColumns(server.RestConfig) diff --git a/pkg/server/store/switchstore/store.go b/pkg/server/store/switchstore/store.go new file mode 100644 index 0000000..ed527c8 --- /dev/null +++ b/pkg/server/store/switchstore/store.go @@ -0,0 +1,59 @@ +package switchstore + +import ( + "github.com/rancher/steve/pkg/schemaserver/types" +) + +type StorePicker func(apiOp *types.APIRequest, schema *types.APISchema, verb, id string) (types.Store, error) + +type Store struct { + Picker StorePicker +} + +func (e *Store) Delete(apiOp *types.APIRequest, schema *types.APISchema, id string) (types.APIObject, error) { + s, err := e.Picker(apiOp, schema, "delete", id) + if err != nil { + return types.APIObject{}, err + } + return s.Delete(apiOp, schema, id) +} + +func (e *Store) ByID(apiOp *types.APIRequest, schema *types.APISchema, id string) (types.APIObject, error) { + s, err := e.Picker(apiOp, schema, "get", id) + if err != nil { + return types.APIObject{}, err + } + return s.ByID(apiOp, schema, id) +} + +func (e *Store) List(apiOp *types.APIRequest, schema *types.APISchema) (types.APIObjectList, error) { + s, err := e.Picker(apiOp, schema, "list", "") + if err != nil { + return types.APIObjectList{}, err + } + return s.List(apiOp, schema) +} + +func (e *Store) Create(apiOp *types.APIRequest, schema *types.APISchema, data types.APIObject) (types.APIObject, error) { + s, err := e.Picker(apiOp, schema, "create", "") + if err != nil { + return types.APIObject{}, err + } + return s.Create(apiOp, schema, data) +} + +func (e *Store) Update(apiOp *types.APIRequest, schema *types.APISchema, data types.APIObject, id string) (types.APIObject, error) { + s, err := e.Picker(apiOp, schema, "update", id) + if err != nil { + return types.APIObject{}, err + } + return s.Update(apiOp, schema, data, id) +} + +func (e *Store) Watch(apiOp *types.APIRequest, schema *types.APISchema, wr types.WatchRequest) (chan types.APIEvent, error) { + s, err := e.Picker(apiOp, schema, "watch", "") + if err != nil { + return nil, err + } + return s.Watch(apiOp, schema, wr) +}