mirror of
https://github.com/niusmallnan/steve.git
synced 2025-06-20 20:01:56 +00:00
Add userpreferences
This commit is contained in:
parent
0ad6c2aba1
commit
655b7a85ee
@ -50,7 +50,7 @@ func (r *RawResource) MarshalJSON() ([]byte, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(data) < 2 || data[0] != '{' || data[len(data)-1] != '}' {
|
||||
if len(data) < 3 || data[0] != '{' || data[len(data)-1] != '}' {
|
||||
return outer, nil
|
||||
}
|
||||
|
||||
@ -288,3 +288,8 @@ func FormatterChain(formatter Formatter, next Formatter) Formatter {
|
||||
next(request, resource)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *APIRequest) Clone() *APIRequest {
|
||||
clone := *r
|
||||
return &clone
|
||||
}
|
||||
|
@ -11,13 +11,16 @@ import (
|
||||
"github.com/rancher/steve/pkg/server/resources/apigroups"
|
||||
"github.com/rancher/steve/pkg/server/resources/common"
|
||||
"github.com/rancher/steve/pkg/server/resources/counts"
|
||||
"github.com/rancher/steve/pkg/server/resources/userpreferences"
|
||||
"github.com/rancher/steve/pkg/server/store/proxy"
|
||||
"k8s.io/client-go/discovery"
|
||||
)
|
||||
|
||||
func DefaultSchemas(baseSchema *types.APISchemas, ccache clustercache.ClusterCache) *types.APISchemas {
|
||||
func DefaultSchemas(baseSchema *types.APISchemas, ccache clustercache.ClusterCache, cg proxy.ClientGetter) *types.APISchemas {
|
||||
counts.Register(baseSchema, ccache)
|
||||
subscribe.Register(baseSchema)
|
||||
apiroot.Register(baseSchema, []string{"v1"}, []string{"proxy:/apis"})
|
||||
userpreferences.Register(baseSchema, cg)
|
||||
return baseSchema
|
||||
}
|
||||
|
||||
|
103
pkg/server/resources/userpreferences/configmap.go
Normal file
103
pkg/server/resources/userpreferences/configmap.go
Normal file
@ -0,0 +1,103 @@
|
||||
package userpreferences
|
||||
|
||||
import (
|
||||
"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/wrangler/pkg/data"
|
||||
"github.com/rancher/wrangler/pkg/data/convert"
|
||||
"github.com/rancher/wrangler/pkg/schemas/validation"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/client-go/dynamic"
|
||||
)
|
||||
|
||||
type configMapStore struct {
|
||||
empty.Store
|
||||
cg proxy.ClientGetter
|
||||
}
|
||||
|
||||
func (e *configMapStore) getClient(apiOp *types.APIRequest) (dynamic.ResourceInterface, error) {
|
||||
cmSchema := apiOp.Schemas.LookupSchema("configmap")
|
||||
if cmSchema == nil {
|
||||
return nil, validation.NotFound
|
||||
}
|
||||
|
||||
return e.cg.AdminClient(apiOp, cmSchema, "kube-system")
|
||||
}
|
||||
|
||||
func (e *configMapStore) ByID(apiOp *types.APIRequest, schema *types.APISchema, id string) (types.APIObject, error) {
|
||||
u := getUser(apiOp)
|
||||
client, err := e.getClient(apiOp)
|
||||
if err != nil {
|
||||
return types.APIObject{}, err
|
||||
}
|
||||
|
||||
pref := &UserPreference{
|
||||
Data: map[string]string{},
|
||||
}
|
||||
result := types.APIObject{
|
||||
Type: "userpreference",
|
||||
ID: u.GetName(),
|
||||
Object: pref,
|
||||
}
|
||||
|
||||
obj, err := client.Get(prefName(u), metav1.GetOptions{})
|
||||
if apierrors.IsNotFound(err) {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
d := data.Object(obj.Object).Map("data")
|
||||
return result, convert.ToObj(d, &pref.Data)
|
||||
}
|
||||
|
||||
func (e *configMapStore) List(apiOp *types.APIRequest, schema *types.APISchema) (types.APIObjectList, error) {
|
||||
obj, err := e.ByID(apiOp, schema, "")
|
||||
if err != nil {
|
||||
return types.APIObjectList{}, err
|
||||
}
|
||||
return types.APIObjectList{
|
||||
Objects: []types.APIObject{
|
||||
obj,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (e *configMapStore) Update(apiOp *types.APIRequest, schema *types.APISchema, data types.APIObject, id string) (types.APIObject, error) {
|
||||
u := getUser(apiOp)
|
||||
client, err := e.getClient(apiOp)
|
||||
if err != nil {
|
||||
return types.APIObject{}, err
|
||||
}
|
||||
|
||||
obj, err := client.Get(prefName(u), metav1.GetOptions{})
|
||||
if apierrors.IsNotFound(err) {
|
||||
_, err = client.Create(&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"name": prefName(u),
|
||||
},
|
||||
"data": data.Data().Map("data"),
|
||||
},
|
||||
}, metav1.CreateOptions{})
|
||||
} else if err == nil {
|
||||
obj.Object["data"] = data.Data().Map("data")
|
||||
_, err = client.Update(obj, metav1.UpdateOptions{})
|
||||
}
|
||||
if err != nil {
|
||||
return types.APIObject{}, err
|
||||
}
|
||||
|
||||
return e.ByID(apiOp, schema, "")
|
||||
}
|
||||
|
||||
func (e *configMapStore) Delete(apiOp *types.APIRequest, schema *types.APISchema, id string) (types.APIObject, error) {
|
||||
u := getUser(apiOp)
|
||||
client, err := e.getClient(apiOp)
|
||||
if err != nil {
|
||||
return types.APIObject{}, err
|
||||
}
|
||||
|
||||
return types.APIObject{}, client.Delete(prefName(u), nil)
|
||||
}
|
136
pkg/server/resources/userpreferences/rancherpref.go
Normal file
136
pkg/server/resources/userpreferences/rancherpref.go
Normal file
@ -0,0 +1,136 @@
|
||||
package userpreferences
|
||||
|
||||
import (
|
||||
"github.com/rancher/steve/pkg/attributes"
|
||||
"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/wrangler/pkg/data/convert"
|
||||
"github.com/rancher/wrangler/pkg/schemas/validation"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/client-go/dynamic"
|
||||
)
|
||||
|
||||
var (
|
||||
rancherSchema = "management.cattle.io.preference"
|
||||
)
|
||||
|
||||
type rancherPrefStore struct {
|
||||
empty.Store
|
||||
cg proxy.ClientGetter
|
||||
}
|
||||
|
||||
func (e *rancherPrefStore) getClient(apiOp *types.APIRequest) (dynamic.ResourceInterface, error) {
|
||||
u := getUser(apiOp).GetName()
|
||||
cmSchema := apiOp.Schemas.LookupSchema(rancherSchema)
|
||||
if cmSchema == nil {
|
||||
return nil, validation.NotFound
|
||||
}
|
||||
|
||||
return e.cg.AdminClient(apiOp, cmSchema, u)
|
||||
}
|
||||
|
||||
func (e *rancherPrefStore) ByID(apiOp *types.APIRequest, schema *types.APISchema, id string) (types.APIObject, error) {
|
||||
u := getUser(apiOp)
|
||||
client, err := e.getClient(apiOp)
|
||||
if err != nil {
|
||||
return types.APIObject{}, err
|
||||
}
|
||||
|
||||
pref := &UserPreference{
|
||||
Data: map[string]string{},
|
||||
}
|
||||
result := types.APIObject{
|
||||
Type: "userpreference",
|
||||
ID: u.GetName(),
|
||||
Object: pref,
|
||||
}
|
||||
|
||||
objs, err := client.List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
for _, obj := range objs.Items {
|
||||
pref.Data[obj.GetName()] = convert.ToString(obj.Object["value"])
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (e *rancherPrefStore) List(apiOp *types.APIRequest, schema *types.APISchema) (types.APIObjectList, error) {
|
||||
obj, err := e.ByID(apiOp, schema, "")
|
||||
if err != nil {
|
||||
return types.APIObjectList{}, err
|
||||
}
|
||||
return types.APIObjectList{
|
||||
Objects: []types.APIObject{
|
||||
obj,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (e *rancherPrefStore) Update(apiOp *types.APIRequest, schema *types.APISchema, data types.APIObject, id string) (types.APIObject, error) {
|
||||
client, err := e.getClient(apiOp)
|
||||
if err != nil {
|
||||
return types.APIObject{}, err
|
||||
}
|
||||
|
||||
gvk := attributes.GVK(apiOp.Schemas.LookupSchema(rancherSchema))
|
||||
|
||||
newValues := map[string]string{}
|
||||
for k, v := range data.Data().Map("data") {
|
||||
newValues[k] = convert.ToString(v)
|
||||
}
|
||||
|
||||
prefs, err := client.List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return types.APIObject{}, err
|
||||
}
|
||||
|
||||
for _, pref := range prefs.Items {
|
||||
key := pref.GetName()
|
||||
newValue, ok := newValues[key]
|
||||
delete(newValues, key)
|
||||
if ok && newValue != pref.Object["value"] {
|
||||
pref.Object["value"] = newValue
|
||||
_, err := client.Update(&pref, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
return types.APIObject{}, err
|
||||
}
|
||||
} else if !ok {
|
||||
err := client.Delete(key, nil)
|
||||
if err != nil {
|
||||
return types.APIObject{}, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for k, v := range newValues {
|
||||
_, err = client.Create(&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": gvk.GroupVersion().String(),
|
||||
"kind": gvk.Kind,
|
||||
"metadata": map[string]interface{}{
|
||||
"name": k,
|
||||
},
|
||||
"value": v,
|
||||
},
|
||||
}, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
return types.APIObject{}, err
|
||||
}
|
||||
}
|
||||
|
||||
return e.ByID(apiOp, schema, "")
|
||||
}
|
||||
|
||||
func (e *rancherPrefStore) Delete(apiOp *types.APIRequest, schema *types.APISchema, id string) (types.APIObject, error) {
|
||||
client, err := e.getClient(apiOp)
|
||||
if err != nil {
|
||||
return types.APIObject{}, err
|
||||
}
|
||||
|
||||
return types.APIObject{}, client.DeleteCollection(nil, metav1.ListOptions{})
|
||||
}
|
89
pkg/server/resources/userpreferences/userpreferences.go
Normal file
89
pkg/server/resources/userpreferences/userpreferences.go
Normal file
@ -0,0 +1,89 @@
|
||||
package userpreferences
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"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/wrangler/pkg/name"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
"k8s.io/apiserver/pkg/endpoints/request"
|
||||
)
|
||||
|
||||
type UserPreference struct {
|
||||
Data map[string]string `json:"data"`
|
||||
}
|
||||
|
||||
func Register(schemas *types.APISchemas, cg proxy.ClientGetter) {
|
||||
schemas.InternalSchemas.TypeName("userpreference", UserPreference{})
|
||||
schemas.MustImportAndCustomize(UserPreference{}, func(schema *types.APISchema) {
|
||||
schema.CollectionMethods = []string{http.MethodGet}
|
||||
schema.ResourceMethods = []string{http.MethodGet}
|
||||
schema.ResourceMethods = []string{http.MethodGet, http.MethodPut, http.MethodDelete}
|
||||
schema.Store = New(cg)
|
||||
})
|
||||
}
|
||||
|
||||
func New(cg proxy.ClientGetter) types.Store {
|
||||
return &Store{
|
||||
rancher: &rancherPrefStore{
|
||||
cg: cg,
|
||||
},
|
||||
configMapStore: &configMapStore{
|
||||
cg: cg,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type Store struct {
|
||||
empty.Store
|
||||
rancher *rancherPrefStore
|
||||
configMapStore *configMapStore
|
||||
}
|
||||
|
||||
func isRancher(apiOp *types.APIRequest) bool {
|
||||
return apiOp.Schemas.LookupSchema(rancherSchema) != nil
|
||||
}
|
||||
|
||||
func (e *Store) ByID(apiOp *types.APIRequest, schema *types.APISchema, id string) (types.APIObject, error) {
|
||||
if isRancher(apiOp) {
|
||||
return e.rancher.ByID(apiOp, schema, id)
|
||||
}
|
||||
return e.configMapStore.ByID(apiOp, schema, id)
|
||||
}
|
||||
|
||||
func (e *Store) List(apiOp *types.APIRequest, schema *types.APISchema) (types.APIObjectList, error) {
|
||||
if isRancher(apiOp) {
|
||||
return e.rancher.List(apiOp, schema)
|
||||
}
|
||||
return e.configMapStore.List(apiOp, schema)
|
||||
}
|
||||
|
||||
func (e *Store) Update(apiOp *types.APIRequest, schema *types.APISchema, data types.APIObject, id string) (types.APIObject, error) {
|
||||
if isRancher(apiOp) {
|
||||
return e.rancher.Update(apiOp, schema, data, id)
|
||||
}
|
||||
return e.configMapStore.Update(apiOp, schema, data, id)
|
||||
}
|
||||
|
||||
func (e *Store) Delete(apiOp *types.APIRequest, schema *types.APISchema, id string) (types.APIObject, error) {
|
||||
if isRancher(apiOp) {
|
||||
return e.rancher.Delete(apiOp, schema, id)
|
||||
}
|
||||
return e.configMapStore.Delete(apiOp, schema, id)
|
||||
}
|
||||
|
||||
func prefName(u user.Info) string {
|
||||
return name.SafeConcatName("pref", u.GetName())
|
||||
}
|
||||
|
||||
func getUser(apiOp *types.APIRequest) user.Info {
|
||||
u, ok := request.UserFrom(apiOp.Context())
|
||||
if !ok {
|
||||
u = &user.DefaultInfo{
|
||||
Name: "dashboard-user",
|
||||
}
|
||||
}
|
||||
return u
|
||||
}
|
@ -68,7 +68,7 @@ func setup(ctx context.Context, server *Server) (http.Handler, *schema.Collectio
|
||||
|
||||
ccache := clustercache.NewClusterCache(ctx, cf.MetadataClient())
|
||||
|
||||
server.BaseSchemas = resources.DefaultSchemas(server.BaseSchemas, ccache)
|
||||
server.BaseSchemas = resources.DefaultSchemas(server.BaseSchemas, ccache, cf)
|
||||
server.SchemaTemplates = append(server.SchemaTemplates, resources.DefaultSchemaTemplates(cf, asl, server.K8s.Discovery())...)
|
||||
|
||||
cols, err := common.NewDynamicColumns(server.RestConfig)
|
||||
|
Loading…
Reference in New Issue
Block a user