mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-11 17:12:51 +00:00
remove deprecated cfg and fn commands (#4930)
* remove deprecated cfg and fn commands * fix lint error * run gofmt
This commit is contained in:
@@ -14,18 +14,9 @@ func GetCfg(name string) *cobra.Command {
|
|||||||
Short: "Commands for reading and writing configuration",
|
Short: "Commands for reading and writing configuration",
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.AddCommand(commands.AnnotateCommand(name))
|
|
||||||
cmd.AddCommand(commands.CatCommand(name))
|
cmd.AddCommand(commands.CatCommand(name))
|
||||||
cmd.AddCommand(commands.CountCommand(name))
|
cmd.AddCommand(commands.CountCommand(name))
|
||||||
cmd.AddCommand(commands.CreateSetterCommand(name))
|
|
||||||
cmd.AddCommand(commands.CreateSubstitutionCommand(name))
|
|
||||||
cmd.AddCommand(commands.FmtCommand(name))
|
|
||||||
cmd.AddCommand(commands.GrepCommand(name))
|
cmd.AddCommand(commands.GrepCommand(name))
|
||||||
cmd.AddCommand(commands.InitCommand(name))
|
|
||||||
cmd.AddCommand(commands.ListSettersCommand(name))
|
|
||||||
cmd.AddCommand(commands.MergeCommand(name))
|
|
||||||
cmd.AddCommand(commands.Merge3Command(name))
|
|
||||||
cmd.AddCommand(commands.SetCommand(name))
|
|
||||||
cmd.AddCommand(commands.TreeCommand(name))
|
cmd.AddCommand(commands.TreeCommand(name))
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
|
|||||||
@@ -14,27 +14,14 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Export commands publicly for composition
|
// Export commands publicly for composition
|
||||||
|
//
|
||||||
|
//nolint:gochecknoglobals
|
||||||
var (
|
var (
|
||||||
Annotate = commands.AnnotateCommand
|
Cat = commands.CatCommand
|
||||||
Cat = commands.CatCommand
|
Count = commands.CountCommand
|
||||||
Count = commands.CountCommand
|
Grep = commands.GrepCommand
|
||||||
CreateSetter = commands.CreateSetterCommand
|
RunFn = commands.RunCommand
|
||||||
CreateSubstitution = commands.CreateSubstitutionCommand
|
Tree = commands.TreeCommand
|
||||||
DeleteSetter = commands.DeleteSetterCommand
|
|
||||||
DeleteSubstitution = commands.DeleteSubstitutionCommand
|
|
||||||
Fmt = commands.FmtCommand
|
|
||||||
Grep = commands.GrepCommand
|
|
||||||
Init = commands.InitCommand
|
|
||||||
ListSetters = commands.ListSettersCommand
|
|
||||||
Merge = commands.MergeCommand
|
|
||||||
Merge3 = commands.Merge3Command
|
|
||||||
RunFn = commands.RunCommand
|
|
||||||
Set = commands.SetCommand
|
|
||||||
Sink = commands.SinkCommand
|
|
||||||
Source = commands.SourceCommand
|
|
||||||
Tree = commands.TreeCommand
|
|
||||||
Wrap = commands.WrapCommand
|
|
||||||
XArgs = commands.XArgsCommand
|
|
||||||
|
|
||||||
StackOnError = &runner.StackOnError
|
StackOnError = &runner.StackOnError
|
||||||
ExitOnError = &runner.ExitOnError
|
ExitOnError = &runner.ExitOnError
|
||||||
|
|||||||
@@ -15,10 +15,5 @@ func GetFn(name string) *cobra.Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cmd.AddCommand(commands.RunCommand(name))
|
cmd.AddCommand(commands.RunCommand(name))
|
||||||
cmd.AddCommand(commands.SinkCommand(name))
|
|
||||||
cmd.AddCommand(commands.SourceCommand(name))
|
|
||||||
cmd.AddCommand(commands.WrapCommand())
|
|
||||||
cmd.AddCommand(commands.XArgsCommand())
|
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,147 +0,0 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package commands
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/ext"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/runner"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewAnnotateRunner returns a command runner.
|
|
||||||
func NewAnnotateRunner(parent string) *AnnotateRunner {
|
|
||||||
r := &AnnotateRunner{}
|
|
||||||
c := &cobra.Command{
|
|
||||||
Use: "annotate [DIR]",
|
|
||||||
Args: cobra.MaximumNArgs(1),
|
|
||||||
Short: commands.AnnotateShort,
|
|
||||||
Long: commands.AnnotateLong,
|
|
||||||
Example: commands.AnnotateExamples,
|
|
||||||
RunE: r.runE,
|
|
||||||
Deprecated: "use the `commonAnnotations` field in your kustomization file.",
|
|
||||||
}
|
|
||||||
runner.FixDocs(parent, c)
|
|
||||||
r.Command = c
|
|
||||||
c.Flags().StringVar(&r.Kind, "kind", "", "Resource kind to annotate")
|
|
||||||
c.Flags().StringVar(&r.ApiVersion, "apiVersion", "", "Resource apiVersion to annotate")
|
|
||||||
c.Flags().StringVar(&r.Name, "name", "", "Resource name to annotate")
|
|
||||||
c.Flags().StringVar(&r.Namespace, "namespace", "", "Resource namespace to annotate")
|
|
||||||
c.Flags().StringSliceVar(&r.Values, "kv", []string{}, "annotation as KEY=VALUE")
|
|
||||||
c.Flags().BoolVarP(&r.RecurseSubPackages, "recurse-subpackages", "R", false,
|
|
||||||
"add annotations recursively in all the nested subpackages")
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func AnnotateCommand(parent string) *cobra.Command {
|
|
||||||
return NewAnnotateRunner(parent).Command
|
|
||||||
}
|
|
||||||
|
|
||||||
type AnnotateRunner struct {
|
|
||||||
Command *cobra.Command
|
|
||||||
Values []string
|
|
||||||
Kind string
|
|
||||||
Name string
|
|
||||||
ApiVersion string
|
|
||||||
Namespace string
|
|
||||||
Path string
|
|
||||||
RecurseSubPackages bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *AnnotateRunner) runE(c *cobra.Command, args []string) error {
|
|
||||||
var input []kio.Reader
|
|
||||||
var output []kio.Writer
|
|
||||||
if len(args) == 0 {
|
|
||||||
rw := &kio.ByteReadWriter{Reader: c.InOrStdin(), Writer: c.OutOrStdout()}
|
|
||||||
input = []kio.Reader{rw}
|
|
||||||
output = []kio.Writer{rw}
|
|
||||||
|
|
||||||
return runner.HandleError(c, kio.Pipeline{
|
|
||||||
Inputs: input,
|
|
||||||
Filters: []kio.Filter{r},
|
|
||||||
Outputs: output,
|
|
||||||
}.Execute())
|
|
||||||
}
|
|
||||||
|
|
||||||
e := runner.ExecuteCmdOnPkgs{
|
|
||||||
Writer: c.OutOrStdout(),
|
|
||||||
NeedOpenAPI: false,
|
|
||||||
RecurseSubPackages: r.RecurseSubPackages,
|
|
||||||
CmdRunner: r,
|
|
||||||
RootPkgPath: args[0],
|
|
||||||
}
|
|
||||||
|
|
||||||
err := e.Execute()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *AnnotateRunner) ExecuteCmd(w io.Writer, pkgPath string) error {
|
|
||||||
rw := &kio.LocalPackageReadWriter{
|
|
||||||
PackagePath: pkgPath,
|
|
||||||
NoDeleteFiles: true,
|
|
||||||
PackageFileName: ext.KRMFileName(),
|
|
||||||
}
|
|
||||||
input := []kio.Reader{rw}
|
|
||||||
output := []kio.Writer{rw}
|
|
||||||
err := kio.Pipeline{
|
|
||||||
Inputs: input,
|
|
||||||
Filters: []kio.Filter{r},
|
|
||||||
Outputs: output,
|
|
||||||
}.Execute()
|
|
||||||
if err != nil {
|
|
||||||
// return err if there is only package
|
|
||||||
if !r.RecurseSubPackages {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// print error message and continue if there are multiple packages to annotate
|
|
||||||
_, _ = fmt.Fprintf(w, "%s\n", err.Error())
|
|
||||||
} else {
|
|
||||||
_, _ = fmt.Fprint(w, "added annotations in the package\n")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *AnnotateRunner) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
|
||||||
for i := range nodes {
|
|
||||||
n := nodes[i]
|
|
||||||
m, err := n.GetMeta()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if r.Kind != "" && r.Kind != m.Kind {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if r.ApiVersion != "" && r.ApiVersion != m.APIVersion {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if r.Namespace != "" && r.Namespace != m.Namespace {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if r.Name != "" && r.Name != m.Name {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := range r.Values {
|
|
||||||
// split key, value pairs
|
|
||||||
kv := strings.SplitN(r.Values[i], "=", 2)
|
|
||||||
if len(kv) != 2 {
|
|
||||||
return nil, errors.Errorf("must specify --kv as KEY=VALUE: %s", r.Values[i])
|
|
||||||
}
|
|
||||||
if err := n.PipeE(yaml.SetAnnotation(kv[0], kv[1])); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nodes, nil
|
|
||||||
}
|
|
||||||
@@ -1,605 +0,0 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package commands
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/copyutil"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestAnnotateCommand(t *testing.T) {
|
|
||||||
var tests = []struct {
|
|
||||||
name string
|
|
||||||
args []string
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "single value",
|
|
||||||
args: []string{"--kv", "a=b"},
|
|
||||||
expected: expectedSingleValue,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "multi value",
|
|
||||||
args: []string{"--kv", "a=b", "--kv", "c=d"},
|
|
||||||
expected: expectedMultiValue,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "filter kind",
|
|
||||||
args: []string{"--kv", "a=b", "--kind", "Service"},
|
|
||||||
expected: expectedFilterKindService,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "filter apiVersion",
|
|
||||||
args: []string{"--kv", "a=b", "--apiVersion", "v1"},
|
|
||||||
expected: expectedFilterApiVersionV1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "filter name",
|
|
||||||
args: []string{"--kv", "a=b", "--name", "bar"},
|
|
||||||
expected: expectedFilterNameBar,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "filter namespace",
|
|
||||||
args: []string{"--kv", "a=b", "--namespace", "bar"},
|
|
||||||
expected: expectedFilterNamespaceBar,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for i := range tests {
|
|
||||||
tt := tests[i]
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
d := initTestDir(t)
|
|
||||||
|
|
||||||
a := NewAnnotateRunner("")
|
|
||||||
a.Command.SetArgs(append([]string{d}, tt.args...))
|
|
||||||
a.Command.SilenceUsage = true
|
|
||||||
a.Command.SilenceErrors = true
|
|
||||||
|
|
||||||
err := a.Command.Execute()
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
actual := &bytes.Buffer{}
|
|
||||||
err = kio.Pipeline{
|
|
||||||
Inputs: []kio.Reader{kio.LocalPackageReader{PackagePath: d}},
|
|
||||||
Outputs: []kio.Writer{kio.ByteWriter{Writer: actual, KeepReaderAnnotations: true}},
|
|
||||||
}.Execute()
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
if !assert.Equal(t,
|
|
||||||
strings.TrimSpace(tt.expected),
|
|
||||||
strings.TrimSpace(actual.String())) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func initTestDir(t *testing.T) string {
|
|
||||||
t.Helper()
|
|
||||||
d := t.TempDir()
|
|
||||||
err := os.WriteFile(filepath.Join(d, "f1.yaml"), []byte(f1Input), 0600)
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
err = os.WriteFile(filepath.Join(d, "f2.yaml"), []byte(f2Input), 0600)
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
return d
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
f1Input = `
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: nginx2
|
|
||||||
name: foo
|
|
||||||
annotations:
|
|
||||||
app: nginx2
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
---
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: foo
|
|
||||||
annotations:
|
|
||||||
app: nginx
|
|
||||||
spec:
|
|
||||||
selector:
|
|
||||||
app: nginx
|
|
||||||
`
|
|
||||||
|
|
||||||
f2Input = `
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Abstraction
|
|
||||||
metadata:
|
|
||||||
name: foo
|
|
||||||
configFn:
|
|
||||||
container:
|
|
||||||
image: gcr.io/example/reconciler:v1
|
|
||||||
annotations:
|
|
||||||
config.kubernetes.io/local-config: "true"
|
|
||||||
namespace: bar
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
---
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: nginx
|
|
||||||
name: bar
|
|
||||||
annotations:
|
|
||||||
app: nginx
|
|
||||||
namespace: foo
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
`
|
|
||||||
|
|
||||||
expectedSingleValue = `kind: Deployment
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: nginx2
|
|
||||||
name: foo
|
|
||||||
annotations:
|
|
||||||
app: nginx2
|
|
||||||
a: 'b'
|
|
||||||
config.kubernetes.io/index: '0'
|
|
||||||
config.kubernetes.io/path: 'f1.yaml'
|
|
||||||
internal.config.kubernetes.io/index: '0'
|
|
||||||
internal.config.kubernetes.io/path: 'f1.yaml'
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
---
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: foo
|
|
||||||
annotations:
|
|
||||||
app: nginx
|
|
||||||
a: 'b'
|
|
||||||
config.kubernetes.io/index: '1'
|
|
||||||
config.kubernetes.io/path: 'f1.yaml'
|
|
||||||
internal.config.kubernetes.io/index: '1'
|
|
||||||
internal.config.kubernetes.io/path: 'f1.yaml'
|
|
||||||
spec:
|
|
||||||
selector:
|
|
||||||
app: nginx
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Abstraction
|
|
||||||
metadata:
|
|
||||||
name: foo
|
|
||||||
configFn:
|
|
||||||
container:
|
|
||||||
image: gcr.io/example/reconciler:v1
|
|
||||||
annotations:
|
|
||||||
config.kubernetes.io/local-config: "true"
|
|
||||||
a: 'b'
|
|
||||||
config.kubernetes.io/index: '0'
|
|
||||||
config.kubernetes.io/path: 'f2.yaml'
|
|
||||||
internal.config.kubernetes.io/index: '0'
|
|
||||||
internal.config.kubernetes.io/path: 'f2.yaml'
|
|
||||||
namespace: bar
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
---
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: nginx
|
|
||||||
name: bar
|
|
||||||
annotations:
|
|
||||||
app: nginx
|
|
||||||
a: 'b'
|
|
||||||
config.kubernetes.io/index: '1'
|
|
||||||
config.kubernetes.io/path: 'f2.yaml'
|
|
||||||
internal.config.kubernetes.io/index: '1'
|
|
||||||
internal.config.kubernetes.io/path: 'f2.yaml'
|
|
||||||
namespace: foo
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
`
|
|
||||||
|
|
||||||
expectedMultiValue = `kind: Deployment
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: nginx2
|
|
||||||
name: foo
|
|
||||||
annotations:
|
|
||||||
app: nginx2
|
|
||||||
a: 'b'
|
|
||||||
c: 'd'
|
|
||||||
config.kubernetes.io/index: '0'
|
|
||||||
config.kubernetes.io/path: 'f1.yaml'
|
|
||||||
internal.config.kubernetes.io/index: '0'
|
|
||||||
internal.config.kubernetes.io/path: 'f1.yaml'
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
---
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: foo
|
|
||||||
annotations:
|
|
||||||
app: nginx
|
|
||||||
a: 'b'
|
|
||||||
c: 'd'
|
|
||||||
config.kubernetes.io/index: '1'
|
|
||||||
config.kubernetes.io/path: 'f1.yaml'
|
|
||||||
internal.config.kubernetes.io/index: '1'
|
|
||||||
internal.config.kubernetes.io/path: 'f1.yaml'
|
|
||||||
spec:
|
|
||||||
selector:
|
|
||||||
app: nginx
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Abstraction
|
|
||||||
metadata:
|
|
||||||
name: foo
|
|
||||||
configFn:
|
|
||||||
container:
|
|
||||||
image: gcr.io/example/reconciler:v1
|
|
||||||
annotations:
|
|
||||||
config.kubernetes.io/local-config: "true"
|
|
||||||
a: 'b'
|
|
||||||
c: 'd'
|
|
||||||
config.kubernetes.io/index: '0'
|
|
||||||
config.kubernetes.io/path: 'f2.yaml'
|
|
||||||
internal.config.kubernetes.io/index: '0'
|
|
||||||
internal.config.kubernetes.io/path: 'f2.yaml'
|
|
||||||
namespace: bar
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
---
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: nginx
|
|
||||||
name: bar
|
|
||||||
annotations:
|
|
||||||
app: nginx
|
|
||||||
a: 'b'
|
|
||||||
c: 'd'
|
|
||||||
config.kubernetes.io/index: '1'
|
|
||||||
config.kubernetes.io/path: 'f2.yaml'
|
|
||||||
internal.config.kubernetes.io/index: '1'
|
|
||||||
internal.config.kubernetes.io/path: 'f2.yaml'
|
|
||||||
namespace: foo
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
`
|
|
||||||
|
|
||||||
expectedFilterKindService = `kind: Deployment
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: nginx2
|
|
||||||
name: foo
|
|
||||||
annotations:
|
|
||||||
app: nginx2
|
|
||||||
config.kubernetes.io/index: '0'
|
|
||||||
config.kubernetes.io/path: 'f1.yaml'
|
|
||||||
internal.config.kubernetes.io/index: '0'
|
|
||||||
internal.config.kubernetes.io/path: 'f1.yaml'
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
---
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: foo
|
|
||||||
annotations:
|
|
||||||
app: nginx
|
|
||||||
a: 'b'
|
|
||||||
config.kubernetes.io/index: '1'
|
|
||||||
config.kubernetes.io/path: 'f1.yaml'
|
|
||||||
internal.config.kubernetes.io/index: '1'
|
|
||||||
internal.config.kubernetes.io/path: 'f1.yaml'
|
|
||||||
spec:
|
|
||||||
selector:
|
|
||||||
app: nginx
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Abstraction
|
|
||||||
metadata:
|
|
||||||
name: foo
|
|
||||||
configFn:
|
|
||||||
container:
|
|
||||||
image: gcr.io/example/reconciler:v1
|
|
||||||
annotations:
|
|
||||||
config.kubernetes.io/local-config: "true"
|
|
||||||
config.kubernetes.io/index: '0'
|
|
||||||
config.kubernetes.io/path: 'f2.yaml'
|
|
||||||
internal.config.kubernetes.io/index: '0'
|
|
||||||
internal.config.kubernetes.io/path: 'f2.yaml'
|
|
||||||
namespace: bar
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
---
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: nginx
|
|
||||||
name: bar
|
|
||||||
annotations:
|
|
||||||
app: nginx
|
|
||||||
config.kubernetes.io/index: '1'
|
|
||||||
config.kubernetes.io/path: 'f2.yaml'
|
|
||||||
internal.config.kubernetes.io/index: '1'
|
|
||||||
internal.config.kubernetes.io/path: 'f2.yaml'
|
|
||||||
namespace: foo
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
`
|
|
||||||
|
|
||||||
expectedFilterApiVersionV1 = `kind: Deployment
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: nginx2
|
|
||||||
name: foo
|
|
||||||
annotations:
|
|
||||||
app: nginx2
|
|
||||||
config.kubernetes.io/index: '0'
|
|
||||||
config.kubernetes.io/path: 'f1.yaml'
|
|
||||||
internal.config.kubernetes.io/index: '0'
|
|
||||||
internal.config.kubernetes.io/path: 'f1.yaml'
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
---
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: foo
|
|
||||||
annotations:
|
|
||||||
app: nginx
|
|
||||||
config.kubernetes.io/index: '1'
|
|
||||||
config.kubernetes.io/path: 'f1.yaml'
|
|
||||||
internal.config.kubernetes.io/index: '1'
|
|
||||||
internal.config.kubernetes.io/path: 'f1.yaml'
|
|
||||||
spec:
|
|
||||||
selector:
|
|
||||||
app: nginx
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Abstraction
|
|
||||||
metadata:
|
|
||||||
name: foo
|
|
||||||
configFn:
|
|
||||||
container:
|
|
||||||
image: gcr.io/example/reconciler:v1
|
|
||||||
annotations:
|
|
||||||
config.kubernetes.io/local-config: "true"
|
|
||||||
a: 'b'
|
|
||||||
config.kubernetes.io/index: '0'
|
|
||||||
config.kubernetes.io/path: 'f2.yaml'
|
|
||||||
internal.config.kubernetes.io/index: '0'
|
|
||||||
internal.config.kubernetes.io/path: 'f2.yaml'
|
|
||||||
namespace: bar
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
---
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: nginx
|
|
||||||
name: bar
|
|
||||||
annotations:
|
|
||||||
app: nginx
|
|
||||||
config.kubernetes.io/index: '1'
|
|
||||||
config.kubernetes.io/path: 'f2.yaml'
|
|
||||||
internal.config.kubernetes.io/index: '1'
|
|
||||||
internal.config.kubernetes.io/path: 'f2.yaml'
|
|
||||||
namespace: foo
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
`
|
|
||||||
|
|
||||||
expectedFilterNameBar = `kind: Deployment
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: nginx2
|
|
||||||
name: foo
|
|
||||||
annotations:
|
|
||||||
app: nginx2
|
|
||||||
config.kubernetes.io/index: '0'
|
|
||||||
config.kubernetes.io/path: 'f1.yaml'
|
|
||||||
internal.config.kubernetes.io/index: '0'
|
|
||||||
internal.config.kubernetes.io/path: 'f1.yaml'
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
---
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: foo
|
|
||||||
annotations:
|
|
||||||
app: nginx
|
|
||||||
config.kubernetes.io/index: '1'
|
|
||||||
config.kubernetes.io/path: 'f1.yaml'
|
|
||||||
internal.config.kubernetes.io/index: '1'
|
|
||||||
internal.config.kubernetes.io/path: 'f1.yaml'
|
|
||||||
spec:
|
|
||||||
selector:
|
|
||||||
app: nginx
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Abstraction
|
|
||||||
metadata:
|
|
||||||
name: foo
|
|
||||||
configFn:
|
|
||||||
container:
|
|
||||||
image: gcr.io/example/reconciler:v1
|
|
||||||
annotations:
|
|
||||||
config.kubernetes.io/local-config: "true"
|
|
||||||
config.kubernetes.io/index: '0'
|
|
||||||
config.kubernetes.io/path: 'f2.yaml'
|
|
||||||
internal.config.kubernetes.io/index: '0'
|
|
||||||
internal.config.kubernetes.io/path: 'f2.yaml'
|
|
||||||
namespace: bar
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
---
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: nginx
|
|
||||||
name: bar
|
|
||||||
annotations:
|
|
||||||
app: nginx
|
|
||||||
a: 'b'
|
|
||||||
config.kubernetes.io/index: '1'
|
|
||||||
config.kubernetes.io/path: 'f2.yaml'
|
|
||||||
internal.config.kubernetes.io/index: '1'
|
|
||||||
internal.config.kubernetes.io/path: 'f2.yaml'
|
|
||||||
namespace: foo
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
`
|
|
||||||
|
|
||||||
expectedFilterNamespaceBar = `kind: Deployment
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: nginx2
|
|
||||||
name: foo
|
|
||||||
annotations:
|
|
||||||
app: nginx2
|
|
||||||
config.kubernetes.io/index: '0'
|
|
||||||
config.kubernetes.io/path: 'f1.yaml'
|
|
||||||
internal.config.kubernetes.io/index: '0'
|
|
||||||
internal.config.kubernetes.io/path: 'f1.yaml'
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
---
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: foo
|
|
||||||
annotations:
|
|
||||||
app: nginx
|
|
||||||
config.kubernetes.io/index: '1'
|
|
||||||
config.kubernetes.io/path: 'f1.yaml'
|
|
||||||
internal.config.kubernetes.io/index: '1'
|
|
||||||
internal.config.kubernetes.io/path: 'f1.yaml'
|
|
||||||
spec:
|
|
||||||
selector:
|
|
||||||
app: nginx
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Abstraction
|
|
||||||
metadata:
|
|
||||||
name: foo
|
|
||||||
configFn:
|
|
||||||
container:
|
|
||||||
image: gcr.io/example/reconciler:v1
|
|
||||||
annotations:
|
|
||||||
config.kubernetes.io/local-config: "true"
|
|
||||||
a: 'b'
|
|
||||||
config.kubernetes.io/index: '0'
|
|
||||||
config.kubernetes.io/path: 'f2.yaml'
|
|
||||||
internal.config.kubernetes.io/index: '0'
|
|
||||||
internal.config.kubernetes.io/path: 'f2.yaml'
|
|
||||||
namespace: bar
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
---
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: nginx
|
|
||||||
name: bar
|
|
||||||
annotations:
|
|
||||||
app: nginx
|
|
||||||
config.kubernetes.io/index: '1'
|
|
||||||
config.kubernetes.io/path: 'f2.yaml'
|
|
||||||
internal.config.kubernetes.io/index: '1'
|
|
||||||
internal.config.kubernetes.io/path: 'f2.yaml'
|
|
||||||
namespace: foo
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
`
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestAnnotateSubPackages(t *testing.T) {
|
|
||||||
var tests = []struct {
|
|
||||||
name string
|
|
||||||
dataset string
|
|
||||||
packagePath string
|
|
||||||
args []string
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "annotate-recurse-subpackages",
|
|
||||||
dataset: "dataset-without-setters",
|
|
||||||
args: []string{"--kv", "foo=bar", "-R"},
|
|
||||||
expected: `${baseDir}/
|
|
||||||
added annotations in the package
|
|
||||||
|
|
||||||
${baseDir}/mysql/
|
|
||||||
added annotations in the package
|
|
||||||
|
|
||||||
${baseDir}/mysql/storage/
|
|
||||||
added annotations in the package
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "annotate-top-level-pkg-no-recurse-subpackages",
|
|
||||||
dataset: "dataset-without-setters",
|
|
||||||
packagePath: "mysql",
|
|
||||||
args: []string{"--kv", "foo=bar"},
|
|
||||||
expected: `${baseDir}/mysql/
|
|
||||||
added annotations in the package
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "annotate-nested-pkg-no-recurse-subpackages",
|
|
||||||
dataset: "dataset-without-setters",
|
|
||||||
packagePath: "mysql/storage",
|
|
||||||
args: []string{"--kv", "foo=bar"},
|
|
||||||
expected: `${baseDir}/mysql/storage/
|
|
||||||
added annotations in the package
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for i := range tests {
|
|
||||||
test := tests[i]
|
|
||||||
t.Run(test.name, func(t *testing.T) {
|
|
||||||
// reset the openAPI afterward
|
|
||||||
openapi.ResetOpenAPI()
|
|
||||||
defer openapi.ResetOpenAPI()
|
|
||||||
sourceDir := filepath.Join("test", "testdata", test.dataset)
|
|
||||||
baseDir := t.TempDir()
|
|
||||||
copyutil.CopyDir(sourceDir, baseDir)
|
|
||||||
runner := NewAnnotateRunner("")
|
|
||||||
actual := &bytes.Buffer{}
|
|
||||||
runner.Command.SetOut(actual)
|
|
||||||
runner.Command.SetArgs(append([]string{filepath.Join(baseDir, test.packagePath)}, test.args...))
|
|
||||||
err := runner.Command.Execute()
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
// normalize path format for windows
|
|
||||||
actualNormalized := strings.ReplaceAll(
|
|
||||||
strings.ReplaceAll(actual.String(), "\\", "/"),
|
|
||||||
"//", "/")
|
|
||||||
|
|
||||||
expected := strings.ReplaceAll(test.expected, "${baseDir}", baseDir)
|
|
||||||
expectedNormalized := strings.ReplaceAll(expected, "\\", "/")
|
|
||||||
if !assert.Contains(t, actualNormalized, expectedNormalized) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,230 +0,0 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package commands
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"k8s.io/kube-openapi/pkg/validation/spec"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/ext"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/runner"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/setters2/settersutil"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewCreateSetterRunner returns a command runner.
|
|
||||||
func NewCreateSetterRunner(parent string) *CreateSetterRunner {
|
|
||||||
r := &CreateSetterRunner{}
|
|
||||||
set := &cobra.Command{
|
|
||||||
Use: "create-setter DIR NAME VALUE",
|
|
||||||
Args: cobra.RangeArgs(2, 3),
|
|
||||||
Short: commands.CreateSetterShort,
|
|
||||||
Long: commands.CreateSetterLong,
|
|
||||||
Example: commands.CreateSetterExamples,
|
|
||||||
PreRunE: r.preRunE,
|
|
||||||
RunE: r.runE,
|
|
||||||
Deprecated: "setter commands will no longer be available in kustomize v5.\n" +
|
|
||||||
"See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.",
|
|
||||||
}
|
|
||||||
set.Flags().StringVar(&r.FieldValue, "value", "",
|
|
||||||
"optional flag, alternative to specifying the value as an argument. e.g. used to specify values that start with '-'")
|
|
||||||
set.Flags().StringVar(&r.SetBy, "set-by", "",
|
|
||||||
"record who the field was default by.")
|
|
||||||
set.Flags().StringVar(&r.Description, "description", "",
|
|
||||||
"record a description for the current setter value.")
|
|
||||||
set.Flags().StringVar(&r.FieldName, "field", "",
|
|
||||||
"name of the field to set, a suffix of the path to the field, or the full"+
|
|
||||||
" path to the field. Default is to match all fields.")
|
|
||||||
set.Flags().StringVar(&r.Type, "type", "",
|
|
||||||
"OpenAPI field type for the setter -- e.g. integer,boolean,string.")
|
|
||||||
set.Flags().BoolVar(&r.Required, "required", false,
|
|
||||||
"indicates that this setter must be set by package consumer before live apply/preview")
|
|
||||||
set.Flags().StringVar(&r.SchemaPath, "schema-path", "",
|
|
||||||
`openAPI schema file path for setter constraints -- file content `+
|
|
||||||
`e.g. {"type": "string", "maxLength": 15, "enum": ["allowedValue1", "allowedValue2"]}`)
|
|
||||||
set.Flags().BoolVarP(&r.RecurseSubPackages, "recurse-subpackages", "R", false,
|
|
||||||
"creates setter recursively in all the nested subpackages")
|
|
||||||
set.Flags().MarkHidden("version")
|
|
||||||
runner.FixDocs(parent, set)
|
|
||||||
r.Command = set
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func CreateSetterCommand(parent string) *cobra.Command {
|
|
||||||
return NewCreateSetterRunner(parent).Command
|
|
||||||
}
|
|
||||||
|
|
||||||
type CreateSetterRunner struct {
|
|
||||||
Command *cobra.Command
|
|
||||||
CreateSetter settersutil.SetterCreator
|
|
||||||
OpenAPIFile string
|
|
||||||
SchemaPath string
|
|
||||||
FieldValue string
|
|
||||||
SetBy string
|
|
||||||
Description string
|
|
||||||
SetterName string
|
|
||||||
Type string
|
|
||||||
FieldName string
|
|
||||||
Schema string
|
|
||||||
Required bool
|
|
||||||
RecurseSubPackages bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *CreateSetterRunner) runE(c *cobra.Command, args []string) error {
|
|
||||||
return runner.HandleError(c, r.createSetter(c, args))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *CreateSetterRunner) preRunE(c *cobra.Command, args []string) error {
|
|
||||||
var err error
|
|
||||||
r.SetterName = args[1]
|
|
||||||
if len(args) > 2 {
|
|
||||||
r.FieldValue = args[2]
|
|
||||||
}
|
|
||||||
r.FieldName, err = c.Flags().GetString("field")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.Type != "array" && !c.Flag("value").Changed && len(args) < 3 {
|
|
||||||
return errors.Errorf("setter name and value must be provided, " +
|
|
||||||
"value can either be an argument or can be passed as a flag --value")
|
|
||||||
}
|
|
||||||
|
|
||||||
err = r.processSchema()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.Type == "array" {
|
|
||||||
if !c.Flag("field").Changed {
|
|
||||||
return errors.Errorf("field flag must be set for array type setters")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *CreateSetterRunner) processSchema() error {
|
|
||||||
sc, err := schemaFromFile(r.SchemaPath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
flagType := r.Type
|
|
||||||
var schemaType string
|
|
||||||
switch {
|
|
||||||
// json schema allows more than one type to be specified, but openapi
|
|
||||||
// only allows one. So we follow the openapi convention.
|
|
||||||
case len(sc.Type) > 1:
|
|
||||||
return errors.Errorf("only one type is supported: %s",
|
|
||||||
strings.Join(sc.Type, ", "))
|
|
||||||
case len(sc.Type) == 1:
|
|
||||||
schemaType = sc.Type[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Since type can be set both through the schema file and through the
|
|
||||||
// --type flag, we make sure the same value is set in both places. If they
|
|
||||||
// are both set with different values, we return an error.
|
|
||||||
switch {
|
|
||||||
case flagType == "" && schemaType != "":
|
|
||||||
r.Type = schemaType
|
|
||||||
case flagType != "" && schemaType == "":
|
|
||||||
sc.Type = []string{flagType}
|
|
||||||
case flagType != "" && schemaType != "":
|
|
||||||
if flagType != schemaType {
|
|
||||||
return errors.Errorf("type provided in type flag (%s) and in schema (%s) doesn't match",
|
|
||||||
r.Type, sc.Type[0])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only marshal the properties in SchemaProps. This means any fields in
|
|
||||||
// the schema file that isn't recognized will be dropped.
|
|
||||||
// TODO: Consider if we should return an error here instead of just dropping
|
|
||||||
// the unknown fields.
|
|
||||||
b, err := json.Marshal(sc.SchemaProps)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Errorf("error marshalling schema: %v", err)
|
|
||||||
}
|
|
||||||
r.Schema = string(b)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *CreateSetterRunner) createSetter(c *cobra.Command, args []string) error {
|
|
||||||
e := runner.ExecuteCmdOnPkgs{
|
|
||||||
NeedOpenAPI: true,
|
|
||||||
Writer: c.OutOrStdout(),
|
|
||||||
RootPkgPath: args[0],
|
|
||||||
RecurseSubPackages: r.RecurseSubPackages,
|
|
||||||
CmdRunner: r,
|
|
||||||
}
|
|
||||||
err := e.Execute()
|
|
||||||
if err != nil {
|
|
||||||
return runner.HandleError(c, err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *CreateSetterRunner) ExecuteCmd(w io.Writer, pkgPath string) error {
|
|
||||||
sc, err := openapi.SchemaFromFile(filepath.Join(pkgPath, ext.KRMFileName()))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
r.CreateSetter = settersutil.SetterCreator{
|
|
||||||
Name: r.SetterName,
|
|
||||||
SetBy: r.SetBy,
|
|
||||||
Description: r.Description,
|
|
||||||
Type: r.Type,
|
|
||||||
Schema: r.Schema,
|
|
||||||
FieldName: r.FieldName,
|
|
||||||
FieldValue: r.FieldValue,
|
|
||||||
Required: r.Required,
|
|
||||||
RecurseSubPackages: r.RecurseSubPackages,
|
|
||||||
OpenAPIFileName: ext.KRMFileName(),
|
|
||||||
OpenAPIPath: filepath.Join(pkgPath, ext.KRMFileName()),
|
|
||||||
ResourcesPath: pkgPath,
|
|
||||||
SettersSchema: sc,
|
|
||||||
}
|
|
||||||
|
|
||||||
err = r.CreateSetter.Create()
|
|
||||||
if err != nil {
|
|
||||||
// return err if RecurseSubPackages is false
|
|
||||||
if !r.CreateSetter.RecurseSubPackages {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// print error message and continue if RecurseSubPackages is true
|
|
||||||
fmt.Fprintf(w, "%s\n", err.Error())
|
|
||||||
} else {
|
|
||||||
fmt.Fprintf(w, "created setter %q\n", r.CreateSetter.Name)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// schemaFromFile reads the contents from schemaPath and returns schema
|
|
||||||
func schemaFromFile(schemaPath string) (*spec.Schema, error) {
|
|
||||||
sc := &spec.Schema{}
|
|
||||||
if schemaPath == "" {
|
|
||||||
return sc, nil
|
|
||||||
}
|
|
||||||
sch, err := os.ReadFile(schemaPath)
|
|
||||||
if err != nil {
|
|
||||||
return sc, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(sch) == 0 {
|
|
||||||
return sc, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
err = sc.UnmarshalJSON(sch)
|
|
||||||
if err != nil {
|
|
||||||
return sc, errors.Errorf("unable to parse schema: %v", err)
|
|
||||||
}
|
|
||||||
return sc, nil
|
|
||||||
}
|
|
||||||
@@ -1,871 +0,0 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package commands_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/copyutil"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestCreateSetterCommand(t *testing.T) {
|
|
||||||
var tests = []struct {
|
|
||||||
name string
|
|
||||||
input string
|
|
||||||
args []string
|
|
||||||
schema string
|
|
||||||
out string
|
|
||||||
inputOpenAPI string
|
|
||||||
expectedOpenAPI string
|
|
||||||
expectedResources string
|
|
||||||
err 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
|
|
||||||
`,
|
|
||||||
inputOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
`,
|
|
||||||
out: `created setter "replicas"`,
|
|
||||||
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 # {"$openapi":"replicas"}
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
name: "add replicas no match",
|
|
||||||
args: []string{"replicas", "3", "--description", "hello world", "--set-by", "me"},
|
|
||||||
input: `
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: nginx-deployment
|
|
||||||
spec:
|
|
||||||
foo: 2
|
|
||||||
`,
|
|
||||||
inputOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
`,
|
|
||||||
out: `created setter "replicas"`,
|
|
||||||
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:
|
|
||||||
foo: 2
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "error if substitution with same name exists",
|
|
||||||
args: []string{"my-image", "3", "--description", "hello world", "--set-by", "me"},
|
|
||||||
inputOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
openAPI:
|
|
||||||
definitions:
|
|
||||||
io.k8s.cli.substitutions.my-image:
|
|
||||||
x-k8s-cli:
|
|
||||||
substitution:
|
|
||||||
name: my-image
|
|
||||||
pattern: something/${my-image-setter}::${my-tag-setter}/nginxotherthing
|
|
||||||
values:
|
|
||||||
- marker: ${my-image-setter}
|
|
||||||
ref: '#/definitions/io.k8s.cli.setters.my-image-setter'
|
|
||||||
- marker: ${my-tag-setter}
|
|
||||||
ref: '#/definitions/io.k8s.cli.setters.my-tag-setter'
|
|
||||||
`,
|
|
||||||
err: `substitution with name "my-image" already exists, substitution and setter can't have same name`,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
name: "error if setter with same name exists",
|
|
||||||
args: []string{
|
|
||||||
"my-image", "ubuntu"},
|
|
||||||
inputOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
openAPI:
|
|
||||||
definitions:
|
|
||||||
io.k8s.cli.setters.my-image:
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: my-image
|
|
||||||
value: "nginx"
|
|
||||||
`,
|
|
||||||
err: `setter with name "my-image" already exists, if you want to modify it, please delete the existing setter and recreate it`,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
name: "add replicas with schema",
|
|
||||||
args: []string{"replicas", "3", "--description", "hello world", "--set-by", "me"},
|
|
||||||
schema: `{"maximum": 10, "type": "integer"}`,
|
|
||||||
input: `
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: nginx-deployment
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
`,
|
|
||||||
inputOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
`,
|
|
||||||
out: `created setter "replicas"`,
|
|
||||||
expectedOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
openAPI:
|
|
||||||
definitions:
|
|
||||||
io.k8s.cli.setters.replicas:
|
|
||||||
maximum: 10
|
|
||||||
type: integer
|
|
||||||
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 # {"$openapi":"replicas"}
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
name: "add replicas not enough arguments",
|
|
||||||
args: []string{"replicas", "--description", "hello world", "--set-by", "me"},
|
|
||||||
err: `setter name and value must be provided, value can either be an argument or can be passed as a flag --value`,
|
|
||||||
input: `
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: nginx-deployment
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
`,
|
|
||||||
inputOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
`,
|
|
||||||
out: `created setter replicas`,
|
|
||||||
expectedOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
`,
|
|
||||||
expectedResources: `
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: nginx-deployment
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
name: "list values with schema",
|
|
||||||
args: []string{"list", "--description", "hello world", "--set-by", "me", "--type", "array", "--field", "spec.list"},
|
|
||||||
schema: `{"maxItems": 3, "type": "array", "items": {"type": "string"}}`,
|
|
||||||
input: `
|
|
||||||
apiVersion: example.com/v1beta1
|
|
||||||
kind: Example1
|
|
||||||
spec:
|
|
||||||
list:
|
|
||||||
- "a"
|
|
||||||
- "b"
|
|
||||||
- "c"
|
|
||||||
---
|
|
||||||
apiVersion: example.com/v1beta1
|
|
||||||
kind: Example2
|
|
||||||
spec:
|
|
||||||
list:
|
|
||||||
- "a"
|
|
||||||
- "b"
|
|
||||||
- "c"
|
|
||||||
---
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: nginx-deployment
|
|
||||||
namespace: myspace
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
template:
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: sidecar
|
|
||||||
image: nginx:1.7.9
|
|
||||||
- name: nginx
|
|
||||||
image: otherspace/nginx:1.7.9
|
|
||||||
`,
|
|
||||||
inputOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
`,
|
|
||||||
out: `created setter "list"`,
|
|
||||||
expectedOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
openAPI:
|
|
||||||
definitions:
|
|
||||||
io.k8s.cli.setters.list:
|
|
||||||
items:
|
|
||||||
type: string
|
|
||||||
maxItems: 3
|
|
||||||
type: array
|
|
||||||
description: hello world
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: list
|
|
||||||
value: ""
|
|
||||||
listValues:
|
|
||||||
- a
|
|
||||||
- b
|
|
||||||
- c
|
|
||||||
setBy: me
|
|
||||||
`,
|
|
||||||
expectedResources: `
|
|
||||||
apiVersion: example.com/v1beta1
|
|
||||||
kind: Example1
|
|
||||||
spec:
|
|
||||||
list: # {"$openapi":"list"}
|
|
||||||
- "a"
|
|
||||||
- "b"
|
|
||||||
- "c"
|
|
||||||
---
|
|
||||||
apiVersion: example.com/v1beta1
|
|
||||||
kind: Example2
|
|
||||||
spec:
|
|
||||||
list: # {"$openapi":"list"}
|
|
||||||
- "a"
|
|
||||||
- "b"
|
|
||||||
- "c"
|
|
||||||
---
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: nginx-deployment
|
|
||||||
namespace: myspace
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
template:
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: sidecar
|
|
||||||
image: nginx:1.7.9
|
|
||||||
- name: nginx
|
|
||||||
image: otherspace/nginx:1.7.9
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
name: "error list path with different values",
|
|
||||||
args: []string{"list", "--description", "hello world", "--set-by", "me", "--type", "array", "--field", "spec.list"},
|
|
||||||
schema: `{"maxItems": 3, "type": "array", "items": {"type": "string"}}`,
|
|
||||||
input: `
|
|
||||||
apiVersion: example.com/v1beta1
|
|
||||||
kind: Example
|
|
||||||
spec:
|
|
||||||
list:
|
|
||||||
- "a"
|
|
||||||
- "b"
|
|
||||||
- "c"
|
|
||||||
---
|
|
||||||
apiVersion: example.com/v1beta1
|
|
||||||
kind: Example
|
|
||||||
spec:
|
|
||||||
list:
|
|
||||||
- "c"
|
|
||||||
- "d"
|
|
||||||
`,
|
|
||||||
inputOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
`,
|
|
||||||
err: `setters can only be created for fields with same values, encountered different ` +
|
|
||||||
`array values for specified field path: [c d], [a b c]`,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
name: "list values error if field not set",
|
|
||||||
args: []string{"list", "a", "--description", "hello world", "--set-by", "me", "--type", "array"},
|
|
||||||
schema: `{"maxItems": 3, "type": "array", "items": {"type": "string"}}`,
|
|
||||||
input: `
|
|
||||||
apiVersion: example.com/v1beta1
|
|
||||||
kind: Example
|
|
||||||
spec:
|
|
||||||
list:
|
|
||||||
- "a"
|
|
||||||
- "b"
|
|
||||||
- "c"
|
|
||||||
`,
|
|
||||||
inputOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
`,
|
|
||||||
out: `created setter replicas`,
|
|
||||||
expectedOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
openAPI:
|
|
||||||
definitions:
|
|
||||||
io.k8s.cli.setters.list:
|
|
||||||
items:
|
|
||||||
type: string
|
|
||||||
maxItems: 3
|
|
||||||
type: array
|
|
||||||
description: hello world
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: list
|
|
||||||
listValues:
|
|
||||||
- a
|
|
||||||
- b
|
|
||||||
- c
|
|
||||||
setBy: me
|
|
||||||
`,
|
|
||||||
expectedResources: `
|
|
||||||
apiVersion: example.com/v1beta1
|
|
||||||
kind: Example
|
|
||||||
spec:
|
|
||||||
list: # {"$openapi":"list"}
|
|
||||||
- "a"
|
|
||||||
- "b"
|
|
||||||
- "c"
|
|
||||||
`,
|
|
||||||
err: `field flag must be set for array type setters`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "add replicas with value set by flag",
|
|
||||||
args: []string{"replicas", "--value", "3", "--description", "hello world", "--set-by", "me"},
|
|
||||||
input: `
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: nginx-deployment
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
`,
|
|
||||||
inputOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
`,
|
|
||||||
out: `created setter "replicas"`,
|
|
||||||
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 # {"$openapi":"replicas"}
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "add setter with . in the name",
|
|
||||||
args: []string{"foo.bar", "3"},
|
|
||||||
input: `
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: nginx-deployment
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
`,
|
|
||||||
inputOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
`,
|
|
||||||
out: `created setter "foo.bar"`,
|
|
||||||
expectedOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
openAPI:
|
|
||||||
definitions:
|
|
||||||
io.k8s.cli.setters.foo.bar:
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: foo.bar
|
|
||||||
value: "3"
|
|
||||||
`,
|
|
||||||
expectedResources: `
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: nginx-deployment
|
|
||||||
spec:
|
|
||||||
replicas: 3 # {"$openapi":"foo.bar"}
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "provide different type values in schema and with flag",
|
|
||||||
args: []string{"replicas", "3", "--description", "hello world", "--type", "string"},
|
|
||||||
schema: `{"type": "integer"}`,
|
|
||||||
input: `
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: nginx-deployment
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
`,
|
|
||||||
inputOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
`,
|
|
||||||
expectedOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
`,
|
|
||||||
expectedResources: `
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: nginx-deployment
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
`,
|
|
||||||
err: `type provided in type flag (string) and in schema (integer) doesn't match`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "invalid json in schema",
|
|
||||||
args: []string{"replicas", "3"},
|
|
||||||
schema: `{"foo": bar`,
|
|
||||||
input: `
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: nginx-deployment
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
`,
|
|
||||||
inputOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
`,
|
|
||||||
expectedOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
`,
|
|
||||||
expectedResources: `
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: nginx-deployment
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
`,
|
|
||||||
err: "unable to parse schema: invalid character 'b' looking for beginning of value",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "unknown fields in schema are dropped",
|
|
||||||
args: []string{"replicas", "3"},
|
|
||||||
schema: `{"foo": "bar"}`,
|
|
||||||
input: `
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: nginx-deployment
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
`,
|
|
||||||
inputOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
`,
|
|
||||||
out: `created setter "replicas"`,
|
|
||||||
expectedOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
openAPI:
|
|
||||||
definitions:
|
|
||||||
io.k8s.cli.setters.replicas:
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: replicas
|
|
||||||
value: "3"
|
|
||||||
`,
|
|
||||||
expectedResources: `
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: nginx-deployment
|
|
||||||
spec:
|
|
||||||
replicas: 3 # {"$openapi":"replicas"}
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "unknown types are not accepted",
|
|
||||||
args: []string{"replicas", "3"},
|
|
||||||
schema: `{"type": "int"}`,
|
|
||||||
input: `
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: nginx-deployment
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
`,
|
|
||||||
inputOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
`,
|
|
||||||
expectedOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
`,
|
|
||||||
expectedResources: `
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: nginx-deployment
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
`,
|
|
||||||
err: `invalid schema: type "int" is not supported. Must be one of: object, array, string, integer, number, boolean, file, null`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "unknown types are not accepted in nested structures",
|
|
||||||
args: []string{"replicas", "3", "--field", "replicas"},
|
|
||||||
schema: `{"maxItems": 3, "type": "array", "items": {"type": "foo"}}`,
|
|
||||||
input: `
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: nginx-deployment
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
`,
|
|
||||||
inputOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
`,
|
|
||||||
expectedOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
`,
|
|
||||||
expectedResources: `
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: nginx-deployment
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
`,
|
|
||||||
err: `invalid schema: type "foo" is not supported. Must be one of: object, array, string, integer, number, boolean, file, null`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "unknown types are not accepted in --type flag",
|
|
||||||
args: []string{"replicas", "3", "--type", "bar"},
|
|
||||||
input: `
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: nginx-deployment
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
`,
|
|
||||||
inputOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
`,
|
|
||||||
expectedOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
`,
|
|
||||||
expectedResources: `
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: nginx-deployment
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
`,
|
|
||||||
err: `invalid schema: type "bar" is not supported. Must be one of: object, array, string, integer, number, boolean, file, null`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "unknown properties in schema are dropped",
|
|
||||||
args: []string{"replicas", "3", "--type", "integer"},
|
|
||||||
schema: `{"maximum": 3, "unknown": 42}`,
|
|
||||||
input: `
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: nginx-deployment
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
`,
|
|
||||||
inputOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
`,
|
|
||||||
out: `created setter "replicas"`,
|
|
||||||
expectedOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
openAPI:
|
|
||||||
definitions:
|
|
||||||
io.k8s.cli.setters.replicas:
|
|
||||||
maximum: 3
|
|
||||||
type: integer
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: replicas
|
|
||||||
value: "3"
|
|
||||||
`,
|
|
||||||
expectedResources: `
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: nginx-deployment
|
|
||||||
spec:
|
|
||||||
replicas: 3 # {"$openapi":"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()
|
|
||||||
|
|
||||||
baseDir := t.TempDir()
|
|
||||||
|
|
||||||
f := filepath.Join(baseDir, "Krmfile")
|
|
||||||
err := os.WriteFile(f, []byte(test.inputOpenAPI), 0600)
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
if test.schema != "" {
|
|
||||||
sch, err := os.CreateTemp("", "schema.json")
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
t.Cleanup(func() {
|
|
||||||
sch.Close()
|
|
||||||
os.Remove(sch.Name())
|
|
||||||
})
|
|
||||||
|
|
||||||
err = os.WriteFile(sch.Name(), []byte(test.schema), 0600)
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
test.args = append(test.args, "--schema-path", sch.Name())
|
|
||||||
}
|
|
||||||
|
|
||||||
r, err := os.CreateTemp(baseDir, "k8s-cli-*.yaml")
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
t.Cleanup(func() { r.Close() })
|
|
||||||
err = os.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{baseDir}, test.args...))
|
|
||||||
err = runner.Command.Execute()
|
|
||||||
if test.err != "" {
|
|
||||||
if !assert.NotNil(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
} else {
|
|
||||||
assert.Equal(t, err.Error(), test.err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
expectedOut := strings.ReplaceAll(test.out, "${baseDir}", baseDir)
|
|
||||||
expectedNormalized := strings.ReplaceAll(expectedOut, "\\", "/")
|
|
||||||
// normalize path format for windows
|
|
||||||
actualNormalized := strings.ReplaceAll(
|
|
||||||
strings.ReplaceAll(out.String(), "\\", "/"),
|
|
||||||
"//", "/")
|
|
||||||
|
|
||||||
if !assert.Contains(t, actualNormalized, expectedNormalized) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
actualResources, err := os.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 := os.ReadFile(f)
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
if !assert.Equal(t,
|
|
||||||
strings.TrimSpace(test.expectedOpenAPI),
|
|
||||||
strings.TrimSpace(string(actualOpenAPI))) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCreateSetterSubPackages(t *testing.T) {
|
|
||||||
var tests = []struct {
|
|
||||||
name string
|
|
||||||
dataset string
|
|
||||||
packagePath string
|
|
||||||
args []string
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "create-setter-recurse-subpackages",
|
|
||||||
dataset: "dataset-without-setters",
|
|
||||||
args: []string{"namespace", "myspace", "-R"},
|
|
||||||
expected: `${baseDir}/mysql/
|
|
||||||
created setter "namespace"
|
|
||||||
|
|
||||||
${baseDir}/mysql/storage/
|
|
||||||
created setter "namespace"
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "create-setter-top-level-pkg-no-recurse-subpackages",
|
|
||||||
dataset: "dataset-without-setters",
|
|
||||||
packagePath: "mysql",
|
|
||||||
args: []string{"namespace", "myspace"},
|
|
||||||
expected: `${baseDir}/mysql/
|
|
||||||
created setter "namespace"
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "create-setter-nested-pkg-no-recurse-subpackages",
|
|
||||||
dataset: "dataset-without-setters",
|
|
||||||
packagePath: "mysql/storage",
|
|
||||||
args: []string{"namespace", "myspace"},
|
|
||||||
expected: `${baseDir}/mysql/storage/
|
|
||||||
created setter "namespace"
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "create-setter-already-exists",
|
|
||||||
dataset: "dataset-with-setters",
|
|
||||||
packagePath: "mysql",
|
|
||||||
args: []string{"namespace", "myspace", "-R"},
|
|
||||||
expected: `${baseDir}/mysql/
|
|
||||||
setter with name "namespace" already exists, if you want to modify it, please delete the existing setter and recreate it
|
|
||||||
|
|
||||||
${baseDir}/mysql/nosetters/
|
|
||||||
created setter "namespace"
|
|
||||||
|
|
||||||
${baseDir}/mysql/storage/
|
|
||||||
setter with name "namespace" already exists, if you want to modify it, please delete the existing setter and recreate it
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for i := range tests {
|
|
||||||
test := tests[i]
|
|
||||||
t.Run(test.name, func(t *testing.T) {
|
|
||||||
// reset the openAPI afterward
|
|
||||||
openapi.ResetOpenAPI()
|
|
||||||
defer openapi.ResetOpenAPI()
|
|
||||||
sourceDir := filepath.Join("test", "testdata", test.dataset)
|
|
||||||
baseDir := t.TempDir()
|
|
||||||
copyutil.CopyDir(sourceDir, baseDir)
|
|
||||||
runner := commands.NewCreateSetterRunner("")
|
|
||||||
actual := &bytes.Buffer{}
|
|
||||||
runner.Command.SetOut(actual)
|
|
||||||
runner.Command.SetArgs(append([]string{filepath.Join(baseDir, test.packagePath)}, test.args...))
|
|
||||||
err := runner.Command.Execute()
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
// normalize path format for windows
|
|
||||||
actualNormalized := strings.ReplaceAll(
|
|
||||||
strings.ReplaceAll(actual.String(), "\\", "/"),
|
|
||||||
"//", "/")
|
|
||||||
|
|
||||||
expected := strings.ReplaceAll(test.expected, "${baseDir}", baseDir)
|
|
||||||
expectedNormalized := strings.ReplaceAll(expected, "\\", "/")
|
|
||||||
if !assert.Contains(t, actualNormalized, expectedNormalized) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,104 +0,0 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package commands
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/ext"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/runner"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
|
||||||
"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",
|
|
||||||
Args: cobra.ExactArgs(2),
|
|
||||||
PreRun: r.preRun,
|
|
||||||
RunE: r.runE,
|
|
||||||
Deprecated: "imperative substitutions will no longer be available in kustomize v5.\n" +
|
|
||||||
"See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.",
|
|
||||||
}
|
|
||||||
cs.Flags().StringVar(&r.CreateSubstitution.FieldName, "field", "",
|
|
||||||
"name of the field to set -- e.g. --field image")
|
|
||||||
cs.Flags().StringVar(&r.CreateSubstitution.FieldValue, "field-value", "",
|
|
||||||
"value of the field to create substitution for -- e.g. --field-value nginx:0.1.0")
|
|
||||||
cs.Flags().StringVar(&r.CreateSubstitution.Pattern, "pattern", "",
|
|
||||||
`substitution pattern -- e.g. --pattern \${my-image-setter}:\${my-tag-setter}`)
|
|
||||||
cs.Flags().BoolVarP(&r.CreateSubstitution.RecurseSubPackages, "recurse-subpackages", "R", false,
|
|
||||||
"creates substitution recursively in all the nested subpackages")
|
|
||||||
_ = cs.MarkFlagRequired("pattern")
|
|
||||||
_ = cs.MarkFlagRequired("field-value")
|
|
||||||
runner.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 {
|
|
||||||
e := runner.ExecuteCmdOnPkgs{
|
|
||||||
NeedOpenAPI: true,
|
|
||||||
Writer: c.OutOrStdout(),
|
|
||||||
RootPkgPath: args[0],
|
|
||||||
RecurseSubPackages: r.CreateSubstitution.RecurseSubPackages,
|
|
||||||
CmdRunner: r,
|
|
||||||
}
|
|
||||||
err := e.Execute()
|
|
||||||
if err != nil {
|
|
||||||
return runner.HandleError(c, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *CreateSubstitutionRunner) ExecuteCmd(w io.Writer, pkgPath string) error {
|
|
||||||
sc, err := openapi.SchemaFromFile(filepath.Join(pkgPath, ext.KRMFileName()))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
r.CreateSubstitution = settersutil.SubstitutionCreator{
|
|
||||||
Name: r.CreateSubstitution.Name,
|
|
||||||
FieldName: r.CreateSubstitution.FieldName,
|
|
||||||
FieldValue: r.CreateSubstitution.FieldValue,
|
|
||||||
RecurseSubPackages: r.CreateSubstitution.RecurseSubPackages,
|
|
||||||
Pattern: r.CreateSubstitution.Pattern,
|
|
||||||
OpenAPIFileName: ext.KRMFileName(),
|
|
||||||
OpenAPIPath: filepath.Join(pkgPath, ext.KRMFileName()),
|
|
||||||
ResourcesPath: pkgPath,
|
|
||||||
SettersSchema: sc,
|
|
||||||
}
|
|
||||||
|
|
||||||
err = r.CreateSubstitution.Create()
|
|
||||||
if err != nil {
|
|
||||||
// return err if RecurseSubPackages is false
|
|
||||||
if !r.CreateSubstitution.RecurseSubPackages {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// print error message and continue if RecurseSubPackages is true
|
|
||||||
fmt.Fprintf(w, "%s\n", err.Error())
|
|
||||||
} else {
|
|
||||||
fmt.Fprintf(w, "created substitution %q\n", r.CreateSubstitution.Name)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *CreateSubstitutionRunner) preRun(c *cobra.Command, args []string) {
|
|
||||||
r.CreateSubstitution.Name = args[1]
|
|
||||||
}
|
|
||||||
@@ -1,505 +0,0 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package commands_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/copyutil"
|
|
||||||
"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
|
|
||||||
err string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "substitution replicas",
|
|
||||||
args: []string{
|
|
||||||
"my-image-subst", "--field-value", "nginx:1.7.9", "--pattern", "${my-image-setter}:${my-tag-setter}"},
|
|
||||||
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.my-image-setter:
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: my-image-setter
|
|
||||||
value: "nginx"
|
|
||||||
io.k8s.cli.setters.my-tag-setter:
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: my-tag-setter
|
|
||||||
value: "1.7.9"
|
|
||||||
`,
|
|
||||||
out: `created substitution "my-image-subst"`,
|
|
||||||
expectedOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
openAPI:
|
|
||||||
definitions:
|
|
||||||
io.k8s.cli.setters.my-image-setter:
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: my-image-setter
|
|
||||||
value: "nginx"
|
|
||||||
io.k8s.cli.setters.my-tag-setter:
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: my-tag-setter
|
|
||||||
value: "1.7.9"
|
|
||||||
io.k8s.cli.substitutions.my-image-subst:
|
|
||||||
x-k8s-cli:
|
|
||||||
substitution:
|
|
||||||
name: my-image-subst
|
|
||||||
pattern: ${my-image-setter}:${my-tag-setter}
|
|
||||||
values:
|
|
||||||
- marker: ${my-image-setter}
|
|
||||||
ref: '#/definitions/io.k8s.cli.setters.my-image-setter'
|
|
||||||
- marker: ${my-tag-setter}
|
|
||||||
ref: '#/definitions/io.k8s.cli.setters.my-tag-setter'
|
|
||||||
`,
|
|
||||||
expectedResources: `
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: nginx-deployment
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
template:
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: nginx
|
|
||||||
image: nginx:1.7.9 # {"$openapi":"my-image-subst"}
|
|
||||||
- name: sidecar
|
|
||||||
image: sidecar:1.7.9
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "error if substitution with same name exists",
|
|
||||||
args: []string{"my-image", "--field-value", "some:image", "--pattern", "some:${image}"},
|
|
||||||
inputOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
openAPI:
|
|
||||||
definitions:
|
|
||||||
io.k8s.cli.substitutions.my-image:
|
|
||||||
x-k8s-cli:
|
|
||||||
substitution:
|
|
||||||
name: my-image
|
|
||||||
pattern: something/${my-image-setter}::${my-tag-setter}/nginxotherthing
|
|
||||||
values:
|
|
||||||
- marker: ${my-image-setter}
|
|
||||||
ref: '#/definitions/io.k8s.cli.setters.my-image-setter'
|
|
||||||
- marker: ${my-tag-setter}
|
|
||||||
ref: '#/definitions/io.k8s.cli.setters.my-tag-setter'
|
|
||||||
`,
|
|
||||||
err: `substitution with name "my-image" already exists`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "error if setter with same name exists",
|
|
||||||
args: []string{
|
|
||||||
"my-image", "--field-value", "nginx:1.7.9", "--pattern", "${my-image-setter}:${my-tag-setter}"},
|
|
||||||
inputOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
openAPI:
|
|
||||||
definitions:
|
|
||||||
io.k8s.cli.setters.my-image:
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: my-image
|
|
||||||
value: "nginx"
|
|
||||||
`,
|
|
||||||
err: `setter with name "my-image" already exists, substitution and setter can't have same name`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "substitution and create setters 1",
|
|
||||||
args: []string{
|
|
||||||
"my-image-subst", "--field-value", "something/nginx::1.7.9/nginxotherthing", "--pattern", "something/${my-image-setter}::${my-tag-setter}/nginxotherthing"},
|
|
||||||
input: `
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: nginx-deployment
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
template:
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: nginx
|
|
||||||
image: something/nginx::1.7.9/nginxotherthing
|
|
||||||
- name: sidecar
|
|
||||||
image: sidecar:1.7.9
|
|
||||||
`,
|
|
||||||
inputOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
`,
|
|
||||||
out: `created substitution "my-image-subst"`,
|
|
||||||
expectedOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
openAPI:
|
|
||||||
definitions:
|
|
||||||
io.k8s.cli.substitutions.my-image-subst:
|
|
||||||
x-k8s-cli:
|
|
||||||
substitution:
|
|
||||||
name: my-image-subst
|
|
||||||
pattern: something/${my-image-setter}::${my-tag-setter}/nginxotherthing
|
|
||||||
values:
|
|
||||||
- marker: ${my-image-setter}
|
|
||||||
ref: '#/definitions/io.k8s.cli.setters.my-image-setter'
|
|
||||||
- marker: ${my-tag-setter}
|
|
||||||
ref: '#/definitions/io.k8s.cli.setters.my-tag-setter'
|
|
||||||
io.k8s.cli.setters.my-image-setter:
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: my-image-setter
|
|
||||||
value: nginx
|
|
||||||
io.k8s.cli.setters.my-tag-setter:
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: my-tag-setter
|
|
||||||
value: 1.7.9
|
|
||||||
`,
|
|
||||||
expectedResources: `
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: nginx-deployment
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
template:
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: nginx
|
|
||||||
image: something/nginx::1.7.9/nginxotherthing # {"$openapi":"my-image-subst"}
|
|
||||||
- name: sidecar
|
|
||||||
image: sidecar:1.7.9
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "nested substitution",
|
|
||||||
args: []string{
|
|
||||||
"my-nested-subst", "--field-value", "something/nginx::1.7.9/nginxotherthing",
|
|
||||||
"--pattern", "something/${my-image-subst}/${my-other-setter}"},
|
|
||||||
input: `
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: nginx-deployment
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
template:
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: nginx
|
|
||||||
image: something/nginx::1.7.9/nginxotherthing
|
|
||||||
- name: sidecar
|
|
||||||
image: nginx::1.7.9 # {"$openapi":"my-image-subst"}
|
|
||||||
`,
|
|
||||||
inputOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
openAPI:
|
|
||||||
definitions:
|
|
||||||
io.k8s.cli.setters.my-image-setter:
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: my-image-setter
|
|
||||||
value: nginx
|
|
||||||
io.k8s.cli.setters.my-tag-setter:
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: my-tag-setter
|
|
||||||
value: 1.7.9
|
|
||||||
io.k8s.cli.substitutions.my-image-subst:
|
|
||||||
x-k8s-cli:
|
|
||||||
substitution:
|
|
||||||
name: my-image-subst
|
|
||||||
pattern: ${my-image-setter}::${my-tag-setter}
|
|
||||||
values:
|
|
||||||
- marker: ${my-image-setter}
|
|
||||||
ref: '#/definitions/io.k8s.cli.setters.my-image-setter'
|
|
||||||
- marker: ${my-tag-setter}
|
|
||||||
ref: '#/definitions/io.k8s.cli.setters.my-tag-setter'
|
|
||||||
`,
|
|
||||||
out: `created substitution "my-nested-subst"`,
|
|
||||||
expectedOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
openAPI:
|
|
||||||
definitions:
|
|
||||||
io.k8s.cli.setters.my-image-setter:
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: my-image-setter
|
|
||||||
value: nginx
|
|
||||||
io.k8s.cli.setters.my-tag-setter:
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: my-tag-setter
|
|
||||||
value: 1.7.9
|
|
||||||
io.k8s.cli.substitutions.my-image-subst:
|
|
||||||
x-k8s-cli:
|
|
||||||
substitution:
|
|
||||||
name: my-image-subst
|
|
||||||
pattern: ${my-image-setter}::${my-tag-setter}
|
|
||||||
values:
|
|
||||||
- marker: ${my-image-setter}
|
|
||||||
ref: '#/definitions/io.k8s.cli.setters.my-image-setter'
|
|
||||||
- marker: ${my-tag-setter}
|
|
||||||
ref: '#/definitions/io.k8s.cli.setters.my-tag-setter'
|
|
||||||
io.k8s.cli.substitutions.my-nested-subst:
|
|
||||||
x-k8s-cli:
|
|
||||||
substitution:
|
|
||||||
name: my-nested-subst
|
|
||||||
pattern: something/${my-image-subst}/${my-other-setter}
|
|
||||||
values:
|
|
||||||
- marker: ${my-image-subst}
|
|
||||||
ref: '#/definitions/io.k8s.cli.substitutions.my-image-subst'
|
|
||||||
- marker: ${my-other-setter}
|
|
||||||
ref: '#/definitions/io.k8s.cli.setters.my-other-setter'
|
|
||||||
io.k8s.cli.setters.my-other-setter:
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: my-other-setter
|
|
||||||
value: nginxotherthing
|
|
||||||
`,
|
|
||||||
expectedResources: `
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: nginx-deployment
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
template:
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: nginx
|
|
||||||
image: something/nginx::1.7.9/nginxotherthing # {"$openapi":"my-nested-subst"}
|
|
||||||
- name: sidecar
|
|
||||||
image: nginx::1.7.9 # {"$openapi":"my-image-subst"}
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "substitution with non-existing setter with same name",
|
|
||||||
args: []string{
|
|
||||||
"foo", "--field-value", "prefix-1234", "--pattern", "prefix-${foo}"},
|
|
||||||
input: `
|
|
||||||
apiVersion: test/v1
|
|
||||||
kind: Foo
|
|
||||||
metadata:
|
|
||||||
name: foo
|
|
||||||
spec:
|
|
||||||
setterVal: 1234
|
|
||||||
substVal: prefix-1234
|
|
||||||
`,
|
|
||||||
inputOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
`,
|
|
||||||
expectedOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
`,
|
|
||||||
expectedResources: `
|
|
||||||
apiVersion: test/v1
|
|
||||||
kind: Foo
|
|
||||||
metadata:
|
|
||||||
name: foo
|
|
||||||
spec:
|
|
||||||
setterVal: 1234
|
|
||||||
substVal: prefix-1234
|
|
||||||
|
|
||||||
`,
|
|
||||||
err: `setters must have different name than the substitution: foo`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for i := range tests {
|
|
||||||
test := tests[i]
|
|
||||||
t.Run(test.name, func(t *testing.T) {
|
|
||||||
// reset the openAPI afterward
|
|
||||||
openapi.ResetOpenAPI()
|
|
||||||
defer openapi.ResetOpenAPI()
|
|
||||||
|
|
||||||
baseDir := t.TempDir()
|
|
||||||
f := filepath.Join(baseDir, "Krmfile")
|
|
||||||
err := os.WriteFile(f, []byte(test.inputOpenAPI), 0600)
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
r, err := os.CreateTemp(baseDir, "k8s-cli-*.yaml")
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
t.Cleanup(func() { r.Close() })
|
|
||||||
err = os.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{baseDir}, test.args...))
|
|
||||||
err = runner.Command.Execute()
|
|
||||||
|
|
||||||
if test.err != "" {
|
|
||||||
if !assert.NotNil(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
assert.Equal(t, test.err, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
expectedOut := strings.ReplaceAll(test.out, "${baseDir}", baseDir)
|
|
||||||
expectedNormalized := strings.ReplaceAll(expectedOut, "\\", "/")
|
|
||||||
// normalize path format for windows
|
|
||||||
actualNormalized := strings.ReplaceAll(
|
|
||||||
strings.ReplaceAll(out.String(), "\\", "/"),
|
|
||||||
"//", "/")
|
|
||||||
|
|
||||||
if !assert.Contains(t, actualNormalized, expectedNormalized) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
actualResources, err := os.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 := os.ReadFile(f)
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
if !assert.Equal(t,
|
|
||||||
strings.TrimSpace(test.expectedOpenAPI),
|
|
||||||
strings.TrimSpace(string(actualOpenAPI))) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCreateSubstSubPackages(t *testing.T) {
|
|
||||||
var tests = []struct {
|
|
||||||
name string
|
|
||||||
dataset string
|
|
||||||
packagePath string
|
|
||||||
args []string
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "create-subst-recurse-subpackages",
|
|
||||||
dataset: "dataset-without-setters",
|
|
||||||
args: []string{"image-tag", "--field-value", "mysql:1.7.9", "--pattern", "${image}:${tag}", "-R"},
|
|
||||||
expected: `${baseDir}/mysql/
|
|
||||||
created substitution "image-tag"
|
|
||||||
|
|
||||||
${baseDir}/mysql/storage/
|
|
||||||
created substitution "image-tag"
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "create-subst-top-level-pkg-no-recurse-subpackages",
|
|
||||||
dataset: "dataset-without-setters",
|
|
||||||
packagePath: "mysql",
|
|
||||||
args: []string{"image-tag", "--field-value", "mysql:1.7.9", "--pattern", "${image}:${tag}"},
|
|
||||||
expected: `${baseDir}/mysql/
|
|
||||||
created substitution "image-tag"`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "create-subst-nested-pkg-no-recurse-subpackages",
|
|
||||||
dataset: "dataset-without-setters",
|
|
||||||
packagePath: "mysql/storage",
|
|
||||||
args: []string{"image-tag", "--field-value", "storage:1.7.9", "--pattern", "${image}:${tag}"},
|
|
||||||
expected: `${baseDir}/mysql/storage/
|
|
||||||
created substitution "image-tag"`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "create-subst-already-exists",
|
|
||||||
dataset: "dataset-with-setters",
|
|
||||||
packagePath: "mysql",
|
|
||||||
args: []string{"image-tag", "--field-value", "mysql:1.7.9", "--pattern", "${image}:${tag}", "-R"},
|
|
||||||
expected: `${baseDir}/mysql/
|
|
||||||
substitution with name "image-tag" already exists
|
|
||||||
|
|
||||||
${baseDir}/mysql/nosetters/
|
|
||||||
created substitution "image-tag"
|
|
||||||
|
|
||||||
${baseDir}/mysql/storage/
|
|
||||||
created substitution "image-tag"`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for i := range tests {
|
|
||||||
test := tests[i]
|
|
||||||
t.Run(test.name, func(t *testing.T) {
|
|
||||||
// reset the openAPI afterward
|
|
||||||
openapi.ResetOpenAPI()
|
|
||||||
defer openapi.ResetOpenAPI()
|
|
||||||
sourceDir := filepath.Join("test", "testdata", test.dataset)
|
|
||||||
baseDir := t.TempDir()
|
|
||||||
copyutil.CopyDir(sourceDir, baseDir)
|
|
||||||
runner := commands.NewCreateSubstitutionRunner("")
|
|
||||||
actual := &bytes.Buffer{}
|
|
||||||
runner.Command.SetOut(actual)
|
|
||||||
runner.Command.SetArgs(append([]string{filepath.Join(baseDir, test.packagePath)}, test.args...))
|
|
||||||
err := runner.Command.Execute()
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
// normalize path format for windows
|
|
||||||
actualNormalized := strings.ReplaceAll(
|
|
||||||
strings.ReplaceAll(actual.String(), "\\", "/"),
|
|
||||||
"//", "/")
|
|
||||||
|
|
||||||
expected := strings.ReplaceAll(test.expected, "${baseDir}", baseDir)
|
|
||||||
expectedNormalized := strings.ReplaceAll(expected, "\\", "/")
|
|
||||||
if !assert.Contains(t, strings.TrimSpace(actualNormalized), strings.TrimSpace(expectedNormalized)) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package commands
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/ext"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/runner"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/fieldmeta"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/setters2/settersutil"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewDeleteRunner returns a command runner.
|
|
||||||
func NewDeleteSetterRunner(parent string) *DeleteSetterRunner {
|
|
||||||
r := &DeleteSetterRunner{}
|
|
||||||
c := &cobra.Command{
|
|
||||||
Use: "delete-setter DIR NAME",
|
|
||||||
Args: cobra.ExactArgs(2),
|
|
||||||
Short: commands.DeleteSetterShort,
|
|
||||||
Long: commands.DeleteSetterLong,
|
|
||||||
Example: commands.DeleteSetterExamples,
|
|
||||||
PreRunE: r.preRunE,
|
|
||||||
RunE: r.runE,
|
|
||||||
}
|
|
||||||
c.Flags().BoolVarP(&r.RecurseSubPackages, "recurse-subpackages", "R", false,
|
|
||||||
"deletes setter recursively in all the nested subpackages")
|
|
||||||
runner.FixDocs(parent, c)
|
|
||||||
r.Command = c
|
|
||||||
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func DeleteSetterCommand(parent string) *cobra.Command {
|
|
||||||
return NewDeleteSetterRunner(parent).Command
|
|
||||||
}
|
|
||||||
|
|
||||||
type DeleteSetterRunner struct {
|
|
||||||
Command *cobra.Command
|
|
||||||
DeleteSetter settersutil.DeleterCreator
|
|
||||||
OpenAPIFile string
|
|
||||||
RecurseSubPackages bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *DeleteSetterRunner) preRunE(c *cobra.Command, args []string) error {
|
|
||||||
r.DeleteSetter.Name = args[1]
|
|
||||||
r.DeleteSetter.DefinitionPrefix = fieldmeta.SetterDefinitionPrefix
|
|
||||||
|
|
||||||
r.OpenAPIFile = filepath.Join(args[0], ext.KRMFileName())
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *DeleteSetterRunner) runE(c *cobra.Command, args []string) error {
|
|
||||||
e := runner.ExecuteCmdOnPkgs{
|
|
||||||
NeedOpenAPI: true,
|
|
||||||
Writer: c.OutOrStdout(),
|
|
||||||
RootPkgPath: args[0],
|
|
||||||
RecurseSubPackages: r.RecurseSubPackages,
|
|
||||||
CmdRunner: r,
|
|
||||||
}
|
|
||||||
err := e.Execute()
|
|
||||||
if err != nil {
|
|
||||||
return runner.HandleError(c, err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *DeleteSetterRunner) ExecuteCmd(w io.Writer, pkgPath string) error {
|
|
||||||
sc, err := openapi.SchemaFromFile(filepath.Join(pkgPath, ext.KRMFileName()))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
r.DeleteSetter = settersutil.DeleterCreator{
|
|
||||||
Name: r.DeleteSetter.Name,
|
|
||||||
DefinitionPrefix: fieldmeta.SetterDefinitionPrefix,
|
|
||||||
RecurseSubPackages: r.RecurseSubPackages,
|
|
||||||
OpenAPIFileName: ext.KRMFileName(),
|
|
||||||
OpenAPIPath: filepath.Join(pkgPath, ext.KRMFileName()),
|
|
||||||
ResourcesPath: pkgPath,
|
|
||||||
SettersSchema: sc,
|
|
||||||
}
|
|
||||||
|
|
||||||
err = r.DeleteSetter.Delete()
|
|
||||||
if err != nil {
|
|
||||||
// return err if RecurseSubPackages is false
|
|
||||||
if !r.DeleteSetter.RecurseSubPackages {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// print error message and continue if RecurseSubPackages is true
|
|
||||||
fmt.Fprintf(w, "%s\n", err.Error())
|
|
||||||
} else {
|
|
||||||
fmt.Fprintf(w, "deleted setter %q\n", r.DeleteSetter.Name)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,443 +0,0 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package commands_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/copyutil"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestDeleteSetterCommand(t *testing.T) {
|
|
||||||
var tests = []struct {
|
|
||||||
name string
|
|
||||||
input string
|
|
||||||
args []string
|
|
||||||
schema string
|
|
||||||
out string
|
|
||||||
inputOpenAPI string
|
|
||||||
expectedOpenAPI string
|
|
||||||
expectedResources string
|
|
||||||
err string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "delete replicas",
|
|
||||||
args: []string{"replicas-setter"},
|
|
||||||
input: `
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: nginx-deployment
|
|
||||||
spec:
|
|
||||||
replicas: 3 # {"$openapi" : "replicas-setter"}
|
|
||||||
`,
|
|
||||||
inputOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
openAPI:
|
|
||||||
definitions:
|
|
||||||
io.k8s.cli.setters.replicas-setter:
|
|
||||||
description: hello world
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: replicas-setter
|
|
||||||
value: "3"
|
|
||||||
setBy: me
|
|
||||||
`,
|
|
||||||
out: `deleted setter "replicas-setter"`,
|
|
||||||
expectedOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
`,
|
|
||||||
expectedResources: `
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: nginx-deployment
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "delete only one setter",
|
|
||||||
args: []string{"replicas-setter"},
|
|
||||||
input: `
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: nginx-deployment
|
|
||||||
spec:
|
|
||||||
replicas: 3 # {"$openapi" : "replicas-setter"}
|
|
||||||
foo: nginx # {"$openapi" : "image"}
|
|
||||||
`,
|
|
||||||
inputOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
openAPI:
|
|
||||||
definitions:
|
|
||||||
io.k8s.cli.setters.replicas-setter:
|
|
||||||
description: hello world
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: replicas-setter
|
|
||||||
value: "3"
|
|
||||||
setBy: me
|
|
||||||
io.k8s.cli.setters.image:
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: image
|
|
||||||
value: nginx
|
|
||||||
`,
|
|
||||||
out: `deleted setter "replicas-setter"`,
|
|
||||||
expectedOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
openAPI:
|
|
||||||
definitions:
|
|
||||||
io.k8s.cli.setters.image:
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: image
|
|
||||||
value: nginx
|
|
||||||
`,
|
|
||||||
expectedResources: `
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: nginx-deployment
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
foo: nginx # {"$openapi" : "image"}
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
name: "delete array setter",
|
|
||||||
args: []string{"list"},
|
|
||||||
inputOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
openAPI:
|
|
||||||
definitions:
|
|
||||||
io.k8s.cli.setters.list:
|
|
||||||
items:
|
|
||||||
type: string
|
|
||||||
maxItems: 3
|
|
||||||
type: array
|
|
||||||
description: hello world
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: list
|
|
||||||
value: ""
|
|
||||||
listValues:
|
|
||||||
- a
|
|
||||||
- b
|
|
||||||
- c
|
|
||||||
setBy: me
|
|
||||||
`,
|
|
||||||
input: `
|
|
||||||
apiVersion: example.com/v1beta1
|
|
||||||
kind: Example1
|
|
||||||
spec:
|
|
||||||
list: # {"$openapi":"list"}
|
|
||||||
- "a"
|
|
||||||
- "b"
|
|
||||||
- "c"
|
|
||||||
---
|
|
||||||
apiVersion: example.com/v1beta1
|
|
||||||
kind: Example2
|
|
||||||
spec:
|
|
||||||
list: # {"$openapi":"list2"}
|
|
||||||
- "a"
|
|
||||||
- "b"
|
|
||||||
- "c"
|
|
||||||
`,
|
|
||||||
out: `deleted setter "list"`,
|
|
||||||
expectedResources: `
|
|
||||||
apiVersion: example.com/v1beta1
|
|
||||||
kind: Example1
|
|
||||||
spec:
|
|
||||||
list:
|
|
||||||
- "a"
|
|
||||||
- "b"
|
|
||||||
- "c"
|
|
||||||
---
|
|
||||||
apiVersion: example.com/v1beta1
|
|
||||||
kind: Example2
|
|
||||||
spec:
|
|
||||||
list: # {"$openapi":"list2"}
|
|
||||||
- "a"
|
|
||||||
- "b"
|
|
||||||
- "c"
|
|
||||||
`,
|
|
||||||
expectedOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
name: "delete non exist setter error",
|
|
||||||
args: []string{"image"},
|
|
||||||
input: `
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: nginx-deployment
|
|
||||||
spec:
|
|
||||||
replicas: 3 # {"$openapi" : "replicas-setter"}
|
|
||||||
`,
|
|
||||||
inputOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
openAPI:
|
|
||||||
definitions:
|
|
||||||
io.k8s.cli.setters.replicas-setter:
|
|
||||||
description: hello world
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: replicas-setter
|
|
||||||
value: "3"
|
|
||||||
setBy: me
|
|
||||||
`,
|
|
||||||
expectedOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
openAPI:
|
|
||||||
definitions:
|
|
||||||
io.k8s.cli.setters.replicas-setter:
|
|
||||||
description: hello world
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: replicas-setter
|
|
||||||
value: "3"
|
|
||||||
setBy: me
|
|
||||||
`,
|
|
||||||
expectedResources: `
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: nginx-deployment
|
|
||||||
spec:
|
|
||||||
replicas: 3 # {"$openapi" : "replicas-setter"}
|
|
||||||
`,
|
|
||||||
err: `setter "image" does not exist`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "delete setter used in substitution error",
|
|
||||||
args: []string{"image-name"},
|
|
||||||
input: `
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
`,
|
|
||||||
inputOpenAPI: `
|
|
||||||
openAPI:
|
|
||||||
definitions:
|
|
||||||
io.k8s.cli.setters.image-name:
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: image-name
|
|
||||||
value: "nginx"
|
|
||||||
io.k8s.cli.setters.image-tag:
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: image-tag
|
|
||||||
value: "1.8.1"
|
|
||||||
io.k8s.cli.substitutions.image:
|
|
||||||
x-k8s-cli:
|
|
||||||
substitution:
|
|
||||||
name: image
|
|
||||||
pattern: IMAGE_NAME:IMAGE_TAG
|
|
||||||
values:
|
|
||||||
- marker: "IMAGE_NAME"
|
|
||||||
ref: "#/definitions/io.k8s.cli.setters.image-name"
|
|
||||||
- marker: "IMAGE_TAG"
|
|
||||||
ref: "#/definitions/io.k8s.cli.setters.image-tag"
|
|
||||||
`,
|
|
||||||
expectedOpenAPI: `
|
|
||||||
openAPI:
|
|
||||||
definitions:
|
|
||||||
io.k8s.cli.setters.image-name:
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: image-name
|
|
||||||
value: "nginx"
|
|
||||||
io.k8s.cli.setters.image-tag:
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: image-tag
|
|
||||||
value: "1.8.1"
|
|
||||||
io.k8s.cli.substitutions.image:
|
|
||||||
x-k8s-cli:
|
|
||||||
substitution:
|
|
||||||
name: image
|
|
||||||
pattern: IMAGE_NAME:IMAGE_TAG
|
|
||||||
values:
|
|
||||||
- marker: "IMAGE_NAME"
|
|
||||||
ref: "#/definitions/io.k8s.cli.setters.image-name"
|
|
||||||
- marker: "IMAGE_TAG"
|
|
||||||
ref: "#/definitions/io.k8s.cli.setters.image-tag"
|
|
||||||
`,
|
|
||||||
expectedResources: `
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
`,
|
|
||||||
err: `setter "image-name" is used in substitution "image", please delete the parent substitution first`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for i := range tests {
|
|
||||||
test := tests[i]
|
|
||||||
t.Run(test.name, func(t *testing.T) {
|
|
||||||
// reset the openAPI afterward
|
|
||||||
openapi.ResetOpenAPI()
|
|
||||||
defer openapi.ResetOpenAPI()
|
|
||||||
|
|
||||||
baseDir := t.TempDir()
|
|
||||||
f := filepath.Join(baseDir, "Krmfile")
|
|
||||||
err := os.WriteFile(f, []byte(test.inputOpenAPI), 0600)
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
r, err := os.CreateTemp(baseDir, "k8s-cli-*.yaml")
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
t.Cleanup(func() { r.Close() })
|
|
||||||
err = os.WriteFile(r.Name(), []byte(test.input), 0600)
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
runner := commands.NewDeleteSetterRunner("")
|
|
||||||
out := &bytes.Buffer{}
|
|
||||||
runner.Command.SetOut(out)
|
|
||||||
runner.Command.SetArgs(append([]string{baseDir}, test.args...))
|
|
||||||
err = runner.Command.Execute()
|
|
||||||
|
|
||||||
if test.err != "" {
|
|
||||||
if !assert.NotNil(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
assert.Equal(t, test.err, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
// normalize path format for windows
|
|
||||||
actualNorm := strings.ReplaceAll(
|
|
||||||
strings.ReplaceAll(out.String(), "\\", "/"),
|
|
||||||
"//", "/")
|
|
||||||
|
|
||||||
expectedOut := strings.ReplaceAll(test.out, "${baseDir}", baseDir)
|
|
||||||
expectedNormalized := strings.ReplaceAll(expectedOut, "\\", "/")
|
|
||||||
|
|
||||||
if !assert.Contains(t, strings.TrimSpace(actualNorm), expectedNormalized) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
actualResources, err := os.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 := os.ReadFile(f)
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
if !assert.Equal(t,
|
|
||||||
strings.TrimSpace(test.expectedOpenAPI),
|
|
||||||
strings.TrimSpace(string(actualOpenAPI))) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDeleteSetterSubPackages(t *testing.T) {
|
|
||||||
var tests = []struct {
|
|
||||||
name string
|
|
||||||
dataset string
|
|
||||||
packagePath string
|
|
||||||
args []string
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "delete-setter-recurse-subpackages",
|
|
||||||
dataset: "dataset-with-setters",
|
|
||||||
args: []string{"namespace", "-R"},
|
|
||||||
expected: `${baseDir}/mysql/
|
|
||||||
deleted setter "namespace"
|
|
||||||
|
|
||||||
${baseDir}/mysql/nosetters/
|
|
||||||
setter "namespace" does not exist
|
|
||||||
|
|
||||||
${baseDir}/mysql/storage/
|
|
||||||
deleted setter "namespace"
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "delete-setter-top-level-pkg-no-recurse-subpackages",
|
|
||||||
dataset: "dataset-with-setters",
|
|
||||||
packagePath: "mysql",
|
|
||||||
args: []string{"namespace"},
|
|
||||||
expected: `${baseDir}/mysql/
|
|
||||||
deleted setter "namespace"
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "delete-setter-nested-pkg-no-recurse-subpackages",
|
|
||||||
dataset: "dataset-with-setters",
|
|
||||||
packagePath: "mysql/storage",
|
|
||||||
args: []string{"namespace"},
|
|
||||||
expected: `${baseDir}/mysql/storage/
|
|
||||||
deleted setter "namespace"
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for i := range tests {
|
|
||||||
test := tests[i]
|
|
||||||
t.Run(test.name, func(t *testing.T) {
|
|
||||||
// reset the openAPI afterward
|
|
||||||
openapi.ResetOpenAPI()
|
|
||||||
defer openapi.ResetOpenAPI()
|
|
||||||
sourceDir := filepath.Join("test", "testdata", test.dataset)
|
|
||||||
baseDir := t.TempDir()
|
|
||||||
copyutil.CopyDir(sourceDir, baseDir)
|
|
||||||
runner := commands.NewDeleteSetterRunner("")
|
|
||||||
actual := &bytes.Buffer{}
|
|
||||||
runner.Command.SetOut(actual)
|
|
||||||
runner.Command.SetArgs(append([]string{filepath.Join(baseDir, test.packagePath)}, test.args...))
|
|
||||||
err := runner.Command.Execute()
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
// normalize path format for windows
|
|
||||||
actualNormalized := strings.ReplaceAll(
|
|
||||||
strings.ReplaceAll(actual.String(), "\\", "/"),
|
|
||||||
"//", "/")
|
|
||||||
|
|
||||||
expected := strings.ReplaceAll(test.expected, "${baseDir}", baseDir)
|
|
||||||
expectedNormalized := strings.ReplaceAll(expected, "\\", "/")
|
|
||||||
if !assert.Equal(t, expectedNormalized, actualNormalized) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package commands
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/ext"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/runner"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/fieldmeta"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/setters2/settersutil"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewDeleteRunner returns a command runner.
|
|
||||||
func NewDeleteSubstitutionRunner(parent string) *DeleteSubstitutionRunner {
|
|
||||||
r := &DeleteSubstitutionRunner{}
|
|
||||||
c := &cobra.Command{
|
|
||||||
Use: "delete-subst DIR NAME",
|
|
||||||
Args: cobra.ExactArgs(2),
|
|
||||||
PreRunE: r.preRunE,
|
|
||||||
RunE: r.runE,
|
|
||||||
}
|
|
||||||
c.Flags().BoolVarP(&r.RecurseSubPackages, "recurse-subpackages", "R", false,
|
|
||||||
"deletes substitution recursively in all the nested subpackages")
|
|
||||||
runner.FixDocs(parent, c)
|
|
||||||
r.Command = c
|
|
||||||
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func DeleteSubstitutionCommand(parent string) *cobra.Command {
|
|
||||||
return NewDeleteSubstitutionRunner(parent).Command
|
|
||||||
}
|
|
||||||
|
|
||||||
type DeleteSubstitutionRunner struct {
|
|
||||||
Command *cobra.Command
|
|
||||||
DeleteSubstitution settersutil.DeleterCreator
|
|
||||||
OpenAPIFile string
|
|
||||||
RecurseSubPackages bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *DeleteSubstitutionRunner) preRunE(c *cobra.Command, args []string) error {
|
|
||||||
r.DeleteSubstitution.Name = args[1]
|
|
||||||
r.DeleteSubstitution.DefinitionPrefix = fieldmeta.SubstitutionDefinitionPrefix
|
|
||||||
|
|
||||||
r.OpenAPIFile = filepath.Join(args[0], ext.KRMFileName())
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *DeleteSubstitutionRunner) runE(c *cobra.Command, args []string) error {
|
|
||||||
e := runner.ExecuteCmdOnPkgs{
|
|
||||||
NeedOpenAPI: true,
|
|
||||||
Writer: c.OutOrStdout(),
|
|
||||||
RootPkgPath: args[0],
|
|
||||||
RecurseSubPackages: r.RecurseSubPackages,
|
|
||||||
CmdRunner: r,
|
|
||||||
}
|
|
||||||
err := e.Execute()
|
|
||||||
if err != nil {
|
|
||||||
return runner.HandleError(c, err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *DeleteSubstitutionRunner) ExecuteCmd(w io.Writer, pkgPath string) error {
|
|
||||||
sc, err := openapi.SchemaFromFile(filepath.Join(pkgPath, ext.KRMFileName()))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
r.DeleteSubstitution = settersutil.DeleterCreator{
|
|
||||||
Name: r.DeleteSubstitution.Name,
|
|
||||||
DefinitionPrefix: fieldmeta.SubstitutionDefinitionPrefix,
|
|
||||||
RecurseSubPackages: r.RecurseSubPackages,
|
|
||||||
OpenAPIFileName: ext.KRMFileName(),
|
|
||||||
OpenAPIPath: filepath.Join(pkgPath, ext.KRMFileName()),
|
|
||||||
ResourcesPath: pkgPath,
|
|
||||||
SettersSchema: sc,
|
|
||||||
}
|
|
||||||
|
|
||||||
err = r.DeleteSubstitution.Delete()
|
|
||||||
if err != nil {
|
|
||||||
// return err if RecurseSubPackages is false
|
|
||||||
if !r.DeleteSubstitution.RecurseSubPackages {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// print error message and continue if RecurseSubPackages is true
|
|
||||||
fmt.Fprintf(w, "%s\n", err.Error())
|
|
||||||
} else {
|
|
||||||
fmt.Fprintf(w, "deleted substitution %q\n", r.DeleteSubstitution.Name)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,560 +0,0 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package commands_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/copyutil"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestDeleteSubstitutionCommand(t *testing.T) {
|
|
||||||
var tests = []struct {
|
|
||||||
name string
|
|
||||||
input string
|
|
||||||
args []string
|
|
||||||
schema string
|
|
||||||
out string
|
|
||||||
inputOpenAPI string
|
|
||||||
expectedOpenAPI string
|
|
||||||
expectedResources string
|
|
||||||
err string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "delete only subst if setter has same name - long ref",
|
|
||||||
args: []string{"my.image"},
|
|
||||||
inputOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
openAPI:
|
|
||||||
definitions:
|
|
||||||
io.k8s.cli.setters.my.image:
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: my.image
|
|
||||||
value: "nginx"
|
|
||||||
io.k8s.cli.setters.my-tag:
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: my-tag
|
|
||||||
value: "1.7.9"
|
|
||||||
io.k8s.cli.substitutions.my.image:
|
|
||||||
x-k8s-cli:
|
|
||||||
substitution:
|
|
||||||
name: my.image
|
|
||||||
pattern: ${my.image}:${my-tag}
|
|
||||||
values:
|
|
||||||
- marker: ${my.image}
|
|
||||||
ref: '#/definitions/io.k8s.cli.setters.my.image'
|
|
||||||
- marker: ${my-tag}
|
|
||||||
ref: '#/definitions/io.k8s.cli.setters.my-tag'
|
|
||||||
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 # {"$openapi":"replicas"}
|
|
||||||
template:
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: nginx # {"$ref":"#/definitions/io.k8s.cli.setters.my.image"}
|
|
||||||
image: nginx:1.7.9 # {"$ref":"#/definitions/io.k8s.cli.substitutions.my.image"}
|
|
||||||
- name: sidecar
|
|
||||||
image: nginx:1.7.9 # {"$ref":"#/definitions/io.k8s.cli.substitutions.my.image"}
|
|
||||||
`,
|
|
||||||
out: `deleted substitution "my.image"`,
|
|
||||||
expectedResources: `
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: nginx-deployment
|
|
||||||
spec:
|
|
||||||
replicas: 3 # {"$openapi":"replicas"}
|
|
||||||
template:
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: nginx # {"$ref":"#/definitions/io.k8s.cli.setters.my.image"}
|
|
||||||
image: nginx:1.7.9
|
|
||||||
- name: sidecar
|
|
||||||
image: nginx:1.7.9
|
|
||||||
`,
|
|
||||||
expectedOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
openAPI:
|
|
||||||
definitions:
|
|
||||||
io.k8s.cli.setters.my.image:
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: my.image
|
|
||||||
value: "nginx"
|
|
||||||
io.k8s.cli.setters.my-tag:
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: my-tag
|
|
||||||
value: "1.7.9"
|
|
||||||
io.k8s.cli.setters.replicas:
|
|
||||||
description: hello world
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: replicas
|
|
||||||
value: "3"
|
|
||||||
setBy: me
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "delete subst - short ref",
|
|
||||||
args: []string{"my-image-sub"},
|
|
||||||
inputOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
openAPI:
|
|
||||||
definitions:
|
|
||||||
io.k8s.cli.setters.my-image:
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: my-image
|
|
||||||
value: "nginx"
|
|
||||||
io.k8s.cli.setters.my-tag:
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: my-tag
|
|
||||||
value: "1.7.9"
|
|
||||||
io.k8s.cli.substitutions.my-image-sub:
|
|
||||||
x-k8s-cli:
|
|
||||||
substitution:
|
|
||||||
name: my-image-sub
|
|
||||||
pattern: ${my-image}:${my-tag}
|
|
||||||
values:
|
|
||||||
- marker: ${my-image}
|
|
||||||
ref: '#/definitions/io.k8s.cli.setters.my-image'
|
|
||||||
- marker: ${my-tag}
|
|
||||||
ref: '#/definitions/io.k8s.cli.setters.my-tag'
|
|
||||||
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 # {"$openapi":"replicas"}
|
|
||||||
template:
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: nginx
|
|
||||||
image: nginx:1.7.9 # {"$openapi":"my-image-sub"}
|
|
||||||
- name: sidecar
|
|
||||||
image: nginx:1.7.9 # {"$openapi":"my-image-sub"}
|
|
||||||
`,
|
|
||||||
out: `deleted substitution "my-image-sub"`,
|
|
||||||
expectedResources: `
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: nginx-deployment
|
|
||||||
spec:
|
|
||||||
replicas: 3 # {"$openapi":"replicas"}
|
|
||||||
template:
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: nginx
|
|
||||||
image: nginx:1.7.9
|
|
||||||
- name: sidecar
|
|
||||||
image: nginx:1.7.9
|
|
||||||
`,
|
|
||||||
expectedOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
openAPI:
|
|
||||||
definitions:
|
|
||||||
io.k8s.cli.setters.my-image:
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: my-image
|
|
||||||
value: "nginx"
|
|
||||||
io.k8s.cli.setters.my-tag:
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: my-tag
|
|
||||||
value: "1.7.9"
|
|
||||||
io.k8s.cli.setters.replicas:
|
|
||||||
description: hello world
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: replicas
|
|
||||||
value: "3"
|
|
||||||
setBy: me
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "error if subst doesn't exist",
|
|
||||||
args: []string{"my-image-sub-not-present"},
|
|
||||||
inputOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
openAPI:
|
|
||||||
definitions:
|
|
||||||
io.k8s.cli.setters.my-image:
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: my-image
|
|
||||||
value: "nginx"
|
|
||||||
io.k8s.cli.setters.my-tag:
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: my-tag
|
|
||||||
value: "1.7.9"
|
|
||||||
io.k8s.cli.substitutions.my-image-sub:
|
|
||||||
x-k8s-cli:
|
|
||||||
substitution:
|
|
||||||
name: my-image-sub
|
|
||||||
pattern: ${my-image}:${my-tag}
|
|
||||||
values:
|
|
||||||
- marker: ${my-image}
|
|
||||||
ref: '#/definitions/io.k8s.cli.setters.my-image'
|
|
||||||
- marker: ${my-tag}
|
|
||||||
ref: '#/definitions/io.k8s.cli.setters.my-tag'
|
|
||||||
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 # {"$openapi":"replicas"}
|
|
||||||
template:
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: nginx
|
|
||||||
image: nginx:1.7.9 # {"$openapi":"my-image-sub"}
|
|
||||||
- name: sidecar
|
|
||||||
image: nginx:1.7.9 # {"$openapi":"my-image-sub"}
|
|
||||||
`,
|
|
||||||
expectedResources: `
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: nginx-deployment
|
|
||||||
spec:
|
|
||||||
replicas: 3 # {"$openapi":"replicas"}
|
|
||||||
template:
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: nginx
|
|
||||||
image: nginx:1.7.9
|
|
||||||
- name: sidecar
|
|
||||||
image: nginx:1.7.9
|
|
||||||
`,
|
|
||||||
expectedOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
openAPI:
|
|
||||||
definitions:
|
|
||||||
io.k8s.cli.setters.my-image:
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: my-image
|
|
||||||
value: "nginx"
|
|
||||||
io.k8s.cli.setters.my-tag:
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: my-tag
|
|
||||||
value: "1.7.9"
|
|
||||||
io.k8s.cli.setters.replicas:
|
|
||||||
description: hello world
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: replicas
|
|
||||||
value: "3"
|
|
||||||
setBy: me
|
|
||||||
`,
|
|
||||||
err: `substitution "my-image-sub-not-present" does not exist`,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
name: "substitution referenced by other substitution",
|
|
||||||
args: []string{"my-image-subst"},
|
|
||||||
inputOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
openAPI:
|
|
||||||
definitions:
|
|
||||||
io.k8s.cli.setters.my-image-setter:
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: my-image-setter
|
|
||||||
value: nginx
|
|
||||||
io.k8s.cli.setters.my-tag-setter:
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: my-tag-setter
|
|
||||||
value: 1.7.9
|
|
||||||
io.k8s.cli.substitutions.my-image-subst:
|
|
||||||
x-k8s-cli:
|
|
||||||
substitution:
|
|
||||||
name: my-image-subst
|
|
||||||
pattern: ${my-image-setter}::${my-tag-setter}
|
|
||||||
values:
|
|
||||||
- marker: ${my-image-setter}
|
|
||||||
ref: '#/definitions/io.k8s.cli.setters.my-image-setter'
|
|
||||||
- marker: ${my-tag-setter}
|
|
||||||
ref: '#/definitions/io.k8s.cli.setters.my-tag-setter'
|
|
||||||
io.k8s.cli.substitutions.my-nested-subst:
|
|
||||||
x-k8s-cli:
|
|
||||||
substitution:
|
|
||||||
name: my-nested-subst
|
|
||||||
pattern: something/${my-image-subst}/${my-other-setter}
|
|
||||||
values:
|
|
||||||
- marker: ${my-image-subst}
|
|
||||||
ref: '#/definitions/io.k8s.cli.substitutions.my-image-subst'
|
|
||||||
- marker: ${my-other-setter}
|
|
||||||
ref: '#/definitions/io.k8s.cli.setters.my-other-setter'
|
|
||||||
io.k8s.cli.setters.my-other-setter:
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: my-other-setter
|
|
||||||
value: nginxotherthing
|
|
||||||
`,
|
|
||||||
input: `
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: nginx-deployment
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
template:
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: nginx
|
|
||||||
image: something/nginx::1.7.9/nginxotherthing # {"$openapi":"my-nested-subst"}
|
|
||||||
- name: sidecar
|
|
||||||
image: nginx::1.7.9 # {"$openapi":"my-image-subst"}
|
|
||||||
`,
|
|
||||||
expectedOpenAPI: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
openAPI:
|
|
||||||
definitions:
|
|
||||||
io.k8s.cli.setters.my-image-setter:
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: my-image-setter
|
|
||||||
value: nginx
|
|
||||||
io.k8s.cli.setters.my-tag-setter:
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: my-tag-setter
|
|
||||||
value: 1.7.9
|
|
||||||
io.k8s.cli.substitutions.my-image-subst:
|
|
||||||
x-k8s-cli:
|
|
||||||
substitution:
|
|
||||||
name: my-image-subst
|
|
||||||
pattern: ${my-image-setter}::${my-tag-setter}
|
|
||||||
values:
|
|
||||||
- marker: ${my-image-setter}
|
|
||||||
ref: '#/definitions/io.k8s.cli.setters.my-image-setter'
|
|
||||||
- marker: ${my-tag-setter}
|
|
||||||
ref: '#/definitions/io.k8s.cli.setters.my-tag-setter'
|
|
||||||
io.k8s.cli.substitutions.my-nested-subst:
|
|
||||||
x-k8s-cli:
|
|
||||||
substitution:
|
|
||||||
name: my-nested-subst
|
|
||||||
pattern: something/${my-image-subst}/${my-other-setter}
|
|
||||||
values:
|
|
||||||
- marker: ${my-image-subst}
|
|
||||||
ref: '#/definitions/io.k8s.cli.substitutions.my-image-subst'
|
|
||||||
- marker: ${my-other-setter}
|
|
||||||
ref: '#/definitions/io.k8s.cli.setters.my-other-setter'
|
|
||||||
io.k8s.cli.setters.my-other-setter:
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: my-other-setter
|
|
||||||
value: nginxotherthing
|
|
||||||
`,
|
|
||||||
expectedResources: `
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: nginx-deployment
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
template:
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: nginx
|
|
||||||
image: something/nginx::1.7.9/nginxotherthing # {"$openapi":"my-nested-subst"}
|
|
||||||
- name: sidecar
|
|
||||||
image: nginx::1.7.9 # {"$openapi":"my-image-subst"}
|
|
||||||
`,
|
|
||||||
err: `substitution "my-image-subst" is used in substitution "my-nested-subst", please delete the parent substitution first`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for i := range tests {
|
|
||||||
test := tests[i]
|
|
||||||
t.Run(test.name, func(t *testing.T) {
|
|
||||||
// reset the openAPI afterward
|
|
||||||
openapi.ResetOpenAPI()
|
|
||||||
defer openapi.ResetOpenAPI()
|
|
||||||
|
|
||||||
baseDir := t.TempDir()
|
|
||||||
f := filepath.Join(baseDir, "Krmfile")
|
|
||||||
err := os.WriteFile(f, []byte(test.inputOpenAPI), 0600)
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
r, err := os.CreateTemp(baseDir, "k8s-cli-*.yaml")
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
t.Cleanup(func() { r.Close() })
|
|
||||||
err = os.WriteFile(r.Name(), []byte(test.input), 0600)
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
runner := commands.NewDeleteSubstitutionRunner("")
|
|
||||||
out := &bytes.Buffer{}
|
|
||||||
runner.Command.SetOut(out)
|
|
||||||
runner.Command.SetArgs(append([]string{baseDir}, test.args...))
|
|
||||||
err = runner.Command.Execute()
|
|
||||||
|
|
||||||
if test.err != "" {
|
|
||||||
if !assert.NotNil(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
assert.Equal(t, test.err, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
// normalize path format for windows
|
|
||||||
actualNorm := strings.ReplaceAll(
|
|
||||||
strings.ReplaceAll(out.String(), "\\", "/"),
|
|
||||||
"//", "/")
|
|
||||||
expectedOut := strings.ReplaceAll(test.out, "${baseDir}", baseDir)
|
|
||||||
expectedNorm := strings.ReplaceAll(expectedOut, "\\", "/")
|
|
||||||
|
|
||||||
if !assert.Contains(t, strings.TrimSpace(actualNorm), expectedNorm) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
actualOpenAPI, err := os.ReadFile(f)
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
if !assert.Equal(t,
|
|
||||||
strings.TrimSpace(test.expectedOpenAPI),
|
|
||||||
strings.TrimSpace(string(actualOpenAPI))) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
actualResources, err := os.ReadFile(r.Name())
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
if !assert.Equal(t,
|
|
||||||
strings.TrimSpace(test.expectedResources),
|
|
||||||
strings.TrimSpace(string(actualResources))) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDeleteSubstitutionSubPackages(t *testing.T) {
|
|
||||||
var tests = []struct {
|
|
||||||
name string
|
|
||||||
dataset string
|
|
||||||
packagePath string
|
|
||||||
args []string
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "delete-substitution-recurse-subpackages",
|
|
||||||
dataset: "dataset-with-setters",
|
|
||||||
args: []string{"image-tag", "-R"},
|
|
||||||
expected: `${baseDir}/mysql/
|
|
||||||
deleted substitution "image-tag"
|
|
||||||
|
|
||||||
${baseDir}/mysql/nosetters/
|
|
||||||
substitution "image-tag" does not exist
|
|
||||||
|
|
||||||
${baseDir}/mysql/storage/
|
|
||||||
substitution "image-tag" does not exist
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "delete-setter-top-level-pkg-no-recurse-subpackages",
|
|
||||||
dataset: "dataset-with-setters",
|
|
||||||
packagePath: "mysql",
|
|
||||||
args: []string{"image-tag"},
|
|
||||||
expected: `${baseDir}/mysql/
|
|
||||||
deleted substitution "image-tag"
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for i := range tests {
|
|
||||||
test := tests[i]
|
|
||||||
t.Run(test.name, func(t *testing.T) {
|
|
||||||
// reset the openAPI afterward
|
|
||||||
openapi.ResetOpenAPI()
|
|
||||||
defer openapi.ResetOpenAPI()
|
|
||||||
sourceDir := filepath.Join("test", "testdata", test.dataset)
|
|
||||||
baseDir := t.TempDir()
|
|
||||||
copyutil.CopyDir(sourceDir, baseDir)
|
|
||||||
runner := commands.NewDeleteSubstitutionRunner("")
|
|
||||||
actual := &bytes.Buffer{}
|
|
||||||
runner.Command.SetOut(actual)
|
|
||||||
runner.Command.SetArgs(append([]string{filepath.Join(baseDir, test.packagePath)}, test.args...))
|
|
||||||
err := runner.Command.Execute()
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
// normalize path format for windows
|
|
||||||
actualNormalized := strings.ReplaceAll(
|
|
||||||
strings.ReplaceAll(actual.String(), "\\", "/"),
|
|
||||||
"//", "/")
|
|
||||||
|
|
||||||
expected := strings.ReplaceAll(test.expected, "${baseDir}", baseDir)
|
|
||||||
expectedNormalized := strings.ReplaceAll(expected, "\\", "/")
|
|
||||||
if !assert.Equal(t, expectedNormalized, actualNormalized) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package commands
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/runner"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/krmfile"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetInitRunner returns a command InitRunner.
|
|
||||||
func GetInitRunner(name string) *InitRunner {
|
|
||||||
r := &InitRunner{}
|
|
||||||
c := &cobra.Command{
|
|
||||||
Use: "init DIR...",
|
|
||||||
Args: cobra.RangeArgs(0, 1),
|
|
||||||
Short: commands.InitShort,
|
|
||||||
Long: commands.InitLong,
|
|
||||||
Example: commands.InitExamples,
|
|
||||||
RunE: r.runE,
|
|
||||||
Deprecated: "setter commands and substitutions will no longer be available in kustomize v5.\n" +
|
|
||||||
"See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.",
|
|
||||||
}
|
|
||||||
runner.FixDocs(name, c)
|
|
||||||
r.Command = c
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func InitCommand(name string) *cobra.Command {
|
|
||||||
return GetInitRunner(name).Command
|
|
||||||
}
|
|
||||||
|
|
||||||
// InitRunner contains the init function
|
|
||||||
type InitRunner struct {
|
|
||||||
Command *cobra.Command
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *InitRunner) runE(c *cobra.Command, args []string) error {
|
|
||||||
var dir string
|
|
||||||
if len(args) == 0 {
|
|
||||||
dir = "."
|
|
||||||
} else {
|
|
||||||
dir = args[0]
|
|
||||||
}
|
|
||||||
filename := filepath.Join(dir, krmfile.KrmfileName)
|
|
||||||
|
|
||||||
if _, err := os.Stat(filename); err == nil || !os.IsNotExist(err) {
|
|
||||||
return errors.Errorf("directory already initialized with a Krmfile")
|
|
||||||
}
|
|
||||||
|
|
||||||
err := os.WriteFile(filename, []byte(strings.TrimSpace(`
|
|
||||||
apiVersion: config.k8s.io/v1alpha1
|
|
||||||
kind: Krmfile
|
|
||||||
`)), 0600)
|
|
||||||
return errors.Wrap(err)
|
|
||||||
}
|
|
||||||
@@ -1,193 +0,0 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package commands
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/olekukonko/tablewriter"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/ext"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/runner"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/fieldmeta"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/setters2"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewListSettersRunner returns a command runner.
|
|
||||||
func NewListSettersRunner(parent string) *ListSettersRunner {
|
|
||||||
r := &ListSettersRunner{}
|
|
||||||
c := &cobra.Command{
|
|
||||||
Use: "list-setters DIR [NAME]",
|
|
||||||
Args: cobra.RangeArgs(1, 2),
|
|
||||||
Short: commands.ListSettersShort,
|
|
||||||
Long: commands.ListSettersLong,
|
|
||||||
Example: commands.ListSettersExamples,
|
|
||||||
PreRunE: r.preRunE,
|
|
||||||
RunE: r.runE,
|
|
||||||
Deprecated: "setter commands will no longer be available in kustomize v5.\n" +
|
|
||||||
"See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.",
|
|
||||||
}
|
|
||||||
c.Flags().BoolVar(&r.Markdown, "markdown", false,
|
|
||||||
"output as github markdown")
|
|
||||||
c.Flags().BoolVar(&r.IncludeSubst, "include-subst", false,
|
|
||||||
"include substitutions in the output")
|
|
||||||
c.Flags().BoolVarP(&r.RecurseSubPackages, "recurse-subpackages", "R", false,
|
|
||||||
"list setters recursively in all the nested subpackages")
|
|
||||||
runner.FixDocs(parent, c)
|
|
||||||
r.Command = c
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func ListSettersCommand(parent string) *cobra.Command {
|
|
||||||
return NewListSettersRunner(parent).Command
|
|
||||||
}
|
|
||||||
|
|
||||||
type ListSettersRunner struct {
|
|
||||||
Command *cobra.Command
|
|
||||||
List setters2.List
|
|
||||||
Markdown bool
|
|
||||||
IncludeSubst bool
|
|
||||||
RecurseSubPackages bool
|
|
||||||
Name string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *ListSettersRunner) preRunE(c *cobra.Command, args []string) error {
|
|
||||||
if len(args) > 1 {
|
|
||||||
r.Name = args[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *ListSettersRunner) runE(c *cobra.Command, args []string) error {
|
|
||||||
e := runner.ExecuteCmdOnPkgs{
|
|
||||||
NeedOpenAPI: true,
|
|
||||||
Writer: c.OutOrStdout(),
|
|
||||||
RootPkgPath: args[0],
|
|
||||||
RecurseSubPackages: r.RecurseSubPackages,
|
|
||||||
CmdRunner: r,
|
|
||||||
}
|
|
||||||
|
|
||||||
err := e.Execute()
|
|
||||||
if err != nil {
|
|
||||||
return runner.HandleError(c, err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *ListSettersRunner) ExecuteCmd(w io.Writer, pkgPath string) error {
|
|
||||||
sc, err := openapi.SchemaFromFile(filepath.Join(pkgPath, ext.KRMFileName()))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
r.List = setters2.List{
|
|
||||||
Name: r.Name,
|
|
||||||
OpenAPIFileName: ext.KRMFileName(),
|
|
||||||
SettersSchema: sc,
|
|
||||||
}
|
|
||||||
openAPIPath := filepath.Join(pkgPath, ext.KRMFileName())
|
|
||||||
if err := r.ListSetters(w, openAPIPath, pkgPath); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if r.IncludeSubst {
|
|
||||||
if err := r.ListSubstitutions(w, openAPIPath); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *ListSettersRunner) ListSetters(w io.Writer, openAPIPath, resourcePath string) error {
|
|
||||||
// use setters v2
|
|
||||||
if err := r.List.ListSetters(openAPIPath, resourcePath); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
table := newTable(w, r.Markdown)
|
|
||||||
table.SetHeader([]string{"NAME", "VALUE", "SET BY", "DESCRIPTION", "COUNT", "REQUIRED", "IS SET"})
|
|
||||||
for i := range r.List.Setters {
|
|
||||||
s := r.List.Setters[i]
|
|
||||||
v := s.Value
|
|
||||||
|
|
||||||
// if the setter is for a list, populate the values
|
|
||||||
if len(s.ListValues) > 0 {
|
|
||||||
v = strings.Join(s.ListValues, ",")
|
|
||||||
v = fmt.Sprintf("[%s]", v)
|
|
||||||
}
|
|
||||||
required := "No"
|
|
||||||
if s.Required {
|
|
||||||
required = "Yes"
|
|
||||||
}
|
|
||||||
isSet := "No"
|
|
||||||
if s.IsSet {
|
|
||||||
isSet = "Yes"
|
|
||||||
}
|
|
||||||
|
|
||||||
table.Append([]string{
|
|
||||||
s.Name, v, s.SetBy, s.Description, fmt.Sprintf("%d", s.Count), required, isSet})
|
|
||||||
}
|
|
||||||
table.Render()
|
|
||||||
|
|
||||||
if len(r.List.Setters) == 0 {
|
|
||||||
// exit non-0 if no matching setters are found
|
|
||||||
if runner.ExitOnError {
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *ListSettersRunner) ListSubstitutions(w io.Writer, openAPIPath string) error {
|
|
||||||
// use setters v2
|
|
||||||
if err := r.List.ListSubst(openAPIPath); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
table := newTable(w, r.Markdown)
|
|
||||||
b := tablewriter.Border{Top: true}
|
|
||||||
table.SetBorders(b)
|
|
||||||
|
|
||||||
table.SetHeader([]string{"SUBSTITUTION", "PATTERN", "REFERENCES"})
|
|
||||||
for i := range r.List.Substitutions {
|
|
||||||
s := r.List.Substitutions[i]
|
|
||||||
refs := ""
|
|
||||||
for _, value := range s.Values {
|
|
||||||
// trim setter and substitution prefixes
|
|
||||||
ref := strings.TrimPrefix(
|
|
||||||
strings.TrimPrefix(value.Ref, fieldmeta.DefinitionsPrefix+fieldmeta.SetterDefinitionPrefix),
|
|
||||||
fieldmeta.DefinitionsPrefix+fieldmeta.SubstitutionDefinitionPrefix)
|
|
||||||
refs = refs + "," + ref
|
|
||||||
}
|
|
||||||
refs = fmt.Sprintf("[%s]", strings.TrimPrefix(refs, ","))
|
|
||||||
table.Append([]string{
|
|
||||||
s.Name, s.Pattern, refs})
|
|
||||||
}
|
|
||||||
if len(r.List.Substitutions) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
table.Render()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func newTable(o io.Writer, m bool) *tablewriter.Table {
|
|
||||||
table := tablewriter.NewWriter(o)
|
|
||||||
table.SetRowLine(false)
|
|
||||||
if m {
|
|
||||||
// markdown format
|
|
||||||
table.SetBorders(tablewriter.Border{Left: true, Top: false, Right: true, Bottom: false})
|
|
||||||
table.SetCenterSeparator("|")
|
|
||||||
} else {
|
|
||||||
table.SetBorder(false)
|
|
||||||
table.SetHeaderLine(false)
|
|
||||||
table.SetColumnSeparator(" ")
|
|
||||||
table.SetCenterSeparator(" ")
|
|
||||||
}
|
|
||||||
table.SetAlignment(tablewriter.ALIGN_LEFT)
|
|
||||||
return table
|
|
||||||
}
|
|
||||||
@@ -1,527 +0,0 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package commands_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"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
|
|
||||||
required: true
|
|
||||||
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 REQUIRED IS SET
|
|
||||||
replicas 3 me hello world 1 Yes No
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
name: "list-replicas inconsistent with openapi",
|
|
||||||
openapi: `
|
|
||||||
openAPI:
|
|
||||||
definitions:
|
|
||||||
io.k8s.cli.setters.replicas:
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: replicas
|
|
||||||
value: "4"
|
|
||||||
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 REQUIRED IS SET
|
|
||||||
replicas 4 me hello world 1 No No
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "list-multiple",
|
|
||||||
args: []string{"--include-subst"},
|
|
||||||
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
|
|
||||||
required: true
|
|
||||||
isSet: false
|
|
||||||
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 REQUIRED IS SET
|
|
||||||
image nginx me2 hello world 2 2 No No
|
|
||||||
replicas 3 me1 hello world 1 1 No No
|
|
||||||
tag 1.7.9 me3 hello world 3 1 Yes No
|
|
||||||
--------------- ----------- --------------
|
|
||||||
SUBSTITUTION PATTERN REFERENCES
|
|
||||||
image IMAGE:TAG [image,tag]
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "list-multiple-resources",
|
|
||||||
args: []string{"--include-subst"},
|
|
||||||
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 REQUIRED IS SET
|
|
||||||
image nginx me2 hello world 2 3 No No
|
|
||||||
replicas 3 me1 hello world 1 2 No No
|
|
||||||
tag 1.7.9 me3 hello world 3 2 No No
|
|
||||||
--------------- ----------- --------------
|
|
||||||
SUBSTITUTION PATTERN REFERENCES
|
|
||||||
image IMAGE:TAG [image,tag]
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
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
|
|
||||||
required: true
|
|
||||||
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-subst:
|
|
||||||
x-k8s-cli:
|
|
||||||
substitution:
|
|
||||||
name: image-subst
|
|
||||||
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-subst"}
|
|
||||||
- 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-subst"}
|
|
||||||
- name: nginx2
|
|
||||||
image: nginx
|
|
||||||
`,
|
|
||||||
expected: ` NAME VALUE SET BY DESCRIPTION COUNT REQUIRED IS SET
|
|
||||||
image nginx me2 hello world 2 3 Yes No
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
name: "list array setter",
|
|
||||||
openapi: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
openAPI:
|
|
||||||
definitions:
|
|
||||||
io.k8s.cli.setters.list:
|
|
||||||
items:
|
|
||||||
type: string
|
|
||||||
maxItems: 3
|
|
||||||
type: array
|
|
||||||
description: hello world
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: list
|
|
||||||
listValues:
|
|
||||||
- a
|
|
||||||
- b
|
|
||||||
- c
|
|
||||||
setBy: me
|
|
||||||
required: true
|
|
||||||
`,
|
|
||||||
input: `
|
|
||||||
apiVersion: example.com/v1beta1
|
|
||||||
kind: Example
|
|
||||||
metadata:
|
|
||||||
annotations:
|
|
||||||
foo: bar
|
|
||||||
spec:
|
|
||||||
list: # {"$openapi":"list"}
|
|
||||||
- "a"
|
|
||||||
- "b"
|
|
||||||
- "c"
|
|
||||||
`,
|
|
||||||
expected: ` NAME VALUE SET BY DESCRIPTION COUNT REQUIRED IS SET
|
|
||||||
list [a,b,c] me hello world 1 Yes No
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
name: "nested substitution",
|
|
||||||
args: []string{"--include-subst"},
|
|
||||||
input: `
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: nginx-deployment
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
template:
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: nginx
|
|
||||||
image: something/nginx::1.7.9/nginxotherthing # {"$openapi":"my-nested-subst"}
|
|
||||||
- name: sidecar
|
|
||||||
image: nginx::1.7.9 # {"$openapi":"my-image-subst"}
|
|
||||||
`,
|
|
||||||
openapi: `
|
|
||||||
apiVersion: v1alpha1
|
|
||||||
kind: Example
|
|
||||||
openAPI:
|
|
||||||
definitions:
|
|
||||||
io.k8s.cli.setters.my-image-setter:
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: my-image-setter
|
|
||||||
value: nginx
|
|
||||||
io.k8s.cli.setters.my-tag-setter:
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: my-tag-setter
|
|
||||||
value: 1.7.9
|
|
||||||
required: true
|
|
||||||
isSet: true
|
|
||||||
io.k8s.cli.substitutions.my-image-subst:
|
|
||||||
x-k8s-cli:
|
|
||||||
substitution:
|
|
||||||
name: my-image-subst
|
|
||||||
pattern: ${my-image-setter}::${my-tag-setter}
|
|
||||||
values:
|
|
||||||
- marker: ${my-image-setter}
|
|
||||||
ref: '#/definitions/io.k8s.cli.setters.my-image-setter'
|
|
||||||
- marker: ${my-tag-setter}
|
|
||||||
ref: '#/definitions/io.k8s.cli.setters.my-tag-setter'
|
|
||||||
io.k8s.cli.substitutions.my-nested-subst:
|
|
||||||
x-k8s-cli:
|
|
||||||
substitution:
|
|
||||||
name: my-nested-subst
|
|
||||||
pattern: something/${my-image-subst}/${my-other-setter}
|
|
||||||
values:
|
|
||||||
- marker: ${my-image-subst}
|
|
||||||
ref: '#/definitions/io.k8s.cli.substitutions.my-image-subst'
|
|
||||||
- marker: ${my-other-setter}
|
|
||||||
ref: '#/definitions/io.k8s.cli.setters.my-other-setter'
|
|
||||||
io.k8s.cli.setters.my-other-setter:
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: my-other-setter
|
|
||||||
value: nginxotherthing
|
|
||||||
`,
|
|
||||||
expected: ` NAME VALUE SET BY DESCRIPTION COUNT REQUIRED IS SET
|
|
||||||
my-image-setter nginx 2 No No
|
|
||||||
my-other-setter nginxotherthing 1 No No
|
|
||||||
my-tag-setter 1.7.9 2 Yes Yes
|
|
||||||
------------------ ------------------------------------------------ -----------------------------------
|
|
||||||
SUBSTITUTION PATTERN REFERENCES
|
|
||||||
my-image-subst ${my-image-setter}::${my-tag-setter} [my-image-setter,my-tag-setter]
|
|
||||||
my-nested-subst something/${my-image-subst}/${my-other-setter} [my-image-subst,my-other-setter]
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for i := range tests {
|
|
||||||
test := tests[i]
|
|
||||||
t.Run(test.name, func(t *testing.T) {
|
|
||||||
// reset the openAPI afterward
|
|
||||||
openapi.ResetOpenAPI()
|
|
||||||
defer openapi.ResetOpenAPI()
|
|
||||||
|
|
||||||
dir := t.TempDir()
|
|
||||||
|
|
||||||
err := os.WriteFile(filepath.Join(dir, "Krmfile"), []byte(test.openapi), 0600)
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
err = os.WriteFile(filepath.Join(dir, "deployment.yaml"), []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{dir}, test.args...))
|
|
||||||
err = runner.Command.Execute()
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
if !assert.Contains(t, actual.String(), test.expected) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
// make sure that the resources are not altered
|
|
||||||
actualResources, err := os.ReadFile(filepath.Join(dir, "deployment.yaml"))
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
if !assert.Equal(t,
|
|
||||||
strings.TrimSpace(test.input),
|
|
||||||
strings.TrimSpace(string(actualResources))) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
actualOpenAPI, err := os.ReadFile(filepath.Join(dir, "Krmfile"))
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
if !assert.Equal(t,
|
|
||||||
strings.TrimSpace(test.openapi),
|
|
||||||
strings.TrimSpace(string(actualOpenAPI))) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestListSettersSubPackages(t *testing.T) {
|
|
||||||
var tests = []struct {
|
|
||||||
name string
|
|
||||||
dataset string
|
|
||||||
args []string
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "list-replicas",
|
|
||||||
dataset: "dataset-with-setters",
|
|
||||||
args: []string{"--include-subst", "-R"},
|
|
||||||
expected: `
|
|
||||||
|
|
||||||
test/testdata/dataset-with-setters/mysql/
|
|
||||||
NAME VALUE SET BY DESCRIPTION COUNT REQUIRED IS SET
|
|
||||||
image mysql 1 No No
|
|
||||||
namespace myspace 1 No No
|
|
||||||
tag 1.7.9 1 No No
|
|
||||||
--------------- ----------------- --------------
|
|
||||||
SUBSTITUTION PATTERN REFERENCES
|
|
||||||
image-tag ${image}:${tag} [image,tag]
|
|
||||||
|
|
||||||
test/testdata/dataset-with-setters/mysql/nosetters/
|
|
||||||
NAME VALUE SET BY DESCRIPTION COUNT REQUIRED IS SET
|
|
||||||
|
|
||||||
test/testdata/dataset-with-setters/mysql/storage/
|
|
||||||
NAME VALUE SET BY DESCRIPTION COUNT REQUIRED IS SET
|
|
||||||
namespace myspace 1 No No
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "list-replicas",
|
|
||||||
dataset: "dataset-with-setters/mysql",
|
|
||||||
expected: `
|
|
||||||
|
|
||||||
test/testdata/dataset-with-setters/mysql/
|
|
||||||
NAME VALUE SET BY DESCRIPTION COUNT REQUIRED IS SET
|
|
||||||
image mysql 1 No No
|
|
||||||
namespace myspace 1 No No
|
|
||||||
tag 1.7.9 1 No No
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for i := range tests {
|
|
||||||
test := tests[i]
|
|
||||||
t.Run(test.name, func(t *testing.T) {
|
|
||||||
// reset the openAPI afterward
|
|
||||||
openapi.ResetOpenAPI()
|
|
||||||
defer openapi.ResetOpenAPI()
|
|
||||||
dir := filepath.Join("test", "testdata", test.dataset)
|
|
||||||
|
|
||||||
runner := commands.NewListSettersRunner("")
|
|
||||||
actual := &bytes.Buffer{}
|
|
||||||
runner.Command.SetOut(actual)
|
|
||||||
runner.Command.SetArgs(append([]string{dir}, test.args...))
|
|
||||||
err := runner.Command.Execute()
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
// normalize path format for windows
|
|
||||||
actualNormalized := strings.ReplaceAll(actual.String(), "\\", "/")
|
|
||||||
|
|
||||||
if !assert.Contains(t, strings.TrimSpace(actualNormalized), strings.TrimSpace(test.expected)) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,140 +0,0 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package commands
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/ext"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/runner"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
|
||||||
"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 --values [VALUE]",
|
|
||||||
Args: cobra.MinimumNArgs(2),
|
|
||||||
Short: commands.SetShort,
|
|
||||||
Long: commands.SetLong,
|
|
||||||
Example: commands.SetExamples,
|
|
||||||
PreRunE: r.preRunE,
|
|
||||||
RunE: r.runE,
|
|
||||||
Deprecated: "setter commands will no longer be available in kustomize v5.\n" +
|
|
||||||
"See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.",
|
|
||||||
}
|
|
||||||
runner.FixDocs(parent, c)
|
|
||||||
r.Command = c
|
|
||||||
c.Flags().StringArrayVar(&r.Values, "values", []string{},
|
|
||||||
"optional flag, the values of the setter to be set to")
|
|
||||||
c.Flags().StringVar(&r.SetBy, "set-by", "",
|
|
||||||
"annotate the field with who set it")
|
|
||||||
c.Flags().StringVar(&r.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().BoolVarP(&r.RecurseSubPackages, "recurse-subpackages", "R", false,
|
|
||||||
"sets recursively in all the nested subpackages")
|
|
||||||
c.Flags().MarkHidden("version")
|
|
||||||
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
var setterVersion string
|
|
||||||
|
|
||||||
func SetCommand(parent string) *cobra.Command {
|
|
||||||
return NewSetRunner(parent).Command
|
|
||||||
}
|
|
||||||
|
|
||||||
type SetRunner struct {
|
|
||||||
Command *cobra.Command
|
|
||||||
Set settersutil.FieldSetter
|
|
||||||
OpenAPIFile string
|
|
||||||
Values []string
|
|
||||||
SetBy string
|
|
||||||
Description string
|
|
||||||
Name string
|
|
||||||
Value string
|
|
||||||
ListValues []string
|
|
||||||
RecurseSubPackages bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *SetRunner) preRunE(c *cobra.Command, args []string) error {
|
|
||||||
valueFlagSet := c.Flag("values").Changed
|
|
||||||
|
|
||||||
if valueFlagSet && len(args) > 2 {
|
|
||||||
return errors.Errorf("value should set either from flag or arg")
|
|
||||||
}
|
|
||||||
|
|
||||||
// make sure that the value is provided either through values flag or as an arg
|
|
||||||
if !valueFlagSet && len(args) < 3 {
|
|
||||||
return errors.Errorf("value must be provided either from flag or arg")
|
|
||||||
}
|
|
||||||
|
|
||||||
r.Name = args[1]
|
|
||||||
if valueFlagSet {
|
|
||||||
r.Value = r.Values[0]
|
|
||||||
} else {
|
|
||||||
r.Value = args[2]
|
|
||||||
}
|
|
||||||
|
|
||||||
// set remaining values as list values
|
|
||||||
if valueFlagSet && len(r.Values) > 1 {
|
|
||||||
r.ListValues = r.Values[1:]
|
|
||||||
} else if !valueFlagSet && len(args) > 3 {
|
|
||||||
r.ListValues = args[3:]
|
|
||||||
}
|
|
||||||
|
|
||||||
r.OpenAPIFile = filepath.Join(args[0], ext.KRMFileName())
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *SetRunner) runE(c *cobra.Command, args []string) error {
|
|
||||||
e := runner.ExecuteCmdOnPkgs{
|
|
||||||
NeedOpenAPI: true,
|
|
||||||
Writer: c.OutOrStdout(),
|
|
||||||
RootPkgPath: args[0],
|
|
||||||
RecurseSubPackages: r.RecurseSubPackages,
|
|
||||||
CmdRunner: r,
|
|
||||||
}
|
|
||||||
err := e.Execute()
|
|
||||||
if err != nil {
|
|
||||||
return runner.HandleError(c, err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *SetRunner) ExecuteCmd(w io.Writer, pkgPath string) error {
|
|
||||||
r.Set = settersutil.FieldSetter{
|
|
||||||
Name: r.Name,
|
|
||||||
Value: r.Value,
|
|
||||||
ListValues: r.ListValues,
|
|
||||||
Description: r.Description,
|
|
||||||
SetBy: r.SetBy,
|
|
||||||
Count: 0,
|
|
||||||
OpenAPIPath: filepath.Join(pkgPath, ext.KRMFileName()),
|
|
||||||
OpenAPIFileName: ext.KRMFileName(),
|
|
||||||
ResourcesPath: pkgPath,
|
|
||||||
RecurseSubPackages: r.RecurseSubPackages,
|
|
||||||
IsSet: true,
|
|
||||||
}
|
|
||||||
count, err := r.Set.Set()
|
|
||||||
if err != nil {
|
|
||||||
// return err if RecurseSubPackages is false
|
|
||||||
if !r.Set.RecurseSubPackages {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// print error message and continue if RecurseSubPackages is true
|
|
||||||
fmt.Fprintf(w, "%s\n", err.Error())
|
|
||||||
} else {
|
|
||||||
fmt.Fprintf(w, "set %d field(s) of setter %q to value %q\n", count, r.Set.Name, r.Set.Value)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,148 +0,0 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package commands
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/runner"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio/filters"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetWrapRunner returns a command runner.
|
|
||||||
func GetWrapRunner() *WrapRunner {
|
|
||||||
r := &WrapRunner{}
|
|
||||||
c := &cobra.Command{
|
|
||||||
Use: "wrap CMD...",
|
|
||||||
Short: "Wrap an executable so it implements the config fn interface",
|
|
||||||
Long: `Wrap an executable so it implements the config fn interface
|
|
||||||
|
|
||||||
wrap simplifies writing config functions by:
|
|
||||||
|
|
||||||
- invoking an executable command converting an input ResourceList into environment
|
|
||||||
- merging the output onto the original input as a set of patches
|
|
||||||
- setting filenames on any Resources missing them
|
|
||||||
|
|
||||||
config function authors may use wrap by using it to invoke a command from a container image
|
|
||||||
|
|
||||||
The following are equivalent:
|
|
||||||
|
|
||||||
kyaml wrap -- CMD
|
|
||||||
|
|
||||||
kyaml xargs -- CMD | kyaml merge | kyaml fmt --set-filenames
|
|
||||||
|
|
||||||
Environment Variables:
|
|
||||||
|
|
||||||
KUST_OVERRIDE_DIR:
|
|
||||||
|
|
||||||
Path to a directory containing patches to apply to after merging.
|
|
||||||
`,
|
|
||||||
Example: `
|
|
||||||
|
|
||||||
`,
|
|
||||||
RunE: r.runE,
|
|
||||||
PreRunE: r.preRunE,
|
|
||||||
SilenceUsage: true,
|
|
||||||
FParseErrWhitelist: cobra.FParseErrWhitelist{UnknownFlags: true},
|
|
||||||
Args: cobra.MinimumNArgs(1),
|
|
||||||
}
|
|
||||||
r.Command = c
|
|
||||||
r.XArgs = GetXArgsRunner()
|
|
||||||
c.Flags().BoolVar(&r.XArgs.EnvOnly,
|
|
||||||
"env-only", true, "only set env vars, not arguments.")
|
|
||||||
c.Flags().StringVar(&r.XArgs.WrapKind,
|
|
||||||
"wrap-kind", "List", "wrap the input xargs give to the command in this type.")
|
|
||||||
c.Flags().StringVar(&r.XArgs.WrapVersion,
|
|
||||||
"wrap-version", "v1", "wrap the input xargs give to the command in this type.")
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
// WrapRunner contains the run function
|
|
||||||
type WrapRunner struct {
|
|
||||||
Command *cobra.Command
|
|
||||||
XArgs *XArgsRunner
|
|
||||||
getEnv func(key string) string
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
KustMergeEnv = "KUST_MERGE"
|
|
||||||
KustOverrideDirEnv = "KUST_OVERRIDE_DIR"
|
|
||||||
)
|
|
||||||
|
|
||||||
func WrapCommand() *cobra.Command {
|
|
||||||
return GetWrapRunner().Command
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *WrapRunner) preRunE(_ *cobra.Command, _ []string) error {
|
|
||||||
_, err := fmt.Fprintln(os.Stderr, `Command "wrap" is deprecated, this will no longer be available in kustomize v5.
|
|
||||||
See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.`)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *WrapRunner) runE(c *cobra.Command, args []string) error {
|
|
||||||
if r.getEnv == nil {
|
|
||||||
r.getEnv = os.Getenv
|
|
||||||
}
|
|
||||||
xargsIn := &bytes.Buffer{}
|
|
||||||
if _, err := io.Copy(xargsIn, c.InOrStdin()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
mergeInput := bytes.NewBuffer(xargsIn.Bytes())
|
|
||||||
// Run the command
|
|
||||||
xargsOut := &bytes.Buffer{}
|
|
||||||
r.XArgs.Command.SetArgs(args)
|
|
||||||
r.XArgs.Command.SetIn(xargsIn)
|
|
||||||
r.XArgs.Command.SetOut(xargsOut)
|
|
||||||
r.XArgs.Command.SetErr(os.Stderr)
|
|
||||||
if err := r.XArgs.Command.Execute(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// merge the results
|
|
||||||
buff := &kio.PackageBuffer{}
|
|
||||||
|
|
||||||
var fltrs []kio.Filter
|
|
||||||
var inputs []kio.Reader
|
|
||||||
if r.getEnv(KustMergeEnv) == "" || r.getEnv(KustMergeEnv) == "true" || r.getEnv(KustMergeEnv) == "1" {
|
|
||||||
inputs = append(inputs, &kio.ByteReader{Reader: mergeInput})
|
|
||||||
fltrs = append(fltrs, &filters.MergeFilter{})
|
|
||||||
}
|
|
||||||
inputs = append(inputs, &kio.ByteReader{Reader: xargsOut})
|
|
||||||
|
|
||||||
if err := (kio.Pipeline{Inputs: inputs, Filters: fltrs, Outputs: []kio.Writer{buff}}).
|
|
||||||
Execute(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
inputs, fltrs = []kio.Reader{buff}, nil
|
|
||||||
if r.getEnv(KustOverrideDirEnv) != "" {
|
|
||||||
// merge the overrides on top of the output
|
|
||||||
fltrs = append(fltrs, filters.MergeFilter{})
|
|
||||||
inputs = append(inputs,
|
|
||||||
kio.LocalPackageReader{
|
|
||||||
OmitReaderAnnotations: true, // don't set path annotations, as they would override
|
|
||||||
PackagePath: r.getEnv(KustOverrideDirEnv)})
|
|
||||||
}
|
|
||||||
fltrs = append(fltrs,
|
|
||||||
&filters.FileSetter{
|
|
||||||
FilenamePattern: filepath.Join("config", filters.DefaultFilenamePattern)},
|
|
||||||
&filters.FormatFilter{})
|
|
||||||
|
|
||||||
err := kio.Pipeline{
|
|
||||||
Inputs: inputs,
|
|
||||||
Filters: fltrs,
|
|
||||||
Outputs: []kio.Writer{kio.ByteWriter{
|
|
||||||
Sort: true,
|
|
||||||
KeepReaderAnnotations: true,
|
|
||||||
Writer: c.OutOrStdout(),
|
|
||||||
WrappingKind: kio.ResourceListKind,
|
|
||||||
WrappingAPIVersion: kio.ResourceListAPIVersion}}}.Execute()
|
|
||||||
return runner.HandleError(c, err)
|
|
||||||
}
|
|
||||||
@@ -1,377 +0,0 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package commands
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/testutil"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
input = `apiVersion: config.kubernetes.io/v1
|
|
||||||
kind: ResourceList
|
|
||||||
functionConfig:
|
|
||||||
metadata:
|
|
||||||
name: test
|
|
||||||
spec:
|
|
||||||
replicas: 11
|
|
||||||
items:
|
|
||||||
- apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: test
|
|
||||||
labels:
|
|
||||||
app: nginx
|
|
||||||
name: test
|
|
||||||
spec:
|
|
||||||
replicas: 5
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app: nginx
|
|
||||||
name: test
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: nginx
|
|
||||||
name: test
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: test
|
|
||||||
image: nginx:v1.7
|
|
||||||
ports:
|
|
||||||
- containerPort: 8080
|
|
||||||
name: http
|
|
||||||
resources:
|
|
||||||
limits:
|
|
||||||
cpu: 500m
|
|
||||||
- apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: test
|
|
||||||
labels:
|
|
||||||
app: nginx
|
|
||||||
name: test
|
|
||||||
spec:
|
|
||||||
ports:
|
|
||||||
# This i the port.
|
|
||||||
- port: 8080
|
|
||||||
targetPort: 8080
|
|
||||||
name: http
|
|
||||||
selector:
|
|
||||||
app: nginx
|
|
||||||
name: test
|
|
||||||
`
|
|
||||||
|
|
||||||
output = `apiVersion: config.kubernetes.io/v1
|
|
||||||
kind: ResourceList
|
|
||||||
items:
|
|
||||||
- apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: test
|
|
||||||
labels:
|
|
||||||
name: test
|
|
||||||
app: nginx
|
|
||||||
annotations:
|
|
||||||
config.kubernetes.io/index: '0'
|
|
||||||
config.kubernetes.io/path: 'config/test_deployment.yaml'
|
|
||||||
internal.config.kubernetes.io/index: '0'
|
|
||||||
internal.config.kubernetes.io/path: 'config/test_deployment.yaml'
|
|
||||||
spec:
|
|
||||||
replicas: 11
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
name: test
|
|
||||||
app: nginx
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
name: test
|
|
||||||
app: nginx
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: test
|
|
||||||
image: nginx:v1.7
|
|
||||||
ports:
|
|
||||||
- name: http
|
|
||||||
containerPort: 8080
|
|
||||||
resources:
|
|
||||||
limits:
|
|
||||||
cpu: 500m
|
|
||||||
- apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: test
|
|
||||||
labels:
|
|
||||||
name: test
|
|
||||||
app: nginx
|
|
||||||
annotations:
|
|
||||||
config.kubernetes.io/index: '0'
|
|
||||||
config.kubernetes.io/path: 'config/test_service.yaml'
|
|
||||||
internal.config.kubernetes.io/index: '0'
|
|
||||||
internal.config.kubernetes.io/path: 'config/test_service.yaml'
|
|
||||||
spec:
|
|
||||||
selector:
|
|
||||||
name: test
|
|
||||||
app: nginx
|
|
||||||
ports:
|
|
||||||
# This i the port.
|
|
||||||
- name: http
|
|
||||||
port: 8080
|
|
||||||
targetPort: 8080
|
|
||||||
`
|
|
||||||
|
|
||||||
outputNoMerge = `apiVersion: config.kubernetes.io/v1
|
|
||||||
kind: ResourceList
|
|
||||||
items:
|
|
||||||
- apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: test
|
|
||||||
labels:
|
|
||||||
name: test
|
|
||||||
app: nginx
|
|
||||||
annotations:
|
|
||||||
config.kubernetes.io/index: '0'
|
|
||||||
config.kubernetes.io/path: 'config/test_deployment.yaml'
|
|
||||||
internal.config.kubernetes.io/index: '0'
|
|
||||||
internal.config.kubernetes.io/path: 'config/test_deployment.yaml'
|
|
||||||
spec:
|
|
||||||
replicas: 11
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
name: test
|
|
||||||
app: nginx
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
name: test
|
|
||||||
app: nginx
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: test
|
|
||||||
image: nginx:v1.7
|
|
||||||
ports:
|
|
||||||
- name: http
|
|
||||||
containerPort: 8080
|
|
||||||
- apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: test
|
|
||||||
labels:
|
|
||||||
name: test
|
|
||||||
app: nginx
|
|
||||||
annotations:
|
|
||||||
config.kubernetes.io/index: '0'
|
|
||||||
config.kubernetes.io/path: 'config/test_service.yaml'
|
|
||||||
internal.config.kubernetes.io/index: '0'
|
|
||||||
internal.config.kubernetes.io/path: 'config/test_service.yaml'
|
|
||||||
spec:
|
|
||||||
selector:
|
|
||||||
name: test
|
|
||||||
app: nginx
|
|
||||||
ports:
|
|
||||||
# This i the port.
|
|
||||||
- name: http
|
|
||||||
port: 8080
|
|
||||||
targetPort: 8080
|
|
||||||
`
|
|
||||||
|
|
||||||
outputOverride = `apiVersion: config.kubernetes.io/v1
|
|
||||||
kind: ResourceList
|
|
||||||
items:
|
|
||||||
- apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: mysql-deployment
|
|
||||||
namespace: myspace
|
|
||||||
annotations:
|
|
||||||
config.kubernetes.io/index: '0'
|
|
||||||
config.kubernetes.io/path: 'config/mysql-deployment_deployment.yaml'
|
|
||||||
internal.config.kubernetes.io/index: '0'
|
|
||||||
internal.config.kubernetes.io/path: 'config/mysql-deployment_deployment.yaml'
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
template:
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: mysql
|
|
||||||
image: mysql:1.7.9
|
|
||||||
- apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: nosetters-deployment
|
|
||||||
namespace: myspace
|
|
||||||
annotations:
|
|
||||||
config.kubernetes.io/index: '0'
|
|
||||||
config.kubernetes.io/path: 'config/nosetters-deployment_deployment.yaml'
|
|
||||||
internal.config.kubernetes.io/index: '0'
|
|
||||||
internal.config.kubernetes.io/path: 'config/nosetters-deployment_deployment.yaml'
|
|
||||||
spec:
|
|
||||||
replicas: 4
|
|
||||||
template:
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: nosetters
|
|
||||||
image: nosetters:1.7.7
|
|
||||||
- apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: storage-deployment
|
|
||||||
namespace: myspace
|
|
||||||
annotations:
|
|
||||||
config.kubernetes.io/index: '0'
|
|
||||||
config.kubernetes.io/path: 'config/storage-deployment_deployment.yaml'
|
|
||||||
internal.config.kubernetes.io/index: '0'
|
|
||||||
internal.config.kubernetes.io/path: 'config/storage-deployment_deployment.yaml'
|
|
||||||
spec:
|
|
||||||
replicas: 4
|
|
||||||
template:
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: storage
|
|
||||||
image: storage:1.7.7
|
|
||||||
- apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: test
|
|
||||||
labels:
|
|
||||||
name: test
|
|
||||||
app: nginx
|
|
||||||
annotations:
|
|
||||||
config.kubernetes.io/index: '0'
|
|
||||||
config.kubernetes.io/path: 'config/test_deployment.yaml'
|
|
||||||
internal.config.kubernetes.io/index: '0'
|
|
||||||
internal.config.kubernetes.io/path: 'config/test_deployment.yaml'
|
|
||||||
spec:
|
|
||||||
replicas: 11
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
name: test
|
|
||||||
app: nginx
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
name: test
|
|
||||||
app: nginx
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: test
|
|
||||||
image: nginx:v1.9
|
|
||||||
ports:
|
|
||||||
- name: http
|
|
||||||
containerPort: 8080
|
|
||||||
resources:
|
|
||||||
limits:
|
|
||||||
cpu: 500m
|
|
||||||
- apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: test
|
|
||||||
labels:
|
|
||||||
name: test
|
|
||||||
app: nginx
|
|
||||||
annotations:
|
|
||||||
config.kubernetes.io/index: '0'
|
|
||||||
config.kubernetes.io/path: 'config/test_service.yaml'
|
|
||||||
internal.config.kubernetes.io/index: '0'
|
|
||||||
internal.config.kubernetes.io/path: 'config/test_service.yaml'
|
|
||||||
spec:
|
|
||||||
selector:
|
|
||||||
name: test
|
|
||||||
app: nginx
|
|
||||||
ports:
|
|
||||||
# This i the port.
|
|
||||||
- name: http
|
|
||||||
port: 8080
|
|
||||||
targetPort: 8080
|
|
||||||
`
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestCmd_wrap(t *testing.T) {
|
|
||||||
testutil.SkipWindows(t)
|
|
||||||
|
|
||||||
_, dir, _, ok := runtime.Caller(0)
|
|
||||||
if !assert.True(t, ok) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
dir = filepath.Dir(dir)
|
|
||||||
|
|
||||||
c := GetWrapRunner()
|
|
||||||
c.Command.SetIn(bytes.NewBufferString(input))
|
|
||||||
out := &bytes.Buffer{}
|
|
||||||
c.Command.SetOut(out)
|
|
||||||
args := []string{"--", filepath.Join(dir, "test", "test.sh")}
|
|
||||||
c.Command.SetArgs(args)
|
|
||||||
c.XArgs.Args = args
|
|
||||||
|
|
||||||
if !assert.NoError(t, c.Command.Execute()) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Equal(t, output, out.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCmd_wrapNoMerge(t *testing.T) {
|
|
||||||
testutil.SkipWindows(t)
|
|
||||||
|
|
||||||
_, dir, _, ok := runtime.Caller(0)
|
|
||||||
if !assert.True(t, ok) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
dir = filepath.Dir(dir)
|
|
||||||
|
|
||||||
c := GetWrapRunner()
|
|
||||||
c.getEnv = func(key string) string {
|
|
||||||
if key == KustMergeEnv {
|
|
||||||
return "false"
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
c.Command.SetIn(bytes.NewBufferString(input))
|
|
||||||
out := &bytes.Buffer{}
|
|
||||||
c.Command.SetOut(out)
|
|
||||||
args := []string{"--", filepath.Join(dir, "test", "test.sh")}
|
|
||||||
c.Command.SetArgs(args)
|
|
||||||
c.XArgs.Args = args
|
|
||||||
if !assert.NoError(t, c.Command.Execute()) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Equal(t, outputNoMerge, out.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCmd_wrapOverride(t *testing.T) {
|
|
||||||
testutil.SkipWindows(t)
|
|
||||||
|
|
||||||
_, dir, _, ok := runtime.Caller(0)
|
|
||||||
if !assert.True(t, ok) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
dir = filepath.Dir(dir)
|
|
||||||
|
|
||||||
c := GetWrapRunner()
|
|
||||||
c.getEnv = func(key string) string {
|
|
||||||
if key == KustOverrideDirEnv {
|
|
||||||
return filepath.Join(dir, "test")
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
c.Command.SetIn(bytes.NewBufferString(input))
|
|
||||||
out := &bytes.Buffer{}
|
|
||||||
c.Command.SetOut(out)
|
|
||||||
args := []string{"--", filepath.Join(dir, "test", "test.sh")}
|
|
||||||
c.Command.SetArgs(args)
|
|
||||||
c.XArgs.Args = args
|
|
||||||
if !assert.NoError(t, c.Command.Execute()) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Equal(t, outputOverride, out.String())
|
|
||||||
}
|
|
||||||
@@ -1,234 +0,0 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package commands
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"strings"
|
|
||||||
"unicode"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/runner"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetXArgsRunner returns a command runner.
|
|
||||||
func GetXArgsRunner() *XArgsRunner {
|
|
||||||
r := &XArgsRunner{}
|
|
||||||
c := &cobra.Command{
|
|
||||||
Use: "xargs -- CMD...",
|
|
||||||
Short: "Convert functionConfig to commandline flags and envs",
|
|
||||||
Long: `Convert functionConfig to commandline flags and envs.
|
|
||||||
|
|
||||||
xargs reads a ResourceList from stdin and parses the functionConfig field. xargs then
|
|
||||||
reads each of the fields under .spec and parses them as flags. If the fields have non-scalar
|
|
||||||
values, then xargs encoded the values as yaml strings.
|
|
||||||
|
|
||||||
CMD:
|
|
||||||
The command to run and pass the functionConfig as arguments.
|
|
||||||
`,
|
|
||||||
Example: `
|
|
||||||
# given this example functionConfig in config.yaml
|
|
||||||
kind: Foo
|
|
||||||
spec:
|
|
||||||
flag1: value1
|
|
||||||
flag2: value2
|
|
||||||
items:
|
|
||||||
- 2
|
|
||||||
- 1
|
|
||||||
|
|
||||||
# this command:
|
|
||||||
$ kyaml cat pkg/ --function-config config.yaml --wrap-kind ResourceList | kyaml run xargs -- app
|
|
||||||
|
|
||||||
# is equivalent to this command:
|
|
||||||
$ kyaml cat pkg/ --function-config config.yaml --wrap-kind ResourceList | app --flag1=value1 --flag2=value2 2 1
|
|
||||||
|
|
||||||
# echo: prints the app arguments
|
|
||||||
$ kyaml cat pkg/ --function-config config.yaml --wrap-kind ResourceList | kyaml reconcile xargs -- echo
|
|
||||||
--flag1=value1 --flag2=value2 2 1
|
|
||||||
|
|
||||||
# env: prints the app env
|
|
||||||
$ kyaml cat pkg/ --function-config config.yaml --wrap-kind ResourceList | kyaml reconcile xargs -- env
|
|
||||||
|
|
||||||
# cat: prints the app stdin -- prints the package contents and functionConfig wrapped in a
|
|
||||||
# ResourceList
|
|
||||||
$ kyaml cat pkg/ --function-config config.yaml --wrap-kind ResourceList | kyaml reconcile xargs --no-flags -- env
|
|
||||||
|
|
||||||
`,
|
|
||||||
RunE: r.runE,
|
|
||||||
PreRunE: r.preRunE,
|
|
||||||
SilenceUsage: true,
|
|
||||||
FParseErrWhitelist: cobra.FParseErrWhitelist{UnknownFlags: true},
|
|
||||||
Args: cobra.MinimumNArgs(1),
|
|
||||||
}
|
|
||||||
r.Command = c
|
|
||||||
r.Command.Flags().BoolVar(&r.EnvOnly, "env-only", false, "only add env vars, not flags")
|
|
||||||
c.Flags().StringVar(&r.WrapKind, "wrap-kind", "List", "wrap the input xargs give to the command in this type.")
|
|
||||||
c.Flags().StringVar(&r.WrapVersion, "wrap-version", "v1", "wrap the input xargs give to the command in this type.")
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
// Runner contains the run function
|
|
||||||
type XArgsRunner struct {
|
|
||||||
Command *cobra.Command
|
|
||||||
Args []string
|
|
||||||
EnvOnly bool
|
|
||||||
WrapKind string
|
|
||||||
WrapVersion string
|
|
||||||
}
|
|
||||||
|
|
||||||
func XArgsCommand() *cobra.Command {
|
|
||||||
return GetXArgsRunner().Command
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *XArgsRunner) preRunE(_ *cobra.Command, _ []string) error {
|
|
||||||
_, err := fmt.Fprintln(os.Stderr, `Command "xargs" is deprecated, this will no longer be available in kustomize v5.
|
|
||||||
See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.`)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *XArgsRunner) runE(c *cobra.Command, _ []string) error {
|
|
||||||
if len(r.Args) == 0 {
|
|
||||||
r.Args = os.Args
|
|
||||||
}
|
|
||||||
cmdIndex := -1
|
|
||||||
for i := range r.Args {
|
|
||||||
if r.Args[i] == "--" {
|
|
||||||
cmdIndex = i + 1
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if cmdIndex < 0 {
|
|
||||||
return fmt.Errorf("must specify -- before command")
|
|
||||||
}
|
|
||||||
r.Args = r.Args[cmdIndex:]
|
|
||||||
run := exec.Command(r.Args[0]) //nolint: gosec
|
|
||||||
|
|
||||||
if len(r.Args) > 1 {
|
|
||||||
r.Args = r.Args[cmdIndex+1:]
|
|
||||||
} else {
|
|
||||||
r.Args = []string{}
|
|
||||||
}
|
|
||||||
run.Stdout = c.OutOrStdout()
|
|
||||||
run.Stderr = c.ErrOrStderr()
|
|
||||||
|
|
||||||
rw := &kio.ByteReadWriter{
|
|
||||||
Reader: c.InOrStdin(),
|
|
||||||
}
|
|
||||||
nodes, err := rw.Read()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
env := os.Environ()
|
|
||||||
|
|
||||||
// append the config to the flags
|
|
||||||
if err = func() error {
|
|
||||||
if rw.FunctionConfig == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
str, err := rw.FunctionConfig.String()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// add the API object to the env
|
|
||||||
env = append(env, fmt.Sprintf("KUST_FUNCTION_CONFIG=%s", str))
|
|
||||||
|
|
||||||
// parse the fields
|
|
||||||
meta := rw.FunctionConfig.Field("metadata")
|
|
||||||
if meta != nil {
|
|
||||||
err = meta.Value.VisitFields(func(node *yaml.MapNode) error {
|
|
||||||
if !r.EnvOnly {
|
|
||||||
r.Args = append(r.Args, fmt.Sprintf("--%s=%s",
|
|
||||||
node.Key.YNode().Value, parseYNode(node.Value.YNode())))
|
|
||||||
}
|
|
||||||
env = append(env, fmt.Sprintf("%s=%s", strings.ToUpper(node.Key.YNode().Value),
|
|
||||||
node.Value.YNode().Value))
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
spec := rw.FunctionConfig.Field("spec")
|
|
||||||
if spec != nil {
|
|
||||||
err = spec.Value.VisitFields(func(node *yaml.MapNode) error {
|
|
||||||
if !r.EnvOnly {
|
|
||||||
r.Args = append(r.Args, fmt.Sprintf("--%s=%s",
|
|
||||||
node.Key.YNode().Value, parseYNode(node.Value.YNode())))
|
|
||||||
}
|
|
||||||
env = append(env, fmt.Sprintf("%s=%s", strings.ToUpper(node.Key.YNode().Value),
|
|
||||||
node.Value.YNode().Value))
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !r.EnvOnly {
|
|
||||||
items := rw.FunctionConfig.Field("items")
|
|
||||||
if items != nil {
|
|
||||||
err = items.Value.VisitElements(func(node *yaml.RNode) error {
|
|
||||||
r.Args = append(r.Args, parseYNode(node.YNode()))
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.WrapKind != "" {
|
|
||||||
if kind := rw.FunctionConfig.Field("kind"); !kind.IsNilOrEmpty() {
|
|
||||||
kind.Value.YNode().Value = r.WrapKind
|
|
||||||
}
|
|
||||||
rw.WrappingKind = r.WrapKind
|
|
||||||
}
|
|
||||||
if r.WrapVersion != "" {
|
|
||||||
if version := rw.FunctionConfig.Field("apiVersion"); !version.IsNilOrEmpty() {
|
|
||||||
version.Value.YNode().Value = r.WrapVersion
|
|
||||||
}
|
|
||||||
rw.WrappingAPIVersion = r.WrapVersion
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
run.Args = append(run.Args, r.Args...)
|
|
||||||
run.Env = append(run.Env, env...)
|
|
||||||
|
|
||||||
// write ResourceList to stdin
|
|
||||||
if err = func() error {
|
|
||||||
in, err := run.StdinPipe()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer in.Close()
|
|
||||||
rw.Writer = in
|
|
||||||
if r.WrapKind != kio.ResourceListKind {
|
|
||||||
rw.FunctionConfig = nil
|
|
||||||
}
|
|
||||||
return rw.Write(nodes)
|
|
||||||
}(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return runner.HandleError(c, run.Run())
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseYNode(node *yaml.Node) string {
|
|
||||||
node.Value = strings.TrimSpace(node.Value)
|
|
||||||
for _, b := range node.Value {
|
|
||||||
if unicode.IsSpace(b) {
|
|
||||||
// wrap in '' -- contains whitespace
|
|
||||||
return fmt.Sprintf("'%s'", node.Value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return node.Value
|
|
||||||
}
|
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package commands_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
flagsInput = `kind: ResourceList
|
|
||||||
items:
|
|
||||||
- apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
spec:
|
|
||||||
template:
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: nginx
|
|
||||||
image: nginx
|
|
||||||
- apiVersion: apps/v1
|
|
||||||
kind: Service
|
|
||||||
spec: {}
|
|
||||||
functionConfig:
|
|
||||||
kind: Foo
|
|
||||||
spec:
|
|
||||||
a: b
|
|
||||||
c: d
|
|
||||||
e: f
|
|
||||||
items:
|
|
||||||
- 1
|
|
||||||
- 3
|
|
||||||
- 2
|
|
||||||
- 4
|
|
||||||
`
|
|
||||||
|
|
||||||
resourceInput = `apiVersion: config.kubernetes.io/v1
|
|
||||||
kind: ResourceList
|
|
||||||
items:
|
|
||||||
- apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
spec:
|
|
||||||
template:
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: nginx
|
|
||||||
image: nginx
|
|
||||||
- apiVersion: apps/v1
|
|
||||||
kind: Service
|
|
||||||
spec: {}
|
|
||||||
functionConfig:
|
|
||||||
kind: Foo
|
|
||||||
`
|
|
||||||
|
|
||||||
resourceOutput = `apiVersion: v1
|
|
||||||
kind: List
|
|
||||||
items:
|
|
||||||
- apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
spec:
|
|
||||||
template:
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: nginx
|
|
||||||
image: nginx
|
|
||||||
- apiVersion: apps/v1
|
|
||||||
kind: Service
|
|
||||||
spec: {}
|
|
||||||
`
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestXArgs_flags(t *testing.T) {
|
|
||||||
c := commands.GetXArgsRunner()
|
|
||||||
c.Command.SetIn(bytes.NewBufferString(flagsInput))
|
|
||||||
out := &bytes.Buffer{}
|
|
||||||
c.Command.SetOut(out)
|
|
||||||
c.Command.SetArgs([]string{"--", "echo"})
|
|
||||||
|
|
||||||
c.Args = []string{"--", "echo"}
|
|
||||||
if !assert.NoError(t, c.Command.Execute()) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
assert.Equal(t, strings.TrimSpace(`--a=b --c=d --e=f 1 3 2 4`), strings.TrimSpace(out.String()))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestXArgs_input(t *testing.T) {
|
|
||||||
c := commands.GetXArgsRunner()
|
|
||||||
c.Command.SetIn(bytes.NewBufferString(resourceInput))
|
|
||||||
out := &bytes.Buffer{}
|
|
||||||
c.Command.SetOut(out)
|
|
||||||
c.Command.SetArgs([]string{"--", "cat"})
|
|
||||||
|
|
||||||
c.Args = []string{"--", "cat"}
|
|
||||||
if !assert.NoError(t, c.Command.Execute()) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
assert.Equal(t, resourceOutput, out.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCmd_env(t *testing.T) {
|
|
||||||
c := commands.GetXArgsRunner()
|
|
||||||
c.Command.SetIn(bytes.NewBufferString(flagsInput))
|
|
||||||
out := &bytes.Buffer{}
|
|
||||||
c.Command.SetOut(out)
|
|
||||||
c.Command.SetArgs([]string{"--env-only", "--", "env"})
|
|
||||||
|
|
||||||
c.Args = []string{"--", "env"}
|
|
||||||
if !assert.NoError(t, c.Command.Execute()) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
assert.Contains(t, out.String(), "\nA=b\nC=d\nE=f\n")
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package e2e
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestCreateSetter(t *testing.T) {
|
|
||||||
tests := []test{
|
|
||||||
{
|
|
||||||
name: "create_setter",
|
|
||||||
args: []string{"cfg", "create-setter", ".", "replicas", "3"},
|
|
||||||
files: map[string]string{
|
|
||||||
"deployment.yaml": `
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: nginx-deployment
|
|
||||||
labels:
|
|
||||||
app: nginx
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
`,
|
|
||||||
"Krmfile": `
|
|
||||||
apiVersion: config.k8s.io/v1alpha1
|
|
||||||
kind: Krmfile
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
expectedFiles: map[string]string{
|
|
||||||
"deployment.yaml": `
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: nginx-deployment
|
|
||||||
labels:
|
|
||||||
app: nginx
|
|
||||||
spec:
|
|
||||||
replicas: 3 # {"$openapi":"replicas"}
|
|
||||||
`,
|
|
||||||
"Krmfile": `
|
|
||||||
apiVersion: config.k8s.io/v1alpha1
|
|
||||||
kind: Krmfile
|
|
||||||
openAPI:
|
|
||||||
definitions:
|
|
||||||
io.k8s.cli.setters.replicas:
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: replicas
|
|
||||||
value: "3"
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
runTests(t, tests)
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package e2e
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func TestInit(t *testing.T) {
|
|
||||||
tests := []test{
|
|
||||||
{
|
|
||||||
name: "init",
|
|
||||||
args: []string{"cfg", "init"},
|
|
||||||
expectedFiles: map[string]string{
|
|
||||||
"Krmfile": `
|
|
||||||
apiVersion: config.k8s.io/v1alpha1
|
|
||||||
kind: Krmfile
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
name: "init",
|
|
||||||
args: []string{"cfg", "init", "."},
|
|
||||||
expectedFiles: map[string]string{
|
|
||||||
"Krmfile": `
|
|
||||||
apiVersion: config.k8s.io/v1alpha1
|
|
||||||
kind: Krmfile
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
runTests(t, tests)
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package e2e
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func TestListSetters(t *testing.T) {
|
|
||||||
tests := []test{
|
|
||||||
{
|
|
||||||
name: "set",
|
|
||||||
args: []string{"cfg", "list-setters", "."},
|
|
||||||
files: map[string]string{
|
|
||||||
"deployment.yaml": `
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: nginx-deployment
|
|
||||||
labels:
|
|
||||||
app: nginx
|
|
||||||
spec:
|
|
||||||
replicas: 3 # {"$openapi":"replicas"}
|
|
||||||
`,
|
|
||||||
"Krmfile": `
|
|
||||||
apiVersion: config.k8s.io/v1alpha1
|
|
||||||
kind: Krmfile
|
|
||||||
openAPI:
|
|
||||||
definitions:
|
|
||||||
io.k8s.cli.setters.replicas:
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: replicas
|
|
||||||
value: "3"
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
expectedStdOut: `
|
|
||||||
./
|
|
||||||
NAME VALUE SET BY DESCRIPTION COUNT REQUIRED IS SET
|
|
||||||
replicas 3 1 No No
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
runTests(t, tests)
|
|
||||||
}
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package e2e
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func TestSet(t *testing.T) {
|
|
||||||
tests := []test{
|
|
||||||
{
|
|
||||||
name: "set",
|
|
||||||
args: []string{"cfg", "set", ".", "replicas", "4"},
|
|
||||||
files: map[string]string{
|
|
||||||
"deployment.yaml": `
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: nginx-deployment
|
|
||||||
labels:
|
|
||||||
app: nginx
|
|
||||||
spec:
|
|
||||||
replicas: 3 # {"$openapi":"replicas"}
|
|
||||||
`,
|
|
||||||
"Krmfile": `
|
|
||||||
apiVersion: config.k8s.io/v1alpha1
|
|
||||||
kind: Krmfile
|
|
||||||
openAPI:
|
|
||||||
definitions:
|
|
||||||
io.k8s.cli.setters.replicas:
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: replicas
|
|
||||||
value: "3"
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
expectedFiles: map[string]string{
|
|
||||||
"deployment.yaml": `
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: nginx-deployment
|
|
||||||
labels:
|
|
||||||
app: nginx
|
|
||||||
spec:
|
|
||||||
replicas: 4 # {"$openapi":"replicas"}
|
|
||||||
`,
|
|
||||||
"Krmfile": `
|
|
||||||
apiVersion: config.k8s.io/v1alpha1
|
|
||||||
kind: Krmfile
|
|
||||||
openAPI:
|
|
||||||
definitions:
|
|
||||||
io.k8s.cli.setters.replicas:
|
|
||||||
x-k8s-cli:
|
|
||||||
setter:
|
|
||||||
name: replicas
|
|
||||||
value: "4"
|
|
||||||
isSet: true
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
runTests(t, tests)
|
|
||||||
}
|
|
||||||
@@ -1,134 +0,0 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package commands
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/ext"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/runner"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio/filters"
|
|
||||||
)
|
|
||||||
|
|
||||||
// FmtCmd returns a command FmtRunner.
|
|
||||||
func GetFmtRunner(name string) *FmtRunner {
|
|
||||||
r := &FmtRunner{}
|
|
||||||
c := &cobra.Command{
|
|
||||||
Use: "fmt DIR...",
|
|
||||||
Short: commands.FmtShort,
|
|
||||||
Long: commands.FmtLong,
|
|
||||||
Example: commands.FmtExamples,
|
|
||||||
RunE: r.runE,
|
|
||||||
PreRunE: r.preRunE,
|
|
||||||
Deprecated: "imperative formatting will no longer be available in kustomize v5.\n" +
|
|
||||||
"Declare a formatting transformer in your kustomization instead.",
|
|
||||||
}
|
|
||||||
runner.FixDocs(name, c)
|
|
||||||
c.Flags().StringVar(&r.FilenamePattern, "pattern", filters.DefaultFilenamePattern,
|
|
||||||
`pattern to use for generating filenames for resources -- may contain the following
|
|
||||||
formatting substitution verbs {'%n': 'metadata.name', '%s': 'metadata.namespace', '%k': 'kind'}`)
|
|
||||||
c.Flags().BoolVar(&r.SetFilenames, "set-filenames", false,
|
|
||||||
`if true, set default filenames on Resources without them`)
|
|
||||||
c.Flags().BoolVar(&r.KeepAnnotations, "keep-annotations", false,
|
|
||||||
`if true, keep index and filename annotations set on Resources.`)
|
|
||||||
c.Flags().BoolVar(&r.Override, "override", false,
|
|
||||||
`if true, override existing filepath annotations.`)
|
|
||||||
c.Flags().BoolVar(&r.UseSchema, "use-schema", false,
|
|
||||||
`if true, uses openapi resource schema to format resources.`)
|
|
||||||
c.Flags().BoolVarP(&r.RecurseSubPackages, "recurse-subpackages", "R", false,
|
|
||||||
"formats resource files recursively in all the nested subpackages")
|
|
||||||
r.Command = c
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func FmtCommand(name string) *cobra.Command {
|
|
||||||
return GetFmtRunner(name).Command
|
|
||||||
}
|
|
||||||
|
|
||||||
// FmtRunner contains the run function
|
|
||||||
type FmtRunner struct {
|
|
||||||
Command *cobra.Command
|
|
||||||
FilenamePattern string
|
|
||||||
SetFilenames bool
|
|
||||||
KeepAnnotations bool
|
|
||||||
Override bool
|
|
||||||
UseSchema bool
|
|
||||||
RecurseSubPackages bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *FmtRunner) preRunE(c *cobra.Command, args []string) error {
|
|
||||||
if r.SetFilenames {
|
|
||||||
r.KeepAnnotations = true
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *FmtRunner) runE(c *cobra.Command, args []string) error {
|
|
||||||
// format stdin if there are no args
|
|
||||||
if len(args) == 0 {
|
|
||||||
rw := &kio.ByteReadWriter{
|
|
||||||
Reader: c.InOrStdin(),
|
|
||||||
Writer: c.OutOrStdout(),
|
|
||||||
KeepReaderAnnotations: r.KeepAnnotations,
|
|
||||||
}
|
|
||||||
return runner.HandleError(c, kio.Pipeline{
|
|
||||||
Inputs: []kio.Reader{rw}, Filters: r.fmtFilters(), Outputs: []kio.Writer{rw}}.Execute())
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, rootPkgPath := range args {
|
|
||||||
e := runner.ExecuteCmdOnPkgs{
|
|
||||||
Writer: c.OutOrStdout(),
|
|
||||||
NeedOpenAPI: false,
|
|
||||||
RecurseSubPackages: r.RecurseSubPackages,
|
|
||||||
CmdRunner: r,
|
|
||||||
RootPkgPath: rootPkgPath,
|
|
||||||
}
|
|
||||||
|
|
||||||
err := e.Execute()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *FmtRunner) ExecuteCmd(w io.Writer, pkgPath string) error {
|
|
||||||
rw := &kio.LocalPackageReadWriter{
|
|
||||||
NoDeleteFiles: true,
|
|
||||||
PackagePath: pkgPath,
|
|
||||||
KeepReaderAnnotations: r.KeepAnnotations, PackageFileName: ext.KRMFileName()}
|
|
||||||
err := kio.Pipeline{
|
|
||||||
Inputs: []kio.Reader{rw}, Filters: r.fmtFilters(), Outputs: []kio.Writer{rw}}.Execute()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
// return err if RecurseSubPackages is false
|
|
||||||
if !r.RecurseSubPackages {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// print error message and continue if RecurseSubPackages is true
|
|
||||||
fmt.Fprintf(w, "%s\n", err.Error())
|
|
||||||
} else {
|
|
||||||
fmt.Fprint(w, "formatted resource files in the package\n")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *FmtRunner) fmtFilters() []kio.Filter {
|
|
||||||
fmtFilters := []kio.Filter{filters.FormatFilter{
|
|
||||||
UseSchema: r.UseSchema,
|
|
||||||
}}
|
|
||||||
|
|
||||||
// format with file names
|
|
||||||
if r.SetFilenames {
|
|
||||||
fmtFilters = append(fmtFilters, &filters.FileSetter{
|
|
||||||
FilenamePattern: r.FilenamePattern,
|
|
||||||
Override: r.Override,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return fmtFilters
|
|
||||||
}
|
|
||||||
@@ -1,241 +0,0 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package commands_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/copyutil"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio/filters/testyaml"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/testutil"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TestCmd_files verifies the fmt command formats the files
|
|
||||||
func TestFmtCommand_files(t *testing.T) {
|
|
||||||
f1, err := os.CreateTemp("", "cmdfmt*.yaml")
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(f1.Name())
|
|
||||||
err = os.WriteFile(f1.Name(), testyaml.UnformattedYaml1, 0600)
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
f2, err := os.CreateTemp("", "cmdfmt*.yaml")
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(f2.Name())
|
|
||||||
err = os.WriteFile(f2.Name(), testyaml.UnformattedYaml2, 0600)
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// fmt the files
|
|
||||||
r := commands.GetFmtRunner("")
|
|
||||||
r.Command.SetArgs([]string{f1.Name(), f2.Name()})
|
|
||||||
err = r.Command.Execute()
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// verify the contents
|
|
||||||
b, err := os.ReadFile(f1.Name())
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !assert.Equal(t, string(testyaml.FormattedYaml1), string(b)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
b, err = os.ReadFile(f2.Name())
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !assert.Equal(t, string(testyaml.FormattedYaml2), string(b)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFmtCommand_stdin(t *testing.T) {
|
|
||||||
out := &bytes.Buffer{}
|
|
||||||
r := commands.GetFmtRunner("")
|
|
||||||
r.Command.SetOut(out)
|
|
||||||
r.Command.SetIn(bytes.NewReader(testyaml.UnformattedYaml1))
|
|
||||||
|
|
||||||
// fmt the input
|
|
||||||
err := r.Command.Execute()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
// verify the output
|
|
||||||
assert.Contains(t, out.String(), string(testyaml.FormattedYaml1))
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestCmd_filesAndstdin verifies that if both files and stdin input are provided, only
|
|
||||||
// the files are formatted and the input is ignored
|
|
||||||
func TestFmtCmd_filesAndStdin(t *testing.T) {
|
|
||||||
f1, err := os.CreateTemp("", "cmdfmt*.yaml")
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = os.WriteFile(f1.Name(), testyaml.UnformattedYaml1, 0600)
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
f2, err := os.CreateTemp("", "cmdfmt*.yaml")
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = os.WriteFile(f2.Name(), testyaml.UnformattedYaml2, 0600)
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
out := &bytes.Buffer{}
|
|
||||||
in := &bytes.Buffer{}
|
|
||||||
r := commands.GetFmtRunner("")
|
|
||||||
r.Command.SetOut(out)
|
|
||||||
r.Command.SetIn(in)
|
|
||||||
|
|
||||||
// fmt the files
|
|
||||||
r = commands.GetFmtRunner("")
|
|
||||||
r.Command.SetArgs([]string{f1.Name(), f2.Name()})
|
|
||||||
err = r.Command.Execute()
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// verify the output
|
|
||||||
b, err := os.ReadFile(f1.Name())
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !assert.Equal(t, string(testyaml.FormattedYaml1), string(b)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
b, err = os.ReadFile(f2.Name())
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !assert.Equal(t, string(testyaml.FormattedYaml2), string(b)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = r.Command.Execute()
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if !assert.Equal(t, "", out.String()) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestCmd_files verifies the fmt command formats the files
|
|
||||||
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()
|
|
||||||
testutil.AssertErrorContains(t, err, "notrealfile:")
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestCmd_files verifies the fmt command formats the files
|
|
||||||
func TestCmd_failFileContents(t *testing.T) {
|
|
||||||
out := &bytes.Buffer{}
|
|
||||||
r := commands.GetFmtRunner("")
|
|
||||||
r.Command.SetOut(out)
|
|
||||||
r.Command.SetIn(strings.NewReader(`{`))
|
|
||||||
|
|
||||||
// fmt the input
|
|
||||||
err := r.Command.Execute()
|
|
||||||
|
|
||||||
// expect an error
|
|
||||||
assert.EqualError(t, err, "MalformedYAMLError: yaml: line 1: did not find expected node content")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFmtSubPackages(t *testing.T) {
|
|
||||||
var tests = []struct {
|
|
||||||
name string
|
|
||||||
dataset string
|
|
||||||
packagePath string
|
|
||||||
args []string
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "fmt-recurse-subpackages",
|
|
||||||
dataset: "dataset-with-setters",
|
|
||||||
args: []string{"-R"},
|
|
||||||
expected: `${baseDir}/
|
|
||||||
formatted resource files in the package
|
|
||||||
|
|
||||||
${baseDir}/mysql/
|
|
||||||
formatted resource files in the package
|
|
||||||
|
|
||||||
${baseDir}/mysql/nosetters/
|
|
||||||
formatted resource files in the package
|
|
||||||
|
|
||||||
${baseDir}/mysql/storage/
|
|
||||||
formatted resource files in the package
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "fmt-top-level-pkg-no-recurse-subpackages",
|
|
||||||
dataset: "dataset-without-setters",
|
|
||||||
packagePath: "mysql",
|
|
||||||
expected: `${baseDir}/mysql/
|
|
||||||
formatted resource files in the package
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "fmt-nested-pkg-no-recurse-subpackages",
|
|
||||||
dataset: "dataset-without-setters",
|
|
||||||
packagePath: "mysql/storage",
|
|
||||||
expected: `${baseDir}/mysql/storage/
|
|
||||||
formatted resource files in the package
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for i := range tests {
|
|
||||||
test := tests[i]
|
|
||||||
t.Run(test.name, func(t *testing.T) {
|
|
||||||
// reset the openAPI afterward
|
|
||||||
openapi.ResetOpenAPI()
|
|
||||||
defer openapi.ResetOpenAPI()
|
|
||||||
sourceDir := filepath.Join("test", "testdata", test.dataset)
|
|
||||||
baseDir := t.TempDir()
|
|
||||||
copyutil.CopyDir(sourceDir, baseDir)
|
|
||||||
runner := commands.GetFmtRunner("")
|
|
||||||
actual := &bytes.Buffer{}
|
|
||||||
runner.Command.SetOut(actual)
|
|
||||||
runner.Command.SetArgs(append([]string{filepath.Join(baseDir, test.packagePath)}, test.args...))
|
|
||||||
err := runner.Command.Execute()
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
// normalize path format for windows
|
|
||||||
actualNormalized := strings.ReplaceAll(
|
|
||||||
strings.ReplaceAll(actual.String(), "\\", "/"),
|
|
||||||
"//", "/")
|
|
||||||
|
|
||||||
expected := strings.ReplaceAll(test.expected, "${baseDir}", baseDir)
|
|
||||||
expectedNormalized := strings.ReplaceAll(expected, "\\", "/")
|
|
||||||
if !assert.Contains(t, strings.TrimSpace(actualNormalized), strings.TrimSpace(expectedNormalized)) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package commands_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestInit_args(t *testing.T) {
|
|
||||||
d := t.TempDir()
|
|
||||||
|
|
||||||
// fmt the files
|
|
||||||
b := &bytes.Buffer{}
|
|
||||||
r := commands.GetInitRunner("")
|
|
||||||
r.Command.SetArgs([]string{d})
|
|
||||||
r.Command.SetOut(b)
|
|
||||||
if !assert.NoError(t, r.Command.Execute()) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
actual, err := os.ReadFile(filepath.Join(d, "Krmfile"))
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
if !assert.Equal(t, strings.TrimSpace(`
|
|
||||||
apiVersion: config.k8s.io/v1alpha1
|
|
||||||
kind: Krmfile
|
|
||||||
`), strings.TrimSpace(string(actual))) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
if !assert.Equal(t, `Command "init" is deprecated, setter commands and substitutions will no longer be available in kustomize v5.
|
|
||||||
See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.
|
|
||||||
`, b.String()) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInit_noargs(t *testing.T) {
|
|
||||||
d := t.TempDir()
|
|
||||||
|
|
||||||
cwd, err := os.Getwd()
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
if !assert.NoError(t, os.Chdir(d)) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Cleanup(func() {
|
|
||||||
if !assert.NoError(t, os.Chdir(cwd)) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
b := &bytes.Buffer{}
|
|
||||||
r := commands.GetInitRunner("")
|
|
||||||
r.Command.SetOut(b)
|
|
||||||
if !assert.NoError(t, r.Command.Execute()) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
actual, err := os.ReadFile(filepath.Join(d, "Krmfile"))
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
if !assert.Equal(t, strings.TrimSpace(`
|
|
||||||
apiVersion: config.k8s.io/v1alpha1
|
|
||||||
kind: Krmfile
|
|
||||||
`), strings.TrimSpace(string(actual))) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
if !assert.Equal(t, `Command "init" is deprecated, setter commands and substitutions will no longer be available in kustomize v5.
|
|
||||||
See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.
|
|
||||||
`, b.String()) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package commands
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/runner"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio/filters"
|
|
||||||
)
|
|
||||||
|
|
||||||
func GetMergeRunner(name string) *MergeRunner {
|
|
||||||
r := &MergeRunner{}
|
|
||||||
c := &cobra.Command{
|
|
||||||
Use: "merge [SOURCE_DIR...] [DESTINATION_DIR]",
|
|
||||||
Short: commands.MergeShort,
|
|
||||||
Long: commands.MergeLong,
|
|
||||||
Example: commands.MergeExamples,
|
|
||||||
RunE: r.runE,
|
|
||||||
Deprecated: "this will no longer be available in kustomize v5.\n" +
|
|
||||||
"See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.",
|
|
||||||
}
|
|
||||||
runner.FixDocs(name, c)
|
|
||||||
r.Command = c
|
|
||||||
r.Command.Flags().BoolVar(&r.InvertOrder, "invert-order", false,
|
|
||||||
"if true, merge Resources in the reverse order")
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func MergeCommand(name string) *cobra.Command {
|
|
||||||
return GetMergeRunner(name).Command
|
|
||||||
}
|
|
||||||
|
|
||||||
// MergeRunner contains the run function
|
|
||||||
type MergeRunner struct {
|
|
||||||
Command *cobra.Command
|
|
||||||
InvertOrder bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *MergeRunner) runE(c *cobra.Command, args []string) error {
|
|
||||||
var inputs []kio.Reader
|
|
||||||
// add the packages in reverse order -- the arg list should be highest precedence first
|
|
||||||
// e.g. merge from -> to, but the MergeFilter is highest precedence last
|
|
||||||
for i := len(args) - 1; i >= 0; i-- {
|
|
||||||
inputs = append(inputs, kio.LocalPackageReader{PackagePath: args[i]})
|
|
||||||
}
|
|
||||||
// if there is no "from" package, read from stdin
|
|
||||||
rw := &kio.ByteReadWriter{
|
|
||||||
Reader: c.InOrStdin(),
|
|
||||||
Writer: c.OutOrStdout(),
|
|
||||||
KeepReaderAnnotations: true,
|
|
||||||
}
|
|
||||||
if len(inputs) < 2 {
|
|
||||||
inputs = append(inputs, rw)
|
|
||||||
}
|
|
||||||
|
|
||||||
// write to the "to" package if specified
|
|
||||||
var outputs []kio.Writer
|
|
||||||
if len(args) != 0 {
|
|
||||||
outputs = append(outputs, kio.LocalPackageWriter{PackagePath: args[len(args)-1]})
|
|
||||||
}
|
|
||||||
// if there is no "to" package, write to stdout
|
|
||||||
if len(outputs) == 0 {
|
|
||||||
outputs = append(outputs, rw)
|
|
||||||
}
|
|
||||||
|
|
||||||
filters := []kio.Filter{filters.MergeFilter{}, filters.FormatFilter{}}
|
|
||||||
return runner.HandleError(c, kio.Pipeline{Inputs: inputs, Filters: filters, Outputs: outputs}.Execute())
|
|
||||||
}
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package commands
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/runner"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio/filters"
|
|
||||||
)
|
|
||||||
|
|
||||||
func GetMerge3Runner(name string) *Merge3Runner {
|
|
||||||
r := &Merge3Runner{}
|
|
||||||
c := &cobra.Command{
|
|
||||||
Use: "merge3 --ancestor [ORIGINAL_DIR] --from [UPDATED_DIR] --to [DESTINATION_DIR]",
|
|
||||||
Short: commands.Merge3Short,
|
|
||||||
Long: commands.Merge3Long,
|
|
||||||
Example: commands.Merge3Examples,
|
|
||||||
RunE: r.runE,
|
|
||||||
Deprecated: "this will no longer be available in kustomize v5.\n" +
|
|
||||||
"See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.",
|
|
||||||
}
|
|
||||||
runner.FixDocs(name, c)
|
|
||||||
c.Flags().StringVar(&r.ancestor, "ancestor", "",
|
|
||||||
"Path to original package")
|
|
||||||
c.Flags().StringVar(&r.fromDir, "from", "",
|
|
||||||
"Path to updated package")
|
|
||||||
c.Flags().StringVar(&r.toDir, "to", "",
|
|
||||||
"Path to destination package")
|
|
||||||
c.Flags().BoolVar(&r.path, "path-merge-key", false,
|
|
||||||
"Use the path as part of the merge key when merging resources")
|
|
||||||
|
|
||||||
r.Command = c
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func Merge3Command(name string) *cobra.Command {
|
|
||||||
return GetMerge3Runner(name).Command
|
|
||||||
}
|
|
||||||
|
|
||||||
// Merge3Runner contains the run function
|
|
||||||
type Merge3Runner struct {
|
|
||||||
Command *cobra.Command
|
|
||||||
ancestor string
|
|
||||||
fromDir string
|
|
||||||
toDir string
|
|
||||||
path bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Merge3Runner) runE(_ *cobra.Command, _ []string) error {
|
|
||||||
matcher := filters.DefaultGVKNNMatcher{MergeOnPath: r.path}
|
|
||||||
err := filters.Merge3{
|
|
||||||
OriginalPath: r.ancestor,
|
|
||||||
UpdatedPath: r.fromDir,
|
|
||||||
DestPath: r.toDir,
|
|
||||||
Matcher: &matcher,
|
|
||||||
}.Merge()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,215 +0,0 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package commands_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/copyutil"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TestMerge3Command verifies the merge3 correctly applies the diff between 2 sets of resources into another
|
|
||||||
func TestMerge3Command(t *testing.T) {
|
|
||||||
datadir := t.TempDir()
|
|
||||||
|
|
||||||
err := os.WriteFile(filepath.Join(datadir, "java-deployment.resource.yaml"), []byte(`apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: app
|
|
||||||
labels:
|
|
||||||
app: java
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app: java
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: java
|
|
||||||
spec:
|
|
||||||
restartPolicy: Always
|
|
||||||
containers:
|
|
||||||
- name: app
|
|
||||||
image: gcr.io/project/app:version
|
|
||||||
command:
|
|
||||||
- java
|
|
||||||
- -jar
|
|
||||||
- /app.jar
|
|
||||||
ports:
|
|
||||||
- containerPort: 8080
|
|
||||||
envFrom:
|
|
||||||
- configMapRef:
|
|
||||||
name: app-config
|
|
||||||
env:
|
|
||||||
- name: JAVA_OPTS
|
|
||||||
value: -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -Djava.security.egd=file:/dev/./urandom
|
|
||||||
imagePullPolicy: Always
|
|
||||||
minReadySeconds: 5
|
|
||||||
`), 0600)
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
expectedDir := t.TempDir()
|
|
||||||
|
|
||||||
err = os.WriteFile(filepath.Join(expectedDir, "java-deployment.resource.yaml"), []byte(`apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: app
|
|
||||||
labels:
|
|
||||||
app: java
|
|
||||||
new-local: label
|
|
||||||
new-remote: label
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app: java
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: java
|
|
||||||
spec:
|
|
||||||
restartPolicy: Always
|
|
||||||
containers:
|
|
||||||
- name: app
|
|
||||||
image: gcr.io/project/app:version
|
|
||||||
command:
|
|
||||||
- java
|
|
||||||
- -jar
|
|
||||||
- /app.jar
|
|
||||||
- otherstuff
|
|
||||||
args:
|
|
||||||
- foo
|
|
||||||
ports:
|
|
||||||
- containerPort: 8080
|
|
||||||
envFrom:
|
|
||||||
- configMapRef:
|
|
||||||
name: app-config
|
|
||||||
env:
|
|
||||||
- name: JAVA_OPTS
|
|
||||||
value: -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -Djava.security.egd=file:/dev/./urandom
|
|
||||||
imagePullPolicy: Always
|
|
||||||
minReadySeconds: 20
|
|
||||||
`), 0600)
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
updatedDir := t.TempDir()
|
|
||||||
|
|
||||||
err = os.WriteFile(filepath.Join(updatedDir, "java-deployment.resource.yaml"), []byte(`apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: app
|
|
||||||
labels:
|
|
||||||
app: java
|
|
||||||
new-remote: label
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app: java
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: java
|
|
||||||
spec:
|
|
||||||
restartPolicy: Always
|
|
||||||
containers:
|
|
||||||
- name: app
|
|
||||||
image: gcr.io/project/app:version
|
|
||||||
command:
|
|
||||||
- java
|
|
||||||
- -jar
|
|
||||||
- /app.jar
|
|
||||||
- otherstuff
|
|
||||||
ports:
|
|
||||||
- containerPort: 8080
|
|
||||||
envFrom:
|
|
||||||
- configMapRef:
|
|
||||||
name: app-config
|
|
||||||
env:
|
|
||||||
- name: JAVA_OPTS
|
|
||||||
value: -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -Djava.security.egd=file:/dev/./urandom
|
|
||||||
imagePullPolicy: Always
|
|
||||||
minReadySeconds: 5
|
|
||||||
`), 0600)
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
destDir := t.TempDir()
|
|
||||||
|
|
||||||
err = os.WriteFile(filepath.Join(destDir, "java-deployment.resource.yaml"), []byte(`apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: app
|
|
||||||
labels:
|
|
||||||
app: java
|
|
||||||
new-local: label
|
|
||||||
spec:
|
|
||||||
replicas: 2
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app: java
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: java
|
|
||||||
spec:
|
|
||||||
restartPolicy: Always
|
|
||||||
containers:
|
|
||||||
- name: app
|
|
||||||
image: gcr.io/project/app:version
|
|
||||||
command:
|
|
||||||
- java
|
|
||||||
- -jar
|
|
||||||
- /app.jar
|
|
||||||
args:
|
|
||||||
- foo
|
|
||||||
ports:
|
|
||||||
- containerPort: 8080
|
|
||||||
envFrom:
|
|
||||||
- configMapRef:
|
|
||||||
name: app-config
|
|
||||||
env:
|
|
||||||
- name: JAVA_OPTS
|
|
||||||
value: -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -Djava.security.egd=file:/dev/./urandom
|
|
||||||
imagePullPolicy: Always
|
|
||||||
minReadySeconds: 20
|
|
||||||
`), 0600)
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Perform merge3 with newly created sets
|
|
||||||
r := commands.GetMerge3Runner("")
|
|
||||||
r.Command.SetArgs([]string{
|
|
||||||
"--ancestor",
|
|
||||||
datadir,
|
|
||||||
"--from",
|
|
||||||
updatedDir,
|
|
||||||
"--to",
|
|
||||||
destDir,
|
|
||||||
})
|
|
||||||
if !assert.NoError(t, r.Command.Execute()) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
diffs, err := copyutil.Diff(destDir, expectedDir)
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify there are no diffs
|
|
||||||
if !assert.Empty(t, diffs.List()) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package commands
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/runner"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetSinkRunner returns a command for Sink.
|
|
||||||
func GetSinkRunner(name string) *SinkRunner {
|
|
||||||
r := &SinkRunner{}
|
|
||||||
c := &cobra.Command{
|
|
||||||
Use: "sink DIR",
|
|
||||||
Short: commands.SinkShort,
|
|
||||||
Long: commands.SinkLong,
|
|
||||||
Example: commands.SinkExamples,
|
|
||||||
RunE: r.runE,
|
|
||||||
PreRunE: r.preRunE,
|
|
||||||
Args: cobra.MaximumNArgs(1),
|
|
||||||
}
|
|
||||||
runner.FixDocs(name, c)
|
|
||||||
r.Command = c
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func SinkCommand(name string) *cobra.Command {
|
|
||||||
return GetSinkRunner(name).Command
|
|
||||||
}
|
|
||||||
|
|
||||||
// SinkRunner contains the run function
|
|
||||||
type SinkRunner struct {
|
|
||||||
Command *cobra.Command
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *SinkRunner) preRunE(c *cobra.Command, args []string) error {
|
|
||||||
_, err := fmt.Fprintln(os.Stderr, `Command "sink" is deprecated, this will no longer be available in kustomize v5.
|
|
||||||
See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.`)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *SinkRunner) runE(c *cobra.Command, args []string) error {
|
|
||||||
var outputs []kio.Writer
|
|
||||||
if len(args) == 1 {
|
|
||||||
outputs = []kio.Writer{&kio.LocalPackageWriter{PackagePath: args[0]}}
|
|
||||||
} else {
|
|
||||||
outputs = []kio.Writer{&kio.ByteWriter{
|
|
||||||
Writer: c.OutOrStdout(),
|
|
||||||
ClearAnnotations: []string{kioutil.PathAnnotation, kioutil.LegacyPathAnnotation}},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err := kio.Pipeline{
|
|
||||||
Inputs: []kio.Reader{&kio.ByteReader{Reader: c.InOrStdin()}},
|
|
||||||
Outputs: outputs}.Execute()
|
|
||||||
return runner.HandleError(c, err)
|
|
||||||
}
|
|
||||||
@@ -1,321 +0,0 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package commands_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSinkCommand(t *testing.T) {
|
|
||||||
d := t.TempDir()
|
|
||||||
|
|
||||||
r := commands.GetSinkRunner("")
|
|
||||||
r.Command.SetIn(bytes.NewBufferString(`apiVersion: config.kubernetes.io/v1
|
|
||||||
kind: ResourceList
|
|
||||||
items:
|
|
||||||
- kind: Deployment
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: nginx2
|
|
||||||
name: foo
|
|
||||||
annotations:
|
|
||||||
app: nginx2
|
|
||||||
config.kubernetes.io/index: '0'
|
|
||||||
config.kubernetes.io/path: 'f1.yaml'
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
- kind: Service
|
|
||||||
metadata:
|
|
||||||
name: foo
|
|
||||||
annotations:
|
|
||||||
app: nginx
|
|
||||||
config.kubernetes.io/index: '1'
|
|
||||||
config.kubernetes.io/path: 'f1.yaml'
|
|
||||||
spec:
|
|
||||||
selector:
|
|
||||||
app: nginx
|
|
||||||
- apiVersion: v1
|
|
||||||
kind: Abstraction
|
|
||||||
metadata:
|
|
||||||
name: foo
|
|
||||||
annotations:
|
|
||||||
config.kubernetes.io/function: |
|
|
||||||
container:
|
|
||||||
image: gcr.io/example/reconciler:v1
|
|
||||||
config.kubernetes.io/local-config: "true"
|
|
||||||
config.kubernetes.io/index: '0'
|
|
||||||
config.kubernetes.io/path: 'f2.yaml'
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
- apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: nginx
|
|
||||||
name: bar
|
|
||||||
annotations:
|
|
||||||
app: nginx
|
|
||||||
config.kubernetes.io/index: '1'
|
|
||||||
config.kubernetes.io/path: 'f2.yaml'
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
`))
|
|
||||||
r.Command.SetArgs([]string{d})
|
|
||||||
if !assert.NoError(t, r.Command.Execute()) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
actual, err := os.ReadFile(filepath.Join(d, "f1.yaml"))
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
expected := `kind: Deployment
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: nginx2
|
|
||||||
name: foo
|
|
||||||
annotations:
|
|
||||||
app: nginx2
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
---
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: foo
|
|
||||||
annotations:
|
|
||||||
app: nginx
|
|
||||||
spec:
|
|
||||||
selector:
|
|
||||||
app: nginx
|
|
||||||
`
|
|
||||||
if !assert.Equal(t, expected, string(actual)) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
actual, err = os.ReadFile(filepath.Join(d, "f2.yaml"))
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
expected = `apiVersion: v1
|
|
||||||
kind: Abstraction
|
|
||||||
metadata:
|
|
||||||
name: foo
|
|
||||||
annotations:
|
|
||||||
config.kubernetes.io/function: |
|
|
||||||
container:
|
|
||||||
image: gcr.io/example/reconciler:v1
|
|
||||||
config.kubernetes.io/local-config: "true"
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
---
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: nginx
|
|
||||||
name: bar
|
|
||||||
annotations:
|
|
||||||
app: nginx
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
`
|
|
||||||
if !assert.Equal(t, expected, string(actual)) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSinkCommandJSON(t *testing.T) {
|
|
||||||
d := t.TempDir()
|
|
||||||
|
|
||||||
r := commands.GetSinkRunner("")
|
|
||||||
r.Command.SetIn(bytes.NewBufferString(`apiVersion: config.kubernetes.io/v1
|
|
||||||
kind: ResourceList
|
|
||||||
items:
|
|
||||||
- {"kind": "Deployment", "metadata": {"labels": {"app": "nginx2"}, "name": "foo",
|
|
||||||
"annotations": {"app": "nginx2", config.kubernetes.io/index: '0',
|
|
||||||
config.kubernetes.io/path: 'f1.json'}}, "spec": {"replicas": 1}}
|
|
||||||
`))
|
|
||||||
r.Command.SetArgs([]string{d})
|
|
||||||
if !assert.NoError(t, r.Command.Execute()) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
actual, err := os.ReadFile(filepath.Join(d, "f1.json"))
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
expected := `{
|
|
||||||
"kind": "Deployment",
|
|
||||||
"metadata": {
|
|
||||||
"annotations": {
|
|
||||||
"app": "nginx2"
|
|
||||||
},
|
|
||||||
"labels": {
|
|
||||||
"app": "nginx2"
|
|
||||||
},
|
|
||||||
"name": "foo"
|
|
||||||
},
|
|
||||||
"spec": {
|
|
||||||
"replicas": 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
if !assert.Equal(t, expected, string(actual)) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSinkCommand_Stdout(t *testing.T) {
|
|
||||||
// fmt the files
|
|
||||||
out := &bytes.Buffer{}
|
|
||||||
r := commands.GetSinkRunner("")
|
|
||||||
r.Command.SetIn(bytes.NewBufferString(`apiVersion: config.kubernetes.io/v1
|
|
||||||
kind: ResourceList
|
|
||||||
items:
|
|
||||||
- kind: Deployment
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: nginx2
|
|
||||||
name: foo
|
|
||||||
annotations:
|
|
||||||
app: nginx2
|
|
||||||
config.kubernetes.io/index: '0'
|
|
||||||
config.kubernetes.io/path: 'f1.yaml'
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
- kind: Service
|
|
||||||
metadata:
|
|
||||||
name: foo
|
|
||||||
annotations:
|
|
||||||
app: nginx
|
|
||||||
config.kubernetes.io/index: '1'
|
|
||||||
config.kubernetes.io/path: 'f1.yaml'
|
|
||||||
spec:
|
|
||||||
selector:
|
|
||||||
app: nginx
|
|
||||||
- apiVersion: v1
|
|
||||||
kind: Abstraction
|
|
||||||
metadata:
|
|
||||||
name: foo
|
|
||||||
annotations:
|
|
||||||
config.kubernetes.io/function: |
|
|
||||||
container:
|
|
||||||
image: gcr.io/example/reconciler:v1
|
|
||||||
config.kubernetes.io/local-config: "true"
|
|
||||||
config.kubernetes.io/index: '0'
|
|
||||||
config.kubernetes.io/path: 'f2.yaml'
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
- apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: nginx
|
|
||||||
name: bar
|
|
||||||
annotations:
|
|
||||||
app: nginx
|
|
||||||
config.kubernetes.io/index: '1'
|
|
||||||
config.kubernetes.io/path: 'f2.yaml'
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
`))
|
|
||||||
|
|
||||||
r.Command.SetOut(out)
|
|
||||||
r.Command.SetArgs([]string{})
|
|
||||||
if !assert.NoError(t, r.Command.Execute()) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
expected := `kind: Deployment
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: nginx2
|
|
||||||
name: foo
|
|
||||||
annotations:
|
|
||||||
app: nginx2
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
---
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: foo
|
|
||||||
annotations:
|
|
||||||
app: nginx
|
|
||||||
spec:
|
|
||||||
selector:
|
|
||||||
app: nginx
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Abstraction
|
|
||||||
metadata:
|
|
||||||
name: foo
|
|
||||||
annotations:
|
|
||||||
config.kubernetes.io/function: |
|
|
||||||
container:
|
|
||||||
image: gcr.io/example/reconciler:v1
|
|
||||||
config.kubernetes.io/local-config: "true"
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
---
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: nginx
|
|
||||||
name: bar
|
|
||||||
annotations:
|
|
||||||
app: nginx
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
`
|
|
||||||
if !assert.Equal(t, expected, out.String()) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSinkCommandJSON_Stdout(t *testing.T) {
|
|
||||||
// fmt the files
|
|
||||||
out := &bytes.Buffer{}
|
|
||||||
r := commands.GetSinkRunner("")
|
|
||||||
r.Command.SetIn(bytes.NewBufferString(`apiVersion: config.kubernetes.io/v1
|
|
||||||
kind: ResourceList
|
|
||||||
items:
|
|
||||||
- {"kind": "Deployment", "metadata": {"labels": {"app": "nginx2"}, "name": "foo",
|
|
||||||
"annotations": {"app": "nginx2", config.kubernetes.io/index: '0',
|
|
||||||
config.kubernetes.io/path: 'f1.json'}}, "spec": {"replicas": 1}}
|
|
||||||
`))
|
|
||||||
|
|
||||||
r.Command.SetOut(out)
|
|
||||||
r.Command.SetArgs([]string{})
|
|
||||||
if !assert.NoError(t, r.Command.Execute()) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
expected := `{
|
|
||||||
"kind": "Deployment",
|
|
||||||
"metadata": {
|
|
||||||
"annotations": {
|
|
||||||
"app": "nginx2"
|
|
||||||
},
|
|
||||||
"labels": {
|
|
||||||
"app": "nginx2"
|
|
||||||
},
|
|
||||||
"name": "foo"
|
|
||||||
},
|
|
||||||
"spec": {
|
|
||||||
"replicas": 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
if !assert.Equal(t, expected, out.String()) {
|
|
||||||
println(out.String())
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package commands
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/runner"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetSourceRunner returns a command for Source.
|
|
||||||
func GetSourceRunner(name string) *SourceRunner {
|
|
||||||
r := &SourceRunner{}
|
|
||||||
c := &cobra.Command{
|
|
||||||
Use: "source DIR",
|
|
||||||
Short: commands.SourceShort,
|
|
||||||
Long: commands.SourceLong,
|
|
||||||
Example: commands.SourceExamples,
|
|
||||||
RunE: r.runE,
|
|
||||||
PreRunE: r.preRunE,
|
|
||||||
}
|
|
||||||
runner.FixDocs(name, c)
|
|
||||||
c.Flags().StringVar(&r.WrapKind, "wrap-kind", kio.ResourceListKind,
|
|
||||||
"output using this format.")
|
|
||||||
c.Flags().StringVar(&r.WrapApiVersion, "wrap-version", kio.ResourceListAPIVersion,
|
|
||||||
"output using this format.")
|
|
||||||
c.Flags().StringVar(&r.FunctionConfig, "function-config", "",
|
|
||||||
"path to function config.")
|
|
||||||
r.Command = c
|
|
||||||
_ = c.MarkFlagFilename("function-config", "yaml", "json", "yml")
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func SourceCommand(name string) *cobra.Command {
|
|
||||||
return GetSourceRunner(name).Command
|
|
||||||
}
|
|
||||||
|
|
||||||
// SourceRunner contains the run function
|
|
||||||
type SourceRunner struct {
|
|
||||||
WrapKind string
|
|
||||||
WrapApiVersion string
|
|
||||||
FunctionConfig string
|
|
||||||
Command *cobra.Command
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *SourceRunner) preRunE(c *cobra.Command, args []string) error {
|
|
||||||
_, err := fmt.Fprintln(os.Stderr, `Command "source" is deprecated, this will no longer be available in kustomize v5.
|
|
||||||
See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.`)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *SourceRunner) runE(c *cobra.Command, args []string) error {
|
|
||||||
// if there is a function-config specified, emit it
|
|
||||||
var functionConfig *yaml.RNode
|
|
||||||
if r.FunctionConfig != "" {
|
|
||||||
configs, err := kio.LocalPackageReader{PackagePath: r.FunctionConfig}.Read()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if len(configs) != 1 {
|
|
||||||
return fmt.Errorf("expected exactly 1 functionConfig, found %d", len(configs))
|
|
||||||
}
|
|
||||||
functionConfig = configs[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
var outputs []kio.Writer
|
|
||||||
outputs = append(outputs, kio.ByteWriter{
|
|
||||||
Writer: c.OutOrStdout(),
|
|
||||||
KeepReaderAnnotations: true,
|
|
||||||
WrappingKind: r.WrapKind,
|
|
||||||
WrappingAPIVersion: r.WrapApiVersion,
|
|
||||||
FunctionConfig: functionConfig,
|
|
||||||
})
|
|
||||||
|
|
||||||
var inputs []kio.Reader
|
|
||||||
for _, a := range args {
|
|
||||||
inputs = append(inputs, kio.LocalPackageReader{PackagePath: a, MatchFilesGlob: kio.MatchAll})
|
|
||||||
}
|
|
||||||
if len(inputs) == 0 {
|
|
||||||
inputs = []kio.Reader{&kio.ByteReader{Reader: c.InOrStdin()}}
|
|
||||||
}
|
|
||||||
|
|
||||||
err := kio.Pipeline{Inputs: inputs, Outputs: outputs}.Execute()
|
|
||||||
return runner.HandleError(c, err)
|
|
||||||
}
|
|
||||||
@@ -1,319 +0,0 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package commands_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSourceCommand(t *testing.T) {
|
|
||||||
d := t.TempDir()
|
|
||||||
|
|
||||||
err := os.WriteFile(filepath.Join(d, "f1.yaml"), []byte(`
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: nginx2
|
|
||||||
name: foo
|
|
||||||
annotations:
|
|
||||||
app: nginx2
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
---
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: foo
|
|
||||||
annotations:
|
|
||||||
app: nginx
|
|
||||||
spec:
|
|
||||||
selector:
|
|
||||||
app: nginx
|
|
||||||
`), 0600)
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = os.WriteFile(filepath.Join(d, "f2.yaml"), []byte(`
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Abstraction
|
|
||||||
metadata:
|
|
||||||
name: foo
|
|
||||||
annotations:
|
|
||||||
config.kubernetes.io/function: |
|
|
||||||
container:
|
|
||||||
image: gcr.io/example/reconciler:v1
|
|
||||||
config.kubernetes.io/local-config: "true"
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
---
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: nginx
|
|
||||||
name: bar
|
|
||||||
annotations:
|
|
||||||
app: nginx
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
`), 0600)
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// fmt the files
|
|
||||||
b := &bytes.Buffer{}
|
|
||||||
r := commands.GetSourceRunner("")
|
|
||||||
r.Command.SetArgs([]string{d})
|
|
||||||
r.Command.SetOut(b)
|
|
||||||
if !assert.NoError(t, r.Command.Execute()) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if !assert.Equal(t, `apiVersion: config.kubernetes.io/v1
|
|
||||||
kind: ResourceList
|
|
||||||
items:
|
|
||||||
- kind: Deployment
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: nginx2
|
|
||||||
name: foo
|
|
||||||
annotations:
|
|
||||||
app: nginx2
|
|
||||||
config.kubernetes.io/index: '0'
|
|
||||||
config.kubernetes.io/path: 'f1.yaml'
|
|
||||||
internal.config.kubernetes.io/index: '0'
|
|
||||||
internal.config.kubernetes.io/path: 'f1.yaml'
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
- kind: Service
|
|
||||||
metadata:
|
|
||||||
name: foo
|
|
||||||
annotations:
|
|
||||||
app: nginx
|
|
||||||
config.kubernetes.io/index: '1'
|
|
||||||
config.kubernetes.io/path: 'f1.yaml'
|
|
||||||
internal.config.kubernetes.io/index: '1'
|
|
||||||
internal.config.kubernetes.io/path: 'f1.yaml'
|
|
||||||
spec:
|
|
||||||
selector:
|
|
||||||
app: nginx
|
|
||||||
- apiVersion: v1
|
|
||||||
kind: Abstraction
|
|
||||||
metadata:
|
|
||||||
name: foo
|
|
||||||
annotations:
|
|
||||||
config.kubernetes.io/function: |
|
|
||||||
container:
|
|
||||||
image: gcr.io/example/reconciler:v1
|
|
||||||
config.kubernetes.io/local-config: "true"
|
|
||||||
config.kubernetes.io/index: '0'
|
|
||||||
config.kubernetes.io/path: 'f2.yaml'
|
|
||||||
internal.config.kubernetes.io/index: '0'
|
|
||||||
internal.config.kubernetes.io/path: 'f2.yaml'
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
- apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: nginx
|
|
||||||
name: bar
|
|
||||||
annotations:
|
|
||||||
app: nginx
|
|
||||||
config.kubernetes.io/index: '1'
|
|
||||||
config.kubernetes.io/path: 'f2.yaml'
|
|
||||||
internal.config.kubernetes.io/index: '1'
|
|
||||||
internal.config.kubernetes.io/path: 'f2.yaml'
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
`, b.String()) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSourceCommandJSON(t *testing.T) {
|
|
||||||
d := t.TempDir()
|
|
||||||
|
|
||||||
err := os.WriteFile(filepath.Join(d, "f1.json"), []byte(`
|
|
||||||
{
|
|
||||||
"kind": "Deployment",
|
|
||||||
"metadata": {
|
|
||||||
"labels": {
|
|
||||||
"app": "nginx2"
|
|
||||||
},
|
|
||||||
"name": "foo",
|
|
||||||
"annotations": {
|
|
||||||
"app": "nginx2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"spec": {
|
|
||||||
"replicas": 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`), 0600)
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = os.WriteFile(filepath.Join(d, "f2.json"), []byte(`
|
|
||||||
{
|
|
||||||
"apiVersion": "v1",
|
|
||||||
"kind": "Abstraction",
|
|
||||||
"metadata": {
|
|
||||||
"name": "foo",
|
|
||||||
"annotations": {
|
|
||||||
"config.kubernetes.io/function": "container:\n image: gcr.io/example/reconciler:v1\n",
|
|
||||||
"config.kubernetes.io/local-config": "true"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"spec": {
|
|
||||||
"replicas": 3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`), 0600)
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// fmt the files
|
|
||||||
b := &bytes.Buffer{}
|
|
||||||
r := commands.GetSourceRunner("")
|
|
||||||
r.Command.SetArgs([]string{d})
|
|
||||||
r.Command.SetOut(b)
|
|
||||||
if !assert.NoError(t, r.Command.Execute()) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if !assert.Equal(t, `apiVersion: config.kubernetes.io/v1
|
|
||||||
kind: ResourceList
|
|
||||||
items:
|
|
||||||
- {"kind": "Deployment", `+
|
|
||||||
`"metadata": {`+
|
|
||||||
`"labels": {"app": "nginx2"}, `+
|
|
||||||
`"name": "foo", `+
|
|
||||||
`"annotations": {`+
|
|
||||||
`"app": "nginx2", `+
|
|
||||||
`config.kubernetes.io/index: '0', config.kubernetes.io/path: 'f1.json', internal.config.kubernetes.io/index: '0', internal.config.kubernetes.io/path: 'f1.json'}}, `+
|
|
||||||
`"spec": {"replicas": 1}}
|
|
||||||
- {"apiVersion": "v1", "kind": "Abstraction", `+
|
|
||||||
`"metadata": {`+
|
|
||||||
`"name": "foo", `+
|
|
||||||
`"annotations": {`+
|
|
||||||
`"config.kubernetes.io/function": "container:\n image: gcr.io/example/reconciler:v1\n", `+
|
|
||||||
`"config.kubernetes.io/local-config": "true", `+
|
|
||||||
`config.kubernetes.io/index: '0', config.kubernetes.io/path: 'f2.json', internal.config.kubernetes.io/index: '0', internal.config.kubernetes.io/path: 'f2.json'}}, `+
|
|
||||||
`"spec": {"replicas": 3}}
|
|
||||||
`, b.String()) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSourceCommand_Stdin(t *testing.T) {
|
|
||||||
in := bytes.NewBufferString(`
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: nginx2
|
|
||||||
name: foo
|
|
||||||
annotations:
|
|
||||||
app: nginx2
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
---
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: foo
|
|
||||||
annotations:
|
|
||||||
app: nginx
|
|
||||||
spec:
|
|
||||||
selector:
|
|
||||||
app: nginx
|
|
||||||
`)
|
|
||||||
|
|
||||||
out := &bytes.Buffer{}
|
|
||||||
r := commands.GetSourceRunner("")
|
|
||||||
r.Command.SetArgs([]string{})
|
|
||||||
r.Command.SetIn(in)
|
|
||||||
r.Command.SetOut(out)
|
|
||||||
if !assert.NoError(t, r.Command.Execute()) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if !assert.Equal(t, `apiVersion: config.kubernetes.io/v1
|
|
||||||
kind: ResourceList
|
|
||||||
items:
|
|
||||||
- kind: Deployment
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: nginx2
|
|
||||||
name: foo
|
|
||||||
annotations:
|
|
||||||
app: nginx2
|
|
||||||
config.kubernetes.io/index: '0'
|
|
||||||
internal.config.kubernetes.io/index: '0'
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
- kind: Service
|
|
||||||
metadata:
|
|
||||||
name: foo
|
|
||||||
annotations:
|
|
||||||
app: nginx
|
|
||||||
config.kubernetes.io/index: '1'
|
|
||||||
internal.config.kubernetes.io/index: '1'
|
|
||||||
spec:
|
|
||||||
selector:
|
|
||||||
app: nginx
|
|
||||||
`, out.String()) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSourceCommandJSON_Stdin(t *testing.T) {
|
|
||||||
in := bytes.NewBufferString(`
|
|
||||||
{
|
|
||||||
"kind": "Deployment",
|
|
||||||
"metadata": {
|
|
||||||
"labels": {
|
|
||||||
"app": "nginx2"
|
|
||||||
},
|
|
||||||
"name": "foo",
|
|
||||||
"annotations": {
|
|
||||||
"app": "nginx2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"spec": {
|
|
||||||
"replicas": 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`)
|
|
||||||
|
|
||||||
out := &bytes.Buffer{}
|
|
||||||
r := commands.GetSourceRunner("")
|
|
||||||
r.Command.SetArgs([]string{})
|
|
||||||
r.Command.SetIn(in)
|
|
||||||
r.Command.SetOut(out)
|
|
||||||
if !assert.NoError(t, r.Command.Execute()) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if !assert.Equal(t, `apiVersion: config.kubernetes.io/v1
|
|
||||||
kind: ResourceList
|
|
||||||
items:
|
|
||||||
- {"kind": "Deployment", `+
|
|
||||||
`"metadata": {`+
|
|
||||||
`"labels": {"app": "nginx2"}, `+
|
|
||||||
`"name": "foo", `+
|
|
||||||
`"annotations": {`+
|
|
||||||
`"app": "nginx2", `+
|
|
||||||
`config.kubernetes.io/index: '0', internal.config.kubernetes.io/index: '0'}}, `+
|
|
||||||
`"spec": {"replicas": 1}}
|
|
||||||
`, out.String()) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
|
||||||
package runner
|
package runner
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
Reference in New Issue
Block a user