Merge pull request #2197 from pwittrock/setters

setters 2.0
This commit is contained in:
Kubernetes Prow Robot
2020-02-11 16:04:09 -08:00
committed by GitHub
9 changed files with 981 additions and 4 deletions

View File

@@ -55,7 +55,7 @@ func (fm *FieldMeta) Read(n *yaml.RNode) error {
}
b, err := json.Marshal(fe)
if err != nil {
return err
return errors.Wrap(err)
}
return json.Unmarshal(b, &fm.Extensions)
}
@@ -67,7 +67,7 @@ func (fm *FieldMeta) Write(n *yaml.RNode) error {
fm.Schema.VendorExtensible.AddExtension("x-kustomize", fm.Extensions)
b, err := json.Marshal(fm.Schema)
if err != nil {
return err
return errors.Wrap(err)
}
n.YNode().LineComment = string(b)
return nil

View File

@@ -49,6 +49,11 @@ func AddSchema(s []byte) (*spec.Schema, error) {
return parse(s)
}
// ResetOpenAPI resets the openapi data to empty
func ResetOpenAPI() {
globalSchema = openapiData{}
}
// AddDefinitions adds the definitions to the global schema.
func AddDefinitions(definitions spec.Definitions) {
// initialize values if they have not yet been set
@@ -123,7 +128,7 @@ func GetSchema(s string) (*ResourceSchema, error) {
// schema as part of the global schema.
// Must be called before the schema is used.
func SuppressBuiltInSchemaUse() {
globalSchema.noUseBuiltInSchema = false
globalSchema.noUseBuiltInSchema = true
}
// Elements returns the Schema for the elements of an array.

View File

@@ -40,7 +40,7 @@ func TestNoUseBuiltInSchema_AddSchema(t *testing.T) {
t.FailNow()
}
s, err := GetSchema(`{"$ref": "#/definitions/io.k8s.config.setters.replicas"}`)
if !assert.Greater(t, len(globalSchema.schema.Definitions), 1) {
if !assert.Equal(t, len(globalSchema.schema.Definitions), 1) {
t.FailNow()
}
if !assert.NoError(t, err) {

157
kyaml/setters2/doc.go Normal file
View File

@@ -0,0 +1,157 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
//
// Package setters2 contains libraries for setting resource field values from OpenAPI setter
// extensions.
//
// Setters
//
// Setters are used to programmatically set configuration field values -- e.g. through a cli or ui.
//
// Setters are defined through OpenAPI definitions using the x-k8s-cli extension.
// Note: additional OpenAPI definitions may be registered through openapi.AddSchema([]byte)
//
// Example OpenAPI schema containing a setter:
//
// {
// "definitions": {
// "io.k8s.cli.setters.replicas": {
// "x-k8s-cli": {
// "setter": {
// "name": "replicas",
// "value": "4"
// }
// }
// }
// }
// }
//
// Setter fields:
//
// x-k8s-cli.setter.name: name of the setter
// x-k8s-cli.setter.value: value of the setter that should be applied to fields
//
// The setter definition key must be of the form "io.k8s.cli.setters.NAME", where NAME matches the
// value of "x-k8s-cli.setter.name".
//
// When Set.Filter is called, the named setter will have its value applied to all resource
// fields referencing it.
//
// Fields may reference setters through a yaml comment containing the serialized JSON OpenAPI.
//
// Example Deployment resource with a "spec.replicas" field set by the "replicas" setter:
//
// apiVersion: apps/v1
// kind: Deployment
// metadata:
// name: nginx-deployment
// spec:
// replicas: 4 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
//
// If the OpenAPI io.k8s.cli.setters.replicas x-k8s-cli.setter.value was changed from "4" to "5",
// then calling Set{Name: "replicas"}.Filter(deployment) would update the Deployment spec.replicas
// value from 4 to 5.
//
// Updated OpenAPI:
//
// {
// "definitions": {
// "io.k8s.cli.setters.replicas": {
// "x-k8s-cli": {
// "setter": {
// "name": "replicas",
// "value": "5"
// }
// }
// }
// }
// }
//
// Updated Deployment Configuration:
//
// apiVersion: apps/v1
// kind: Deployment
// metadata:
// name: nginx-deployment
// spec:
// replicas: 5 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
//
// Substitutions
//
// Substitutions are used to programmatically set configuration field values using multiple
// setters which are substituted into a pattern string.
//
// Substitutions may be used when a field value does not cleanly map to a single setter, but
// instead matches some string pattern where setters may be substituted in.
//
// Fields may reference substitutions the same way they do setters, however substitutions
// reference setters from which they are derived.
//
// Example OpenAPI schema containing a substitution derived from 2 setters:
//
// {
// "definitions": {
// "io.k8s.cli.setters.image-name": {
// "x-k8s-cli": {
// "setter": {
// "name": "image-name",
// "value": "nginx"
// }
// }
// },
// "io.k8s.cli.setters.image-tag": {
// "x-k8s-cli": {
// "setter": {
// "name": "image-tag",
// "value": "1.8.1"
// }
// }
// },
// "io.k8s.cli.substitutions.image-name-tag": {
// "x-k8s-cli": {
// "substitution": {
// "name": "image-name-tag",
// "pattern": "IMAGE_NAME:IMAGE_TAG",
// "values": [
// {"marker": "IMAGE_NAME", "ref": "#/definitions/io.k8s.cli.setters.image-name"}
// {"marker": "IMAGE_TAG", "ref": "#/definitions/io.k8s.cli.setters.image-tag"}
// ]
// }
// }
// }
// }
// }
//
// Substitution Fields.
//
// x-k8s-cli.substitution.name: name of the substitution
// x-k8s-cli.substitution.pattern: string pattern to substitute markers into
// x-k8s-cli.substitution.values.marker: the marker substring within pattern to replace
// x-k8s-cli.substitution.values.ref: the setter ref containing the value to replace the marker with
//
// The substitution is composed of a "pattern" containing markers, and a list of setter "values"
// which are substituted into the markers.
//
// Example Deployment with substitution:
//
// apiVersion: apps/v1
// kind: Deployment
// metadata:
// name: nginx-deployment
// spec:
// template:
// spec:
// containers:
// - name: nginx
// image: nginx:1.8.1 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image-name-tag"}
//
// spec.template.spec.containers[name=nginx].image is set by the "image" substitution any time
// either "image-name" or "image-tag" is set. Whenever any setter referenced by a substitution
// is set, the substitution will be recalculated by substituting its values into its pattern.
//
//
// If the OpenAPI io.k8s.cli.setters.image-name x-k8s-cli.setter.value was changed from "1.8.1"
// to "1.8.2", then calling either Set{Name: "image-name"}.Filter(deployment) or
// Set{Name: "image-tag"}.Filter(deployment) would update the Deployment field
// spec.template.spec.container[name=nginx].image from "nginx:1.8.1" to "nginx:1.8.2".
package setters2

View File

@@ -0,0 +1,130 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package setters2
import (
"fmt"
"sigs.k8s.io/kustomize/kyaml/openapi"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
// ExampleSet demonstrates using Set to replace the current field value in an object
func ExampleSet() {
openapi.ResetOpenAPI()
// OpenAPI definitions with setter extensions on definitions
schema := `
{
"definitions": {
"io.k8s.cli.setters.replicas": {
"x-k8s-cli": {
"setter": {
"name": "replicas",
"value": "4"
}
}
}
}
}
`
// Resource with field referencing OpenAPI definition
deployment := `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
`
_, err := openapi.AddSchema([]byte(schema)) // add the schema definitions
if err != nil {
panic(err)
}
object := yaml.MustParse(deployment) // parse the configuration
err = object.PipeE(&Set{Name: "replicas"}) // set replicas from the setter
if err != nil {
panic(err)
}
fmt.Println(object.MustString())
// Output:
// apiVersion: apps/v1
// kind: Deployment
// metadata:
// name: nginx-deployment
// spec:
// replicas: 4 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
}
// ExampleSet_Substitution demonstrates using Set to substitute a value into the field of
// an object. Only part of the field value is modified.
func ExampleSet_substitution() {
openapi.ResetOpenAPI()
// set the version setter
schema := `
{
"definitions": {
"io.k8s.cli.setters.version": {
"x-k8s-cli": {
"setter": {
"name": "version",
"value": "1.8.1"
}
}
},
"io.k8s.cli.substitutions.image": {
"x-k8s-cli": {
"substitution": {
"name": "image",
"pattern": "nginx:VERSION",
"values": [
{"marker": "VERSION", "ref": "#/definitions/io.k8s.cli.setters.version"}
]
}
}
}
}
}`
deployment := `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
template:
spec:
containers:
- name: nginx
image: nginx:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
`
_, err := openapi.AddSchema([]byte(schema)) // add the schema definitions
if err != nil {
panic(err)
}
object := yaml.MustParse(deployment) // parse the configuration
err = object.PipeE(&Set{Name: "version"}) // set replicas from the setter
if err != nil {
panic(err)
}
// Print the object with the update value
fmt.Println(object.MustString())
// Output:
// apiVersion: apps/v1
// kind: Deployment
// metadata:
// name: nginx-deployment
// spec:
// template:
// spec:
// containers:
// - name: nginx
// image: nginx:1.8.1 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
}

113
kyaml/setters2/set.go Normal file
View File

@@ -0,0 +1,113 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package setters2
import (
"strings"
"github.com/go-openapi/spec"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/openapi"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
// Set sets resource field values from an OpenAPI setter
type Set struct {
// Name is the name of the setter to set on the object. i.e. matches the x-k8s-cli.setter.name
// of the setter that should have its value applied to fields which reference it.
Name string
}
// Filter implements Set as a yaml.Filter
func (s *Set) Filter(object *yaml.RNode) (*yaml.RNode, error) {
return object, accept(s, object)
}
// visitScalar
func (s *Set) visitScalar(object *yaml.RNode, _ string) error {
// get the openAPI for this field describing how to apply the setter
ext, err := getExtFromComment(object)
if err != nil {
return err
}
if ext == nil {
return nil
}
// perform a direct set of the field if it matches
if s.set(object, ext) {
return nil
}
// perform a substitution of the field if it matches
if sub, err := s.substitute(object, ext); sub || err != nil {
return err
}
return nil
}
// substitute updates the value of field from ext if ext contains a substitution that
// depends on a setter whose name matches s.Name.
func (s *Set) substitute(field *yaml.RNode, ext *cliExtension) (bool, error) {
nameMatch := false
// check partial setters to see if they contain the setter as part of a
// substitution
if ext.Substitution == nil {
return false, nil
}
p := ext.Substitution.Pattern
// substitute each setter into the pattern to get the new value
for _, v := range ext.Substitution.Values {
if v.Ref == "" {
return false, errors.Errorf(
"missing reference on substitution " + ext.Substitution.Name)
}
ref, err := spec.NewRef(v.Ref)
if err != nil {
return false, errors.Wrap(err)
}
setter, err := openapi.Resolve(&ref) // resolve the setter to its openAPI def
if err != nil {
return false, errors.Wrap(err)
}
subSetter, err := getExtFromSchema(setter) // parse the extension out of the openAPI
if err != nil {
return false, errors.Wrap(err)
}
// substitute the setters current value into the substitution pattern
p = strings.ReplaceAll(p, v.Marker, subSetter.Setter.Value)
if subSetter.Setter.Name == s.Name {
// the substitution depends on the specified setter
nameMatch = true
}
}
if !nameMatch {
// doesn't depend on the setter, don't modify its value
return false, nil
}
// TODO(pwittrock): validate the field value
field.YNode().Value = p
return true, nil
}
// set applies the value from ext to field if its name matches s.Name
func (s *Set) set(field *yaml.RNode, ext *cliExtension) bool {
// check full setter
if ext.Setter == nil || ext.Setter.Name != s.Name {
return false
}
// TODO(pwittrock): validate the field value
// this has a full setter, set its value
field.YNode().Value = ext.Setter.Value
return true
}

440
kyaml/setters2/set_test.go Normal file
View File

@@ -0,0 +1,440 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package setters2
import (
"encoding/json"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/kyaml/openapi"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
func TestSet_Filter(t *testing.T) {
var tests = []struct {
name string
setter string
openapi string
input string
expected string
}{
{
name: "set-replicas",
setter: "replicas",
openapi: `
openAPI:
definitions:
io.k8s.cli.setters.no-match-1':
x-k8s-cli:
setter:
name: no-match-1
value: "1"
io.k8s.cli.setters.replicas:
x-k8s-cli:
setter:
name: replicas
value: "4"
io.k8s.cli.setters.no-match-2':
x-k8s-cli:
setter:
name: no-match-2
value: "2"
`,
input: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
`,
expected: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 4 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
`,
},
{
name: "set-arg",
setter: "arg1",
openapi: `
openAPI:
definitions:
io.k8s.cli.setters.replicas:
x-k8s-cli:
setter:
name: replicas
value: "4"
io.k8s.cli.setters.arg1:
x-k8s-cli:
setter:
name: arg1
value: "some value"
`,
input: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
template:
spec:
containers:
- name: nginx
args:
- a
- b # {"$ref": "#/definitions/io.k8s.cli.setters.arg1"}
`,
expected: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
template:
spec:
containers:
- name: nginx
args:
- a
- some value # {"$ref": "#/definitions/io.k8s.cli.setters.arg1"}`,
},
{
name: "substitute-image-tag",
setter: "image-tag",
openapi: `
openAPI:
definitions:
io.k8s.cli.setters.image-name:
x-k8s-cli:
setter:
name: image-name
value: "nginx"
io.k8s.cli.setters.image-tag:
x-k8s-cli:
setter:
name: image-tag
value: "1.8.1"
io.k8s.cli.substitutions.image:
x-k8s-cli:
substitution:
name: image
pattern: IMAGE_NAME:IMAGE_TAG
values:
- marker: "IMAGE_NAME"
ref: "#/definitions/io.k8s.cli.setters.image-name"
- marker: "IMAGE_TAG"
ref: "#/definitions/io.k8s.cli.setters.image-tag"
`,
input: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
template:
spec:
containers:
- name: nginx
image: nginx:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
`,
expected: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
template:
spec:
containers:
- name: nginx
image: nginx:1.8.1 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
`,
},
{
name: "substitute-annotation",
setter: "project",
openapi: `
openAPI:
definitions:
io.k8s.cli.setters.project:
x-k8s-cli:
setter:
name: project
value: "a"
io.k8s.cli.setters.location:
x-k8s-cli:
setter:
name: location
value: "b"
io.k8s.cli.setters.cluster:
x-k8s-cli:
setter:
name: cluster
value: "c"
io.k8s.cli.substitutions.key:
x-k8s-cli:
substitution:
name: key
pattern: https://container.googleapis.com/v1/projects/PROJECT/locations/LOCATION/clusters/CLUSTER
values:
- marker: "PROJECT"
ref: "#/definitions/io.k8s.cli.setters.project"
- marker: "LOCATION"
ref: "#/definitions/io.k8s.cli.setters.location"
- marker: "CLUSTER"
ref: "#/definitions/io.k8s.cli.setters.cluster"
`,
input: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
annotations:
key: 'https://container.googleapis.com/v1/projects/a/locations/a/clusters/a' # {"$ref": "#/definitions/io.k8s.cli.substitutions.key"}
`,
expected: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
annotations:
key: 'https://container.googleapis.com/v1/projects/a/locations/b/clusters/c' # {"$ref": "#/definitions/io.k8s.cli.substitutions.key"}
`,
},
{
name: "substitute-not-match-setter",
setter: "not-real",
openapi: `
openAPI:
definitions:
io.k8s.cli.setters.project:
x-k8s-cli:
setter:
name: project
value: "a"
io.k8s.cli.setters.location:
x-k8s-cli:
setter:
name: location
value: "b"
io.k8s.cli.setters.cluster:
x-k8s-cli:
setter:
name: cluster
value: "c"
io.k8s.cli.substitutions.key:
x-k8s-cli:
substitution:
name: key
pattern: https://container.googleapis.com/v1/projects/PROJECT/locations/LOCATION/clusters/CLUSTER
values:
- marker: "PROJECT"
ref: "#/definitions/io.k8s.cli.setters.project"
- marker: "LOCATION"
ref: "#/definitions/io.k8s.cli.setters.location"
- marker: "CLUSTER"
ref: "#/definitions/io.k8s.cli.setters.cluster"
`,
input: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
annotations:
key: 'https://container.googleapis.com/v1/projects/a/locations/a/clusters/a' # {"$ref": "#/definitions/io.k8s.cli.substitutions.key"}
`,
expected: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
annotations:
key: 'https://container.googleapis.com/v1/projects/a/locations/a/clusters/a' # {"$ref": "#/definitions/io.k8s.cli.substitutions.key"}
`,
},
{
name: "substitute-image-name",
setter: "image-name",
openapi: `
openAPI:
definitions:
io.k8s.cli.setters.image-name:
x-k8s-cli:
setter:
name: image-name
value: "foo"
io.k8s.cli.setters.image-tag:
x-k8s-cli:
setter:
name: image-tag
value: "1.7.9"
io.k8s.cli.substitutions.image:
x-k8s-cli:
substitution:
name: image
pattern: IMAGE_NAME:IMAGE_TAG
values:
- marker: "IMAGE_NAME"
ref: "#/definitions/io.k8s.cli.setters.image-name"
- marker: "IMAGE_TAG"
ref: "#/definitions/io.k8s.cli.setters.image-tag"
`,
input: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
template:
spec:
containers:
- name: nginx
image: nginx:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
`,
expected: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
template:
spec:
containers:
- name: nginx
image: foo:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
`,
},
{
name: "substitute-substring",
setter: "image-tag",
openapi: `
openAPI:
definitions:
io.k8s.cli.setters.image-name:
x-k8s-cli:
setter:
name: image-name
value: "nginx"
io.k8s.cli.setters.image-tag:
x-k8s-cli:
setter:
name: image-tag
value: "1.8.1"
io.k8s.cli.substitutions.image:
x-k8s-cli:
substitution:
name: image
pattern: IMAGE_NAME:IMAGE_TAG
values:
- marker: "IMAGE_NAME"
ref: "#/definitions/io.k8s.cli.setters.image-name"
- marker: "IMAGE_TAG"
ref: "#/definitions/io.k8s.cli.setters.image-tag"
`,
input: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
template:
spec:
containers:
- name: nginx
image: a:a # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
`,
expected: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
template:
spec:
containers:
- name: nginx
image: nginx:1.8.1 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
`,
},
}
for i := range tests {
test := tests[i]
t.Run(test.name, func(t *testing.T) {
// reset the openAPI afterward
defer openapi.ResetOpenAPI()
initSchema(t, test.openapi)
// parse the input to be modified
r, err := yaml.Parse(test.input)
if !assert.NoError(t, err) {
t.FailNow()
}
// invoke the setter
instance := &Set{Name: test.setter}
result, err := instance.Filter(r)
if !assert.NoError(t, err) {
t.FailNow()
}
// compare the actual and expected output
actual, err := result.String()
if !assert.NoError(t, err) {
t.FailNow()
}
actual = strings.TrimSpace(actual)
expected := strings.TrimSpace(test.expected)
if !assert.Equal(t, expected, actual) {
t.FailNow()
}
})
}
}
// initSchema initializes the openAPI with the definitions from s
func initSchema(t *testing.T, s string) {
// parse out the schema from the input openAPI
y, err := yaml.Parse(s)
if !assert.NoError(t, err) {
t.FailNow()
}
// get the field containing the openAPI
f := y.Field("openAPI")
if !assert.NotNil(t, f) {
t.FailNow()
}
defs, err := f.Value.String()
if !assert.NoError(t, err) {
t.FailNow()
}
// convert the yaml openAPI to an interface{}
// which can be marshalled into json
var o interface{}
err = yaml.Unmarshal([]byte(defs), &o)
if !assert.NoError(t, err) {
t.FailNow()
}
// convert the interface{} into a json string
j, err := json.Marshal(o)
if !assert.NoError(t, err) {
t.FailNow()
}
// reset the openAPI to clear existing definitions
openapi.ResetOpenAPI()
// add the json schema to the global schema
_, err = openapi.AddSchema(j)
if !assert.NoError(t, err) {
t.FailNow()
}
}

87
kyaml/setters2/types.go Normal file
View File

@@ -0,0 +1,87 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package setters2
import (
"encoding/json"
"github.com/go-openapi/spec"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/fieldmeta"
"sigs.k8s.io/kustomize/kyaml/openapi"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
type cliExtension struct {
Setter *setter `yaml:"setter,omitempty" json:"setter,omitempty"`
Substitution *substitution `yaml:"substitution,omitempty" json:"substitution,omitempty"`
}
type setter struct {
Name string `yaml:"name,omitempty" json:"name,omitempty"`
Value string `yaml:"value,omitempty" json:"value,omitempty"`
}
type substitution struct {
Name string `yaml:"name,omitempty" json:"name,omitempty"`
Pattern string `yaml:"pattern,omitempty" json:"pattern,omitempty"`
Values []substitutionSetterReference `yaml:"values,omitempty" json:"values,omitempty"`
}
type substitutionSetterReference struct {
Ref string `yaml:"ref,omitempty" json:"ref,omitempty"`
Marker string `yaml:"marker,omitempty" json:"marker,omitempty"`
}
//K8sCliExtensionKey is the name of the OpenAPI field containing the setter extensions
const K8sCliExtensionKey = "x-k8s-cli"
// getExtFromSchema returns the cliExtension openAPI extension if it is present in schema
func getExtFromSchema(schema *spec.Schema) (*cliExtension, error) {
cep := schema.VendorExtensible.Extensions[K8sCliExtensionKey]
if cep == nil {
return nil, nil
}
b, err := json.Marshal(cep)
if err != nil {
return nil, err
}
val := &cliExtension{}
if err := json.Unmarshal(b, val); err != nil {
return nil, err
}
return val, nil
}
// getExtFromComment returns the cliExtension openAPI extension if it is present as
// a comment on the field.
func getExtFromComment(object *yaml.RNode) (*cliExtension, error) {
// TODO(pwittrock): also use path to the field to get openapi, not just comments
// parse comment containing the extended openapi for this field
fm := fieldmeta.FieldMeta{}
if err := fm.Read(object); err != nil {
return nil, errors.Wrap(err)
}
if fm.Schema.Ref.String() == "" {
return nil, nil
}
// resolve the comment reference to the extended openapi definitions
r, err := openapi.Resolve(&fm.Schema.Ref)
if err != nil {
return nil, errors.Wrap(err)
}
if r == nil {
// no schema found
// TODO(pwittrock): should this be an error if it doesn't resolve?
return nil, nil
}
// get the cli extension from the openapi (contains setter information)
ext, err := getExtFromSchema(r)
if err != nil {
return nil, errors.Wrap(err)
}
return ext, nil
}

45
kyaml/setters2/walk.go Normal file
View File

@@ -0,0 +1,45 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package setters2
import (
"sigs.k8s.io/kustomize/kyaml/yaml"
)
// visitor is implemented by structs which need to walk the configuration.
// visitor is provided to accept to walk configuration
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
}
// accept invokes the appropriate function on v for each field in object
func accept(v visitor, object *yaml.RNode) error {
return acceptImpl(v, object, "")
}
// acceptImpl implements accept using recursion
func acceptImpl(v visitor, object *yaml.RNode, p string) 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 {
// Traverse each field value
return acceptImpl(v, node.Value, p+"."+node.Key.YNode().Value)
})
case yaml.SequenceNode:
return object.VisitElements(func(node *yaml.RNode) error {
// Traverse each list element
return acceptImpl(v, node, p)
})
case yaml.ScalarNode:
// Visit the scalar field
return v.visitScalar(object, p)
}
return nil
}