mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-05-17 18:25:26 +00:00
Merge pull request #2203 from pwittrock/setter-wiring
Manually merging since the prow automation does not appear to be configured correctly
This commit is contained in:
@@ -45,22 +45,23 @@ Advanced Documentation Topics:
|
||||
|
||||
// Export commands publicly for composition
|
||||
var (
|
||||
Annotate = commands.AnnotateCommand
|
||||
Cat = commands.CatCommand
|
||||
Count = commands.CountCommand
|
||||
CreateSetter = commands.CreateSetterCommand
|
||||
Fmt = commands.FmtCommand
|
||||
Grep = commands.GrepCommand
|
||||
ListSetters = commands.ListSettersCommand
|
||||
Merge = commands.MergeCommand
|
||||
Merge3 = commands.Merge3Command
|
||||
RunFn = commands.RunFnCommand
|
||||
Set = commands.SetCommand
|
||||
Sink = commands.SinkCommand
|
||||
Source = commands.SourceCommand
|
||||
Tree = commands.TreeCommand
|
||||
Wrap = commands.WrapCommand
|
||||
XArgs = commands.XArgsCommand
|
||||
Annotate = commands.AnnotateCommand
|
||||
Cat = commands.CatCommand
|
||||
Count = commands.CountCommand
|
||||
CreateSetter = commands.CreateSetterCommand
|
||||
CreateSubstitution = commands.CreateSubstitutionCommand
|
||||
Fmt = commands.FmtCommand
|
||||
Grep = commands.GrepCommand
|
||||
ListSetters = commands.ListSettersCommand
|
||||
Merge = commands.MergeCommand
|
||||
Merge3 = commands.Merge3Command
|
||||
RunFn = commands.RunFnCommand
|
||||
Set = commands.SetCommand
|
||||
Sink = commands.SinkCommand
|
||||
Source = commands.SourceCommand
|
||||
Tree = commands.TreeCommand
|
||||
Wrap = commands.WrapCommand
|
||||
XArgs = commands.XArgsCommand
|
||||
|
||||
StackOnError = &commands.StackOnError
|
||||
ExitOnError = &commands.ExitOnError
|
||||
@@ -107,6 +108,7 @@ func NewConfigCommand(name string) *cobra.Command {
|
||||
root.AddCommand(commands.SetCommand(name))
|
||||
root.AddCommand(commands.ListSettersCommand(name))
|
||||
root.AddCommand(commands.CreateSetterCommand(name))
|
||||
root.AddCommand(commands.CreateSubstitutionCommand(name))
|
||||
root.AddCommand(commands.SinkCommand(name))
|
||||
root.AddCommand(commands.SourceCommand(name))
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/setters"
|
||||
"sigs.k8s.io/kustomize/kyaml/setters2/settersutil"
|
||||
)
|
||||
|
||||
// NewCreateSetterRunner returns a command runner.
|
||||
@@ -23,21 +24,28 @@ func NewCreateSetterRunner(parent string) *CreateSetterRunner {
|
||||
RunE: r.runE,
|
||||
}
|
||||
set.Flags().StringVar(&r.Set.SetPartialField.SetBy, "set-by", "",
|
||||
"set the setBy annotation.")
|
||||
"record who the field was default by.")
|
||||
set.Flags().StringVar(&r.Set.SetPartialField.Description, "description", "",
|
||||
"set the description of the field value.")
|
||||
"record a description for the current setter value.")
|
||||
set.Flags().StringVar(&r.Set.SetPartialField.Field, "field", "",
|
||||
"name of the field to set -- e.g. --field port")
|
||||
"name of the field to set -- e.g. --field port. defaults to all fields match"+
|
||||
"VALUE. maybe be the field name, field path, or partial field path (suffix)")
|
||||
set.Flags().StringVar(&r.Set.ResourceMeta.Name, "name", "",
|
||||
"name of the Resource on which to create the setter.")
|
||||
set.Flags().MarkHidden("name")
|
||||
set.Flags().StringVar(&r.Set.ResourceMeta.Kind, "kind", "",
|
||||
"kind of the Resource on which to create the setter.")
|
||||
set.Flags().MarkHidden("kind")
|
||||
set.Flags().StringVar(&r.Set.SetPartialField.Type, "type", "",
|
||||
"valid OpenAPI field type -- e.g. integer,boolean,string.")
|
||||
set.Flags().MarkHidden("type")
|
||||
set.Flags().BoolVar(&r.Set.SetPartialField.Partial, "partial", false,
|
||||
"create a partial setter for only part of the field value.")
|
||||
set.Flags().MarkHidden("partial")
|
||||
set.Flags().StringVar(&setterVersion, "version", "",
|
||||
"use this version of the setter format")
|
||||
set.Flags().MarkHidden("version")
|
||||
fixDocs(parent, set)
|
||||
set.MarkFlagRequired("type")
|
||||
r.Command = set
|
||||
return r
|
||||
}
|
||||
@@ -47,8 +55,10 @@ func CreateSetterCommand(parent string) *cobra.Command {
|
||||
}
|
||||
|
||||
type CreateSetterRunner struct {
|
||||
Command *cobra.Command
|
||||
Set setters.CreateSetter
|
||||
Command *cobra.Command
|
||||
Set setters.CreateSetter
|
||||
CreateSetter settersutil.SetterCreator
|
||||
OpenAPIFile string
|
||||
}
|
||||
|
||||
func (r *CreateSetterRunner) runE(c *cobra.Command, args []string) error {
|
||||
@@ -56,12 +66,40 @@ func (r *CreateSetterRunner) runE(c *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
func (r *CreateSetterRunner) preRunE(c *cobra.Command, args []string) error {
|
||||
var err error
|
||||
r.Set.SetPartialField.Setter.Name = args[1]
|
||||
r.Set.SetPartialField.Setter.Value = args[2]
|
||||
r.CreateSetter.Name = args[1]
|
||||
r.CreateSetter.FieldValue = args[2]
|
||||
r.CreateSetter.FieldName, err = c.Flags().GetString("field")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if setterVersion == "" {
|
||||
if len(args) < 3 {
|
||||
setterVersion = "v1"
|
||||
} else if err := initSetterVersion(c, args); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if setterVersion == "v2" {
|
||||
var err error
|
||||
r.OpenAPIFile, err = GetOpenAPIFile(args)
|
||||
r.CreateSetter.Description = r.Set.SetPartialField.Description
|
||||
r.CreateSetter.SetBy = r.Set.SetPartialField.SetBy
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *CreateSetterRunner) set(c *cobra.Command, args []string) error {
|
||||
if setterVersion == "v2" {
|
||||
return r.CreateSetter.Create(r.OpenAPIFile, args[0])
|
||||
}
|
||||
|
||||
rw := &kio.LocalPackageReadWriter{PackagePath: args[0]}
|
||||
err := kio.Pipeline{
|
||||
Inputs: []kio.Reader{rw},
|
||||
|
||||
128
cmd/config/internal/commands/cmdcreatesetter_test.go
Normal file
128
cmd/config/internal/commands/cmdcreatesetter_test.go
Normal file
@@ -0,0 +1,128 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package commands_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
)
|
||||
|
||||
func TestCreateSetterCommand(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
input string
|
||||
args []string
|
||||
out string
|
||||
expectedOpenAPI string
|
||||
expectedResources string
|
||||
}{
|
||||
{
|
||||
name: "add replicas",
|
||||
args: []string{"replicas", "3", "--description", "hello world", "--set-by", "me"},
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
`,
|
||||
expectedOpenAPI: `
|
||||
apiVersion: v1alpha1
|
||||
kind: Example
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.replicas:
|
||||
description: hello world
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: replicas
|
||||
value: "3"
|
||||
setBy: me
|
||||
`,
|
||||
expectedResources: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 3 # {"$ref":"#/definitions/io.k8s.cli.setters.replicas"}
|
||||
`,
|
||||
},
|
||||
}
|
||||
for i := range tests {
|
||||
test := tests[i]
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
// reset the openAPI afterward
|
||||
openapi.ResetOpenAPI()
|
||||
defer openapi.ResetOpenAPI()
|
||||
|
||||
f, err := ioutil.TempFile("", "k8s-cli-")
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
defer os.Remove(f.Name())
|
||||
err = ioutil.WriteFile(f.Name(), []byte(`
|
||||
apiVersion: v1alpha1
|
||||
kind: Example
|
||||
`), 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
commands.GetOpenAPIFile = func(args []string) (s string, err error) {
|
||||
return f.Name(), nil
|
||||
}
|
||||
|
||||
r, err := ioutil.TempFile("", "k8s-cli-*.yaml")
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
defer os.Remove(r.Name())
|
||||
err = ioutil.WriteFile(r.Name(), []byte(test.input), 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
runner := commands.NewCreateSetterRunner("")
|
||||
out := &bytes.Buffer{}
|
||||
runner.Command.SetOut(out)
|
||||
runner.Command.SetArgs(append([]string{r.Name()}, test.args...))
|
||||
err = runner.Command.Execute()
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if !assert.Equal(t, test.out, out.String()) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
actualResources, err := ioutil.ReadFile(r.Name())
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
if !assert.Equal(t,
|
||||
strings.TrimSpace(test.expectedResources),
|
||||
strings.TrimSpace(string(actualResources))) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
actualOpenAPI, err := ioutil.ReadFile(f.Name())
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
if !assert.Equal(t,
|
||||
strings.TrimSpace(test.expectedOpenAPI),
|
||||
strings.TrimSpace(string(actualOpenAPI))) {
|
||||
t.FailNow()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
80
cmd/config/internal/commands/cmdcreatesubstitution.go
Normal file
80
cmd/config/internal/commands/cmdcreatesubstitution.go
Normal file
@@ -0,0 +1,80 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/setters2"
|
||||
"sigs.k8s.io/kustomize/kyaml/setters2/settersutil"
|
||||
)
|
||||
|
||||
// NewCreateSubstitutionRunner returns a command runner.
|
||||
func NewCreateSubstitutionRunner(parent string) *CreateSubstitutionRunner {
|
||||
r := &CreateSubstitutionRunner{}
|
||||
cs := &cobra.Command{
|
||||
Use: "create-subst DIR NAME VALUE",
|
||||
Args: cobra.ExactArgs(3),
|
||||
PreRunE: r.preRunE,
|
||||
RunE: r.runE,
|
||||
}
|
||||
cs.Flags().StringVar(&r.CreateSubstitution.FieldName, "field", "",
|
||||
"name of the field to set -- e.g. --field port")
|
||||
cs.Flags().StringVar(&r.CreateSubstitution.Pattern, "pattern", "",
|
||||
"substitution pattern")
|
||||
cs.Flags().StringSliceVar(&r.Values, "value", []string{""},
|
||||
"substitution values for the pattern. format is PATTERN_MARKER=SETTER_NAME"+
|
||||
"where PATTERN_MARKER is the pattern substring to replace, and SETTER_NAME is the"+
|
||||
"setter from which to take the replacement value.")
|
||||
_ = cs.MarkFlagRequired("pattern")
|
||||
fixDocs(parent, cs)
|
||||
r.Command = cs
|
||||
return r
|
||||
}
|
||||
|
||||
func CreateSubstitutionCommand(parent string) *cobra.Command {
|
||||
return NewCreateSubstitutionRunner(parent).Command
|
||||
}
|
||||
|
||||
type CreateSubstitutionRunner struct {
|
||||
Command *cobra.Command
|
||||
CreateSubstitution settersutil.SubstitutionCreator
|
||||
OpenAPIFile string
|
||||
Values []string
|
||||
}
|
||||
|
||||
func (r *CreateSubstitutionRunner) runE(c *cobra.Command, args []string) error {
|
||||
return handleError(c, r.CreateSubstitution.Create(r.OpenAPIFile, args[0]))
|
||||
}
|
||||
|
||||
func (r *CreateSubstitutionRunner) preRunE(c *cobra.Command, args []string) error {
|
||||
var err error
|
||||
r.CreateSubstitution.Name = args[1]
|
||||
r.CreateSubstitution.FieldValue = args[2]
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.OpenAPIFile, err = GetOpenAPIFile(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// parse the marker values
|
||||
for i := range r.Values {
|
||||
parts := strings.SplitN(r.Values[i], "=", 2)
|
||||
if len(parts) < 2 {
|
||||
return errors.Errorf("values must be specified as PATTERN_MARKER=SETTER_NAME")
|
||||
}
|
||||
ref := setters2.DefinitionsPrefix + setters2.SetterDefinitionPrefix + parts[1]
|
||||
r.CreateSubstitution.Values = append(
|
||||
r.CreateSubstitution.Values,
|
||||
setters2.Value{Marker: parts[0], Ref: ref},
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
237
cmd/config/internal/commands/cmdcreatesubstitution_test.go
Normal file
237
cmd/config/internal/commands/cmdcreatesubstitution_test.go
Normal file
@@ -0,0 +1,237 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package commands_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
)
|
||||
|
||||
func TestCreateSubstitutionCommand(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
inputOpenAPI string
|
||||
input string
|
||||
args []string
|
||||
out string
|
||||
expectedOpenAPI string
|
||||
expectedResources string
|
||||
}{
|
||||
{
|
||||
name: "substitution replicas",
|
||||
args: []string{
|
||||
"image", "nginx:1.7.9", "--pattern", "IMAGE:TAG",
|
||||
"--value", "IMAGE=image", "--value", "TAG=tag"},
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.7.9
|
||||
- name: sidecar
|
||||
image: sidecar:1.7.9
|
||||
`,
|
||||
inputOpenAPI: `
|
||||
apiVersion: v1alpha1
|
||||
kind: Example
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.image:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: image
|
||||
value: "nginx"
|
||||
io.k8s.cli.setters.tag:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: tag
|
||||
value: "1.7.9"
|
||||
`,
|
||||
expectedOpenAPI: `
|
||||
apiVersion: v1alpha1
|
||||
kind: Example
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.image:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: image
|
||||
value: "nginx"
|
||||
io.k8s.cli.setters.tag:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: tag
|
||||
value: "1.7.9"
|
||||
io.k8s.cli.substitutions.image:
|
||||
x-k8s-cli:
|
||||
substitution:
|
||||
name: image
|
||||
pattern: IMAGE:TAG
|
||||
values:
|
||||
- marker: IMAGE
|
||||
ref: '#/definitions/io.k8s.cli.setters.image'
|
||||
- marker: TAG
|
||||
ref: '#/definitions/io.k8s.cli.setters.tag'
|
||||
`,
|
||||
expectedResources: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.7.9 # {"$ref":"#/definitions/io.k8s.cli.substitutions.image"}
|
||||
- name: sidecar
|
||||
image: sidecar:1.7.9
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "substitution and create setters 1",
|
||||
args: []string{
|
||||
"image", "something/nginx:1.7.9", "--pattern", "something/IMAGE:TAG",
|
||||
"--value", "IMAGE=image", "--value", "TAG=tag"},
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: something/nginx:1.7.9
|
||||
- name: sidecar
|
||||
image: sidecar:1.7.9
|
||||
`,
|
||||
inputOpenAPI: `
|
||||
apiVersion: v1alpha1
|
||||
kind: Example
|
||||
`,
|
||||
expectedOpenAPI: `
|
||||
apiVersion: v1alpha1
|
||||
kind: Example
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.image:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: image
|
||||
value: nginx
|
||||
io.k8s.cli.setters.tag:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: tag
|
||||
value: 1.7.9
|
||||
io.k8s.cli.substitutions.image:
|
||||
x-k8s-cli:
|
||||
substitution:
|
||||
name: image
|
||||
pattern: something/IMAGE:TAG
|
||||
values:
|
||||
- marker: IMAGE
|
||||
ref: '#/definitions/io.k8s.cli.setters.image'
|
||||
- marker: TAG
|
||||
ref: '#/definitions/io.k8s.cli.setters.tag'
|
||||
`,
|
||||
expectedResources: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: something/nginx:1.7.9 # {"$ref":"#/definitions/io.k8s.cli.substitutions.image"}
|
||||
- name: sidecar
|
||||
image: sidecar:1.7.9
|
||||
`,
|
||||
},
|
||||
}
|
||||
for i := range tests {
|
||||
test := tests[i]
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
// reset the openAPI afterward
|
||||
openapi.ResetOpenAPI()
|
||||
defer openapi.ResetOpenAPI()
|
||||
|
||||
f, err := ioutil.TempFile("", "k8s-cli-")
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
defer os.Remove(f.Name())
|
||||
err = ioutil.WriteFile(f.Name(), []byte(test.inputOpenAPI), 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
commands.GetOpenAPIFile = func(args []string) (s string, err error) {
|
||||
return f.Name(), nil
|
||||
}
|
||||
|
||||
r, err := ioutil.TempFile("", "k8s-cli-*.yaml")
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
defer os.Remove(r.Name())
|
||||
err = ioutil.WriteFile(r.Name(), []byte(test.input), 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
runner := commands.NewCreateSubstitutionRunner("")
|
||||
out := &bytes.Buffer{}
|
||||
runner.Command.SetOut(out)
|
||||
runner.Command.SetArgs(append([]string{r.Name()}, test.args...))
|
||||
err = runner.Command.Execute()
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if !assert.Equal(t, test.out, out.String()) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
actualResources, err := ioutil.ReadFile(r.Name())
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
if !assert.Equal(t,
|
||||
strings.TrimSpace(test.expectedResources),
|
||||
strings.TrimSpace(string(actualResources))) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
actualOpenAPI, err := ioutil.ReadFile(f.Name())
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
if !assert.Equal(t,
|
||||
strings.TrimSpace(test.expectedOpenAPI),
|
||||
strings.TrimSpace(string(actualOpenAPI))) {
|
||||
t.FailNow()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,15 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/olekukonko/tablewriter"
|
||||
"github.com/spf13/cobra"
|
||||
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
||||
"sigs.k8s.io/kustomize/kyaml/setters"
|
||||
"sigs.k8s.io/kustomize/kyaml/setters2"
|
||||
)
|
||||
|
||||
// NewListSettersRunner returns a command runner.
|
||||
@@ -33,15 +39,57 @@ func ListSettersCommand(parent string) *cobra.Command {
|
||||
type ListSettersRunner struct {
|
||||
Command *cobra.Command
|
||||
Lookup setters.LookupSetters
|
||||
List setters2.List
|
||||
}
|
||||
|
||||
func (r *ListSettersRunner) preRunE(c *cobra.Command, args []string) error {
|
||||
if len(args) > 1 {
|
||||
r.Lookup.Name = args[1]
|
||||
r.List.Name = args[1]
|
||||
}
|
||||
|
||||
initSetterVersion(c, args)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *ListSettersRunner) runE(c *cobra.Command, args []string) error {
|
||||
if setterVersion == "v2" {
|
||||
// use setters v2
|
||||
path, err := GetOpenAPIFile(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := r.List.List(path, args[0]); err != nil {
|
||||
return err
|
||||
}
|
||||
table := newTable(c.OutOrStdout())
|
||||
table.SetHeader([]string{"NAME", "VALUE", "SET BY", "DESCRIPTION", "COUNT"})
|
||||
for i := range r.List.Setters {
|
||||
s := r.List.Setters[i]
|
||||
table.Append([]string{
|
||||
s.Name, s.Value, s.SetBy, s.Description, fmt.Sprintf("%d", s.Count)})
|
||||
}
|
||||
table.Render()
|
||||
|
||||
if len(r.List.Setters) == 0 {
|
||||
// exit non-0 if no matching setters are found
|
||||
if ExitOnError {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return handleError(c, lookup(r.Lookup, c, args))
|
||||
}
|
||||
|
||||
func newTable(o io.Writer) *tablewriter.Table {
|
||||
table := tablewriter.NewWriter(o)
|
||||
table.SetRowLine(false)
|
||||
table.SetBorder(false)
|
||||
table.SetHeaderLine(false)
|
||||
table.SetColumnSeparator(" ")
|
||||
table.SetCenterSeparator(" ")
|
||||
table.SetAlignment(tablewriter.ALIGN_LEFT)
|
||||
return table
|
||||
}
|
||||
|
||||
296
cmd/config/internal/commands/cmdlistsetters_test.go
Normal file
296
cmd/config/internal/commands/cmdlistsetters_test.go
Normal file
@@ -0,0 +1,296 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package commands_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
)
|
||||
|
||||
func TestListSettersCommand(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
openapi string
|
||||
input string
|
||||
args []string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "list-replicas",
|
||||
openapi: `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.replicas:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: replicas
|
||||
value: "3"
|
||||
setBy: me
|
||||
description: "hello world"
|
||||
`,
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
|
||||
`,
|
||||
expected: ` NAME VALUE SET BY DESCRIPTION COUNT
|
||||
replicas 3 me hello world 1
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "list-multiple",
|
||||
openapi: `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.replicas:
|
||||
description: "hello world 1"
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: replicas
|
||||
value: "3"
|
||||
setBy: me1
|
||||
io.k8s.cli.setters.image:
|
||||
description: "hello world 2"
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: image
|
||||
value: "nginx"
|
||||
setBy: me2
|
||||
io.k8s.cli.setters.tag:
|
||||
description: "hello world 3"
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: tag
|
||||
value: "1.7.9"
|
||||
setBy: me3
|
||||
io.k8s.cli.substitutions.image:
|
||||
x-k8s-cli:
|
||||
substitution:
|
||||
name: image
|
||||
pattern: IMAGE:TAG
|
||||
values:
|
||||
- marker: IMAGE
|
||||
ref: '#/definitions/io.k8s.cli.setters.image'
|
||||
- marker: TAG
|
||||
ref: '#/definitions/io.k8s.cli.setters.tag'
|
||||
`,
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
|
||||
- name: nginx2
|
||||
image: nginx # {"$ref": "#/definitions/io.k8s.cli.setters.image"}
|
||||
`,
|
||||
expected: ` NAME VALUE SET BY DESCRIPTION COUNT
|
||||
image nginx me2 hello world 2 2
|
||||
replicas 3 me1 hello world 1 1
|
||||
tag 1.7.9 me3 hello world 3 1
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "list-multiple-resources",
|
||||
openapi: `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.replicas:
|
||||
description: "hello world 1"
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: replicas
|
||||
value: "3"
|
||||
setBy: me1
|
||||
io.k8s.cli.setters.image:
|
||||
description: "hello world 2"
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: image
|
||||
value: "nginx"
|
||||
setBy: me2
|
||||
io.k8s.cli.setters.tag:
|
||||
description: "hello world 3"
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: tag
|
||||
value: "1.7.9"
|
||||
setBy: me3
|
||||
io.k8s.cli.substitutions.image:
|
||||
x-k8s-cli:
|
||||
substitution:
|
||||
name: image
|
||||
pattern: IMAGE:TAG
|
||||
values:
|
||||
- marker: IMAGE
|
||||
ref: '#/definitions/io.k8s.cli.setters.image'
|
||||
- marker: TAG
|
||||
ref: '#/definitions/io.k8s.cli.setters.tag'
|
||||
`,
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment-1
|
||||
spec:
|
||||
replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
|
||||
- name: nginx2
|
||||
image: nginx # {"$ref": "#/definitions/io.k8s.cli.setters.image"}
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment-2
|
||||
spec:
|
||||
replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
|
||||
- name: nginx2
|
||||
image: nginx
|
||||
`,
|
||||
expected: ` NAME VALUE SET BY DESCRIPTION COUNT
|
||||
image nginx me2 hello world 2 3
|
||||
replicas 3 me1 hello world 1 2
|
||||
tag 1.7.9 me3 hello world 3 2
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "list-name",
|
||||
args: []string{"image"},
|
||||
openapi: `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.replicas:
|
||||
description: "hello world 1"
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: replicas
|
||||
value: "3"
|
||||
setBy: me1
|
||||
io.k8s.cli.setters.image:
|
||||
description: "hello world 2"
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: image
|
||||
value: "nginx"
|
||||
setBy: me2
|
||||
io.k8s.cli.setters.tag:
|
||||
description: "hello world 3"
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: tag
|
||||
value: "1.7.9"
|
||||
setBy: me3
|
||||
io.k8s.cli.substitutions.image:
|
||||
x-k8s-cli:
|
||||
substitution:
|
||||
name: image
|
||||
pattern: IMAGE:TAG
|
||||
values:
|
||||
- marker: IMAGE
|
||||
ref: '#/definitions/io.k8s.cli.setters.image'
|
||||
- marker: TAG
|
||||
ref: '#/definitions/io.k8s.cli.setters.tag'
|
||||
`,
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment-1
|
||||
spec:
|
||||
replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
|
||||
- name: nginx2
|
||||
image: nginx # {"$ref": "#/definitions/io.k8s.cli.setters.image"}
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment-2
|
||||
spec:
|
||||
replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
|
||||
- name: nginx2
|
||||
image: nginx
|
||||
`,
|
||||
expected: ` NAME VALUE SET BY DESCRIPTION COUNT
|
||||
image nginx me2 hello world 2 3
|
||||
`,
|
||||
},
|
||||
}
|
||||
for i := range tests {
|
||||
test := tests[i]
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
// reset the openAPI afterward
|
||||
openapi.ResetOpenAPI()
|
||||
defer openapi.ResetOpenAPI()
|
||||
|
||||
f, err := ioutil.TempFile("", "k8s-cli-")
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
defer os.Remove(f.Name())
|
||||
commands.GetOpenAPIFile = func(args []string) (s string, err error) {
|
||||
err = ioutil.WriteFile(f.Name(), []byte(test.openapi), 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
return f.Name(), nil
|
||||
}
|
||||
|
||||
r, err := ioutil.TempFile("", "k8s-cli-*.yaml")
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
defer os.Remove(r.Name())
|
||||
err = ioutil.WriteFile(r.Name(), []byte(test.input), 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
runner := commands.NewListSettersRunner("")
|
||||
actual := &bytes.Buffer{}
|
||||
runner.Command.SetOut(actual)
|
||||
runner.Command.SetArgs(append([]string{r.Name()}, test.args...))
|
||||
err = runner.Command.Execute()
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if !assert.Equal(t, test.expected, actual.String()) {
|
||||
t.FailNow()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -6,19 +6,21 @@ package commands
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/olekukonko/tablewriter"
|
||||
"github.com/spf13/cobra"
|
||||
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/setters"
|
||||
"sigs.k8s.io/kustomize/kyaml/setters2/settersutil"
|
||||
)
|
||||
|
||||
// NewSetRunner returns a command runner.
|
||||
func NewSetRunner(parent string) *SetRunner {
|
||||
r := &SetRunner{}
|
||||
c := &cobra.Command{
|
||||
Use: "set DIR [NAME] [VALUE]",
|
||||
Use: "set DIR NAME [VALUE]",
|
||||
Args: cobra.RangeArgs(1, 3),
|
||||
Short: commands.SetShort,
|
||||
Long: commands.SetLong,
|
||||
@@ -32,18 +34,48 @@ func NewSetRunner(parent string) *SetRunner {
|
||||
"annotate the field with who set it")
|
||||
c.Flags().StringVar(&r.Perform.Description, "description", "",
|
||||
"annotate the field with a description of its value")
|
||||
c.Flags().StringVar(&setterVersion, "version", "",
|
||||
"use this version of the setter format")
|
||||
c.Flags().MarkHidden("version")
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
var setterVersion string
|
||||
|
||||
var GetOpenAPIFile = func(args []string) (string, error) {
|
||||
return filepath.Join(args[0], "kustomization"), nil
|
||||
}
|
||||
|
||||
func SetCommand(parent string) *cobra.Command {
|
||||
return NewSetRunner(parent).Command
|
||||
}
|
||||
|
||||
type SetRunner struct {
|
||||
Command *cobra.Command
|
||||
Lookup setters.LookupSetters
|
||||
Perform setters.PerformSetters
|
||||
Command *cobra.Command
|
||||
Lookup setters.LookupSetters
|
||||
Perform setters.PerformSetters
|
||||
Set settersutil.FieldSetter
|
||||
OpenAPIFile string
|
||||
}
|
||||
|
||||
func initSetterVersion(c *cobra.Command, args []string) error {
|
||||
setterVersion = "v2"
|
||||
l := setters.LookupSetters{}
|
||||
|
||||
// backwards compatibility for resources with setter v1
|
||||
err := kio.Pipeline{
|
||||
Inputs: []kio.Reader{&kio.LocalPackageReader{PackagePath: args[0]}},
|
||||
Filters: []kio.Filter{&l},
|
||||
}.Execute()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(l.SetterCounts) > 0 {
|
||||
setterVersion = "v1"
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *SetRunner) preRunE(c *cobra.Command, args []string) error {
|
||||
@@ -55,15 +87,37 @@ func (r *SetRunner) preRunE(c *cobra.Command, args []string) error {
|
||||
r.Perform.Value = args[2]
|
||||
}
|
||||
|
||||
if setterVersion == "" {
|
||||
if len(args) < 3 {
|
||||
setterVersion = "v1"
|
||||
} else if err := initSetterVersion(c, args); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if setterVersion == "v2" {
|
||||
var err error
|
||||
r.Set.Name = args[1]
|
||||
r.Set.Value = args[2]
|
||||
r.Set.Description = r.Perform.Description
|
||||
r.Set.SetBy = r.Perform.SetBy
|
||||
r.OpenAPIFile, err = GetOpenAPIFile(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *SetRunner) runE(c *cobra.Command, args []string) error {
|
||||
|
||||
if setterVersion == "v2" {
|
||||
count, err := r.Set.Set(r.OpenAPIFile, args[0])
|
||||
fmt.Fprintf(c.OutOrStdout(), "set %d fields\n", count)
|
||||
return handleError(c, err)
|
||||
}
|
||||
if len(args) == 3 {
|
||||
return handleError(c, r.perform(c, args))
|
||||
}
|
||||
|
||||
return handleError(c, lookup(r.Lookup, c, args))
|
||||
}
|
||||
|
||||
|
||||
275
cmd/config/internal/commands/cmdset_test.go
Normal file
275
cmd/config/internal/commands/cmdset_test.go
Normal file
@@ -0,0 +1,275 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package commands_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
)
|
||||
|
||||
func TestSetCommand(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
inputOpenAPI string
|
||||
input string
|
||||
args []string
|
||||
out string
|
||||
expectedOpenAPI string
|
||||
expectedResources string
|
||||
}{
|
||||
{
|
||||
name: "set replicas",
|
||||
args: []string{"replicas", "4", "--description", "hi there", "--set-by", "pw"},
|
||||
out: "set 1 fields\n",
|
||||
inputOpenAPI: `
|
||||
apiVersion: v1alpha1
|
||||
kind: Example
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.replicas:
|
||||
description: hello world
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: replicas
|
||||
value: "3"
|
||||
setBy: me
|
||||
`,
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 3 # {"$ref":"#/definitions/io.k8s.cli.setters.replicas"}
|
||||
`,
|
||||
expectedOpenAPI: `
|
||||
apiVersion: v1alpha1
|
||||
kind: Example
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.replicas:
|
||||
description: hi there
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: replicas
|
||||
value: "4"
|
||||
setBy: pw
|
||||
`,
|
||||
expectedResources: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 4 # {"$ref":"#/definitions/io.k8s.cli.setters.replicas"}
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "set replicas no description",
|
||||
args: []string{"replicas", "4"},
|
||||
out: "set 1 fields\n",
|
||||
inputOpenAPI: `
|
||||
apiVersion: v1alpha1
|
||||
kind: Example
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.replicas:
|
||||
description: hello world
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: replicas
|
||||
value: "3"
|
||||
setBy: me
|
||||
`,
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 3 # {"$ref":"#/definitions/io.k8s.cli.setters.replicas"}
|
||||
`,
|
||||
expectedOpenAPI: `
|
||||
apiVersion: v1alpha1
|
||||
kind: Example
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.replicas:
|
||||
description: hello world
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: replicas
|
||||
value: "4"
|
||||
setBy: me
|
||||
`,
|
||||
expectedResources: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 4 # {"$ref":"#/definitions/io.k8s.cli.setters.replicas"}
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "set image",
|
||||
args: []string{"tag", "1.8.1"},
|
||||
out: "set 1 fields\n",
|
||||
inputOpenAPI: `
|
||||
apiVersion: v1alpha1
|
||||
kind: Example
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.image:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: image
|
||||
value: "nginx"
|
||||
io.k8s.cli.setters.tag:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: tag
|
||||
value: "1.7.9"
|
||||
io.k8s.cli.substitutions.image:
|
||||
x-k8s-cli:
|
||||
substitution:
|
||||
name: image
|
||||
pattern: IMAGE:TAG
|
||||
values:
|
||||
- marker: IMAGE
|
||||
ref: '#/definitions/io.k8s.cli.setters.image'
|
||||
- marker: TAG
|
||||
ref: '#/definitions/io.k8s.cli.setters.tag'
|
||||
`,
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.7.9 # {"$ref":"#/definitions/io.k8s.cli.substitutions.image"}
|
||||
- name: sidecar
|
||||
image: sidecar:1.7.9
|
||||
`,
|
||||
expectedOpenAPI: `
|
||||
apiVersion: v1alpha1
|
||||
kind: Example
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.image:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: image
|
||||
value: "nginx"
|
||||
io.k8s.cli.setters.tag:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: tag
|
||||
value: "1.8.1"
|
||||
io.k8s.cli.substitutions.image:
|
||||
x-k8s-cli:
|
||||
substitution:
|
||||
name: image
|
||||
pattern: IMAGE:TAG
|
||||
values:
|
||||
- marker: IMAGE
|
||||
ref: '#/definitions/io.k8s.cli.setters.image'
|
||||
- marker: TAG
|
||||
ref: '#/definitions/io.k8s.cli.setters.tag'
|
||||
|
||||
`,
|
||||
expectedResources: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.8.1 # {"$ref":"#/definitions/io.k8s.cli.substitutions.image"}
|
||||
- name: sidecar
|
||||
image: sidecar:1.7.9
|
||||
`,
|
||||
},
|
||||
}
|
||||
for i := range tests {
|
||||
test := tests[i]
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
// reset the openAPI afterward
|
||||
openapi.ResetOpenAPI()
|
||||
defer openapi.ResetOpenAPI()
|
||||
|
||||
f, err := ioutil.TempFile("", "k8s-cli-")
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
defer os.Remove(f.Name())
|
||||
err = ioutil.WriteFile(f.Name(), []byte(test.inputOpenAPI), 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
commands.GetOpenAPIFile = func(args []string) (s string, err error) {
|
||||
return f.Name(), nil
|
||||
}
|
||||
|
||||
r, err := ioutil.TempFile("", "k8s-cli-*.yaml")
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
defer os.Remove(r.Name())
|
||||
err = ioutil.WriteFile(r.Name(), []byte(test.input), 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
runner := commands.NewSetRunner("")
|
||||
out := &bytes.Buffer{}
|
||||
runner.Command.SetOut(out)
|
||||
runner.Command.SetArgs(append([]string{r.Name()}, test.args...))
|
||||
err = runner.Command.Execute()
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if !assert.Equal(t, test.out, out.String()) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
actualResources, err := ioutil.ReadFile(r.Name())
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
if !assert.Equal(t,
|
||||
strings.TrimSpace(test.expectedResources),
|
||||
strings.TrimSpace(string(actualResources))) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
actualOpenAPI, err := ioutil.ReadFile(f.Name())
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
if !assert.Equal(t,
|
||||
strings.TrimSpace(test.expectedOpenAPI),
|
||||
strings.TrimSpace(string(actualOpenAPI))) {
|
||||
t.FailNow()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -143,6 +143,8 @@ func TestCmd_failFiles(t *testing.T) {
|
||||
// fmt the files
|
||||
r := commands.GetFmtRunner("")
|
||||
r.Command.SetArgs([]string{"notrealfile"})
|
||||
r.Command.SilenceUsage = true
|
||||
r.Command.SilenceErrors = true
|
||||
err := r.Command.Execute()
|
||||
assert.EqualError(t, err, "lstat notrealfile: no such file or directory")
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
type FieldMeta struct {
|
||||
Schema spec.Schema
|
||||
|
||||
Extensions *XKustomize
|
||||
Extensions XKustomize
|
||||
}
|
||||
|
||||
type XKustomize struct {
|
||||
@@ -62,9 +62,22 @@ func (fm *FieldMeta) Read(n *yaml.RNode) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
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 fm.Extensions != nil {
|
||||
if !isExtensionEmpty(fm.Extensions) {
|
||||
fm.Schema.VendorExtensible.AddExtension("x-kustomize", fm.Extensions)
|
||||
} else {
|
||||
delete(fm.Schema.VendorExtensible.Extensions, "x-kustomize")
|
||||
|
||||
@@ -6,6 +6,7 @@ package openapi
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"sync"
|
||||
|
||||
"github.com/go-openapi/spec"
|
||||
@@ -44,6 +45,66 @@ func SchemaForResourceType(t yaml.TypeMeta) *ResourceSchema {
|
||||
return &ResourceSchema{Schema: rs}
|
||||
}
|
||||
|
||||
// SupplementaryOpenAPIFieldName is the conventional field name (JSON/YAML) containing
|
||||
// supplementary OpenAPI definitions.
|
||||
const SupplementaryOpenAPIFieldName = "openAPI"
|
||||
|
||||
// AddSchemaFromFile reads the file at path and parses the OpenAPI definitions
|
||||
// from the field "openAPI"
|
||||
func AddSchemaFromFile(path string) error {
|
||||
return AddSchemaFromFileUsingField(path, SupplementaryOpenAPIFieldName)
|
||||
}
|
||||
|
||||
// AddSchemaFromFileUsingField reads the file at path and parses the OpenAPI definitions
|
||||
// from the specified field. If field is the empty string, use the whole document as
|
||||
// OpenAPI.
|
||||
func AddSchemaFromFileUsingField(path, field string) error {
|
||||
b, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// parse the yaml file (json is a subset of yaml, so will also parse)
|
||||
y, err := yaml.Parse(string(b))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if field != "" {
|
||||
// get the field containing the openAPI
|
||||
m := y.Field(field)
|
||||
if yaml.IsFieldEmpty(m) {
|
||||
// doesn't contain openAPI definitions
|
||||
return nil
|
||||
}
|
||||
y = m.Value
|
||||
}
|
||||
|
||||
oAPI, err := y.String()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// convert the yaml openAPI to a JSON string by unmarshalling it to an
|
||||
// interface{} and the marshalling it to a string
|
||||
var o interface{}
|
||||
err = yaml.Unmarshal([]byte(oAPI), &o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
j, err := json.Marshal(o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// add the json schema to the global schema
|
||||
_, err = AddSchema(j)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddSchema parses s, and adds definitions from s to the global schema.
|
||||
func AddSchema(s []byte) (*spec.Schema, error) {
|
||||
return parse(s)
|
||||
@@ -296,41 +357,6 @@ func resolve(root interface{}, ref *spec.Ref) (*spec.Schema, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func PopulateDefsInOpenAPI(s string) error {
|
||||
y, err := yaml.Parse(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// get the field containing the openAPI
|
||||
f := y.Field("openAPI")
|
||||
|
||||
defs, err := f.Value.String()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// convert the yaml openAPI to an interface{}
|
||||
// which can be marshalled into json
|
||||
var o interface{}
|
||||
err = yaml.Unmarshal([]byte(defs), &o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// convert the interface{} into a json string
|
||||
j, err := json.Marshal(o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// add the json schema to the global schema
|
||||
_, err = AddSchema(j)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func rootSchema() *spec.Schema {
|
||||
initSchema()
|
||||
return &globalSchema.schema
|
||||
|
||||
@@ -5,6 +5,7 @@ package openapi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -123,8 +124,8 @@ func TestSchemaForResourceType(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPopulateDefsInOpenAPI_Setter(t *testing.T) {
|
||||
globalSchema = openapiData{}
|
||||
func TestAddSchemaFromFile(t *testing.T) {
|
||||
ResetOpenAPI()
|
||||
inputyaml := `
|
||||
openAPI:
|
||||
definitions:
|
||||
@@ -134,8 +135,15 @@ openAPI:
|
||||
name: image-name
|
||||
value: "nginx"
|
||||
`
|
||||
err := PopulateDefsInOpenAPI(inputyaml)
|
||||
f, err := ioutil.TempFile("", "openapi-")
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
if !assert.NoError(t, ioutil.WriteFile(f.Name(), []byte(inputyaml), 0600)) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
err = AddSchemaFromFile(f.Name())
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
@@ -153,7 +161,7 @@ openAPI:
|
||||
}
|
||||
|
||||
func TestPopulateDefsInOpenAPI_Substitution(t *testing.T) {
|
||||
globalSchema = openapiData{}
|
||||
ResetOpenAPI()
|
||||
inputyaml := `
|
||||
openAPI:
|
||||
definitions:
|
||||
@@ -178,11 +186,18 @@ openAPI:
|
||||
- marker: "IMAGE_TAG"
|
||||
ref: "#/definitions/io.k8s.cli.setters.image-tag"
|
||||
`
|
||||
err := PopulateDefsInOpenAPI(inputyaml)
|
||||
|
||||
f, err := ioutil.TempFile("", "openapi-")
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
if !assert.NoError(t, ioutil.WriteFile(f.Name(), []byte(inputyaml), 0600)) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if !assert.NoError(t, AddSchemaFromFile(f.Name())) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
s, err := GetSchema(`{"$ref": "#/definitions/io.k8s.cli.substitutions.image"}`)
|
||||
|
||||
@@ -199,3 +214,26 @@ openAPI:
|
||||
` map[marker:IMAGE_TAG ref:#/definitions/io.k8s.cli.setters.image-tag]]]]]`,
|
||||
fmt.Sprintf("%v", s.Schema.Extensions))
|
||||
}
|
||||
|
||||
func TestAddSchemaFromFile_empty(t *testing.T) {
|
||||
ResetOpenAPI()
|
||||
inputyaml := `
|
||||
kind: Example
|
||||
`
|
||||
|
||||
f, err := ioutil.TempFile("", "openapi-")
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
if !assert.NoError(t, ioutil.WriteFile(f.Name(), []byte(inputyaml), 0600)) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if !assert.NoError(t, AddSchemaFromFile(f.Name())) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if !assert.Equal(t, len(globalSchema.schema.Definitions), 0) {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"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"
|
||||
)
|
||||
|
||||
@@ -72,3 +73,128 @@ func (a *Add) visitScalar(object *yaml.RNode, p string) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
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/"
|
||||
)
|
||||
|
||||
// SetterDefinition may be used to update a files OpenAPI definitions with a new setter.
|
||||
type SetterDefinition struct {
|
||||
// Name is the name of the setter to create or update.
|
||||
Name string `yaml:"name"`
|
||||
|
||||
// Value is the value of the setter.
|
||||
Value string `yaml:"value"`
|
||||
|
||||
// SetBy is the person or role that last set the value.
|
||||
SetBy string `yaml:"setBy,omitempty"`
|
||||
|
||||
// Description is a description of the value.
|
||||
Description string `yaml:"description,omitempty"`
|
||||
|
||||
// Count is the number of fields set by this setter.
|
||||
Count int `yaml:"count,omitempty"`
|
||||
}
|
||||
|
||||
func (sd SetterDefinition) AddToFile(path string) error {
|
||||
return yaml.UpdateFile(sd, path)
|
||||
}
|
||||
|
||||
func (sd SetterDefinition) Filter(object *yaml.RNode) (*yaml.RNode, error) {
|
||||
key := SetterDefinitionPrefix + sd.Name
|
||||
|
||||
def, err := object.Pipe(yaml.LookupCreate(
|
||||
yaml.MappingNode, openapi.SupplementaryOpenAPIFieldName, "definitions", key))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if sd.Description != "" {
|
||||
err = def.PipeE(yaml.FieldSetter{Name: "description", StringValue: sd.Description})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// don't write the description to the extension
|
||||
sd.Description = ""
|
||||
}
|
||||
|
||||
ext, err := def.Pipe(yaml.LookupCreate(yaml.MappingNode, K8sCliExtensionKey))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b, err := yaml.Marshal(sd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
y, err := yaml.Parse(string(b))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := ext.PipeE(yaml.SetField("setter", y)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return object, nil
|
||||
}
|
||||
|
||||
// SetterDefinition may be used to update a files OpenAPI definitions with a new substitution.
|
||||
type SubstitutionDefinition struct {
|
||||
// Name is the name of the substitution to create or update
|
||||
Name string `yaml:"name"`
|
||||
|
||||
// Pattern is the substitution pattern into which setter values are substituted
|
||||
Pattern string `yaml:"pattern"`
|
||||
|
||||
// Values are setters which are substituted into pattern to produce a field value
|
||||
Values []Value `yaml:"values"`
|
||||
}
|
||||
|
||||
type Value struct {
|
||||
// Marker is the string marker in pattern that is replace by the referenced setter.
|
||||
Marker string `yaml:"marker"`
|
||||
|
||||
// Ref is a reference to a setter to pull the replacement value from.
|
||||
Ref string `yaml:"ref"`
|
||||
}
|
||||
|
||||
func (sd SubstitutionDefinition) AddToFile(path string) error {
|
||||
return yaml.UpdateFile(sd, path)
|
||||
}
|
||||
|
||||
func (sd SubstitutionDefinition) Filter(object *yaml.RNode) (*yaml.RNode, error) {
|
||||
// create the substitution extension value by marshalling the SubstitutionDefinition itself
|
||||
b, err := yaml.Marshal(sd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sub, err := yaml.Parse(string(b))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// lookup or create the definition for the substitution
|
||||
defKey := SubstitutionDefinitionPrefix + sd.Name
|
||||
def, err := object.Pipe(yaml.LookupCreate(
|
||||
yaml.MappingNode, openapi.SupplementaryOpenAPIFieldName, "definitions", defKey, "x-k8s-cli"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// set the substitution on the definition
|
||||
if err := def.PipeE(yaml.SetField("substitution", sub)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return object, nil
|
||||
}
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
package setters2
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -204,3 +207,152 @@ spec:
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var resourcefile = `apiVersion: resource.dev/v1alpha1
|
||||
kind: resourcefile
|
||||
metadata:
|
||||
name: hello-world-set
|
||||
upstream:
|
||||
type: git
|
||||
git:
|
||||
commit: 5c1c019b59299a4f6c7edd1ff5ff54d720621bbe
|
||||
directory: /package-examples/helloworld-set
|
||||
ref: v0.1.0
|
||||
packageMetadata:
|
||||
shortDescription: example package using setters`
|
||||
|
||||
func TestAdd_Filter2(t *testing.T) {
|
||||
path := filepath.Join(os.TempDir(), "resourcefile")
|
||||
|
||||
//write initial resourcefile to temp path
|
||||
err := ioutil.WriteFile(path, []byte(resourcefile), 0666)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
//add a setter definition
|
||||
sd := SetterDefinition{
|
||||
Name: "image",
|
||||
Value: "1",
|
||||
}
|
||||
|
||||
err = sd.AddToFile(path)
|
||||
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// update setter definition
|
||||
sd2 := SetterDefinition{
|
||||
Name: "image",
|
||||
Value: "2",
|
||||
}
|
||||
|
||||
err = sd2.AddToFile(path)
|
||||
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
expected := `apiVersion: resource.dev/v1alpha1
|
||||
kind: resourcefile
|
||||
metadata:
|
||||
name: hello-world-set
|
||||
upstream:
|
||||
type: git
|
||||
git:
|
||||
commit: 5c1c019b59299a4f6c7edd1ff5ff54d720621bbe
|
||||
directory: /package-examples/helloworld-set
|
||||
ref: v0.1.0
|
||||
packageMetadata:
|
||||
shortDescription: example package using setters
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.image:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: image
|
||||
value: "2"
|
||||
`
|
||||
assert.Equal(t, expected, string(b))
|
||||
}
|
||||
|
||||
func TestAddUpdateSubstitution(t *testing.T) {
|
||||
path := filepath.Join(os.TempDir(), "resourcefile")
|
||||
|
||||
//write initial resourcefile to temp path
|
||||
err := ioutil.WriteFile(path, []byte(resourcefile), 0666)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
value1 := Value{
|
||||
Marker: "IMAGE_NAME",
|
||||
Ref: "#/definitions/io.k8s.cli.setters.image-name",
|
||||
}
|
||||
|
||||
value2 := Value{
|
||||
Marker: "IMAGE_TAG",
|
||||
Ref: "#/definitions/io.k8s.cli.setters.image-tag",
|
||||
}
|
||||
|
||||
values := []Value{value1, value2}
|
||||
|
||||
//add a setter definition
|
||||
subd := SubstitutionDefinition{
|
||||
Name: "image",
|
||||
Pattern: "IMAGE_NAME:IMAGE_TAG",
|
||||
Values: values,
|
||||
}
|
||||
|
||||
err = subd.AddToFile(path)
|
||||
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// update setter definition
|
||||
subd2 := SubstitutionDefinition{
|
||||
Name: "image",
|
||||
Pattern: "IMAGE_NAME:IMAGE_TAG2",
|
||||
}
|
||||
|
||||
err = subd2.AddToFile(path)
|
||||
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
expected := `apiVersion: resource.dev/v1alpha1
|
||||
kind: resourcefile
|
||||
metadata:
|
||||
name: hello-world-set
|
||||
upstream:
|
||||
type: git
|
||||
git:
|
||||
commit: 5c1c019b59299a4f6c7edd1ff5ff54d720621bbe
|
||||
directory: /package-examples/helloworld-set
|
||||
ref: v0.1.0
|
||||
packageMetadata:
|
||||
shortDescription: example package using setters
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.substitutions.image:
|
||||
x-k8s-cli:
|
||||
substitution:
|
||||
name: image
|
||||
pattern: IMAGE_NAME:IMAGE_TAG2
|
||||
values: []
|
||||
`
|
||||
assert.Equal(t, expected, string(b))
|
||||
}
|
||||
|
||||
117
kyaml/setters2/list.go
Normal file
117
kyaml/setters2/list.go
Normal file
@@ -0,0 +1,117 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package setters2
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// List lists the setters specified in the OpenAPI
|
||||
type List struct {
|
||||
Name string
|
||||
|
||||
Setters []SetterDefinition
|
||||
}
|
||||
|
||||
// List initializes l.Setters with the setters from the OpenAPI definitions in the file
|
||||
func (l *List) List(openAPIPath, resourcePath string) error {
|
||||
if err := openapi.AddSchemaFromFile(openAPIPath); err != nil {
|
||||
return err
|
||||
}
|
||||
y, err := yaml.ReadFile(openAPIPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return l.list(y, resourcePath)
|
||||
}
|
||||
|
||||
func (l *List) list(object *yaml.RNode, resourcePath string) error {
|
||||
// read the OpenAPI definitions
|
||||
def, err := object.Pipe(yaml.LookupCreate(yaml.MappingNode, "openAPI", "definitions"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if yaml.IsEmpty(def) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// iterate over definitions -- find those that are setters
|
||||
err = def.VisitFields(func(node *yaml.MapNode) error {
|
||||
setter := SetterDefinition{}
|
||||
|
||||
// the definition key -- contains the setter name
|
||||
key := node.Key.YNode().Value
|
||||
|
||||
if !strings.HasPrefix(key, SetterDefinitionPrefix) {
|
||||
// not a setter -- doesn't have the right prefix
|
||||
return nil
|
||||
}
|
||||
|
||||
setterNode, err := node.Value.Pipe(yaml.Lookup(K8sCliExtensionKey, "setter"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if yaml.IsEmpty(setterNode) {
|
||||
// has the setter prefix, but missing the setter extension
|
||||
return errors.Errorf("missing x-k8s-cli.setter for %s", key)
|
||||
}
|
||||
|
||||
// unmarshal the yaml for the setter extension into the definition struct
|
||||
b, err := setterNode.String()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := yaml.Unmarshal([]byte(b), &setter); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if l.Name != "" && l.Name != setter.Name {
|
||||
// not the setter that was requested by list
|
||||
return nil
|
||||
}
|
||||
|
||||
// the description is not part of the extension, and should be pulled out
|
||||
// separately from the extension values.
|
||||
description := node.Value.Field("description")
|
||||
if description != nil {
|
||||
setter.Description = description.Value.YNode().Value
|
||||
}
|
||||
|
||||
// count the number of fields set by this setter
|
||||
setter.Count, err = l.count(resourcePath, setter.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
l.Setters = append(l.Setters, setter)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// sort the setters by their name
|
||||
sort.Slice(l.Setters, func(i, j int) bool {
|
||||
return l.Setters[i].Name < l.Setters[j].Name
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// count returns the number of fields set by the setter with name
|
||||
func (l *List) count(path, name string) (int, error) {
|
||||
s := &Set{Name: name}
|
||||
err := kio.Pipeline{
|
||||
Inputs: []kio.Reader{&kio.LocalPackageReader{PackagePath: path}},
|
||||
Filters: []kio.Filter{kio.FilterAll(s)},
|
||||
}.Execute()
|
||||
|
||||
return s.Count, err
|
||||
}
|
||||
289
kyaml/setters2/list_test.go
Normal file
289
kyaml/setters2/list_test.go
Normal file
@@ -0,0 +1,289 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package setters2
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
)
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
setter string
|
||||
openapi string
|
||||
input string
|
||||
expected []SetterDefinition
|
||||
}{
|
||||
{
|
||||
name: "list-replicas",
|
||||
openapi: `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.replicas:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: replicas
|
||||
value: "3"
|
||||
setBy: me
|
||||
description: "hello world"
|
||||
`,
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
|
||||
`,
|
||||
expected: []SetterDefinition{
|
||||
{Name: "replicas", Value: "3", SetBy: "me", Description: "hello world", Count: 1},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list-multiple",
|
||||
openapi: `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.replicas:
|
||||
description: "hello world 1"
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: replicas
|
||||
value: "3"
|
||||
setBy: me1
|
||||
io.k8s.cli.setters.image:
|
||||
description: "hello world 2"
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: image
|
||||
value: "nginx"
|
||||
setBy: me2
|
||||
io.k8s.cli.setters.tag:
|
||||
description: "hello world 3"
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: tag
|
||||
value: "1.7.9"
|
||||
setBy: me3
|
||||
io.k8s.cli.substitutions.image:
|
||||
x-k8s-cli:
|
||||
substitution:
|
||||
name: image
|
||||
pattern: IMAGE:TAG
|
||||
values:
|
||||
- marker: IMAGE
|
||||
ref: '#/definitions/io.k8s.cli.setters.image'
|
||||
- marker: TAG
|
||||
ref: '#/definitions/io.k8s.cli.setters.tag'
|
||||
`,
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
|
||||
- name: nginx2
|
||||
image: nginx # {"$ref": "#/definitions/io.k8s.cli.setters.image"}
|
||||
`,
|
||||
expected: []SetterDefinition{
|
||||
{Name: "image", Value: "nginx", SetBy: "me2", Description: "hello world 2", Count: 2},
|
||||
{Name: "replicas", Value: "3", SetBy: "me1", Description: "hello world 1", Count: 1},
|
||||
{Name: "tag", Value: "1.7.9", SetBy: "me3", Description: "hello world 3", Count: 1},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list-multiple-resources",
|
||||
openapi: `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.replicas:
|
||||
description: "hello world 1"
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: replicas
|
||||
value: "3"
|
||||
setBy: me1
|
||||
io.k8s.cli.setters.image:
|
||||
description: "hello world 2"
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: image
|
||||
value: "nginx"
|
||||
setBy: me2
|
||||
io.k8s.cli.setters.tag:
|
||||
description: "hello world 3"
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: tag
|
||||
value: "1.7.9"
|
||||
setBy: me3
|
||||
io.k8s.cli.substitutions.image:
|
||||
x-k8s-cli:
|
||||
substitution:
|
||||
name: image
|
||||
pattern: IMAGE:TAG
|
||||
values:
|
||||
- marker: IMAGE
|
||||
ref: '#/definitions/io.k8s.cli.setters.image'
|
||||
- marker: TAG
|
||||
ref: '#/definitions/io.k8s.cli.setters.tag'
|
||||
`,
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment-1
|
||||
spec:
|
||||
replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
|
||||
- name: nginx2
|
||||
image: nginx # {"$ref": "#/definitions/io.k8s.cli.setters.image"}
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment-2
|
||||
spec:
|
||||
replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
|
||||
- name: nginx2
|
||||
image: nginx
|
||||
`,
|
||||
expected: []SetterDefinition{
|
||||
{Name: "image", Value: "nginx", SetBy: "me2", Description: "hello world 2", Count: 3},
|
||||
{Name: "replicas", Value: "3", SetBy: "me1", Description: "hello world 1", Count: 2},
|
||||
{Name: "tag", Value: "1.7.9", SetBy: "me3", Description: "hello world 3", Count: 2},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list-name",
|
||||
openapi: `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.replicas:
|
||||
description: "hello world 1"
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: replicas
|
||||
value: "3"
|
||||
setBy: me1
|
||||
io.k8s.cli.setters.image:
|
||||
description: "hello world 2"
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: image
|
||||
value: "nginx"
|
||||
setBy: me2
|
||||
io.k8s.cli.setters.tag:
|
||||
description: "hello world 3"
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: tag
|
||||
value: "1.7.9"
|
||||
setBy: me3
|
||||
io.k8s.cli.substitutions.image:
|
||||
x-k8s-cli:
|
||||
substitution:
|
||||
name: image
|
||||
pattern: IMAGE:TAG
|
||||
values:
|
||||
- marker: IMAGE
|
||||
ref: '#/definitions/io.k8s.cli.setters.image'
|
||||
- marker: TAG
|
||||
ref: '#/definitions/io.k8s.cli.setters.tag'
|
||||
`,
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment-1
|
||||
spec:
|
||||
replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
|
||||
- name: nginx2
|
||||
image: nginx # {"$ref": "#/definitions/io.k8s.cli.setters.image"}
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment-2
|
||||
spec:
|
||||
replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
|
||||
- name: nginx2
|
||||
image: nginx
|
||||
`,
|
||||
setter: "image",
|
||||
expected: []SetterDefinition{
|
||||
{Name: "image", Value: "nginx", SetBy: "me2", Description: "hello world 2", Count: 3},
|
||||
},
|
||||
},
|
||||
}
|
||||
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)
|
||||
|
||||
f, err := ioutil.TempFile("", "k8s-cli-")
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
defer os.Remove(f.Name())
|
||||
err = ioutil.WriteFile(f.Name(), []byte(test.openapi), 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
r, err := ioutil.TempFile("", "k8s-cli-*.yaml")
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
defer os.Remove(r.Name())
|
||||
err = ioutil.WriteFile(r.Name(), []byte(test.input), 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// invoke the setter
|
||||
instance := &List{Name: test.setter}
|
||||
err = instance.List(f.Name(), r.Name())
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if !assert.Equal(t, test.expected, instance.Setters) {
|
||||
t.FailNow()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,9 @@ 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
|
||||
|
||||
// Count is the number of fields that were updated by calling Filter
|
||||
Count int
|
||||
}
|
||||
|
||||
// Filter implements Set as a yaml.Filter
|
||||
@@ -25,7 +28,7 @@ func (s *Set) Filter(object *yaml.RNode) (*yaml.RNode, error) {
|
||||
}
|
||||
|
||||
// visitScalar
|
||||
func (s *Set) visitScalar(object *yaml.RNode, _ string) error {
|
||||
func (s *Set) visitScalar(object *yaml.RNode, p string) error {
|
||||
// get the openAPI for this field describing how to apply the setter
|
||||
ext, err := getExtFromComment(object)
|
||||
if err != nil {
|
||||
@@ -37,14 +40,18 @@ func (s *Set) visitScalar(object *yaml.RNode, _ string) error {
|
||||
|
||||
// perform a direct set of the field if it matches
|
||||
if s.set(object, ext) {
|
||||
s.Count++
|
||||
return nil
|
||||
}
|
||||
|
||||
// perform a substitution of the field if it matches
|
||||
if sub, err := s.substitute(object, ext); sub || err != nil {
|
||||
sub, err := s.substitute(object, ext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if sub {
|
||||
s.Count++
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -111,3 +118,54 @@ func (s *Set) set(field *yaml.RNode, ext *cliExtension) bool {
|
||||
field.YNode().Value = ext.Setter.Value
|
||||
return true
|
||||
}
|
||||
|
||||
// SetOpenAPI updates a setter value
|
||||
type SetOpenAPI struct {
|
||||
// Name is the name of the setter to add
|
||||
Name string `yaml:"name"`
|
||||
// Value is the current value of the setter
|
||||
Value string `yaml:"value"`
|
||||
|
||||
Description string `yaml:"description"`
|
||||
|
||||
SetBy string `yaml:"setBy"`
|
||||
}
|
||||
|
||||
// UpdateFile updates the OpenAPI definitions in a file with the given setter value.
|
||||
func (s SetOpenAPI) UpdateFile(path string) error {
|
||||
return yaml.UpdateFile(s, path)
|
||||
}
|
||||
|
||||
func (s SetOpenAPI) Filter(object *yaml.RNode) (*yaml.RNode, error) {
|
||||
key := SetterDefinitionPrefix + s.Name
|
||||
def, err := object.Pipe(yaml.Lookup(
|
||||
"openAPI", "definitions", key, "x-k8s-cli", "setter"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if def == nil {
|
||||
return nil, errors.Errorf("no setter %s found", s.Name)
|
||||
}
|
||||
if err := def.PipeE(&yaml.FieldSetter{Name: "value", StringValue: s.Value}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if s.SetBy != "" {
|
||||
if err := def.PipeE(&yaml.FieldSetter{Name: "setBy", StringValue: s.SetBy}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if s.Description != "" {
|
||||
d, err := object.Pipe(yaml.LookupCreate(
|
||||
yaml.MappingNode, "openAPI", "definitions", key))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := d.PipeE(&yaml.FieldSetter{Name: "description", StringValue: s.Description}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return object, nil
|
||||
}
|
||||
|
||||
@@ -438,3 +438,218 @@ func initSchema(t *testing.T, s string) {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetOpenAPI_Filter(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
setter string
|
||||
value string
|
||||
input string
|
||||
expected string
|
||||
description string
|
||||
setBy string
|
||||
err string
|
||||
}{
|
||||
{
|
||||
name: "set-replicas",
|
||||
setter: "replicas",
|
||||
value: "3",
|
||||
input: `
|
||||
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"
|
||||
`,
|
||||
expected: `
|
||||
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: "3"
|
||||
io.k8s.cli.setters.no-match-2':
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: no-match-2
|
||||
value: "2"
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "set-replicas-description",
|
||||
setter: "replicas",
|
||||
value: "3",
|
||||
description: "hello world",
|
||||
input: `
|
||||
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"
|
||||
`,
|
||||
expected: `
|
||||
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: "3"
|
||||
description: hello world
|
||||
io.k8s.cli.setters.no-match-2':
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: no-match-2
|
||||
value: "2"
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "set-replicas-set-by",
|
||||
setter: "replicas",
|
||||
value: "3",
|
||||
setBy: "carl",
|
||||
input: `
|
||||
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"
|
||||
`,
|
||||
expected: `
|
||||
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: "3"
|
||||
setBy: carl
|
||||
io.k8s.cli.setters.no-match-2':
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: no-match-2
|
||||
value: "2"
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "error",
|
||||
setter: "replicas",
|
||||
err: "no setter replicas found",
|
||||
input: `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.no-match-1':
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: no-match-1
|
||||
value: "1"
|
||||
io.k8s.cli.setters.no-match-2':
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: no-match-2
|
||||
value: "2"
|
||||
`,
|
||||
expected: `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.no-match-1':
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: no-match-1
|
||||
value: "1"
|
||||
io.k8s.cli.setters.no-match-2':
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: no-match-2
|
||||
value: "2"
|
||||
`,
|
||||
},
|
||||
}
|
||||
for i := range tests {
|
||||
test := tests[i]
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
in, err := yaml.Parse(test.input)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// invoke the setter
|
||||
instance := &SetOpenAPI{
|
||||
Name: test.setter, Value: test.value,
|
||||
SetBy: test.setBy, Description: test.description}
|
||||
result, err := instance.Filter(in)
|
||||
if test.err != "" {
|
||||
if !assert.EqualError(t, err, test.err) {
|
||||
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()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package setters2
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
const DefinitionPrefix = "io.k8s.cli.setters."
|
||||
|
||||
type SetterDefinition struct {
|
||||
Name string
|
||||
Value string
|
||||
}
|
||||
|
||||
func (sd SetterDefinition) AddSetterToFile(path string) error {
|
||||
b, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
y, err := yaml.Parse(string(b))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := y.PipeE(sd); err != nil {
|
||||
return err
|
||||
}
|
||||
out, err := y.String()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ioutil.WriteFile(path, []byte(out), 0600); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sd SetterDefinition) Filter(object *yaml.RNode) (*yaml.RNode, error) {
|
||||
key := DefinitionPrefix + sd.Name
|
||||
|
||||
def, err := object.Pipe(yaml.LookupCreate(
|
||||
yaml.MappingNode, "openAPI", "definitions", key, "x-k8s-cli", "setter"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := def.PipeE(yaml.FieldSetter{Name: "name", StringValue: sd.Name}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := def.PipeE(yaml.FieldSetter{Name: "value", StringValue: sd.Value}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return object, nil
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package setters2
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var resourcefile = `apiVersion: resource.dev/v1alpha1
|
||||
kind: resourcefile
|
||||
metadata:
|
||||
name: hello-world-set
|
||||
upstream:
|
||||
type: git
|
||||
git:
|
||||
commit: 5c1c019b59299a4f6c7edd1ff5ff54d720621bbe
|
||||
directory: /package-examples/helloworld-set
|
||||
ref: v0.1.0
|
||||
packageMetadata:
|
||||
shortDescription: example package using setters`
|
||||
|
||||
func TestAddUpdateSetter(t *testing.T) {
|
||||
path := os.TempDir() + "/resourcefile"
|
||||
|
||||
//write initial resourcefile to temp path
|
||||
err := ioutil.WriteFile(path, []byte(resourcefile), 0666)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
//add a setter definition
|
||||
sd := SetterDefinition{
|
||||
Name: "image",
|
||||
Value: "1",
|
||||
}
|
||||
|
||||
err = sd.AddSetterToFile(path)
|
||||
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// update setter definition
|
||||
sd2 := SetterDefinition{
|
||||
Name: "image",
|
||||
Value: "2",
|
||||
}
|
||||
|
||||
err = sd2.AddSetterToFile(path)
|
||||
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
expected := `apiVersion: resource.dev/v1alpha1
|
||||
kind: resourcefile
|
||||
metadata:
|
||||
name: hello-world-set
|
||||
upstream:
|
||||
type: git
|
||||
git:
|
||||
commit: 5c1c019b59299a4f6c7edd1ff5ff54d720621bbe
|
||||
directory: /package-examples/helloworld-set
|
||||
ref: v0.1.0
|
||||
packageMetadata:
|
||||
shortDescription: example package using setters
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.image:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: image
|
||||
value: 2
|
||||
`
|
||||
assert.Equal(t, expected, string(b))
|
||||
}
|
||||
48
kyaml/setters2/settersutil/fieldsetter.go
Normal file
48
kyaml/setters2/settersutil/fieldsetter.go
Normal file
@@ -0,0 +1,48 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package settersutil
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
"sigs.k8s.io/kustomize/kyaml/setters2"
|
||||
)
|
||||
|
||||
// FieldSetter sets the value for a field setter.
|
||||
type FieldSetter struct {
|
||||
// Name is the name of the setter to set
|
||||
Name string
|
||||
|
||||
// Value is the value to set
|
||||
Value string
|
||||
|
||||
Description string
|
||||
|
||||
SetBy string
|
||||
}
|
||||
|
||||
// Set updates the OpenAPI definitions and resources with the new setter value
|
||||
func (fs FieldSetter) Set(openAPIPath, resourcesPath string) (int, error) {
|
||||
// Update the OpenAPI definitions
|
||||
soa := setters2.SetOpenAPI{
|
||||
Name: fs.Name, Value: fs.Value, Description: fs.Description, SetBy: fs.SetBy}
|
||||
if err := soa.UpdateFile(openAPIPath); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Load the updated definitions
|
||||
if err := openapi.AddSchemaFromFile(openAPIPath); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Update the resources with the new value
|
||||
inout := &kio.LocalPackageReadWriter{PackagePath: resourcesPath}
|
||||
s := &setters2.Set{Name: fs.Name}
|
||||
err := kio.Pipeline{
|
||||
Inputs: []kio.Reader{inout},
|
||||
Filters: []kio.Filter{kio.FilterAll(s)},
|
||||
Outputs: []kio.Writer{inout},
|
||||
}.Execute()
|
||||
return s.Count, err
|
||||
}
|
||||
60
kyaml/setters2/settersutil/settercreator.go
Normal file
60
kyaml/setters2/settersutil/settercreator.go
Normal file
@@ -0,0 +1,60 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package settersutil
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
"sigs.k8s.io/kustomize/kyaml/setters2"
|
||||
)
|
||||
|
||||
// SetterCreator creates or updates a setter in the OpenAPI definitions, and inserts references
|
||||
// to the setter from matching resource fields.
|
||||
type SetterCreator struct {
|
||||
// Name is the name of the setter to create or update.
|
||||
Name string
|
||||
|
||||
Description string
|
||||
|
||||
SetBy 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
|
||||
|
||||
// FieldValue if set will add the OpenAPI reference to fields if they have this value.
|
||||
// Optional. If unspecified match all field values.
|
||||
FieldValue string
|
||||
}
|
||||
|
||||
func (c SetterCreator) Create(openAPIPath, resourcesPath string) error {
|
||||
// Update the OpenAPI definitions to hace the setter
|
||||
sd := setters2.SetterDefinition{
|
||||
Name: c.Name, Value: c.FieldValue, Description: c.Description, SetBy: c.SetBy}
|
||||
if err := sd.AddToFile(openAPIPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Load the updated definitions
|
||||
if err := openapi.AddSchemaFromFile(openAPIPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Update the resources with the setter reference
|
||||
inout := &kio.LocalPackageReadWriter{PackagePath: resourcesPath}
|
||||
return kio.Pipeline{
|
||||
Inputs: []kio.Reader{inout},
|
||||
Filters: []kio.Filter{kio.FilterAll(
|
||||
&setters2.Add{
|
||||
FieldName: c.FieldName,
|
||||
FieldValue: c.FieldValue,
|
||||
Ref: setters2.DefinitionsPrefix + setters2.SetterDefinitionPrefix + c.Name,
|
||||
})},
|
||||
Outputs: []kio.Writer{inout},
|
||||
}.Execute()
|
||||
}
|
||||
208
kyaml/setters2/settersutil/substitutioncreator.go
Normal file
208
kyaml/setters2/settersutil/substitutioncreator.go
Normal file
@@ -0,0 +1,208 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package settersutil
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/kind/pkg/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
"sigs.k8s.io/kustomize/kyaml/setters2"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// SubstitutionCreator creates or updates a substitution in the OpenAPI definitions, and
|
||||
// inserts references to the substitution from matching resource fields.
|
||||
type SubstitutionCreator struct {
|
||||
// Name is the name of the substitution to create
|
||||
Name string
|
||||
|
||||
// Pattern is the substitution pattern
|
||||
Pattern string
|
||||
|
||||
// Values are the substitution values for the pattern
|
||||
Values []setters2.Value
|
||||
|
||||
// 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
|
||||
|
||||
// FieldValue if set will add the OpenAPI reference to fields if they have this value.
|
||||
// Optional. If unspecified match all field values.
|
||||
FieldValue string
|
||||
}
|
||||
|
||||
func (c SubstitutionCreator) Create(openAPIPath, resourcesPath string) error {
|
||||
d := setters2.SubstitutionDefinition{
|
||||
Name: c.Name,
|
||||
Values: c.Values,
|
||||
Pattern: c.Pattern,
|
||||
}
|
||||
|
||||
err := c.CreateSettersForSubstitution(openAPIPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := d.AddToFile(openAPIPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Load the updated definitions
|
||||
if err := openapi.AddSchemaFromFile(openAPIPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Update the resources with the setter reference
|
||||
inout := &kio.LocalPackageReadWriter{PackagePath: resourcesPath}
|
||||
return kio.Pipeline{
|
||||
Inputs: []kio.Reader{inout},
|
||||
Filters: []kio.Filter{kio.FilterAll(
|
||||
&setters2.Add{
|
||||
FieldName: c.FieldName,
|
||||
FieldValue: c.FieldValue,
|
||||
Ref: setters2.DefinitionsPrefix + setters2.SubstitutionDefinitionPrefix + c.Name,
|
||||
})},
|
||||
Outputs: []kio.Writer{inout},
|
||||
}.Execute()
|
||||
}
|
||||
|
||||
// CreateSettersForSubstitution creates the setters for all the references in the substitution
|
||||
// values if they don't already exist in openAPIPath file.
|
||||
func (c SubstitutionCreator) CreateSettersForSubstitution(openAPIPath string) error {
|
||||
b, err := ioutil.ReadFile(openAPIPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// parse the yaml file
|
||||
y, err := yaml.Parse(string(b))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m, err := c.GetValuesForMarkers()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// for each ref in values, check if the setter already exists, if not create them
|
||||
for _, value := range c.Values {
|
||||
obj, err := y.Pipe(yaml.Lookup(
|
||||
// get the setter key from ref. Ex: from #/definitions/io.k8s.cli.setters.image_setter
|
||||
// extract io.k8s.cli.setters.image_setter
|
||||
"openAPI", "definitions", strings.TrimPrefix(value.Ref, "#/definitions/")))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if obj == nil {
|
||||
sd := setters2.SetterDefinition{
|
||||
// get the setter name from ref. Ex: from #/definitions/io.k8s.cli.setters.image_setter
|
||||
// extract image_setter
|
||||
Name: strings.Split(value.Ref, ".")[4],
|
||||
Value: m[value.Marker],
|
||||
}
|
||||
sd.AddToFile(openAPIPath)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetValuesForMarkers parses the pattern and field value to derive values for the
|
||||
// markers in the pattern string. Returns error if the marker values can't be derived
|
||||
func (c SubstitutionCreator) GetValuesForMarkers() (map[string]string, error) {
|
||||
m := make(map[string]string)
|
||||
indices, err := c.GetStartIndices()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s:=c.FieldValue
|
||||
p:=c.Pattern
|
||||
i:=0
|
||||
j:=0
|
||||
// iterate s, p with indices i, j respectively and when j hits the index of a marker, freeze j and iterate
|
||||
// i and capture string till we find the substring just after current marker and before next marker
|
||||
|
||||
// Ex: s = "something/ubuntu:0.1.0", p = "something/IMAGE::VERSION", till j reaches 10
|
||||
// just proceed i and j and check if s[i]==p[j]
|
||||
// when j is 10, freeze j and move i till it sees substring '::' which derives IMAGE = ubuntu and so on.
|
||||
for i< len(s) && j< len(p) {
|
||||
if marker, ok := indices[j]; ok {
|
||||
value := ""
|
||||
e := j+len(marker)
|
||||
|
||||
for i<len(s) && (e == len(p) ||
|
||||
s[i:min(len(s), i+lenToNextMarker(indices, e))] != p[e:min(e+lenToNextMarker(indices, e), len(p))]) {
|
||||
value += string(s[i])
|
||||
i++
|
||||
}
|
||||
// if marker is repeated in the pattern, make sure that the corresponding values
|
||||
// are same and throw error if not.
|
||||
if prevValue, ok := m[marker]; ok && prevValue != value {
|
||||
return nil, errors.Errorf("Same marker is found to have different values in field value.")
|
||||
}
|
||||
m[marker] = value
|
||||
j+=len(marker)
|
||||
} else {
|
||||
if s[i] != p[j] {
|
||||
return nil, errors.Errorf("Unable to derive values for markers. Create setters for all markers.")
|
||||
}
|
||||
i++
|
||||
j++
|
||||
}
|
||||
}
|
||||
// check if both strings are completely visited or throw error
|
||||
if i<len(s) || j<len(p) {
|
||||
return nil, errors.Errorf("Unable to derive values for markers. Create setters for all markers and then try again.")
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// GetStartIndices returns the start indices of all the markers in the pattern
|
||||
func (c SubstitutionCreator) GetStartIndices() (map[int]string, error) {
|
||||
inds := make(map[int]string)
|
||||
p := c.Pattern
|
||||
for _, value := range c.Values {
|
||||
m := value.Marker
|
||||
found := false
|
||||
for i, _ := range p {
|
||||
if strings.HasPrefix(p[i:], m) {
|
||||
inds[i] = m
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return nil, errors.Errorf("Unable to find marker " + m + " in the pattern")
|
||||
}
|
||||
}
|
||||
return inds, nil
|
||||
}
|
||||
|
||||
// lenToNextMarker takes in the indices map, an index and returns the distance to
|
||||
// next greater index
|
||||
func lenToNextMarker(m map[int]string, j int) int {
|
||||
res := math.MaxInt32
|
||||
for k, _ := range m {
|
||||
if k > j {
|
||||
res = min(k-j, res)
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func min(a int, b int) int {
|
||||
if a<b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package setters2
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
type SubstitutionDefinition struct {
|
||||
Name string `yaml:"name"`
|
||||
Pattern string `yaml:"pattern"`
|
||||
Values []Value `yaml:"value"`
|
||||
}
|
||||
|
||||
type Value struct {
|
||||
Marker string `yaml:"marker"`
|
||||
Ref string `yaml:"ref"`
|
||||
}
|
||||
|
||||
func (subd SubstitutionDefinition) AddSubstitutionToFile(path string) error {
|
||||
b, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
y, err := yaml.Parse(string(b))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := y.PipeE(subd); err != nil {
|
||||
return err
|
||||
}
|
||||
out, err := y.String()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ioutil.WriteFile(path, []byte(out), 0666); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (subd SubstitutionDefinition) Filter(object *yaml.RNode) (*yaml.RNode, error) {
|
||||
key := DefinitionPrefix + subd.Name
|
||||
|
||||
def, err := object.Pipe(yaml.LookupCreate(
|
||||
yaml.MappingNode, "openAPI", "definitions", key, "x-k8s-cli", "substitution"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := def.PipeE(yaml.FieldSetter{Name: "name", StringValue: subd.Name}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := def.PipeE(yaml.FieldSetter{Name: "pattern", StringValue: subd.Pattern}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return object, nil
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package setters2
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestAddUpdateSubstitution(t *testing.T) {
|
||||
path := os.TempDir() + "/resourcefile"
|
||||
|
||||
//write initial resourcefile to temp path
|
||||
err := ioutil.WriteFile(path, []byte(resourcefile), 0666)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
value1 := Value{
|
||||
Marker: "IMAGE_NAME",
|
||||
Ref: "#/definitions/io.k8s.cli.setters.image-name",
|
||||
}
|
||||
|
||||
value2 := Value{
|
||||
Marker: "IMAGE_TAG",
|
||||
Ref: "#/definitions/io.k8s.cli.setters.image-tag",
|
||||
}
|
||||
|
||||
values := []Value{value1, value2}
|
||||
|
||||
//add a setter definition
|
||||
subd := SubstitutionDefinition{
|
||||
Name: "image",
|
||||
Pattern: "IMAGE_NAME:IMAGE_TAG",
|
||||
Values: values,
|
||||
}
|
||||
|
||||
err = subd.AddSubstitutionToFile(path)
|
||||
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// update setter definition
|
||||
subd2 := SubstitutionDefinition{
|
||||
Name: "image",
|
||||
Pattern: "IMAGE_NAME:IMAGE_TAG2",
|
||||
}
|
||||
|
||||
err = subd2.AddSubstitutionToFile(path)
|
||||
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
expected := `apiVersion: resource.dev/v1alpha1
|
||||
kind: resourcefile
|
||||
metadata:
|
||||
name: hello-world-set
|
||||
upstream:
|
||||
type: git
|
||||
git:
|
||||
commit: 5c1c019b59299a4f6c7edd1ff5ff54d720621bbe
|
||||
directory: /package-examples/helloworld-set
|
||||
ref: v0.1.0
|
||||
packageMetadata:
|
||||
shortDescription: example package using setters
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.image:
|
||||
x-k8s-cli:
|
||||
substitution:
|
||||
name: image
|
||||
pattern: IMAGE_NAME:IMAGE_TAG2
|
||||
`
|
||||
assert.Equal(t, expected, string(b))
|
||||
}
|
||||
@@ -6,6 +6,7 @@ package yaml
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
@@ -99,6 +100,42 @@ func Parse(value string) (*RNode, error) {
|
||||
return Parser{Value: value}.Filter(nil)
|
||||
}
|
||||
|
||||
// ReadFile parses a single Resource from a yaml file
|
||||
func ReadFile(path string) (*RNode, error) {
|
||||
b, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return Parse(string(b))
|
||||
}
|
||||
|
||||
// WriteFile writes a single Resource to a yaml file
|
||||
func WriteFile(node *RNode, path string) error {
|
||||
out, err := node.String()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ioutil.WriteFile(path, []byte(out), 0600)
|
||||
}
|
||||
|
||||
// UpdateFile reads the file at path, applies the filter to it, and write the result back.
|
||||
// path must contain a exactly 1 resource (YAML).
|
||||
func UpdateFile(filter Filter, path string) error {
|
||||
// Read the yaml
|
||||
y, err := ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Update the yaml
|
||||
if err := y.PipeE(filter); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Write the yaml
|
||||
return WriteFile(y, path)
|
||||
}
|
||||
|
||||
// TODO(pwittrock): test this
|
||||
func GetStyle(styles ...string) Style {
|
||||
var style Style
|
||||
|
||||
Reference in New Issue
Block a user