From 6b05d03f468e4573ac0b2a4f187d9e11bf0b3bd5 Mon Sep 17 00:00:00 2001 From: Darren Shepherd Date: Tue, 14 Nov 2017 21:33:57 -0700 Subject: [PATCH] Random fixes --- generator/generator.go | 4 ++ store/crd/crd_store.go | 9 ++-- types/mapping/mapper/embed.go | 5 +++ types/mapping/mapper/move.go | 62 +++++++++++++++++++++------- types/mapping/mapper/object.go | 8 ++-- types/mapping/mapper/read_only.go | 30 ++++++++++++++ types/mapping/mapper/slice_to_map.go | 8 +++- types/reflection.go | 22 +++++++--- types/schemas.go | 6 +++ 9 files changed, 123 insertions(+), 31 deletions(-) create mode 100644 types/mapping/mapper/read_only.go diff --git a/generator/generator.go b/generator/generator.go index 378891fe..0f599f7d 100644 --- a/generator/generator.go +++ b/generator/generator.go @@ -55,6 +55,10 @@ func getTypeString(nullable bool, typeName string, schema *types.Schema, schemas name = "float64" case "int": name = "int64" + case "multiline": + return "string" + case "masked": + return "string" case "password": return "string" case "date": diff --git a/store/crd/crd_store.go b/store/crd/crd_store.go index 58bcb342..52d89e53 100644 --- a/store/crd/crd_store.go +++ b/store/crd/crd_store.go @@ -221,8 +221,6 @@ func (c *Store) Create(apiContext *types.APIContext, schema *types.Schema, data namespace, _ := data["namespace"].(string) - //mapping.Metadata.Forward(data) - data["apiVersion"] = crd.Spec.Group + "/" + crd.Spec.Version data["kind"] = crd.Status.AcceptedNames.Kind @@ -230,10 +228,10 @@ func (c *Store) Create(apiContext *types.APIContext, schema *types.Schema, data req := c.k8sClient.Post(). Prefix("apis", crd.Spec.Group, crd.Spec.Version). + Resource(crd.Status.AcceptedNames.Plural). Body(&unstructured.Unstructured{ Object: data, - }). - Resource(crd.Status.AcceptedNames.Plural) + }) if crd.Spec.Scope == apiext.NamespaceScoped { req.Namespace(namespace) @@ -352,7 +350,8 @@ func (c *Store) createCRD(schema *types.Schema, ready map[string]apiext.CustomRe Spec: apiext.CustomResourceDefinitionSpec{ Group: schema.Version.Group, Version: schema.Version.Version, - Scope: getScope(schema), + //Scope: getScope(schema), + Scope: apiext.ClusterScoped, Names: apiext.CustomResourceDefinitionNames{ Plural: plural, Kind: capitalize(schema.ID), diff --git a/types/mapping/mapper/embed.go b/types/mapping/mapper/embed.go index 0db2b9ae..ea5c3e80 100644 --- a/types/mapping/mapper/embed.go +++ b/types/mapping/mapper/embed.go @@ -8,6 +8,7 @@ import ( type Embed struct { Field string + ReadOnly bool ignoreOverride bool embeddedFields []string } @@ -56,6 +57,10 @@ func (e *Embed) ModifySchema(schema *types.Schema, schemas *types.Schemas) error e.Field, schema.ID, name) } } + if e.ReadOnly { + field.Create = false + field.Update = false + } schema.ResourceFields[name] = field e.embeddedFields = append(e.embeddedFields, name) } diff --git a/types/mapping/mapper/move.go b/types/mapping/mapper/move.go index c47d29eb..2016b82e 100644 --- a/types/mapping/mapper/move.go +++ b/types/mapping/mapper/move.go @@ -3,6 +3,8 @@ package mapper import ( "fmt" + "strings" + "github.com/rancher/norman/types" "github.com/rancher/norman/types/convert" ) @@ -12,39 +14,69 @@ type Move struct { } func (m Move) FromInternal(data map[string]interface{}) { - if v, ok := data[m.From]; ok { - delete(data, m.From) - data[m.To] = v + if v, ok := RemoveValue(data, strings.Split(m.From, "/")...); ok { + PutValue(data, v, strings.Split(m.To, "/")...) } } func (m Move) ToInternal(data map[string]interface{}) { - if v, ok := data[m.To]; ok { - delete(data, m.To) - data[m.From] = v + if v, ok := RemoveValue(data, strings.Split(m.To, "/")...); ok { + PutValue(data, v, strings.Split(m.From, "/")...) } } -func (m Move) ModifySchema(schema *types.Schema, schemas *types.Schemas) error { - internalSchema, err := getInternal(schema) +func (m Move) ModifySchema(s *types.Schema, schemas *types.Schemas) error { + internalSchema, err := getInternal(s) if err != nil { return err } - field, ok := internalSchema.ResourceFields[m.From] + _, _, fromInternalField, ok, err := getField(internalSchema, schemas, m.From) + if err != nil { + return err + } if !ok { return fmt.Errorf("missing field %s on internal schema %s", m.From, internalSchema.ID) } - _, ok = schema.ResourceFields[m.To] - if ok { - return fmt.Errorf("field %s already exists on schema %s", m.From, internalSchema.ID) + fromSchema, _, _, _, err := getField(s, schemas, m.From) + if err != nil { + return err } - delete(schema.ResourceFields, m.From) + toSchema, toFieldName, toField, ok, err := getField(s, schemas, m.To) + if err != nil { + return err + } + _, ok = toSchema.ResourceFields[toFieldName] + if ok && !strings.Contains(m.To, "/") { + return fmt.Errorf("field %s already exists on schema %s", m.To, s.ID) + } - field.CodeName = convert.Capitalize(m.To) - schema.ResourceFields[m.To] = field + delete(fromSchema.ResourceFields, m.From) + + toField.CodeName = convert.Capitalize(toFieldName) + toSchema.ResourceFields[toFieldName] = fromInternalField return nil } + +func getField(schema *types.Schema, schemas *types.Schemas, target string) (*types.Schema, string, types.Field, bool, error) { + parts := strings.Split(target, "/") + for i, part := range parts { + if i == len(parts)-1 { + continue + } + + subSchema := schemas.Schema(&schema.Version, schema.ResourceFields[part].Type) + if subSchema == nil { + return nil, "", types.Field{}, false, fmt.Errorf("failed to find field or schema for %s on %s", part, schema.ID) + } + + schema = subSchema + } + + name := parts[len(parts)-1] + f, ok := schema.ResourceFields[name] + return schema, name, f, ok, nil +} diff --git a/types/mapping/mapper/object.go b/types/mapping/mapper/object.go index 0c5f8f71..afb0a366 100644 --- a/types/mapping/mapper/object.go +++ b/types/mapping/mapper/object.go @@ -6,14 +6,14 @@ type Object struct { types.TypeMapper } -func NewObject(mappers []types.Mapper) *Object { +func NewObject(mappers ...types.Mapper) *Object { return &Object{ TypeMapper: types.TypeMapper{ - Mappers: append(mappers, - &Drop{"status"}, + Mappers: append([]types.Mapper{ &Embed{Field: "metadata"}, &Embed{Field: "spec"}, - ), + &ReadOnly{"status"}, + }, mappers...), }, } } diff --git a/types/mapping/mapper/read_only.go b/types/mapping/mapper/read_only.go new file mode 100644 index 00000000..8e5056e6 --- /dev/null +++ b/types/mapping/mapper/read_only.go @@ -0,0 +1,30 @@ +package mapper + +import ( + "fmt" + + "github.com/rancher/norman/types" +) + +type ReadOnly struct { + Field string +} + +func (r *ReadOnly) FromInternal(data map[string]interface{}) { +} + +func (r *ReadOnly) ToInternal(data map[string]interface{}) { +} + +func (r *ReadOnly) ModifySchema(schema *types.Schema, schemas *types.Schemas) error { + field, ok := schema.ResourceFields[r.Field] + if !ok { + return fmt.Errorf("failed to find field %s on schema %s", r.Field, schema.ID) + } + + field.Create = false + field.Update = false + schema.ResourceFields[r.Field] = field + + return nil +} diff --git a/types/mapping/mapper/slice_to_map.go b/types/mapping/mapper/slice_to_map.go index c80b827c..1b94449c 100644 --- a/types/mapping/mapper/slice_to_map.go +++ b/types/mapping/mapper/slice_to_map.go @@ -24,7 +24,9 @@ func (s SliceToMap) FromInternal(data map[string]interface{}) { } } - data[s.Field] = result + if len(result) > 0 { + data[s.Field] = result + } } func (s SliceToMap) ToInternal(data map[string]interface{}) { @@ -39,7 +41,9 @@ func (s SliceToMap) ToInternal(data map[string]interface{}) { } } - data[s.Field] = result + if len(result) > 0 { + data[s.Field] = result + } } func (s SliceToMap) ModifySchema(schema *types.Schema, schemas *types.Schemas) error { diff --git a/types/reflection.go b/types/reflection.go index 72b47a68..e0e5e968 100644 --- a/types/reflection.go +++ b/types/reflection.go @@ -13,6 +13,7 @@ import ( var ( resourceType = reflect.TypeOf(Resource{}) + typeType = reflect.TypeOf(metav1.TypeMeta{}) metaType = reflect.TypeOf(metav1.ObjectMeta{}) blacklistNames = map[string]bool{ "links": true, @@ -114,6 +115,9 @@ func (s *Schemas) readFields(schema *Schema, t reflect.Type) error { schema.ResourceMethods = []string{"GET", "PUT", "DELETE"} } + hasType := false + hasMeta := false + for i := 0; i < t.NumField(); i++ { field := t.Field(i) @@ -128,6 +132,14 @@ func (s *Schemas) readFields(schema *Schema, t reflect.Type) error { continue } + if field.Anonymous && jsonName == "" && field.Type == typeType { + hasType = true + } + + if field.Anonymous && jsonName == "metadata" && field.Type == metaType { + hasMeta = true + } + if field.Anonymous && jsonName == "" { t := field.Type if t.Kind() == reflect.Ptr { @@ -177,15 +189,15 @@ func (s *Schemas) readFields(schema *Schema, t reflect.Type) error { schemaField.Type = inferedType } - if field.Type == metaType { - schema.CollectionMethods = []string{"GET", "POST"} - schema.ResourceMethods = []string{"GET", "PUT", "DELETE"} - } - logrus.Debugf("Setting field %s.%s: %#v", schema.ID, fieldName, schemaField) schema.ResourceFields[fieldName] = schemaField } + if hasType && hasMeta { + schema.CollectionMethods = []string{"GET", "POST"} + schema.ResourceMethods = []string{"GET", "PUT", "DELETE"} + } + return nil } diff --git a/types/schemas.go b/types/schemas.go index f7a69054..bbdf4256 100644 --- a/types/schemas.go +++ b/types/schemas.go @@ -13,6 +13,8 @@ type SchemaCollection struct { Data []Schema } +type SchemaInitFunc func(*Schemas) *Schemas + type Schemas struct { schemasByPath map[string]map[string]*Schema mappers map[string]map[string]Mapper @@ -28,6 +30,10 @@ func NewSchemas() *Schemas { } } +func (s *Schemas) Init(initFunc SchemaInitFunc) *Schemas { + return initFunc(s) +} + func (s *Schemas) Err() error { return NewErrors(s.errors) }