Skip to content

Commit

Permalink
Major refactor of subcontexts
Browse files Browse the repository at this point in the history
  • Loading branch information
ibuildthecloud committed Feb 9, 2018
1 parent b62e043 commit 51346b1
Show file tree
Hide file tree
Showing 17 changed files with 311 additions and 292 deletions.
11 changes: 10 additions & 1 deletion api/writer/json.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,17 +147,26 @@ func (j *JSONResponseWriter) addLinks(b *builder.Builder, schema *types.Schema,
rawResource.Links["remove"] = self
}

subContextVersion := context.Schemas.SubContextVersionForSchema(schema)
for _, backRef := range context.Schemas.References(schema) {
if !backRef.Schema.CanList(context) {
continue
}

if schema.SubContext == "" {
if subContextVersion == nil {
rawResource.Links[backRef.Schema.PluralName] = context.URLBuilder.FilterLink(backRef.Schema, backRef.FieldName, rawResource.ID)
} else {
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 {
Expand Down
12 changes: 10 additions & 2 deletions example/main.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package main

import (
"context"
"fmt"
"net/http"
"os"

"github.com/rancher/norman/api"
"github.com/rancher/norman/store/crd"
"github.com/rancher/norman/store/proxy"
"github.com/rancher/norman/types"
"github.com/rancher/norman/types/factory"
"k8s.io/client-go/tools/clientcmd"
Expand Down Expand Up @@ -39,13 +41,19 @@ func main() {
panic(err)
}

store, err := crd.NewCRDStoreFromConfig(*kubeConfig)
client, err := proxy.NewClientGetterFromConfig(*kubeConfig)
if err != nil {
panic(err)
}

crdFactory := crd.Factory{
ClientGetter: client,
}

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()
Expand Down
1 change: 1 addition & 0 deletions httperror/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ var (
ActionNotAvailable = ErrorCode{"ActionNotAvailable", 404}
InvalidState = ErrorCode{"InvalidState", 422}
ServerError = ErrorCode{"ServerError", 500}
ClusterUnavailable = ErrorCode{"ClusterUnavailable", 503}
PermissionDenied = ErrorCode{"PermissionDenied", 403}

MethodNotAllowed = ErrorCode{"MethodNotAllow", 405}
Expand Down
105 changes: 63 additions & 42 deletions parse/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ import (
"regexp"
"strings"

"sort"

"github.com/rancher/norman/api/builtin"
"github.com/rancher/norman/httperror"
"github.com/rancher/norman/types"
"github.com/rancher/norman/urlbuilder"
)
Expand Down Expand Up @@ -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) {
result := ParsedURL{}

version := Version(schemas, url.Path)
if version == nil {
return result, nil
}

path := url.Path
path = multiSlashRegexp.ReplaceAllString(path, "/")
version, prefix, parts, subContext := parseVersionAndSubContext(schemas, path)

parts := strings.SplitN(path[len(version.Path):], "/", 4)
prefix, parts, subContext := parseSubContext(schemas, version, parts)
if version == nil {
return result, nil
}

result.Version = version.Path
result.SubContext = subContext
result.SubContextPrefix = prefix
result.Action, result.Method = parseAction(url)
result.Query = url.Query()

result.Type = safeIndex(parts, 1)
result.ID = safeIndex(parts, 2)
result.Link = safeIndex(parts, 3)
result.Type = safeIndex(parts, 0)
result.ID = safeIndex(parts, 1)
result.Link = safeIndex(parts, 2)

return result, nil
}
Expand Down Expand Up @@ -121,11 +121,14 @@ func Parse(rw http.ResponseWriter, req *http.Request, schemas *types.Schemas, ur
}

if result.Schema == nil {
if result.Type != "" {
err = httperror.NewAPIError(httperror.NotFound, "failed to find schema "+result.Type)
}
result.Method = http.MethodGet
result.Type = "apiRoot"
result.Schema = result.Schemas.Schema(&builtin.Version, "apiRoot")
result.ID = result.Version.Path
return result, nil
return result, err
}

result.Type = result.Schema.ID
Expand All @@ -137,29 +140,61 @@ func Parse(rw http.ResponseWriter, req *http.Request, schemas *types.Schemas, ur
return result, nil
}

func parseSubContext(schemas *types.Schemas, version *types.APIVersion, parts []string) (string, []string, map[string]string) {
subContext := ""
result := map[string]string{}
func versionsForPath(schemas *types.Schemas, path string) []types.APIVersion {
var matchedVersion []types.APIVersion
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] != "" {
resourceType := parts[1]
resourceID := parts[2]
func parseVersionAndSubContext(schemas *types.Schemas, path string) (*types.APIVersion, string, []string, map[string]string) {
versions := versionsForPath(schemas, path)
if len(versions) == 0 {
return nil, "", nil, nil
}
version := &versions[0]

if !version.SubContexts[resourceType] {
break
}
if strings.HasSuffix(path, "/") {
path = path[:len(path)-1]
}

subSchema := schemas.Schema(version, parts[3])
if subSchema == nil {
break
}
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

result[resourceType] = resourceID
subContext = subContext + "/" + resourceType + "/" + resourceID
parts = append(parts[:1], parts[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
}
return &version, "", paths[1:], attrs
}
}

return subContext, parts, result
return version, "/" + paths[0], paths[1:], attrs
}

func DefaultResolver(typeName string, apiContext *types.APIContext) error {
Expand Down Expand Up @@ -223,20 +258,6 @@ func parseAction(url *url.URL) (string, string) {
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) {
req.ParseMultipartForm(maxFormSize)
if req.MultipartForm != nil {
Expand Down
4 changes: 2 additions & 2 deletions parse/subcontext.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ func (d *DefaultSubContextAttributeProvider) Create(apiContext *types.APIContext
func (d *DefaultSubContextAttributeProvider) create(apiContext *types.APIContext, schema *types.Schema) map[string]string {
result := map[string]string{}

for subContext, value := range apiContext.SubContext {
subContextSchema := apiContext.Schemas.SubContext(subContext)
for subContextSchemaID, value := range apiContext.SubContext {
subContextSchema := apiContext.Schemas.Schema(nil, subContextSchemaID)
if subContextSchema == nil {
continue
}
Expand Down
11 changes: 6 additions & 5 deletions pkg/subscribe/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,10 @@ func Handler(apiContext *types.APIContext, _ types.RequestHandler) error {
}

func getMatchingSchemas(apiContext *types.APIContext) []*types.Schema {
apiVersions := apiContext.Request.URL.Query()["apiVersions"]
resourceTypes := apiContext.Request.URL.Query()["resourceTypes"]

var schemas []*types.Schema
for _, schema := range apiContext.Schemas.Schemas() {
if !matches(apiVersions, schema.Version.Path) {
continue
}
for _, schema := range apiContext.Schemas.SchemasForVersion(*apiContext.Version) {
if !matches(resourceTypes, schema.ID) {
continue
}
Expand Down Expand Up @@ -151,9 +147,14 @@ func streamStore(ctx context.Context, eg *errgroup.Group, apiContext *types.APIC
opts := parse.QueryOptions(apiContext, schema)
events, err := schema.Store.Watch(apiContext, schema, &opts)
if err != nil || events == nil {
if err != nil {
logrus.Errorf("failed on subscribe %s: %v", schema.ID, err)
}
return err
}

logrus.Debugf("watching %s", schema.ID)

for e := range events {
result <- e
}
Expand Down
Loading

0 comments on commit 51346b1

Please sign in to comment.