Setters: support for setting string list fields

This commit is contained in:
Phillip Wittrock
2020-03-07 13:53:24 -08:00
parent 0b1ad031a9
commit 370502ed4b
13 changed files with 320 additions and 57 deletions

View File

@@ -4,6 +4,8 @@
package setters2
import (
"sigs.k8s.io/kustomize/kyaml/fieldmeta"
"sigs.k8s.io/kustomize/kyaml/openapi"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
@@ -13,33 +15,100 @@ type visitor interface {
// visitScalar is called for each scalar field value on a resource
// node is the scalar field value
// path is the path to the field; path elements are separated by '.'
visitScalar(node *yaml.RNode, path string) error
// oa is the OpenAPI schema for the field
visitScalar(node *yaml.RNode, path string, oa *openapi.ResourceSchema) error
// visitSequence is called for each sequence field value on a resource
// node is the sequence field value
// path is the path to the field
// oa is the OpenAPI schema for the field
visitSequence(node *yaml.RNode, path string, oa *openapi.ResourceSchema) error
}
// accept invokes the appropriate function on v for each field in object
func accept(v visitor, object *yaml.RNode) error {
return acceptImpl(v, object, "")
// get the OpenAPI for the type if it exists
oa := getSchema(object, nil, "")
return acceptImpl(v, object, "", oa)
}
// acceptImpl implements accept using recursion
func acceptImpl(v visitor, object *yaml.RNode, p string) error {
func acceptImpl(v visitor, object *yaml.RNode, p string, oa *openapi.ResourceSchema) error {
switch object.YNode().Kind {
case yaml.DocumentNode:
// Traverse the child of the document
return accept(v, yaml.NewRNode(object.YNode()))
case yaml.MappingNode:
return object.VisitFields(func(node *yaml.MapNode) error {
// get the schema for the field and propagate it
oa = getSchema(node.Key, oa, node.Key.YNode().Value)
// Traverse each field value
return acceptImpl(v, node.Value, p+"."+node.Key.YNode().Value)
return acceptImpl(v, node.Value, p+"."+node.Key.YNode().Value, oa)
})
case yaml.SequenceNode:
// get the schema for the sequence node, use the schema provided if not present
// on the field
if err := v.visitSequence(object, p, oa); err != nil {
return err
}
// get the schema for the elements
oa = getSchema(object, oa, "")
return object.VisitElements(func(node *yaml.RNode) error {
// Traverse each list element
return acceptImpl(v, node, p)
return acceptImpl(v, node, p, oa)
})
case yaml.ScalarNode:
// Visit the scalar field
return v.visitScalar(object, p)
oa = getSchema(object, oa, "")
return v.visitScalar(object, p, oa)
}
return nil
}
// getSchema returns OpenAPI schema for an RNode or field of the
// RNode. It will overriding the provide schema with field specific values
// if they are found
// r is the Node to get the Schema for
// s is the provided schema for the field if known
// field is the name of the field
func getSchema(r *yaml.RNode, s *openapi.ResourceSchema, field string) *openapi.ResourceSchema {
// get the override schema if it exists on the field
fm := fieldmeta.FieldMeta{}
if err := fm.Read(r); err == nil && !fm.IsEmpty() {
// per-field schema, this is fine
if fm.Schema.Ref.String() != "" {
// resolve the reference
s, err := openapi.Resolve(&fm.Schema.Ref)
if err == nil && s != nil {
fm.Schema = *s
}
}
return &openapi.ResourceSchema{Schema: &fm.Schema}
}
// get the schema for a field of the node if the field is provided
if s != nil && field != "" {
return s.Field(field)
}
// get the schema for the elements if this is a list
if s != nil && r.YNode().Kind == yaml.SequenceNode {
return s.Elements()
}
// use the provided schema if present
if s != nil {
return s
}
if yaml.IsEmpty(r) {
return nil
}
// lookup the schema for the type
m, _ := r.GetMeta()
if m.Kind == "" || m.APIVersion == "" {
return nil
}
return openapi.SchemaForResourceType(yaml.TypeMeta{Kind: m.Kind, APIVersion: m.APIVersion})
}