mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-07-17 09:49:13 +00:00
Make recurse logic public
This commit is contained in:
163
cmd/config/runner/runner.go
Normal file
163
cmd/config/runner/runner.go
Normal file
@@ -0,0 +1,163 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
package runner
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"sigs.k8s.io/kustomize/cmd/config/ext"
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
"sigs.k8s.io/kustomize/kyaml/pathutil"
|
||||
)
|
||||
|
||||
// CmdRunner interface holds ExecuteCmd definition which executes respective command's
|
||||
// implementation on single package
|
||||
type CmdRunner interface {
|
||||
ExecuteCmd(w io.Writer, pkgPath string) error
|
||||
}
|
||||
|
||||
// ExecuteCmdOnPkgs struct holds the parameters necessary to
|
||||
// execute the filter command on packages in rootPkgPath
|
||||
type ExecuteCmdOnPkgs struct {
|
||||
RootPkgPath string
|
||||
RecurseSubPackages bool
|
||||
NeedOpenAPI bool
|
||||
CmdRunner CmdRunner
|
||||
Writer io.Writer
|
||||
SkipPkgPathPrint bool
|
||||
}
|
||||
|
||||
// ExecuteCmdOnPkgs takes the function definition for a command to be executed on single package, applies that definition
|
||||
// recursively on all the subpackages present in rootPkgPath if recurseSubPackages is true, else applies the command on rootPkgPath only
|
||||
func (e ExecuteCmdOnPkgs) Execute() error {
|
||||
pkgsPaths, err := pathutil.DirsWithFile(e.RootPkgPath, ext.KRMFileName(), e.RecurseSubPackages)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(pkgsPaths) == 0 {
|
||||
// at this point, there are no openAPI files in the rootPkgPath
|
||||
if e.NeedOpenAPI {
|
||||
// few executions need openAPI file to be present(ex: setters commands), if true throw an error
|
||||
return errors.Errorf("unable to find %q in package %q", ext.KRMFileName(), e.RootPkgPath)
|
||||
}
|
||||
|
||||
// add root path for commands which doesn't need openAPI(ex: annotate, fmt)
|
||||
pkgsPaths = []string{e.RootPkgPath}
|
||||
}
|
||||
|
||||
// for commands which doesn't need openAPI file, make sure that the root package is
|
||||
// included all the times
|
||||
if !e.NeedOpenAPI && !containsString(pkgsPaths, e.RootPkgPath) {
|
||||
pkgsPaths = append([]string{e.RootPkgPath}, pkgsPaths...)
|
||||
}
|
||||
|
||||
for i := range pkgsPaths {
|
||||
pkgPath := pkgsPaths[i]
|
||||
// Add schema present in openAPI file for current package
|
||||
if e.NeedOpenAPI {
|
||||
if err := openapi.AddSchemaFromFile(filepath.Join(pkgPath, ext.KRMFileName())); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if !e.SkipPkgPathPrint {
|
||||
fmt.Fprintf(e.Writer, "%s/\n", pkgPath)
|
||||
}
|
||||
|
||||
err := e.CmdRunner.ExecuteCmd(e.Writer, pkgPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if i != len(pkgsPaths)-1 {
|
||||
fmt.Fprint(e.Writer, "\n")
|
||||
}
|
||||
|
||||
// Delete schema present in openAPI file for current package
|
||||
if e.NeedOpenAPI {
|
||||
if err := openapi.DeleteSchemaInFile(filepath.Join(pkgPath, ext.KRMFileName())); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ParseFieldPath parse a flag value into a field path
|
||||
func ParseFieldPath(path string) ([]string, error) {
|
||||
// fixup '\.' so we don't split on it
|
||||
match := strings.ReplaceAll(path, "\\.", "$$$$")
|
||||
parts := strings.Split(match, ".")
|
||||
for i := range parts {
|
||||
parts[i] = strings.ReplaceAll(parts[i], "$$$$", ".")
|
||||
}
|
||||
|
||||
// split the list index from the list field
|
||||
var newParts []string
|
||||
for i := range parts {
|
||||
if !strings.Contains(parts[i], "[") {
|
||||
newParts = append(newParts, parts[i])
|
||||
continue
|
||||
}
|
||||
p := strings.Split(parts[i], "[")
|
||||
if len(p) != 2 {
|
||||
return nil, fmt.Errorf("unrecognized path element: %s. "+
|
||||
"Should be of the form 'list[field=value]'", parts[i])
|
||||
}
|
||||
p[1] = "[" + p[1]
|
||||
newParts = append(newParts, p[0], p[1])
|
||||
}
|
||||
return newParts, nil
|
||||
}
|
||||
|
||||
func HandleError(c *cobra.Command, err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
if StackOnError {
|
||||
if err, ok := err.(*errors.Error); ok {
|
||||
fmt.Fprintf(os.Stderr, "%s", err.Stack())
|
||||
}
|
||||
}
|
||||
|
||||
if ExitOnError {
|
||||
fmt.Fprintf(c.ErrOrStderr(), "Error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// ExitOnError if true, will cause commands to call os.Exit instead of returning an error.
|
||||
// Used for skipping printing usage on failure.
|
||||
var ExitOnError bool
|
||||
|
||||
// StackOnError if true, will print a stack trace on failure.
|
||||
var StackOnError bool
|
||||
|
||||
const cmdName = "kustomize config"
|
||||
|
||||
// FixDocs replaces instances of old with new in the docs for c
|
||||
func FixDocs(new string, c *cobra.Command) {
|
||||
c.Use = strings.ReplaceAll(c.Use, cmdName, new)
|
||||
c.Short = strings.ReplaceAll(c.Short, cmdName, new)
|
||||
c.Long = strings.ReplaceAll(c.Long, cmdName, new)
|
||||
c.Example = strings.ReplaceAll(c.Example, cmdName, new)
|
||||
}
|
||||
|
||||
// containsString returns true if slice contains s
|
||||
func containsString(slice []string, s string) bool {
|
||||
for _, item := range slice {
|
||||
if item == s {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
184
cmd/config/runner/runner_test.go
Normal file
184
cmd/config/runner/runner_test.go
Normal file
@@ -0,0 +1,184 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package runner
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
)
|
||||
|
||||
func TestExecuteCmdOnPkgs(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
recurse bool
|
||||
pkgPath string
|
||||
needOpenAPI bool
|
||||
errMsg string
|
||||
expectedOut string
|
||||
}{
|
||||
{
|
||||
name: "need_Krmfile_error",
|
||||
recurse: true,
|
||||
needOpenAPI: true,
|
||||
pkgPath: "subpkg1/subdir1",
|
||||
errMsg: `unable to find "Krmfile" in package`,
|
||||
},
|
||||
{
|
||||
name: "Krmfile_not_needed_no_err",
|
||||
recurse: true,
|
||||
needOpenAPI: false,
|
||||
pkgPath: "subpkg1/subdir1",
|
||||
expectedOut: `${baseDir}/subpkg1/subdir1/
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "executeCmd_returns_error",
|
||||
recurse: true,
|
||||
needOpenAPI: false,
|
||||
pkgPath: "subpkg4",
|
||||
expectedOut: `${baseDir}/subpkg4/
|
||||
`,
|
||||
errMsg: `this command returns an error if package has error.txt file`,
|
||||
},
|
||||
{
|
||||
name: "executeCmd_prints_pkgpaths",
|
||||
recurse: true,
|
||||
needOpenAPI: false,
|
||||
pkgPath: "subpkg2",
|
||||
expectedOut: `${baseDir}/subpkg2/
|
||||
|
||||
${baseDir}/subpkg2/subpkg3/
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
dir, err := ioutil.TempDir("", "")
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
err = createTestDirStructure(dir)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
for i := range tests {
|
||||
test := tests[i]
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
actual := &bytes.Buffer{}
|
||||
r := &TestRunner{}
|
||||
e := ExecuteCmdOnPkgs{
|
||||
NeedOpenAPI: test.needOpenAPI,
|
||||
Writer: actual,
|
||||
RootPkgPath: filepath.Join(dir, test.pkgPath),
|
||||
RecurseSubPackages: test.recurse,
|
||||
CmdRunner: r,
|
||||
}
|
||||
err := e.Execute()
|
||||
if test.errMsg == "" {
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
} else {
|
||||
if !assert.Error(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
if !assert.Contains(t, err.Error(), test.errMsg) {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
// normalize path format for windows
|
||||
actualNormalized := strings.Replace(
|
||||
strings.Replace(actual.String(), "\\", "/", -1),
|
||||
"//", "/", -1)
|
||||
|
||||
expected := strings.Replace(test.expectedOut, "${baseDir}", dir+"/", -1)
|
||||
expectedNormalized := strings.Replace(
|
||||
strings.Replace(expected, "\\", "/", -1),
|
||||
"//", "/", -1)
|
||||
if !assert.Equal(t, expectedNormalized, actualNormalized) {
|
||||
t.FailNow()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func createTestDirStructure(dir string) error {
|
||||
/*
|
||||
Adds the folders to the input dir with following structure
|
||||
dir
|
||||
├── subpkg1
|
||||
│ ├── Krmfile
|
||||
│ └── subdir1
|
||||
├── subpkg4
|
||||
│ ├── Krmfile
|
||||
│ └── error.txt
|
||||
└── subpkg2
|
||||
├── subpkg3
|
||||
│ ├── Krmfile
|
||||
│ └── subdir2
|
||||
└── Krmfile
|
||||
*/
|
||||
err := os.MkdirAll(filepath.Join(dir, "subpkg1/subdir1"), 0777|os.ModeDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.MkdirAll(filepath.Join(dir, "subpkg2/subpkg3/subdir2"), 0777|os.ModeDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.MkdirAll(filepath.Join(dir, "subpkg4"), 0777|os.ModeDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ioutil.WriteFile(filepath.Join(dir, "subpkg1", "Krmfile"), []byte(""), 0777)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ioutil.WriteFile(filepath.Join(dir, "subpkg2", "Krmfile"), []byte(""), 0777)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ioutil.WriteFile(filepath.Join(dir, "subpkg2/subpkg3", "Krmfile"), []byte(""), 0777)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ioutil.WriteFile(filepath.Join(dir, "subpkg4", "error.txt"), []byte(""), 0777)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ioutil.WriteFile(filepath.Join(dir, "subpkg4", "Krmfile"), []byte(""), 0777)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ioutil.WriteFile(filepath.Join(dir, "Krmfile"), []byte(""), 0777)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type TestRunner struct{}
|
||||
|
||||
func (r *TestRunner) ExecuteCmd(w io.Writer, pkgPath string) error {
|
||||
children, err := ioutil.ReadDir(pkgPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, child := range children {
|
||||
if child.Name() == "error.txt" {
|
||||
return errors.Errorf("this command returns an error if package has error.txt file")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user