diff --git a/cmd/config/internal/commands/cmdcreatesetter.go b/cmd/config/internal/commands/cmdcreatesetter.go index 7f21fdafb..3444eb416 100644 --- a/cmd/config/internal/commands/cmdcreatesetter.go +++ b/cmd/config/internal/commands/cmdcreatesetter.go @@ -6,6 +6,7 @@ package commands import ( "encoding/json" "fmt" + "io" "io/ioutil" "path/filepath" "strings" @@ -16,8 +17,6 @@ import ( "sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands" "sigs.k8s.io/kustomize/kyaml/errors" "sigs.k8s.io/kustomize/kyaml/kio" - "sigs.k8s.io/kustomize/kyaml/openapi" - "sigs.k8s.io/kustomize/kyaml/pathutil" "sigs.k8s.io/kustomize/kyaml/setters" "sigs.k8s.io/kustomize/kyaml/setters2/settersutil" ) @@ -82,7 +81,7 @@ type CreateSetterRunner struct { } func (r *CreateSetterRunner) runE(c *cobra.Command, args []string) error { - return handleError(c, r.set(c, args)) + return handleError(c, r.createSetter(c, args)) } func (r *CreateSetterRunner) preRunE(c *cobra.Command, args []string) error { @@ -178,59 +177,18 @@ func (r *CreateSetterRunner) processSchema() error { return nil } -func (r *CreateSetterRunner) set(c *cobra.Command, args []string) error { +func (r *CreateSetterRunner) createSetter(c *cobra.Command, args []string) error { if setterVersion == "v2" { - openAPIFileName, err := ext.OpenAPIFileName() + e := executeCmdOnPkgs{ + needOpenAPI: true, + writer: c.OutOrStdout(), + rootPkgPath: args[0], + recurseSubPackages: r.CreateSetter.RecurseSubPackages, + cmdRunner: r, + } + err := e.execute() if err != nil { - return err - } - - r.CreateSetter.OpenAPIFileName = openAPIFileName - resourcePackagesPaths, err := pathutil.DirsWithFile(args[0], openAPIFileName, r.CreateSetter.RecurseSubPackages) - if err != nil { - return err - } - - if len(resourcePackagesPaths) == 0 { - return errors.Errorf("unable to find %q in package %q", r.CreateSetter.OpenAPIFileName, args[0]) - } - for _, resourcesPath := range resourcePackagesPaths { - r.CreateSetter = settersutil.SetterCreator{ - Name: r.CreateSetter.Name, - SetBy: r.CreateSetter.SetBy, - Description: r.CreateSetter.Description, - Type: r.CreateSetter.Type, - Schema: r.CreateSetter.Schema, - FieldName: r.CreateSetter.FieldName, - FieldValue: r.CreateSetter.FieldValue, - Required: r.CreateSetter.Required, - RecurseSubPackages: r.CreateSetter.RecurseSubPackages, - OpenAPIFileName: openAPIFileName, - OpenAPIPath: filepath.Join(resourcesPath, openAPIFileName), - ResourcesPath: resourcesPath, - } - - // Add schema present in openAPI file for current package - if err := openapi.AddSchemaFromFile(r.CreateSetter.OpenAPIPath); err != nil { - return err - } - err := r.CreateSetter.Create() - if err != nil { - // return err if there is only package - if len(resourcePackagesPaths) == 1 { - return err - } else { - // print error message and continue if there are multiple packages to set - fmt.Fprintf(c.OutOrStdout(), "%s in package %q\n\n", err.Error(), r.CreateSetter.ResourcesPath) - } - } else { - fmt.Fprintf(c.OutOrStdout(), "created setter %q in package %q\n\n", r.CreateSetter.Name, r.CreateSetter.ResourcesPath) - } - - // Delete schema present in openAPI file for current package - if err := openapi.DeleteSchemaInFile(r.CreateSetter.OpenAPIPath); err != nil { - return err - } + return handleError(c, err) } return nil } @@ -246,6 +204,41 @@ func (r *CreateSetterRunner) set(c *cobra.Command, args []string) error { return nil } +func (r *CreateSetterRunner) executeCmd(w io.Writer, pkgPath string) error { + openAPIFileName, err := ext.OpenAPIFileName() + if err != nil { + return err + } + r.CreateSetter = settersutil.SetterCreator{ + Name: r.CreateSetter.Name, + SetBy: r.CreateSetter.SetBy, + Description: r.CreateSetter.Description, + Type: r.CreateSetter.Type, + Schema: r.CreateSetter.Schema, + FieldName: r.CreateSetter.FieldName, + FieldValue: r.CreateSetter.FieldValue, + Required: r.CreateSetter.Required, + RecurseSubPackages: r.CreateSetter.RecurseSubPackages, + OpenAPIFileName: openAPIFileName, + OpenAPIPath: filepath.Join(pkgPath, openAPIFileName), + ResourcesPath: pkgPath, + } + + err = r.CreateSetter.Create() + if err != nil { + // return err if RecurseSubPackages is false + if !r.CreateSetter.RecurseSubPackages { + return err + } else { + // print error message and continue if RecurseSubPackages is true + fmt.Fprintf(w, "%s in package %q\n\n", err.Error(), pkgPath) + } + } else { + fmt.Fprintf(w, "created setter %q in package %q\n\n", r.CreateSetter.Name, pkgPath) + } + return nil +} + // schemaFromFile reads the contents from schemaPath and returns schema func schemaFromFile(schemaPath string) (*spec.Schema, error) { sc := &spec.Schema{} diff --git a/cmd/config/internal/commands/cmdcreatesubstitution.go b/cmd/config/internal/commands/cmdcreatesubstitution.go index 269002af4..469f818f3 100644 --- a/cmd/config/internal/commands/cmdcreatesubstitution.go +++ b/cmd/config/internal/commands/cmdcreatesubstitution.go @@ -5,13 +5,11 @@ package commands import ( "fmt" + "io" "path/filepath" "github.com/spf13/cobra" "sigs.k8s.io/kustomize/cmd/config/ext" - "sigs.k8s.io/kustomize/kyaml/errors" - "sigs.k8s.io/kustomize/kyaml/openapi" - "sigs.k8s.io/kustomize/kyaml/pathutil" "sigs.k8s.io/kustomize/kyaml/setters2/settersutil" ) @@ -51,54 +49,48 @@ type CreateSubstitutionRunner struct { } func (r *CreateSubstitutionRunner) runE(c *cobra.Command, args []string) error { + e := executeCmdOnPkgs{ + needOpenAPI: true, + writer: c.OutOrStdout(), + rootPkgPath: args[0], + recurseSubPackages: r.CreateSubstitution.RecurseSubPackages, + cmdRunner: r, + } + err := e.execute() + if err != nil { + return handleError(c, err) + } + + return nil +} + +func (r *CreateSubstitutionRunner) executeCmd(w io.Writer, pkgPath string) error { openAPIFileName, err := ext.OpenAPIFileName() 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: openAPIFileName, + OpenAPIPath: filepath.Join(pkgPath, openAPIFileName), + ResourcesPath: pkgPath, + } - r.CreateSubstitution.OpenAPIFileName = openAPIFileName - resourcePackagesPaths, err := pathutil.DirsWithFile(args[0], openAPIFileName, r.CreateSubstitution.RecurseSubPackages) + err = r.CreateSubstitution.Create() if err != nil { - return err - } - - if len(resourcePackagesPaths) == 0 { - return errors.Errorf("unable to find %q in package %q", r.CreateSubstitution.OpenAPIFileName, args[0]) - } - - for _, resourcesPath := range resourcePackagesPaths { - 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: openAPIFileName, - OpenAPIPath: filepath.Join(resourcesPath, openAPIFileName), - ResourcesPath: resourcesPath, - } - - // Add schema present in openAPI file for current package - if err := openapi.AddSchemaFromFile(r.CreateSubstitution.OpenAPIPath); err != nil { + // return err if RecurseSubPackages is false + if !r.CreateSubstitution.RecurseSubPackages { return err - } - err := r.CreateSubstitution.Create() - if err != nil { - // return err if there is only package - if len(resourcePackagesPaths) == 1 { - return err - } else { - // print error message and continue if there are multiple packages to set - fmt.Fprintf(c.OutOrStdout(), "%s in package %q\n\n", err.Error(), r.CreateSubstitution.ResourcesPath) - } } else { - fmt.Fprintf(c.OutOrStdout(), "created substitution %q in package %q\n\n", r.CreateSubstitution.Name, r.CreateSubstitution.ResourcesPath) - } - - // Delete schema present in openAPI file for current package - if err := openapi.DeleteSchemaInFile(r.CreateSubstitution.OpenAPIPath); err != nil { - return err + // print error message and continue if RecurseSubPackages is true + fmt.Fprintf(w, "%s in package %q\n\n", err.Error(), pkgPath) } + } else { + fmt.Fprintf(w, "created substitution %q in package %q\n\n", r.CreateSubstitution.Name, pkgPath) } return nil } diff --git a/cmd/config/internal/commands/cmdlistsetters.go b/cmd/config/internal/commands/cmdlistsetters.go index bbdaefa8c..71691d599 100644 --- a/cmd/config/internal/commands/cmdlistsetters.go +++ b/cmd/config/internal/commands/cmdlistsetters.go @@ -14,9 +14,7 @@ import ( "github.com/spf13/cobra" "sigs.k8s.io/kustomize/cmd/config/ext" "sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands" - "sigs.k8s.io/kustomize/kyaml/errors" "sigs.k8s.io/kustomize/kyaml/fieldmeta" - "sigs.k8s.io/kustomize/kyaml/pathutil" "sigs.k8s.io/kustomize/kyaml/setters" "sigs.k8s.io/kustomize/kyaml/setters2" ) @@ -69,48 +67,51 @@ func (r *ListSettersRunner) preRunE(c *cobra.Command, args []string) error { func (r *ListSettersRunner) runE(c *cobra.Command, args []string) error { if setterVersion == "v2" { - openAPIFileName, err := ext.OpenAPIFileName() - if err != nil { - return err + e := executeCmdOnPkgs{ + needOpenAPI: true, + writer: c.OutOrStdout(), + rootPkgPath: args[0], + recurseSubPackages: r.RecurseSubPackages, + cmdRunner: r, } - resourcePaths, err := pathutil.DirsWithFile(args[0], openAPIFileName, r.RecurseSubPackages) + err := e.execute() if err != nil { - return err - } - if len(resourcePaths) == 0 { - return errors.Errorf("unable to find %s in package %s", openAPIFileName, args[0]) - } - - // list setters for all the subpackages with openAPI file paths - for _, resourcePath := range resourcePaths { - r.List = setters2.List{ - Name: r.List.Name, - OpenAPIFileName: openAPIFileName, - } - openAPIPath := filepath.Join(resourcePath, openAPIFileName) - fmt.Fprintf(c.OutOrStdout(), "\n%s/\n", resourcePath) - if err := r.ListSetters(c, openAPIPath, resourcePath); err != nil { - return err - } - if r.IncludeSubst { - if err := r.ListSubstitutions(c, openAPIPath); err != nil { - return err - } - } + return handleError(c, err) } return nil } - return handleError(c, lookup(r.Lookup, c, args)) } -func (r *ListSettersRunner) ListSetters(c *cobra.Command, openAPIPath, resourcePath string) error { +func (r *ListSettersRunner) executeCmd(w io.Writer, pkgPath string) error { + openAPIFileName, err := ext.OpenAPIFileName() + if err != nil { + return err + } + r.List = setters2.List{ + Name: r.List.Name, + OpenAPIFileName: openAPIFileName, + } + openAPIPath := filepath.Join(pkgPath, openAPIFileName) + fmt.Fprintf(w, "\n%s/\n", pkgPath) + 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(c.OutOrStdout(), r.Markdown) + table := newTable(w, r.Markdown) table.SetHeader([]string{"NAME", "VALUE", "SET BY", "DESCRIPTION", "COUNT", "REQUIRED"}) for i := range r.List.Setters { s := r.List.Setters[i] @@ -141,12 +142,12 @@ func (r *ListSettersRunner) ListSetters(c *cobra.Command, openAPIPath, resourceP return nil } -func (r *ListSettersRunner) ListSubstitutions(c *cobra.Command, openAPIPath string) error { +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(c.OutOrStdout(), r.Markdown) + table := newTable(w, r.Markdown) b := tablewriter.Border{Top: true} table.SetBorders(b) diff --git a/cmd/config/internal/commands/cmdset.go b/cmd/config/internal/commands/cmdset.go index d3c106845..de1ab9ede 100644 --- a/cmd/config/internal/commands/cmdset.go +++ b/cmd/config/internal/commands/cmdset.go @@ -5,6 +5,7 @@ package commands import ( "fmt" + "io" "os" "path/filepath" @@ -14,7 +15,6 @@ import ( "sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands" "sigs.k8s.io/kustomize/kyaml/errors" "sigs.k8s.io/kustomize/kyaml/kio" - "sigs.k8s.io/kustomize/kyaml/pathutil" "sigs.k8s.io/kustomize/kyaml/setters" "sigs.k8s.io/kustomize/kyaml/setters2/settersutil" ) @@ -135,46 +135,16 @@ func (r *SetRunner) preRunE(c *cobra.Command, args []string) error { func (r *SetRunner) runE(c *cobra.Command, args []string) error { if setterVersion == "v2" { - openAPIFileName, err := ext.OpenAPIFileName() + e := executeCmdOnPkgs{ + needOpenAPI: true, + writer: c.OutOrStdout(), + rootPkgPath: args[0], + recurseSubPackages: r.Set.RecurseSubPackages, + cmdRunner: r, + } + err := e.execute() if err != nil { - return err - } - - r.Set.OpenAPIFileName = openAPIFileName - resourcePackagesPaths, err := pathutil.DirsWithFile(args[0], openAPIFileName, r.Set.RecurseSubPackages) - if err != nil { - return err - } - - if len(resourcePackagesPaths) == 0 { - return errors.Errorf("unable to find %q in package %q", r.Set.OpenAPIFileName, args[0]) - } - - for _, resourcesPath := range resourcePackagesPaths { - r.Set = settersutil.FieldSetter{ - Name: r.Set.Name, - Value: r.Set.Value, - ListValues: r.Set.ListValues, - Description: r.Set.Description, - SetBy: r.Set.SetBy, - Count: 0, - OpenAPIPath: filepath.Join(resourcesPath, openAPIFileName), - OpenAPIFileName: openAPIFileName, - ResourcesPath: resourcesPath, - RecurseSubPackages: r.Set.RecurseSubPackages, - } - count, err := r.Set.Set() - if err != nil { - // return err if there is only package - if len(resourcePackagesPaths) == 1 { - return err - } else { - // print error message and continue if there are multiple packages to set - fmt.Fprintf(c.OutOrStdout(), "%s in package %q\n", err.Error(), r.Set.ResourcesPath) - } - } else { - fmt.Fprintf(c.OutOrStdout(), "set %d fields in package %q\n", count, r.Set.ResourcesPath) - } + return handleError(c, err) } return nil } @@ -184,6 +154,38 @@ func (r *SetRunner) runE(c *cobra.Command, args []string) error { return handleError(c, lookup(r.Lookup, c, args)) } +func (r *SetRunner) executeCmd(w io.Writer, pkgPath string) error { + openAPIFileName, err := ext.OpenAPIFileName() + if err != nil { + return err + } + r.Set = settersutil.FieldSetter{ + Name: r.Set.Name, + Value: r.Set.Value, + ListValues: r.Set.ListValues, + Description: r.Set.Description, + SetBy: r.Set.SetBy, + Count: 0, + OpenAPIPath: filepath.Join(pkgPath, openAPIFileName), + OpenAPIFileName: openAPIFileName, + ResourcesPath: pkgPath, + RecurseSubPackages: r.Set.RecurseSubPackages, + } + count, err := r.Set.Set() + if err != nil { + // return err if RecurseSubPackages is false + if !r.Set.RecurseSubPackages { + return err + } else { + // print error message and continue if RecurseSubPackages is true + fmt.Fprintf(w, "%s in package %q\n", err.Error(), r.Set.ResourcesPath) + } + } else { + fmt.Fprintf(w, "set %d fields in package %q\n", count, r.Set.ResourcesPath) + } + return nil +} + func lookup(l setters.LookupSetters, c *cobra.Command, args []string) error { // lookup the setters err := kio.Pipeline{ diff --git a/cmd/config/internal/commands/util.go b/cmd/config/internal/commands/util.go index 87b2ede87..a76327d89 100644 --- a/cmd/config/internal/commands/util.go +++ b/cmd/config/internal/commands/util.go @@ -5,13 +5,81 @@ package commands 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 +} + +// 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 { + openAPIFileName, err := ext.OpenAPIFileName() + if err != nil { + return err + } + + pkgsPaths, err := pathutil.DirsWithFile(e.rootPkgPath, openAPIFileName, 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", openAPIFileName, e.rootPkgPath) + } + + // add root path for commands which doesn't need openAPI(ex: annotate, fmt) + pkgsPaths = []string{e.rootPkgPath} + } + + for _, pkgPath := range pkgsPaths { + // Add schema present in openAPI file for current package + if e.needOpenAPI { + if err := openapi.AddSchemaFromFile(filepath.Join(pkgPath, openAPIFileName)); err != nil { + return err + } + } + + err := e.cmdRunner.executeCmd(e.writer, pkgPath) + if err != nil { + return err + } + + // Delete schema present in openAPI file for current package + if e.needOpenAPI { + if err := openapi.DeleteSchemaInFile(filepath.Join(pkgPath, openAPIFileName)); 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 diff --git a/cmd/config/internal/commands/util_test.go b/cmd/config/internal/commands/util_test.go new file mode 100644 index 000000000..df3a7b7ad --- /dev/null +++ b/cmd/config/internal/commands/util_test.go @@ -0,0 +1,179 @@ +package commands + +import ( + "bytes" + "fmt" + "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", + 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, strings.TrimSpace(expectedNormalized), strings.TrimSpace(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") + } + } + fmt.Fprintf(w, "%s\n", pkgPath) + return nil +}