github.com/wmuizelaar/kpt@v0.0.0-20221018115725-bd564717b2ed/thirdparty/cmdconfig/commands/runner/runner.go (about) 1 // Copyright 2019 The Kubernetes Authors. 2 // SPDX-License-Identifier: Apache-2.0 3 4 package runner 5 6 import ( 7 "context" 8 "fmt" 9 "io" 10 "os" 11 "strings" 12 13 "github.com/GoogleContainerTools/kpt/internal/printer" 14 kptfilev1 "github.com/GoogleContainerTools/kpt/pkg/api/kptfile/v1" 15 "github.com/go-errors/errors" 16 "github.com/spf13/cobra" 17 "sigs.k8s.io/kustomize/kyaml/pathutil" 18 ) 19 20 // CmdRunner interface holds ExecuteCmd definition which executes respective command's 21 // implementation on single package 22 type CmdRunner interface { 23 ExecuteCmd(w io.Writer, pkgPath string) error 24 } 25 26 // ExecuteCmdOnPkgs struct holds the parameters necessary to 27 // execute the filter command on packages in rootPkgPath 28 type ExecuteCmdOnPkgs struct { 29 RootPkgPath string 30 RecurseSubPackages bool 31 NeedOpenAPI bool 32 CmdRunner CmdRunner 33 Writer io.Writer 34 SkipPkgPathPrint bool 35 } 36 37 // ExecuteCmdOnPkgs takes the function definition for a command to be executed on single package, applies that definition 38 // recursively on all the subpackages present in rootPkgPath if recurseSubPackages is true, else applies the command on rootPkgPath only 39 func (e ExecuteCmdOnPkgs) Execute() error { 40 pkgsPaths, err := pathutil.DirsWithFile(e.RootPkgPath, kptfilev1.KptFileName, e.RecurseSubPackages) 41 if err != nil { 42 return err 43 } 44 45 if len(pkgsPaths) == 0 { 46 // at this point, there are no openAPI files in the rootPkgPath 47 if e.NeedOpenAPI { 48 // few executions need openAPI file to be present(ex: setters commands), if true throw an error 49 return errors.Errorf("unable to find %q in package %q", kptfilev1.KptFileName, e.RootPkgPath) 50 } 51 52 // add root path for commands which doesn't need openAPI(ex: annotate, fmt) 53 pkgsPaths = []string{e.RootPkgPath} 54 } 55 56 // for commands which doesn't need openAPI file, make sure that the root package is 57 // included all the times 58 if !e.NeedOpenAPI && !containsString(pkgsPaths, e.RootPkgPath) { 59 pkgsPaths = append([]string{e.RootPkgPath}, pkgsPaths...) 60 } 61 62 for i := range pkgsPaths { 63 err := e.processPkg(pkgsPaths[i]) 64 if err != nil { 65 return err 66 } 67 if i != len(pkgsPaths)-1 { 68 fmt.Fprint(e.Writer, "\n") 69 } 70 } 71 return nil 72 } 73 74 func (e ExecuteCmdOnPkgs) processPkg(pkgPath string) error { 75 // Add schema present in openAPI file for current package 76 if !e.SkipPkgPathPrint { 77 fmt.Fprintf(e.Writer, "%s/\n", pkgPath) 78 } 79 80 return e.CmdRunner.ExecuteCmd(e.Writer, pkgPath) 81 } 82 83 // ParseFieldPath parse a flag value into a field path 84 func ParseFieldPath(path string) ([]string, error) { 85 // fixup '\.' so we don't split on it 86 match := strings.ReplaceAll(path, "\\.", "$$$$") 87 parts := strings.Split(match, ".") 88 for i := range parts { 89 parts[i] = strings.ReplaceAll(parts[i], "$$$$", ".") 90 } 91 92 // split the list index from the list field 93 var newParts []string 94 for i := range parts { 95 if !strings.Contains(parts[i], "[") { 96 newParts = append(newParts, parts[i]) 97 continue 98 } 99 p := strings.Split(parts[i], "[") 100 if len(p) != 2 { 101 return nil, fmt.Errorf("unrecognized path element: %s. "+ 102 "Should be of the form 'list[field=value]'", parts[i]) 103 } 104 p[1] = "[" + p[1] 105 newParts = append(newParts, p[0], p[1]) 106 } 107 return newParts, nil 108 } 109 110 func HandleError(ctx context.Context, err error) error { 111 if err == nil { 112 return nil 113 } 114 pr := printer.FromContextOrDie(ctx) 115 if StackOnError { 116 if err, ok := err.(*errors.Error); ok { 117 pr.Printf("%s", err.Stack()) 118 } 119 } 120 121 if ExitOnError { 122 pr.Printf("Error: %v\n", err) 123 os.Exit(1) 124 } 125 return err 126 } 127 128 // ExitOnError if true, will cause commands to call os.Exit instead of returning an error. 129 // Used for skipping printing usage on failure. 130 var ExitOnError bool 131 132 // StackOnError if true, will print a stack trace on failure. 133 var StackOnError bool 134 135 const cmdName = "kustomize config" 136 137 // FixDocs replaces instances of old with new in the docs for c 138 func FixDocs(new string, c *cobra.Command) { 139 c.Use = strings.ReplaceAll(c.Use, cmdName, new) 140 c.Short = strings.ReplaceAll(c.Short, cmdName, new) 141 c.Long = strings.ReplaceAll(c.Long, cmdName, new) 142 c.Example = strings.ReplaceAll(c.Example, cmdName, new) 143 } 144 145 // containsString returns true if slice contains s 146 func containsString(slice []string, s string) bool { 147 for _, item := range slice { 148 if item == s { 149 return true 150 } 151 } 152 return false 153 }