mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-11 09:02:53 +00:00
setters 2.0: support for adding references to setters
This commit is contained in:
@@ -17,7 +17,7 @@ import (
|
|||||||
type FieldMeta struct {
|
type FieldMeta struct {
|
||||||
Schema spec.Schema
|
Schema spec.Schema
|
||||||
|
|
||||||
Extensions XKustomize
|
Extensions *XKustomize
|
||||||
}
|
}
|
||||||
|
|
||||||
type XKustomize struct {
|
type XKustomize struct {
|
||||||
@@ -64,7 +64,11 @@ func (fm *FieldMeta) Read(n *yaml.RNode) error {
|
|||||||
|
|
||||||
// Write writes the FieldMeta to a node
|
// Write writes the FieldMeta to a node
|
||||||
func (fm *FieldMeta) Write(n *yaml.RNode) error {
|
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)
|
b, err := json.Marshal(fm.Schema)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err)
|
return errors.Wrap(err)
|
||||||
|
|||||||
74
kyaml/setters2/add.go
Normal file
74
kyaml/setters2/add.go
Normal 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
206
kyaml/setters2/add_test.go
Normal 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()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -154,4 +154,10 @@
|
|||||||
// to "1.8.2", then calling either Set{Name: "image-name"}.Filter(deployment) or
|
// 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
|
// 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".
|
// 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
|
package setters2
|
||||||
|
|||||||
@@ -128,3 +128,75 @@ spec:
|
|||||||
// - name: nginx
|
// - name: nginx
|
||||||
// image: nginx:1.8.1 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
|
// 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"}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user