275
vendor/sigs.k8s.io/kustomize/kyaml/fieldmeta/fieldmeta.go
generated
vendored
Normal file
275
vendor/sigs.k8s.io/kustomize/kyaml/fieldmeta/fieldmeta.go
generated
vendored
Normal file
@@ -0,0 +1,275 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package fieldmeta
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/go-openapi/spec"
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// FieldMeta contains metadata that may be attached to fields as comments
|
||||
type FieldMeta struct {
|
||||
Schema spec.Schema
|
||||
|
||||
Extensions XKustomize
|
||||
|
||||
SettersSchema *spec.Schema
|
||||
}
|
||||
|
||||
type XKustomize struct {
|
||||
SetBy string `yaml:"setBy,omitempty" json:"setBy,omitempty"`
|
||||
PartialFieldSetters []PartialFieldSetter `yaml:"partialSetters,omitempty" json:"partialSetters,omitempty"`
|
||||
FieldSetter *PartialFieldSetter `yaml:"setter,omitempty" json:"setter,omitempty"`
|
||||
}
|
||||
|
||||
// PartialFieldSetter defines how to set part of a field rather than the full field
|
||||
// value. e.g. the tag part of an image field
|
||||
type PartialFieldSetter struct {
|
||||
// Name is the name of this setter.
|
||||
Name string `yaml:"name" json:"name"`
|
||||
|
||||
// Value is the current value that has been set.
|
||||
Value string `yaml:"value" json:"value"`
|
||||
}
|
||||
|
||||
// IsEmpty returns true if the FieldMeta has any empty Schema
|
||||
func (fm *FieldMeta) IsEmpty() bool {
|
||||
if fm == nil {
|
||||
return true
|
||||
}
|
||||
return reflect.DeepEqual(fm.Schema, spec.Schema{})
|
||||
}
|
||||
|
||||
// Read reads the FieldMeta from a node
|
||||
func (fm *FieldMeta) Read(n *yaml.RNode) error {
|
||||
// check for metadata on head and line comments
|
||||
comments := []string{n.YNode().LineComment, n.YNode().HeadComment}
|
||||
for _, c := range comments {
|
||||
if c == "" {
|
||||
continue
|
||||
}
|
||||
c := strings.TrimLeft(c, "#")
|
||||
|
||||
// check for new short hand notation or fall back to openAPI ref format
|
||||
if !fm.processShortHand(c) {
|
||||
// if it doesn't Unmarshal that is fine, it means there is no metadata
|
||||
// other comments are valid, they just don't parse
|
||||
// TODO: consider more sophisticated parsing techniques similar to what is used
|
||||
// for go struct tags.
|
||||
if err := fm.Schema.UnmarshalJSON([]byte(c)); err != nil {
|
||||
// note: don't return an error if the comment isn't a fieldmeta struct
|
||||
return nil
|
||||
}
|
||||
}
|
||||
fe := fm.Schema.VendorExtensible.Extensions["x-kustomize"]
|
||||
if fe == nil {
|
||||
return nil
|
||||
}
|
||||
b, err := json.Marshal(fe)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
return json.Unmarshal(b, &fm.Extensions)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// processShortHand parses the comment for short hand ref, loads schema to fm
|
||||
// and returns true if successful, returns false for any other cases and not throw
|
||||
// error, as the comment might not be a setter ref
|
||||
func (fm *FieldMeta) processShortHand(comment string) bool {
|
||||
input := map[string]string{}
|
||||
err := json.Unmarshal([]byte(comment), &input)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
name := input[shortHandRef]
|
||||
if name == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
// check if setter with the name exists, else check for a substitution
|
||||
// setter and substitution can't have same name in shorthand
|
||||
|
||||
setterRef, err := spec.NewRef(DefinitionsPrefix + SetterDefinitionPrefix + name)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
setterRefBytes, err := setterRef.MarshalJSON()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if _, err := openapi.Resolve(&setterRef, fm.SettersSchema); err == nil {
|
||||
setterErr := fm.Schema.UnmarshalJSON(setterRefBytes)
|
||||
return setterErr == nil
|
||||
}
|
||||
|
||||
substRef, err := spec.NewRef(DefinitionsPrefix + SubstitutionDefinitionPrefix + name)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
substRefBytes, err := substRef.MarshalJSON()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if _, err := openapi.Resolve(&substRef, fm.SettersSchema); err == nil {
|
||||
substErr := fm.Schema.UnmarshalJSON(substRefBytes)
|
||||
return substErr == nil
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isExtensionEmpty(x XKustomize) bool {
|
||||
if x.FieldSetter != nil {
|
||||
return false
|
||||
}
|
||||
if x.SetBy != "" {
|
||||
return false
|
||||
}
|
||||
if len(x.PartialFieldSetters) > 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Write writes the FieldMeta to a node
|
||||
func (fm *FieldMeta) Write(n *yaml.RNode) error {
|
||||
if !isExtensionEmpty(fm.Extensions) {
|
||||
return fm.WriteV1Setters(n)
|
||||
}
|
||||
|
||||
// Ref is removed when a setter is deleted, so the Ref string could be empty.
|
||||
if fm.Schema.Ref.String() != "" {
|
||||
// Ex: {"$ref":"#/definitions/io.k8s.cli.setters.replicas"} should be converted to
|
||||
// {"$openAPI":"replicas"} and added to the line comment
|
||||
ref := fm.Schema.Ref.String()
|
||||
var shortHandRefValue string
|
||||
switch {
|
||||
case strings.HasPrefix(ref, DefinitionsPrefix+SetterDefinitionPrefix):
|
||||
shortHandRefValue = strings.TrimPrefix(ref, DefinitionsPrefix+SetterDefinitionPrefix)
|
||||
case strings.HasPrefix(ref, DefinitionsPrefix+SubstitutionDefinitionPrefix):
|
||||
shortHandRefValue = strings.TrimPrefix(ref, DefinitionsPrefix+SubstitutionDefinitionPrefix)
|
||||
default:
|
||||
return fmt.Errorf("unexpected ref format: %s", ref)
|
||||
}
|
||||
n.YNode().LineComment = fmt.Sprintf(`{"%s":"%s"}`, shortHandRef,
|
||||
shortHandRefValue)
|
||||
} else {
|
||||
n.YNode().LineComment = ""
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteV1Setters is the v1 setters way of writing setter definitions
|
||||
// TODO: pmarupaka - remove this method after migration
|
||||
func (fm *FieldMeta) WriteV1Setters(n *yaml.RNode) error {
|
||||
fm.Schema.VendorExtensible.AddExtension("x-kustomize", fm.Extensions)
|
||||
b, err := json.Marshal(fm.Schema)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
n.YNode().LineComment = string(b)
|
||||
return nil
|
||||
}
|
||||
|
||||
// FieldValueType defines the type of input to register
|
||||
type FieldValueType string
|
||||
|
||||
const (
|
||||
// String defines a string flag
|
||||
String FieldValueType = "string"
|
||||
// Bool defines a bool flag
|
||||
Bool = "boolean"
|
||||
// Int defines an int flag
|
||||
Int = "integer"
|
||||
)
|
||||
|
||||
func (it FieldValueType) String() string {
|
||||
if it == "" {
|
||||
return "string"
|
||||
}
|
||||
return string(it)
|
||||
}
|
||||
|
||||
func (it FieldValueType) Validate(value string) error {
|
||||
switch it {
|
||||
case Int:
|
||||
if _, err := strconv.Atoi(value); err != nil {
|
||||
return errors.WrapPrefixf(err, "value must be an int")
|
||||
}
|
||||
case Bool:
|
||||
if _, err := strconv.ParseBool(value); err != nil {
|
||||
return errors.WrapPrefixf(err, "value must be a bool")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (it FieldValueType) Tag() string {
|
||||
switch it {
|
||||
case String:
|
||||
return yaml.NodeTagString
|
||||
case Bool:
|
||||
return yaml.NodeTagBool
|
||||
case Int:
|
||||
return yaml.NodeTagInt
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (it FieldValueType) TagForValue(value string) string {
|
||||
switch it {
|
||||
case String:
|
||||
return yaml.NodeTagString
|
||||
case Bool:
|
||||
if _, err := strconv.ParseBool(string(it)); err != nil {
|
||||
return ""
|
||||
}
|
||||
return yaml.NodeTagBool
|
||||
case Int:
|
||||
if _, err := strconv.ParseInt(string(it), 0, 32); err != nil {
|
||||
return ""
|
||||
}
|
||||
return yaml.NodeTagInt
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
const (
|
||||
// CLIDefinitionsPrefix is the prefix for cli definition keys.
|
||||
CLIDefinitionsPrefix = "io.k8s.cli."
|
||||
|
||||
// SetterDefinitionPrefix is the prefix for setter definition keys.
|
||||
SetterDefinitionPrefix = CLIDefinitionsPrefix + "setters."
|
||||
|
||||
// SubstitutionDefinitionPrefix is the prefix for substitution definition keys.
|
||||
SubstitutionDefinitionPrefix = CLIDefinitionsPrefix + "substitutions."
|
||||
|
||||
// DefinitionsPrefix is the prefix used to reference definitions in the OpenAPI
|
||||
DefinitionsPrefix = "#/definitions/"
|
||||
)
|
||||
|
||||
// shortHandRef is the shorthand reference to setters and substitutions
|
||||
var shortHandRef = "$openapi"
|
||||
|
||||
func SetShortHandRef(ref string) {
|
||||
shortHandRef = ref
|
||||
}
|
||||
|
||||
func ShortHandRef() string {
|
||||
return shortHandRef
|
||||
}
|
||||
Reference in New Issue
Block a user