mirror of
https://github.com/rancher/norman.git
synced 2025-08-19 15:57:09 +00:00
Major refactor of subcontexts
This commit is contained in:
parent
b62e043067
commit
722cedfe01
@ -147,17 +147,26 @@ func (j *JSONResponseWriter) addLinks(b *builder.Builder, schema *types.Schema,
|
|||||||
rawResource.Links["remove"] = self
|
rawResource.Links["remove"] = self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
subContextVersion := context.Schemas.SubContextVersionForSchema(schema)
|
||||||
for _, backRef := range context.Schemas.References(schema) {
|
for _, backRef := range context.Schemas.References(schema) {
|
||||||
if !backRef.Schema.CanList(context) {
|
if !backRef.Schema.CanList(context) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if schema.SubContext == "" {
|
if subContextVersion == nil {
|
||||||
rawResource.Links[backRef.Schema.PluralName] = context.URLBuilder.FilterLink(backRef.Schema, backRef.FieldName, rawResource.ID)
|
rawResource.Links[backRef.Schema.PluralName] = context.URLBuilder.FilterLink(backRef.Schema, backRef.FieldName, rawResource.ID)
|
||||||
} else {
|
} else {
|
||||||
rawResource.Links[backRef.Schema.PluralName] = context.URLBuilder.SubContextCollection(schema, rawResource.ID, backRef.Schema)
|
rawResource.Links[backRef.Schema.PluralName] = context.URLBuilder.SubContextCollection(schema, rawResource.ID, backRef.Schema)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if subContextVersion != nil {
|
||||||
|
for _, subSchema := range context.Schemas.SchemasForVersion(*subContextVersion) {
|
||||||
|
if subSchema.CanList(context) {
|
||||||
|
rawResource.Links[subSchema.PluralName] = context.URLBuilder.SubContextCollection(schema, rawResource.ID, subSchema)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newCollection(apiContext *types.APIContext) *types.GenericCollection {
|
func newCollection(apiContext *types.APIContext) *types.GenericCollection {
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/rancher/norman/api"
|
"github.com/rancher/norman/api"
|
||||||
"github.com/rancher/norman/store/crd"
|
"github.com/rancher/norman/store/crd"
|
||||||
|
"github.com/rancher/norman/store/proxy"
|
||||||
"github.com/rancher/norman/types"
|
"github.com/rancher/norman/types"
|
||||||
"github.com/rancher/norman/types/factory"
|
"github.com/rancher/norman/types/factory"
|
||||||
"k8s.io/client-go/tools/clientcmd"
|
"k8s.io/client-go/tools/clientcmd"
|
||||||
@ -39,13 +41,19 @@ func main() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
store, err := crd.NewCRDStoreFromConfig(*kubeConfig)
|
client, err := proxy.NewClientGetterFromConfig(*kubeConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
crdFactory := crd.Factory{
|
||||||
|
ClientGetter: client,
|
||||||
|
}
|
||||||
|
|
||||||
Schemas.MustImportAndCustomize(&version, Foo{}, func(schema *types.Schema) {
|
Schemas.MustImportAndCustomize(&version, Foo{}, func(schema *types.Schema) {
|
||||||
schema.Store = store
|
if err := crdFactory.AssignStores(context.Background(), types.DefaultStorageContext, schema); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
server := api.NewAPIServer()
|
server := api.NewAPIServer()
|
||||||
|
@ -24,6 +24,7 @@ var (
|
|||||||
ActionNotAvailable = ErrorCode{"ActionNotAvailable", 404}
|
ActionNotAvailable = ErrorCode{"ActionNotAvailable", 404}
|
||||||
InvalidState = ErrorCode{"InvalidState", 422}
|
InvalidState = ErrorCode{"InvalidState", 422}
|
||||||
ServerError = ErrorCode{"ServerError", 500}
|
ServerError = ErrorCode{"ServerError", 500}
|
||||||
|
ClusterUnavailable = ErrorCode{"ClusterUnavailable", 503}
|
||||||
PermissionDenied = ErrorCode{"PermissionDenied", 403}
|
PermissionDenied = ErrorCode{"PermissionDenied", 403}
|
||||||
|
|
||||||
MethodNotAllowed = ErrorCode{"MethodNotAllow", 405}
|
MethodNotAllowed = ErrorCode{"MethodNotAllow", 405}
|
||||||
|
105
parse/parse.go
105
parse/parse.go
@ -6,7 +6,10 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"sort"
|
||||||
|
|
||||||
"github.com/rancher/norman/api/builtin"
|
"github.com/rancher/norman/api/builtin"
|
||||||
|
"github.com/rancher/norman/httperror"
|
||||||
"github.com/rancher/norman/types"
|
"github.com/rancher/norman/types"
|
||||||
"github.com/rancher/norman/urlbuilder"
|
"github.com/rancher/norman/urlbuilder"
|
||||||
)
|
)
|
||||||
@ -42,26 +45,23 @@ type URLParser func(schema *types.Schemas, url *url.URL) (ParsedURL, error)
|
|||||||
func DefaultURLParser(schemas *types.Schemas, url *url.URL) (ParsedURL, error) {
|
func DefaultURLParser(schemas *types.Schemas, url *url.URL) (ParsedURL, error) {
|
||||||
result := ParsedURL{}
|
result := ParsedURL{}
|
||||||
|
|
||||||
version := Version(schemas, url.Path)
|
path := url.Path
|
||||||
|
path = multiSlashRegexp.ReplaceAllString(path, "/")
|
||||||
|
version, prefix, parts, subContext := parseVersionAndSubContext(schemas, path)
|
||||||
|
|
||||||
if version == nil {
|
if version == nil {
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
path := url.Path
|
|
||||||
path = multiSlashRegexp.ReplaceAllString(path, "/")
|
|
||||||
|
|
||||||
parts := strings.SplitN(path[len(version.Path):], "/", 4)
|
|
||||||
prefix, parts, subContext := parseSubContext(schemas, version, parts)
|
|
||||||
|
|
||||||
result.Version = version.Path
|
result.Version = version.Path
|
||||||
result.SubContext = subContext
|
result.SubContext = subContext
|
||||||
result.SubContextPrefix = prefix
|
result.SubContextPrefix = prefix
|
||||||
result.Action, result.Method = parseAction(url)
|
result.Action, result.Method = parseAction(url)
|
||||||
result.Query = url.Query()
|
result.Query = url.Query()
|
||||||
|
|
||||||
result.Type = safeIndex(parts, 1)
|
result.Type = safeIndex(parts, 0)
|
||||||
result.ID = safeIndex(parts, 2)
|
result.ID = safeIndex(parts, 1)
|
||||||
result.Link = safeIndex(parts, 3)
|
result.Link = safeIndex(parts, 2)
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
@ -121,11 +121,14 @@ func Parse(rw http.ResponseWriter, req *http.Request, schemas *types.Schemas, ur
|
|||||||
}
|
}
|
||||||
|
|
||||||
if result.Schema == nil {
|
if result.Schema == nil {
|
||||||
|
if result.Type != "" {
|
||||||
|
err = httperror.NewAPIError(httperror.NotFound, "failed to find schema "+result.Type)
|
||||||
|
}
|
||||||
result.Method = http.MethodGet
|
result.Method = http.MethodGet
|
||||||
result.Type = "apiRoot"
|
result.Type = "apiRoot"
|
||||||
result.Schema = result.Schemas.Schema(&builtin.Version, "apiRoot")
|
result.Schema = result.Schemas.Schema(&builtin.Version, "apiRoot")
|
||||||
result.ID = result.Version.Path
|
result.ID = result.Version.Path
|
||||||
return result, nil
|
return result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
result.Type = result.Schema.ID
|
result.Type = result.Schema.ID
|
||||||
@ -137,29 +140,61 @@ func Parse(rw http.ResponseWriter, req *http.Request, schemas *types.Schemas, ur
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseSubContext(schemas *types.Schemas, version *types.APIVersion, parts []string) (string, []string, map[string]string) {
|
func versionsForPath(schemas *types.Schemas, path string) []types.APIVersion {
|
||||||
subContext := ""
|
var matchedVersion []types.APIVersion
|
||||||
result := map[string]string{}
|
for _, version := range schemas.Versions() {
|
||||||
|
if strings.HasPrefix(path, version.Path) {
|
||||||
|
matchedVersion = append(matchedVersion, version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort.Slice(matchedVersion, func(i, j int) bool {
|
||||||
|
return len(matchedVersion[i].Path) > len(matchedVersion[j].Path)
|
||||||
|
})
|
||||||
|
return matchedVersion
|
||||||
|
}
|
||||||
|
|
||||||
for len(parts) > 3 && version != nil && parts[3] != "" {
|
func parseVersionAndSubContext(schemas *types.Schemas, path string) (*types.APIVersion, string, []string, map[string]string) {
|
||||||
resourceType := parts[1]
|
versions := versionsForPath(schemas, path)
|
||||||
resourceID := parts[2]
|
if len(versions) == 0 {
|
||||||
|
return nil, "", nil, nil
|
||||||
|
}
|
||||||
|
version := &versions[0]
|
||||||
|
|
||||||
if !version.SubContexts[resourceType] {
|
if strings.HasSuffix(path, "/") {
|
||||||
|
path = path[:len(path)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
versionParts := strings.Split(version.Path, "/")
|
||||||
|
pathParts := strings.Split(path, "/")
|
||||||
|
paths := pathParts[len(versionParts):]
|
||||||
|
|
||||||
|
if !version.SubContext || len(versions) < 2 {
|
||||||
|
return version, "", paths, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(paths) < 2 {
|
||||||
|
// Handle case like /v3/clusters/foo where /v3 and /v3/clusters are API versions.
|
||||||
|
// In this situation you want the version to be /v3 and the path "clusters", "foo"
|
||||||
|
return &versions[1], "", pathParts[len(versionParts)-1:], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Length is always >= 3
|
||||||
|
|
||||||
|
attrs := map[string]string{
|
||||||
|
version.SubContextSchema: paths[0],
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, version := range versions {
|
||||||
|
schema := schemas.Schema(&version, paths[1])
|
||||||
|
if schema != nil {
|
||||||
|
if i == 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
return &version, "", paths[1:], attrs
|
||||||
subSchema := schemas.Schema(version, parts[3])
|
}
|
||||||
if subSchema == nil {
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result[resourceType] = resourceID
|
return version, "/" + paths[0], paths[1:], attrs
|
||||||
subContext = subContext + "/" + resourceType + "/" + resourceID
|
|
||||||
parts = append(parts[:1], parts[3:]...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return subContext, parts, result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func DefaultResolver(typeName string, apiContext *types.APIContext) error {
|
func DefaultResolver(typeName string, apiContext *types.APIContext) error {
|
||||||
@ -223,20 +258,6 @@ func parseAction(url *url.URL) (string, string) {
|
|||||||
return action, ""
|
return action, ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func Version(schemas *types.Schemas, path string) *types.APIVersion {
|
|
||||||
path = multiSlashRegexp.ReplaceAllString(path, "/")
|
|
||||||
for _, version := range schemas.Versions() {
|
|
||||||
if version.Path == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(path, version.Path) {
|
|
||||||
return &version
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func Body(req *http.Request) (map[string]interface{}, error) {
|
func Body(req *http.Request) (map[string]interface{}, error) {
|
||||||
req.ParseMultipartForm(maxFormSize)
|
req.ParseMultipartForm(maxFormSize)
|
||||||
if req.MultipartForm != nil {
|
if req.MultipartForm != nil {
|
||||||
|
@ -29,8 +29,8 @@ func (d *DefaultSubContextAttributeProvider) Create(apiContext *types.APIContext
|
|||||||
func (d *DefaultSubContextAttributeProvider) create(apiContext *types.APIContext, schema *types.Schema) map[string]string {
|
func (d *DefaultSubContextAttributeProvider) create(apiContext *types.APIContext, schema *types.Schema) map[string]string {
|
||||||
result := map[string]string{}
|
result := map[string]string{}
|
||||||
|
|
||||||
for subContext, value := range apiContext.SubContext {
|
for subContextSchemaID, value := range apiContext.SubContext {
|
||||||
subContextSchema := apiContext.Schemas.SubContext(subContext)
|
subContextSchema := apiContext.Schemas.Schema(nil, subContextSchemaID)
|
||||||
if subContextSchema == nil {
|
if subContextSchema == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -34,14 +34,10 @@ func Handler(apiContext *types.APIContext, _ types.RequestHandler) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getMatchingSchemas(apiContext *types.APIContext) []*types.Schema {
|
func getMatchingSchemas(apiContext *types.APIContext) []*types.Schema {
|
||||||
apiVersions := apiContext.Request.URL.Query()["apiVersions"]
|
|
||||||
resourceTypes := apiContext.Request.URL.Query()["resourceTypes"]
|
resourceTypes := apiContext.Request.URL.Query()["resourceTypes"]
|
||||||
|
|
||||||
var schemas []*types.Schema
|
var schemas []*types.Schema
|
||||||
for _, schema := range apiContext.Schemas.Schemas() {
|
for _, schema := range apiContext.Schemas.SchemasForVersion(*apiContext.Version) {
|
||||||
if !matches(apiVersions, schema.Version.Path) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !matches(resourceTypes, schema.ID) {
|
if !matches(resourceTypes, schema.ID) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -151,9 +147,14 @@ func streamStore(ctx context.Context, eg *errgroup.Group, apiContext *types.APIC
|
|||||||
opts := parse.QueryOptions(apiContext, schema)
|
opts := parse.QueryOptions(apiContext, schema)
|
||||||
events, err := schema.Store.Watch(apiContext, schema, &opts)
|
events, err := schema.Store.Watch(apiContext, schema, &opts)
|
||||||
if err != nil || events == nil {
|
if err != nil || events == nil {
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("failed on subscribe %s: %v", schema.ID, err)
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logrus.Debugf("watching %s", schema.ID)
|
||||||
|
|
||||||
for e := range events {
|
for e := range events {
|
||||||
result <- e
|
result <- e
|
||||||
}
|
}
|
||||||
|
@ -1,136 +0,0 @@
|
|||||||
package crd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/rancher/norman/store/proxy"
|
|
||||||
"github.com/rancher/norman/types"
|
|
||||||
apiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
|
||||||
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
|
||||||
apiextclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
|
||||||
"k8s.io/client-go/dynamic"
|
|
||||||
"k8s.io/client-go/rest"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Store struct {
|
|
||||||
Factory *Factory
|
|
||||||
k8sClient rest.Interface
|
|
||||||
schemaStores map[string]types.Store
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewCRDStoreFromConfig(config rest.Config) (*Store, error) {
|
|
||||||
dynamicConfig := config
|
|
||||||
if dynamicConfig.NegotiatedSerializer == nil {
|
|
||||||
configConfig := dynamic.ContentConfig()
|
|
||||||
dynamicConfig.NegotiatedSerializer = configConfig.NegotiatedSerializer
|
|
||||||
}
|
|
||||||
|
|
||||||
k8sClient, err := rest.UnversionedRESTClientFor(&dynamicConfig)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
apiExtClient, err := clientset.NewForConfig(&dynamicConfig)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return NewCRDStoreFromClients(apiExtClient, k8sClient), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewCRDStoreFromClients(apiExtClientSet apiextclientset.Interface, k8sClient rest.Interface) *Store {
|
|
||||||
return &Store{
|
|
||||||
Factory: &Factory{
|
|
||||||
APIExtClientSet: apiExtClientSet,
|
|
||||||
},
|
|
||||||
k8sClient: k8sClient,
|
|
||||||
schemaStores: map[string]types.Store{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func key(schema *types.Schema) string {
|
|
||||||
if !strings.EqualFold(schema.BaseType, schema.ID) {
|
|
||||||
return schema.Version.Path + "/" + schema.BaseType
|
|
||||||
}
|
|
||||||
|
|
||||||
return schema.Version.Path + "/" + schema.ID
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Store) ByID(apiContext *types.APIContext, schema *types.Schema, id string) (map[string]interface{}, error) {
|
|
||||||
store, ok := c.schemaStores[key(schema)]
|
|
||||||
if !ok {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return store.ByID(apiContext, schema, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Store) Delete(apiContext *types.APIContext, schema *types.Schema, id string) (map[string]interface{}, error) {
|
|
||||||
store, ok := c.schemaStores[key(schema)]
|
|
||||||
if !ok {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return store.Delete(apiContext, schema, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Store) List(apiContext *types.APIContext, schema *types.Schema, opt *types.QueryOptions) ([]map[string]interface{}, error) {
|
|
||||||
store, ok := c.schemaStores[key(schema)]
|
|
||||||
if !ok {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return store.List(apiContext, schema, opt)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Store) Watch(apiContext *types.APIContext, schema *types.Schema, opt *types.QueryOptions) (chan map[string]interface{}, error) {
|
|
||||||
store, ok := c.schemaStores[key(schema)]
|
|
||||||
if !ok {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return store.Watch(apiContext, schema, opt)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Store) Update(apiContext *types.APIContext, schema *types.Schema, data map[string]interface{}, id string) (map[string]interface{}, error) {
|
|
||||||
store, ok := c.schemaStores[key(schema)]
|
|
||||||
if !ok {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return store.Update(apiContext, schema, data, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Store) Create(apiContext *types.APIContext, schema *types.Schema, data map[string]interface{}) (map[string]interface{}, error) {
|
|
||||||
store, ok := c.schemaStores[key(schema)]
|
|
||||||
if !ok {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return store.Create(apiContext, schema, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Store) AddSchemas(ctx context.Context, schemas ...*types.Schema) error {
|
|
||||||
schemaStatus := map[*types.Schema]*apiext.CustomResourceDefinition{}
|
|
||||||
var allSchemas []*types.Schema
|
|
||||||
|
|
||||||
for _, schema := range schemas {
|
|
||||||
if schema.Store != nil || !schema.CanList(nil) || !strings.EqualFold(schema.BaseType, schema.ID) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
schema.Store = c
|
|
||||||
allSchemas = append(allSchemas, schema)
|
|
||||||
}
|
|
||||||
|
|
||||||
schemaStatus, err := c.Factory.AddSchemas(ctx, allSchemas...)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for schema, crd := range schemaStatus {
|
|
||||||
c.schemaStores[key(schema)] = proxy.NewProxyStore(c.k8sClient,
|
|
||||||
[]string{"apis"},
|
|
||||||
crd.Spec.Group,
|
|
||||||
crd.Spec.Version,
|
|
||||||
crd.Status.AcceptedNames.Kind,
|
|
||||||
crd.Status.AcceptedNames.Plural)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -5,6 +5,9 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/rancher/norman/store/proxy"
|
||||||
"github.com/rancher/norman/types"
|
"github.com/rancher/norman/types"
|
||||||
"github.com/rancher/norman/types/convert"
|
"github.com/rancher/norman/types/convert"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
@ -13,48 +16,58 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
"k8s.io/client-go/dynamic"
|
|
||||||
"k8s.io/client-go/rest"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Factory struct {
|
type Factory struct {
|
||||||
APIExtClientSet clientset.Interface
|
ClientGetter proxy.ClientGetter
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFactoryFromConfig(config rest.Config) (*Factory, error) {
|
func (c *Factory) AssignStores(ctx context.Context, storageContext types.StorageContext, schemas ...*types.Schema) error {
|
||||||
dynamicConfig := config
|
schemaStatus, err := c.CreateCRDs(ctx, storageContext, schemas...)
|
||||||
if dynamicConfig.NegotiatedSerializer == nil {
|
if err != nil {
|
||||||
configConfig := dynamic.ContentConfig()
|
return err
|
||||||
dynamicConfig.NegotiatedSerializer = configConfig.NegotiatedSerializer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
apiExtClient, err := clientset.NewForConfig(&dynamicConfig)
|
for _, schema := range schemas {
|
||||||
|
crd, ok := schemaStatus[schema]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("failed to create create/find CRD for %s", schema.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
schema.Store = proxy.NewProxyStore(c.ClientGetter,
|
||||||
|
storageContext,
|
||||||
|
[]string{"apis"},
|
||||||
|
crd.Spec.Group,
|
||||||
|
crd.Spec.Version,
|
||||||
|
crd.Status.AcceptedNames.Kind,
|
||||||
|
crd.Status.AcceptedNames.Plural)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Factory) CreateCRDs(ctx context.Context, storageContext types.StorageContext, schemas ...*types.Schema) (map[*types.Schema]*apiext.CustomResourceDefinition, error) {
|
||||||
|
schemaStatus := map[*types.Schema]*apiext.CustomResourceDefinition{}
|
||||||
|
|
||||||
|
apiClient, err := c.ClientGetter.APIExtClient(nil, storageContext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Factory{
|
ready, err := c.getReadyCRDs(apiClient)
|
||||||
APIExtClientSet: apiExtClient,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Factory) AddSchemas(ctx context.Context, schemas ...*types.Schema) (map[*types.Schema]*apiext.CustomResourceDefinition, error) {
|
|
||||||
schemaStatus := map[*types.Schema]*apiext.CustomResourceDefinition{}
|
|
||||||
|
|
||||||
ready, err := c.getReadyCRDs()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, schema := range schemas {
|
for _, schema := range schemas {
|
||||||
crd, err := c.createCRD(schema, ready)
|
crd, err := c.createCRD(apiClient, schema, ready)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
schemaStatus[schema] = crd
|
schemaStatus[schema] = crd
|
||||||
}
|
}
|
||||||
|
|
||||||
ready, err = c.getReadyCRDs()
|
ready, err = c.getReadyCRDs(apiClient)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -63,7 +76,7 @@ func (c *Factory) AddSchemas(ctx context.Context, schemas ...*types.Schema) (map
|
|||||||
if readyCrd, ok := ready[crd.Name]; ok {
|
if readyCrd, ok := ready[crd.Name]; ok {
|
||||||
schemaStatus[schema] = readyCrd
|
schemaStatus[schema] = readyCrd
|
||||||
} else {
|
} else {
|
||||||
if err := c.waitCRD(ctx, crd.Name, schema, schemaStatus); err != nil {
|
if err := c.waitCRD(ctx, apiClient, crd.Name, schema, schemaStatus); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -72,7 +85,7 @@ func (c *Factory) AddSchemas(ctx context.Context, schemas ...*types.Schema) (map
|
|||||||
return schemaStatus, nil
|
return schemaStatus, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Factory) waitCRD(ctx context.Context, crdName string, schema *types.Schema, schemaStatus map[*types.Schema]*apiext.CustomResourceDefinition) error {
|
func (c *Factory) waitCRD(ctx context.Context, apiClient clientset.Interface, crdName string, schema *types.Schema, schemaStatus map[*types.Schema]*apiext.CustomResourceDefinition) error {
|
||||||
logrus.Infof("Waiting for CRD %s to become available", crdName)
|
logrus.Infof("Waiting for CRD %s to become available", crdName)
|
||||||
defer logrus.Infof("Done waiting for CRD %s to become available", crdName)
|
defer logrus.Infof("Done waiting for CRD %s to become available", crdName)
|
||||||
|
|
||||||
@ -83,7 +96,7 @@ func (c *Factory) waitCRD(ctx context.Context, crdName string, schema *types.Sch
|
|||||||
}
|
}
|
||||||
first = false
|
first = false
|
||||||
|
|
||||||
crd, err := c.APIExtClientSet.ApiextensionsV1beta1().CustomResourceDefinitions().Get(crdName, metav1.GetOptions{})
|
crd, err := apiClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(crdName, metav1.GetOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
@ -106,7 +119,7 @@ func (c *Factory) waitCRD(ctx context.Context, crdName string, schema *types.Sch
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Factory) createCRD(schema *types.Schema, ready map[string]*apiext.CustomResourceDefinition) (*apiext.CustomResourceDefinition, error) {
|
func (c *Factory) createCRD(apiClient clientset.Interface, schema *types.Schema, ready map[string]*apiext.CustomResourceDefinition) (*apiext.CustomResourceDefinition, error) {
|
||||||
plural := strings.ToLower(schema.PluralName)
|
plural := strings.ToLower(schema.PluralName)
|
||||||
name := strings.ToLower(plural + "." + schema.Version.Group)
|
name := strings.ToLower(plural + "." + schema.Version.Group)
|
||||||
|
|
||||||
@ -136,15 +149,15 @@ func (c *Factory) createCRD(schema *types.Schema, ready map[string]*apiext.Custo
|
|||||||
}
|
}
|
||||||
|
|
||||||
logrus.Infof("Creating CRD %s", name)
|
logrus.Infof("Creating CRD %s", name)
|
||||||
crd, err := c.APIExtClientSet.ApiextensionsV1beta1().CustomResourceDefinitions().Create(crd)
|
crd, err := apiClient.ApiextensionsV1beta1().CustomResourceDefinitions().Create(crd)
|
||||||
if errors.IsAlreadyExists(err) {
|
if errors.IsAlreadyExists(err) {
|
||||||
return crd, nil
|
return crd, nil
|
||||||
}
|
}
|
||||||
return crd, err
|
return crd, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Factory) getReadyCRDs() (map[string]*apiext.CustomResourceDefinition, error) {
|
func (c *Factory) getReadyCRDs(apiClient clientset.Interface) (map[string]*apiext.CustomResourceDefinition, error) {
|
||||||
list, err := c.APIExtClientSet.ApiextensionsV1beta1().CustomResourceDefinitions().List(metav1.ListOptions{})
|
list, err := apiClient.ApiextensionsV1beta1().CustomResourceDefinitions().List(metav1.ListOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,10 @@ import (
|
|||||||
type Store struct {
|
type Store struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *Store) Context() types.StorageContext {
|
||||||
|
return types.DefaultStorageContext
|
||||||
|
}
|
||||||
|
|
||||||
func (e *Store) Delete(apiContext *types.APIContext, schema *types.Schema, id string) (map[string]interface{}, error) {
|
func (e *Store) Delete(apiContext *types.APIContext, schema *types.Schema, id string) (map[string]interface{}, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,8 @@ import (
|
|||||||
"github.com/rancher/norman/types"
|
"github.com/rancher/norman/types"
|
||||||
"github.com/rancher/norman/types/convert"
|
"github.com/rancher/norman/types/convert"
|
||||||
"github.com/rancher/norman/types/values"
|
"github.com/rancher/norman/types/values"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
@ -28,8 +30,57 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type ClientGetter interface {
|
||||||
|
Config(apiContext *types.APIContext, context types.StorageContext) (rest.Config, error)
|
||||||
|
UnversionedClient(apiContext *types.APIContext, context types.StorageContext) (rest.Interface, error)
|
||||||
|
APIExtClient(apiContext *types.APIContext, context types.StorageContext) (clientset.Interface, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type simpleClientGetter struct {
|
||||||
|
restConfig rest.Config
|
||||||
|
client rest.Interface
|
||||||
|
apiExtClient clientset.Interface
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClientGetterFromConfig(config rest.Config) (ClientGetter, error) {
|
||||||
|
dynamicConfig := config
|
||||||
|
if dynamicConfig.NegotiatedSerializer == nil {
|
||||||
|
configConfig := dynamic.ContentConfig()
|
||||||
|
dynamicConfig.NegotiatedSerializer = configConfig.NegotiatedSerializer
|
||||||
|
}
|
||||||
|
|
||||||
|
unversionedClient, err := rest.UnversionedRESTClientFor(&dynamicConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
apiExtClient, err := clientset.NewForConfig(&dynamicConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &simpleClientGetter{
|
||||||
|
restConfig: config,
|
||||||
|
client: unversionedClient,
|
||||||
|
apiExtClient: apiExtClient,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *simpleClientGetter) Config(apiContext *types.APIContext, context types.StorageContext) (rest.Config, error) {
|
||||||
|
return s.restConfig, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *simpleClientGetter) UnversionedClient(apiContext *types.APIContext, context types.StorageContext) (rest.Interface, error) {
|
||||||
|
return s.client, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *simpleClientGetter) APIExtClient(apiContext *types.APIContext, context types.StorageContext) (clientset.Interface, error) {
|
||||||
|
return s.apiExtClient, nil
|
||||||
|
}
|
||||||
|
|
||||||
type Store struct {
|
type Store struct {
|
||||||
k8sClient rest.Interface
|
clientGetter ClientGetter
|
||||||
|
storageContext types.StorageContext
|
||||||
prefix []string
|
prefix []string
|
||||||
group string
|
group string
|
||||||
version string
|
version string
|
||||||
@ -38,11 +89,12 @@ type Store struct {
|
|||||||
authContext map[string]string
|
authContext map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewProxyStore(k8sClient rest.Interface,
|
func NewProxyStore(clientGetter ClientGetter, storageContext types.StorageContext,
|
||||||
prefix []string, group, version, kind, resourcePlural string) types.Store {
|
prefix []string, group, version, kind, resourcePlural string) types.Store {
|
||||||
return &errorStore{
|
return &errorStore{
|
||||||
Store: &Store{
|
Store: &Store{
|
||||||
k8sClient: k8sClient,
|
clientGetter: clientGetter,
|
||||||
|
storageContext: storageContext,
|
||||||
prefix: prefix,
|
prefix: prefix,
|
||||||
group: group,
|
group: group,
|
||||||
version: version,
|
version: version,
|
||||||
@ -56,10 +108,11 @@ func NewProxyStore(k8sClient rest.Interface,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRawProxyStore(k8sClient rest.Interface,
|
func NewRawProxyStore(clientGetter ClientGetter, storageContext types.StorageContext,
|
||||||
prefix []string, group, version, kind, resourcePlural string) *Store {
|
prefix []string, group, version, kind, resourcePlural string) *Store {
|
||||||
return &Store{
|
return &Store{
|
||||||
k8sClient: k8sClient,
|
clientGetter: clientGetter,
|
||||||
|
storageContext: storageContext,
|
||||||
prefix: prefix,
|
prefix: prefix,
|
||||||
group: group,
|
group: group,
|
||||||
version: version,
|
version: version,
|
||||||
@ -83,6 +136,10 @@ func (p *Store) doAuthed(apiContext *types.APIContext, request *rest.Request) re
|
|||||||
return request.Do()
|
return request.Do()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Store) k8sClient(apiContext *types.APIContext) (rest.Interface, error) {
|
||||||
|
return p.clientGetter.UnversionedClient(apiContext, p.storageContext)
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Store) ByID(apiContext *types.APIContext, schema *types.Schema, id string) (map[string]interface{}, error) {
|
func (p *Store) ByID(apiContext *types.APIContext, schema *types.Schema, id string) (map[string]interface{}, error) {
|
||||||
_, result, err := p.byID(apiContext, schema, id)
|
_, result, err := p.byID(apiContext, schema, id)
|
||||||
return result, err
|
return result, err
|
||||||
@ -91,19 +148,33 @@ func (p *Store) ByID(apiContext *types.APIContext, schema *types.Schema, id stri
|
|||||||
func (p *Store) byID(apiContext *types.APIContext, schema *types.Schema, id string) (string, map[string]interface{}, error) {
|
func (p *Store) byID(apiContext *types.APIContext, schema *types.Schema, id string) (string, map[string]interface{}, error) {
|
||||||
namespace, id := splitID(id)
|
namespace, id := splitID(id)
|
||||||
|
|
||||||
req := p.common(namespace, p.k8sClient.Get()).
|
k8sClient, err := p.k8sClient(apiContext)
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req := p.common(namespace, k8sClient.Get()).
|
||||||
Name(id)
|
Name(id)
|
||||||
|
|
||||||
return p.singleResult(apiContext, schema, req)
|
return p.singleResult(apiContext, schema, req)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Store) Context() types.StorageContext {
|
||||||
|
return p.storageContext
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Store) List(apiContext *types.APIContext, schema *types.Schema, opt *types.QueryOptions) ([]map[string]interface{}, error) {
|
func (p *Store) List(apiContext *types.APIContext, schema *types.Schema, opt *types.QueryOptions) ([]map[string]interface{}, error) {
|
||||||
namespace := getNamespace(apiContext, opt)
|
namespace := getNamespace(apiContext, opt)
|
||||||
|
|
||||||
req := p.common(namespace, p.k8sClient.Get())
|
k8sClient, err := p.k8sClient(apiContext)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req := p.common(namespace, k8sClient.Get())
|
||||||
|
|
||||||
resultList := &unstructured.UnstructuredList{}
|
resultList := &unstructured.UnstructuredList{}
|
||||||
err := req.Do().Into(resultList)
|
err = req.Do().Into(resultList)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -120,8 +191,13 @@ func (p *Store) List(apiContext *types.APIContext, schema *types.Schema, opt *ty
|
|||||||
func (p *Store) Watch(apiContext *types.APIContext, schema *types.Schema, opt *types.QueryOptions) (chan map[string]interface{}, error) {
|
func (p *Store) Watch(apiContext *types.APIContext, schema *types.Schema, opt *types.QueryOptions) (chan map[string]interface{}, error) {
|
||||||
namespace := getNamespace(apiContext, opt)
|
namespace := getNamespace(apiContext, opt)
|
||||||
|
|
||||||
|
k8sClient, err := p.k8sClient(apiContext)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
timeout := int64(60 * 60)
|
timeout := int64(60 * 60)
|
||||||
req := p.common(namespace, p.k8sClient.Get())
|
req := p.common(namespace, k8sClient.Get())
|
||||||
req.VersionedParams(&metav1.ListOptions{
|
req.VersionedParams(&metav1.ListOptions{
|
||||||
Watch: true,
|
Watch: true,
|
||||||
TimeoutSeconds: &timeout,
|
TimeoutSeconds: &timeout,
|
||||||
@ -139,6 +215,7 @@ func (p *Store) Watch(apiContext *types.APIContext, schema *types.Schema, opt *t
|
|||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
<-apiContext.Request.Context().Done()
|
<-apiContext.Request.Context().Done()
|
||||||
|
logrus.Debugf("stopping watcher for %s", schema.ID)
|
||||||
watcher.Stop()
|
watcher.Stop()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@ -152,6 +229,7 @@ func (p *Store) Watch(apiContext *types.APIContext, schema *types.Schema, opt *t
|
|||||||
}
|
}
|
||||||
result <- apiContext.AccessControl.Filter(apiContext, data.Object, p.authContext)
|
result <- apiContext.AccessControl.Filter(apiContext, data.Object, p.authContext)
|
||||||
}
|
}
|
||||||
|
logrus.Debugf("closing watcher for %s", schema.ID)
|
||||||
close(result)
|
close(result)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@ -196,7 +274,12 @@ func (p *Store) Create(apiContext *types.APIContext, schema *types.Schema, data
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
req := p.common(namespace, p.k8sClient.Post()).
|
k8sClient, err := p.k8sClient(apiContext)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req := p.common(namespace, k8sClient.Post()).
|
||||||
Body(&unstructured.Unstructured{
|
Body(&unstructured.Unstructured{
|
||||||
Object: data,
|
Object: data,
|
||||||
})
|
})
|
||||||
@ -219,8 +302,13 @@ func (p *Store) toInternal(mapper types.Mapper, data map[string]interface{}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *Store) Update(apiContext *types.APIContext, schema *types.Schema, data map[string]interface{}, id string) (map[string]interface{}, error) {
|
func (p *Store) Update(apiContext *types.APIContext, schema *types.Schema, data map[string]interface{}, id string) (map[string]interface{}, error) {
|
||||||
|
k8sClient, err := p.k8sClient(apiContext)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
namespace, id := splitID(id)
|
namespace, id := splitID(id)
|
||||||
req := p.common(namespace, p.k8sClient.Get()).
|
req := p.common(namespace, k8sClient.Get()).
|
||||||
Name(id)
|
Name(id)
|
||||||
|
|
||||||
resourceVersion, existing, err := p.singleResultRaw(apiContext, schema, req)
|
resourceVersion, existing, err := p.singleResultRaw(apiContext, schema, req)
|
||||||
@ -235,7 +323,7 @@ func (p *Store) Update(apiContext *types.APIContext, schema *types.Schema, data
|
|||||||
values.PutValue(existing, namespace, "metadata", "namespace")
|
values.PutValue(existing, namespace, "metadata", "namespace")
|
||||||
values.PutValue(existing, id, "metadata", "name")
|
values.PutValue(existing, id, "metadata", "name")
|
||||||
|
|
||||||
req = p.common(namespace, p.k8sClient.Put()).
|
req = p.common(namespace, k8sClient.Put()).
|
||||||
Body(&unstructured.Unstructured{
|
Body(&unstructured.Unstructured{
|
||||||
Object: existing,
|
Object: existing,
|
||||||
}).
|
}).
|
||||||
@ -246,16 +334,21 @@ func (p *Store) Update(apiContext *types.APIContext, schema *types.Schema, data
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *Store) Delete(apiContext *types.APIContext, schema *types.Schema, id string) (map[string]interface{}, error) {
|
func (p *Store) Delete(apiContext *types.APIContext, schema *types.Schema, id string) (map[string]interface{}, error) {
|
||||||
|
k8sClient, err := p.k8sClient(apiContext)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
namespace, id := splitID(id)
|
namespace, id := splitID(id)
|
||||||
|
|
||||||
prop := metav1.DeletePropagationForeground
|
prop := metav1.DeletePropagationForeground
|
||||||
req := p.common(namespace, p.k8sClient.Delete()).
|
req := p.common(namespace, k8sClient.Delete()).
|
||||||
Body(&metav1.DeleteOptions{
|
Body(&metav1.DeleteOptions{
|
||||||
PropagationPolicy: &prop,
|
PropagationPolicy: &prop,
|
||||||
}).
|
}).
|
||||||
Name(id)
|
Name(id)
|
||||||
|
|
||||||
err := p.doAuthed(apiContext, req).Error()
|
err = p.doAuthed(apiContext, req).Error()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -18,29 +18,33 @@ type Store struct {
|
|||||||
StreamTransformer StreamTransformerFunc
|
StreamTransformer StreamTransformerFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Store) ByID(apiContext *types.APIContext, schema *types.Schema, id string) (map[string]interface{}, error) {
|
func (s *Store) Context() types.StorageContext {
|
||||||
data, err := t.Store.ByID(apiContext, schema, id)
|
return s.Store.Context()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) ByID(apiContext *types.APIContext, schema *types.Schema, id string) (map[string]interface{}, error) {
|
||||||
|
data, err := s.Store.ByID(apiContext, schema, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if t.Transformer == nil {
|
if s.Transformer == nil {
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
return t.Transformer(apiContext, data)
|
return s.Transformer(apiContext, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Store) Watch(apiContext *types.APIContext, schema *types.Schema, opt *types.QueryOptions) (chan map[string]interface{}, error) {
|
func (s *Store) Watch(apiContext *types.APIContext, schema *types.Schema, opt *types.QueryOptions) (chan map[string]interface{}, error) {
|
||||||
c, err := t.Store.Watch(apiContext, schema, opt)
|
c, err := s.Store.Watch(apiContext, schema, opt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if t.StreamTransformer != nil {
|
if s.StreamTransformer != nil {
|
||||||
return t.StreamTransformer(apiContext, c)
|
return s.StreamTransformer(apiContext, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
return convert.Chan(c, func(data map[string]interface{}) map[string]interface{} {
|
return convert.Chan(c, func(data map[string]interface{}) map[string]interface{} {
|
||||||
item, err := t.Transformer(apiContext, data)
|
item, err := s.Transformer(apiContext, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -48,23 +52,23 @@ func (t *Store) Watch(apiContext *types.APIContext, schema *types.Schema, opt *t
|
|||||||
}), nil
|
}), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Store) List(apiContext *types.APIContext, schema *types.Schema, opt *types.QueryOptions) ([]map[string]interface{}, error) {
|
func (s *Store) List(apiContext *types.APIContext, schema *types.Schema, opt *types.QueryOptions) ([]map[string]interface{}, error) {
|
||||||
data, err := t.Store.List(apiContext, schema, opt)
|
data, err := s.Store.List(apiContext, schema, opt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if t.ListTransformer != nil {
|
if s.ListTransformer != nil {
|
||||||
return t.ListTransformer(apiContext, data)
|
return s.ListTransformer(apiContext, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
if t.Transformer == nil {
|
if s.Transformer == nil {
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var result []map[string]interface{}
|
var result []map[string]interface{}
|
||||||
for _, item := range data {
|
for _, item := range data {
|
||||||
item, err := t.Transformer(apiContext, item)
|
item, err := s.Transformer(apiContext, item)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -76,28 +80,28 @@ func (t *Store) List(apiContext *types.APIContext, schema *types.Schema, opt *ty
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Store) Create(apiContext *types.APIContext, schema *types.Schema, data map[string]interface{}) (map[string]interface{}, error) {
|
func (s *Store) Create(apiContext *types.APIContext, schema *types.Schema, data map[string]interface{}) (map[string]interface{}, error) {
|
||||||
data, err := t.Store.Create(apiContext, schema, data)
|
data, err := s.Store.Create(apiContext, schema, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if t.Transformer == nil {
|
if s.Transformer == nil {
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
return t.Transformer(apiContext, data)
|
return s.Transformer(apiContext, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Store) Update(apiContext *types.APIContext, schema *types.Schema, data map[string]interface{}, id string) (map[string]interface{}, error) {
|
func (s *Store) Update(apiContext *types.APIContext, schema *types.Schema, data map[string]interface{}, id string) (map[string]interface{}, error) {
|
||||||
data, err := t.Store.Update(apiContext, schema, data, id)
|
data, err := s.Store.Update(apiContext, schema, data, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if t.Transformer == nil {
|
if s.Transformer == nil {
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
return t.Transformer(apiContext, data)
|
return s.Transformer(apiContext, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Store) Delete(apiContext *types.APIContext, schema *types.Schema, id string) (map[string]interface{}, error) {
|
func (s *Store) Delete(apiContext *types.APIContext, schema *types.Schema, id string) (map[string]interface{}, error) {
|
||||||
return t.Store.Delete(apiContext, schema, id)
|
return s.Store.Delete(apiContext, schema, id)
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,10 @@ type StoreWrapper struct {
|
|||||||
store types.Store
|
store types.Store
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *StoreWrapper) Context() types.StorageContext {
|
||||||
|
return s.store.Context()
|
||||||
|
}
|
||||||
|
|
||||||
func (s *StoreWrapper) ByID(apiContext *types.APIContext, schema *types.Schema, id string) (map[string]interface{}, error) {
|
func (s *StoreWrapper) ByID(apiContext *types.APIContext, schema *types.Schema, id string) (map[string]interface{}, error) {
|
||||||
data, err := s.store.ByID(apiContext, schema, id)
|
data, err := s.store.ByID(apiContext, schema, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -131,10 +131,6 @@ func (s *Schemas) MustCustomizeType(version *APIVersion, obj interface{}, f func
|
|||||||
|
|
||||||
f(schema)
|
f(schema)
|
||||||
|
|
||||||
if schema.SubContext != "" {
|
|
||||||
s.schemasBySubContext[schema.SubContext] = schema
|
|
||||||
}
|
|
||||||
|
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +31,6 @@ type Schemas struct {
|
|||||||
sync.Mutex
|
sync.Mutex
|
||||||
typeNames map[reflect.Type]string
|
typeNames map[reflect.Type]string
|
||||||
schemasByPath map[string]map[string]*Schema
|
schemasByPath map[string]map[string]*Schema
|
||||||
schemasBySubContext map[string]*Schema
|
|
||||||
mappers map[string]map[string][]Mapper
|
mappers map[string]map[string][]Mapper
|
||||||
references map[string][]BackReference
|
references map[string][]BackReference
|
||||||
embedded map[string]*Schema
|
embedded map[string]*Schema
|
||||||
@ -47,7 +46,6 @@ func NewSchemas() *Schemas {
|
|||||||
return &Schemas{
|
return &Schemas{
|
||||||
typeNames: map[reflect.Type]string{},
|
typeNames: map[reflect.Type]string{},
|
||||||
schemasByPath: map[string]map[string]*Schema{},
|
schemasByPath: map[string]map[string]*Schema{},
|
||||||
schemasBySubContext: map[string]*Schema{},
|
|
||||||
mappers: map[string]map[string][]Mapper{},
|
mappers: map[string]map[string][]Mapper{},
|
||||||
references: map[string][]BackReference{},
|
references: map[string][]BackReference{},
|
||||||
embedded: map[string]*Schema{},
|
embedded: map[string]*Schema{},
|
||||||
@ -62,12 +60,6 @@ func (s *Schemas) Err() error {
|
|||||||
return NewErrors(s.errors)
|
return NewErrors(s.errors)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Schemas) SubContext(subContext string) *Schema {
|
|
||||||
s.Lock()
|
|
||||||
defer s.Unlock()
|
|
||||||
return s.schemasBySubContext[subContext]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Schemas) AddSchemas(schema *Schemas) *Schemas {
|
func (s *Schemas) AddSchemas(schema *Schemas) *Schemas {
|
||||||
for _, schema := range schema.Schemas() {
|
for _, schema := range schema.Schemas() {
|
||||||
s.AddSchema(*schema)
|
s.AddSchema(*schema)
|
||||||
@ -86,8 +78,6 @@ func (s *Schemas) doRemoveSchema(schema Schema) *Schemas {
|
|||||||
|
|
||||||
s.removeReferences(&schema)
|
s.removeReferences(&schema)
|
||||||
|
|
||||||
delete(s.schemasBySubContext, schema.SubContext)
|
|
||||||
|
|
||||||
if schema.Embed {
|
if schema.Embed {
|
||||||
s.removeEmbed(&schema)
|
s.removeEmbed(&schema)
|
||||||
}
|
}
|
||||||
@ -145,10 +135,6 @@ func (s *Schemas) doAddSchema(schema Schema) *Schemas {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if schema.SubContext != "" {
|
|
||||||
s.schemasBySubContext[schema.SubContext] = &schema
|
|
||||||
}
|
|
||||||
|
|
||||||
if schema.Embed {
|
if schema.Embed {
|
||||||
s.embed(&schema)
|
s.embed(&schema)
|
||||||
}
|
}
|
||||||
@ -343,6 +329,16 @@ func (s *Schemas) doSchema(version *APIVersion, name string, lock bool) *Schema
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Schemas) SubContextVersionForSchema(schema *Schema) *APIVersion {
|
||||||
|
fullName := fmt.Sprintf("%s/schemas/%s", schema.Version.Path, schema.ID)
|
||||||
|
for _, version := range s.Versions() {
|
||||||
|
if version.SubContextSchema == fullName {
|
||||||
|
return &version
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type multiErrors struct {
|
type multiErrors struct {
|
||||||
errors []error
|
errors []error
|
||||||
}
|
}
|
||||||
|
@ -186,7 +186,12 @@ type URLBuilder interface {
|
|||||||
ResourceLinkByID(schema *Schema, id string) string
|
ResourceLinkByID(schema *Schema, id string) string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type StorageContext string
|
||||||
|
|
||||||
|
var DefaultStorageContext StorageContext
|
||||||
|
|
||||||
type Store interface {
|
type Store interface {
|
||||||
|
Context() StorageContext
|
||||||
ByID(apiContext *APIContext, schema *Schema, id string) (map[string]interface{}, error)
|
ByID(apiContext *APIContext, schema *Schema, id string) (map[string]interface{}, error)
|
||||||
List(apiContext *APIContext, schema *Schema, opt *QueryOptions) ([]map[string]interface{}, error)
|
List(apiContext *APIContext, schema *Schema, opt *QueryOptions) ([]map[string]interface{}, error)
|
||||||
Create(apiContext *APIContext, schema *Schema, data map[string]interface{}) (map[string]interface{}, error)
|
Create(apiContext *APIContext, schema *Schema, data map[string]interface{}) (map[string]interface{}, error)
|
||||||
|
@ -72,7 +72,8 @@ type APIVersion struct {
|
|||||||
Group string `json:"group,omitempty"`
|
Group string `json:"group,omitempty"`
|
||||||
Version string `json:"version,omitempty"`
|
Version string `json:"version,omitempty"`
|
||||||
Path string `json:"path,omitempty"`
|
Path string `json:"path,omitempty"`
|
||||||
SubContexts map[string]bool `json:"subContext,omitempty"`
|
SubContext bool `json:"subContext,omitempty"`
|
||||||
|
SubContextSchema string `json:"filterField,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Namespaced struct{}
|
type Namespaced struct{}
|
||||||
@ -90,7 +91,6 @@ type Schema struct {
|
|||||||
PkgName string `json:"-"`
|
PkgName string `json:"-"`
|
||||||
Type string `json:"type,omitempty"`
|
Type string `json:"type,omitempty"`
|
||||||
BaseType string `json:"baseType,omitempty"`
|
BaseType string `json:"baseType,omitempty"`
|
||||||
SubContext string `json:"-,omitempty"`
|
|
||||||
Links map[string]string `json:"links"`
|
Links map[string]string `json:"links"`
|
||||||
Version APIVersion `json:"version"`
|
Version APIVersion `json:"version"`
|
||||||
PluralName string `json:"pluralName,omitempty"`
|
PluralName string `json:"pluralName,omitempty"`
|
||||||
|
@ -122,7 +122,7 @@ func (u *urlBuilder) Collection(schema *types.Schema, versionOverride *types.API
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (u *urlBuilder) SubContextCollection(subContext *types.Schema, contextName string, schema *types.Schema) string {
|
func (u *urlBuilder) SubContextCollection(subContext *types.Schema, contextName string, schema *types.Schema) string {
|
||||||
return u.constructBasicURL(schema.Version, subContext.SubContext, contextName, u.getPluralName(schema))
|
return u.constructBasicURL(subContext.Version, subContext.PluralName, contextName, u.getPluralName(schema))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *urlBuilder) Version(version types.APIVersion) string {
|
func (u *urlBuilder) Version(version types.APIVersion) string {
|
||||||
|
Loading…
Reference in New Issue
Block a user