setters 2.0: support for adding references to setters

This commit is contained in:
Phillip Wittrock
2020-02-11 11:52:13 -08:00
parent b05ab6e0e3
commit 437be2831f
5 changed files with 364 additions and 2 deletions

View File

@@ -17,7 +17,7 @@ import (
type FieldMeta struct {
Schema spec.Schema
Extensions XKustomize
Extensions *XKustomize
}
type XKustomize struct {
@@ -64,7 +64,11 @@ func (fm *FieldMeta) Read(n *yaml.RNode) error {
// Write writes the FieldMeta to a node
func (fm *FieldMeta) Write(n *yaml.RNode) error {
fm.Schema.VendorExtensible.AddExtension("x-kustomize", fm.Extensions)
if fm.Extensions != nil {
fm.Schema.VendorExtensible.AddExtension("x-kustomize", fm.Extensions)
} else {
delete(fm.Schema.VendorExtensible.Extensions, "x-kustomize")
}
b, err := json.Marshal(fm.Schema)
if err != nil {
return errors.Wrap(err)

74
kyaml/setters2/add.go Normal file
View File

@@ -0,0 +1,74 @@
// 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/fieldmeta"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
// Add creates or updates setter or substitution references from resource fields.
// Requires that at least one of FieldValue and FieldName have been set.
type Add struct {
// FieldValue if set will add the OpenAPI reference to fields if they have this value.
// Optional. If unspecified match all field values.
FieldValue string
// FieldName if set will add the OpenAPI reference to fields with this name or path
// FieldName may be the full name of the field, full path to the field, or the path suffix.
// e.g. all of the following would match spec.template.spec.containers.image --
// [image, containers.image, spec.containers.image, template.spec.containers.image,
// spec.template.spec.containers.image]
// Optional. If unspecified match all field names.
FieldName string
// Ref is the OpenAPI reference to set on the matching fields as a comment.
Ref string
}
// Filter implements yaml.Filter
func (a *Add) Filter(object *yaml.RNode) (*yaml.RNode, error) {
if a.FieldName == "" && a.FieldValue == "" {
return nil, errors.Errorf("must specify either fieldName or fieldValue")
}
if a.Ref == "" {
return nil, errors.Errorf("must specify ref")
}
return object, accept(a, object)
}
// visitScalar implements visitor
// visitScalar will set the field metadata on each scalar field whose name + value match
func (a *Add) visitScalar(object *yaml.RNode, p string) error {
// check if the field matches
if a.FieldName != "" && !strings.HasSuffix(p, a.FieldName) {
return nil
}
if a.FieldValue != "" && a.FieldValue != object.YNode().Value {
return nil
}
// read the field metadata
fm := fieldmeta.FieldMeta{}
if err := fm.Read(object); err != nil {
return err
}
// create the ref on the field metadata
r, err := spec.NewRef(a.Ref)
if err != nil {
return err
}
fm.Schema.Ref = r
// write the field metadata
if err := fm.Write(object); err != nil {
return err
}
return nil
}

206
kyaml/setters2/add_test.go Normal file
View File

@@ -0,0 +1,206 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package setters2
import (
"strings"
"testing"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
func TestAdd_Filter(t *testing.T) {
var tests = []struct {
name string
add Add
input string
expected string
err string
}{
{
name: "add-replicas",
add: Add{
FieldValue: "3",
Ref: "#/definitions/io.k8s.cli.setters.replicas",
},
input: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3
`,
expected: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3 # {"$ref":"#/definitions/io.k8s.cli.setters.replicas"}
`,
},
{
name: "add-replicas-annotations",
add: Add{
FieldValue: "3",
Ref: "#/definitions/io.k8s.cli.setters.replicas",
},
input: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
annotations:
something: 3
spec:
replicas: 3
`,
expected: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
annotations:
something: 3 # {"$ref":"#/definitions/io.k8s.cli.setters.replicas"}
spec:
replicas: 3 # {"$ref":"#/definitions/io.k8s.cli.setters.replicas"}
`,
},
{
name: "add-replicas-name",
add: Add{
FieldValue: "3",
FieldName: "replicas",
Ref: "#/definitions/io.k8s.cli.setters.replicas",
},
input: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
annotations:
something: 3
spec:
replicas: 3
`,
expected: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
annotations:
something: 3
spec:
replicas: 3 # {"$ref":"#/definitions/io.k8s.cli.setters.replicas"}
`,
},
{
name: "add-replicas-2x",
add: Add{
FieldValue: "3",
FieldName: "replicas",
Ref: "#/definitions/io.k8s.cli.setters.replicas",
},
input: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
annotations:
replicas: 3
spec:
replicas: 3
`,
expected: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
annotations:
replicas: 3 # {"$ref":"#/definitions/io.k8s.cli.setters.replicas"}
spec:
replicas: 3 # {"$ref":"#/definitions/io.k8s.cli.setters.replicas"}
`,
},
{
name: "add-replicas-1x",
add: Add{
FieldValue: "3",
FieldName: "spec.replicas",
Ref: "#/definitions/io.k8s.cli.setters.replicas",
},
input: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
annotations:
replicas: 3
spec:
replicas: 3
`,
expected: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
annotations:
replicas: 3
spec:
replicas: 3 # {"$ref":"#/definitions/io.k8s.cli.setters.replicas"}
`,
},
{
name: "add-replicas-error",
add: Add{
Ref: "#/definitions/io.k8s.cli.setters.replicas",
},
err: "must specify either fieldName or fieldValue",
input: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3
`,
},
}
for i := range tests {
test := tests[i]
t.Run(test.name, func(t *testing.T) {
// parse the input to be modified
r, err := yaml.Parse(test.input)
if !assert.NoError(t, err) {
t.FailNow()
}
// invoke add
result, err := test.add.Filter(r)
if test.err != "" {
if !assert.Equal(t, test.err, err.Error()) {
t.FailNow()
}
return
}
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()
}
})
}
}

View File

@@ -154,4 +154,10 @@
// 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".
//
// Adding Field References
//
// References to setters and substitutions may be added to fields using the Add Filter.
// Add will write a JSON OpenAPI string as a comment to any fields matching the specified
// FieldName add FieldValue.
package setters2

View File

@@ -128,3 +128,75 @@ spec:
// - name: nginx
// image: nginx:1.8.1 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
}
// ExampleAdd demonstrates adding a setter reference to fields.
func ExampleAdd_fieldName() {
deployment := `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
annotations:
something: 3
spec:
replicas: 3
`
object := yaml.MustParse(deployment) // parse the configuration
err := object.PipeE(&Add{
Ref: "#/definitions/io.k8s.cli.setters.replicas",
FieldName: "replicas",
})
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
// annotations:
// something: 3
// spec:
// replicas: 3 # {"$ref":"#/definitions/io.k8s.cli.setters.replicas"}
}
// ExampleAdd demonstrates adding a setter reference to fields.
func ExampleAdd_fieldValue() {
deployment := `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
annotations:
something: 3
spec:
replicas: 3
`
object := yaml.MustParse(deployment) // parse the configuration
err := object.PipeE(&Add{
Ref: "#/definitions/io.k8s.cli.setters.replicas",
FieldValue: "3",
})
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
// annotations:
// something: 3 # {"$ref":"#/definitions/io.k8s.cli.setters.replicas"}
// spec:
// replicas: 3 # {"$ref":"#/definitions/io.k8s.cli.setters.replicas"}
}