...

Source file src/sigs.k8s.io/cli-utils/pkg/common/path.go

Documentation: sigs.k8s.io/cli-utils/pkg/common

     1  // Copyright 2020 The Kubernetes Authors.
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  package common
     5  
     6  import (
     7  	"fmt"
     8  	"io"
     9  	"os"
    10  	"path/filepath"
    11  
    12  	"k8s.io/cli-runtime/pkg/genericclioptions"
    13  	"k8s.io/klog/v2"
    14  	"sigs.k8s.io/kustomize/kyaml/kio"
    15  	"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
    16  	"sigs.k8s.io/kustomize/kyaml/yaml"
    17  )
    18  
    19  const (
    20  	stdinDash    = "-"
    21  	tmpDirPrefix = "diff-cmd-config"
    22  	fileRegexp   = "config-*.yaml"
    23  )
    24  
    25  func processPaths(paths []string) genericclioptions.FileNameFlags {
    26  	// No arguments means we are reading from StdIn
    27  	fileNameFlags := genericclioptions.FileNameFlags{}
    28  	if len(paths) == 0 {
    29  		fileNames := []string{stdinDash}
    30  		fileNameFlags.Filenames = &fileNames
    31  		return fileNameFlags
    32  	}
    33  
    34  	// Must be a single directory here; set recursive flag.
    35  	t := true
    36  	fileNameFlags.Filenames = &paths
    37  	fileNameFlags.Recursive = &t
    38  	return fileNameFlags
    39  }
    40  
    41  // DemandOneDirectoryOrStdin processes "paths" to ensure the
    42  // single argument in the array is a directory. Returns FileNameFlags
    43  // populated with the directory (recursive flag set), or
    44  // the StdIn dash. An empty array gets treated as StdIn
    45  // (adding dash to the array). Returns an error if more than
    46  // one element in the array or the filepath is not a directory.
    47  func DemandOneDirectory(paths []string) (genericclioptions.FileNameFlags, error) {
    48  	result := genericclioptions.FileNameFlags{}
    49  	if len(paths) == 1 {
    50  		dirPath := paths[0]
    51  		if !IsDir(dirPath) {
    52  			return result, fmt.Errorf("argument '%s' is not but must be a directory", dirPath)
    53  		}
    54  	}
    55  	if len(paths) > 1 {
    56  		return result, fmt.Errorf(
    57  			"specify exactly one directory path argument; rejecting %v", paths)
    58  	}
    59  	result = processPaths(paths)
    60  	return result, nil
    61  }
    62  
    63  func IsDir(dir string) bool {
    64  	if f, err := os.Stat(dir); err == nil {
    65  		if f.Mode().IsDir() {
    66  			return true
    67  		}
    68  	}
    69  	return false
    70  }
    71  
    72  // ExpandPackageDir expands the one package directory entry in the flags to all
    73  // the config file paths recursively. Excludes the inventory object (since
    74  // this object is specially processed). Used for the diff command, so it will
    75  // not always show a diff of the inventory object. Must be called AFTER
    76  // DemandOneDirectory.
    77  func ExpandPackageDir(f genericclioptions.FileNameFlags) (genericclioptions.FileNameFlags, error) {
    78  	if len(*f.Filenames) != 1 {
    79  		return f, fmt.Errorf("expand package directory should pass one package directory. "+
    80  			"Passed the following paths: %v", f.Filenames)
    81  	}
    82  	_, configFilepaths, err := ExpandDir((*f.Filenames)[0])
    83  	if err != nil {
    84  		return f, err
    85  	}
    86  	f.Filenames = &configFilepaths
    87  	return f, nil
    88  }
    89  
    90  // FilterInputFile copies the resource config on stdin into a file
    91  // at the tmpDir, filtering the inventory object. It is the
    92  // responsibility of the caller to clean up the tmpDir. Returns
    93  // an error if one occurs.
    94  func FilterInputFile(in io.Reader, tmpDir string) error {
    95  	// Copy the config from "in" into a local temp file.
    96  	dir, err := os.MkdirTemp("", tmpDirPrefix)
    97  	if err != nil {
    98  		return err
    99  	}
   100  	tmpFile, err := os.CreateTemp(dir, fileRegexp)
   101  	if err != nil {
   102  		return err
   103  	}
   104  	defer os.RemoveAll(dir)
   105  	klog.V(6).Infof("Temp File: %s", tmpFile.Name())
   106  	if _, err := io.Copy(tmpFile, in); err != nil {
   107  		return err
   108  	}
   109  	// Read the config stored locally, parsing into RNodes
   110  	r := kio.LocalPackageReader{PackagePath: dir}
   111  	nodes, err := r.Read()
   112  	if err != nil {
   113  		return err
   114  	}
   115  	klog.V(6).Infof("Num read configs: %d", len(nodes))
   116  	// Filter RNodes to remove the inventory object.
   117  	filteredNodes := []*yaml.RNode{}
   118  	for _, node := range nodes {
   119  		meta, err := node.GetMeta()
   120  		if err != nil {
   121  			continue
   122  		}
   123  		// If object has inventory label, skip it.
   124  		labels := meta.Labels
   125  		if _, exists := labels[InventoryLabel]; !exists {
   126  			filteredNodes = append(filteredNodes, node)
   127  		}
   128  	}
   129  	// Write the remaining configs into a file in the tmpDir
   130  	w := kio.LocalPackageWriter{
   131  		PackagePath:           tmpDir,
   132  		KeepReaderAnnotations: false,
   133  	}
   134  	klog.V(6).Infof("Writing %d configs", len(filteredNodes))
   135  	return w.Write(filteredNodes)
   136  }
   137  
   138  // ExpandDir takes a single package directory as a parameter, and returns
   139  // the inventory template filepath and an array of config file paths. If no
   140  // inventory template file, then the first return value is an empty string.
   141  // Returns an error if one occurred while processing the paths.
   142  func ExpandDir(dir string) (string, []string, error) {
   143  	filepaths := []string{}
   144  	r := kio.LocalPackageReader{PackagePath: dir}
   145  	nodes, err := r.Read()
   146  	if err != nil {
   147  		return "", filepaths, err
   148  	}
   149  	var invFilepath string
   150  	for _, node := range nodes {
   151  		meta, err := node.GetMeta()
   152  		if err != nil {
   153  			continue
   154  		}
   155  		path := meta.Annotations[kioutil.PathAnnotation]
   156  		path = filepath.Join(dir, path)
   157  		// If object has inventory label, skip it.
   158  		labels := meta.Labels
   159  		if _, exists := labels[InventoryLabel]; exists {
   160  			if invFilepath == "" {
   161  				invFilepath = path
   162  			}
   163  			continue
   164  		}
   165  		filepaths = append(filepaths, path)
   166  	}
   167  	return invFilepath, filepaths, nil
   168  }
   169  

View as plain text