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  }