sigs.k8s.io/kubebuilder/v3@v3.14.0/pkg/plugin/util/util.go (about)

     1  /*
     2  Copyright 2019 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package util
    18  
    19  import (
    20  	"bufio"
    21  	"bytes"
    22  	"crypto/rand"
    23  	"errors"
    24  	"fmt"
    25  	"math/big"
    26  	"os"
    27  	"regexp"
    28  	"strings"
    29  )
    30  
    31  const (
    32  	// KubebuilderBinName define the name of the kubebuilder binary to be used in the tests
    33  	KubebuilderBinName = "kubebuilder"
    34  )
    35  
    36  // RandomSuffix returns a 4-letter string.
    37  func RandomSuffix() (string, error) {
    38  	source := []rune("abcdefghijklmnopqrstuvwxyz")
    39  	res := make([]rune, 4)
    40  	for i := range res {
    41  		bi := new(big.Int)
    42  		r, err := rand.Int(rand.Reader, bi.SetInt64(int64(len(source))))
    43  		if err != nil {
    44  			return "", err
    45  		}
    46  		res[i] = source[r.Int64()]
    47  	}
    48  	return string(res), nil
    49  }
    50  
    51  // GetNonEmptyLines converts given command output string into individual objects
    52  // according to line breakers, and ignores the empty elements in it.
    53  func GetNonEmptyLines(output string) []string {
    54  	var res []string
    55  	elements := strings.Split(output, "\n")
    56  	for _, element := range elements {
    57  		if element != "" {
    58  			res = append(res, element)
    59  		}
    60  	}
    61  
    62  	return res
    63  }
    64  
    65  // InsertCode searches target content in the file and insert `toInsert` after the target.
    66  func InsertCode(filename, target, code string) error {
    67  	// false positive
    68  	// nolint:gosec
    69  	contents, err := os.ReadFile(filename)
    70  	if err != nil {
    71  		return err
    72  	}
    73  	idx := strings.Index(string(contents), target)
    74  	if idx == -1 {
    75  		return fmt.Errorf("string %s not found in %s", target, string(contents))
    76  	}
    77  	out := string(contents[:idx+len(target)]) + code + string(contents[idx+len(target):])
    78  	// false positive
    79  	// nolint:gosec
    80  	return os.WriteFile(filename, []byte(out), 0644)
    81  }
    82  
    83  // UncommentCode searches for target in the file and remove the comment prefix
    84  // of the target content. The target content may span multiple lines.
    85  func UncommentCode(filename, target, prefix string) error {
    86  	// false positive
    87  	// nolint:gosec
    88  	content, err := os.ReadFile(filename)
    89  	if err != nil {
    90  		return err
    91  	}
    92  	strContent := string(content)
    93  
    94  	idx := strings.Index(strContent, target)
    95  	if idx < 0 {
    96  		return fmt.Errorf("unable to find the code %s to be uncomment", target)
    97  	}
    98  
    99  	out := new(bytes.Buffer)
   100  	_, err = out.Write(content[:idx])
   101  	if err != nil {
   102  		return err
   103  	}
   104  
   105  	scanner := bufio.NewScanner(bytes.NewBufferString(target))
   106  	if !scanner.Scan() {
   107  		return nil
   108  	}
   109  	for {
   110  		_, err := out.WriteString(strings.TrimPrefix(scanner.Text(), prefix))
   111  		if err != nil {
   112  			return err
   113  		}
   114  		// Avoid writing a newline in case the previous line was the last in target.
   115  		if !scanner.Scan() {
   116  			break
   117  		}
   118  		if _, err := out.WriteString("\n"); err != nil {
   119  			return err
   120  		}
   121  	}
   122  
   123  	_, err = out.Write(content[idx+len(target):])
   124  	if err != nil {
   125  		return err
   126  	}
   127  	// false positive
   128  	// nolint:gosec
   129  	return os.WriteFile(filename, out.Bytes(), 0644)
   130  }
   131  
   132  // ImplementWebhooks will mock an webhook data
   133  func ImplementWebhooks(filename string) error {
   134  	// false positive
   135  	// nolint:gosec
   136  	bs, err := os.ReadFile(filename)
   137  	if err != nil {
   138  		return err
   139  	}
   140  	str := string(bs)
   141  
   142  	str, err = EnsureExistAndReplace(
   143  		str,
   144  		"import (",
   145  		`import (
   146  	"errors"`)
   147  	if err != nil {
   148  		return err
   149  	}
   150  
   151  	// implement defaulting webhook logic
   152  	str, err = EnsureExistAndReplace(
   153  		str,
   154  		"// TODO(user): fill in your defaulting logic.",
   155  		`if r.Spec.Count == 0 {
   156  		r.Spec.Count = 5
   157  	}`)
   158  	if err != nil {
   159  		return err
   160  	}
   161  
   162  	// implement validation webhook logic
   163  	str, err = EnsureExistAndReplace(
   164  		str,
   165  		"// TODO(user): fill in your validation logic upon object creation.",
   166  		`if r.Spec.Count < 0 {
   167  		return nil, errors.New(".spec.count must >= 0")
   168  	}`)
   169  	if err != nil {
   170  		return err
   171  	}
   172  	str, err = EnsureExistAndReplace(
   173  		str,
   174  		"// TODO(user): fill in your validation logic upon object update.",
   175  		`if r.Spec.Count < 0 {
   176  		return nil, errors.New(".spec.count must >= 0")
   177  	}`)
   178  	if err != nil {
   179  		return err
   180  	}
   181  	// false positive
   182  	// nolint:gosec
   183  	return os.WriteFile(filename, []byte(str), 0644)
   184  }
   185  
   186  // EnsureExistAndReplace check if the content exists and then do the replace
   187  func EnsureExistAndReplace(input, match, replace string) (string, error) {
   188  	if !strings.Contains(input, match) {
   189  		return "", fmt.Errorf("can't find %q", match)
   190  	}
   191  	return strings.Replace(input, match, replace, -1), nil
   192  }
   193  
   194  func HasFragment(path, target string) (bool, error) {
   195  	_, err := os.Stat(path)
   196  	if err != nil {
   197  		return false, err
   198  	}
   199  
   200  	// false positive
   201  	// nolint:gosec
   202  	b, err := os.ReadFile(path)
   203  	if err != nil {
   204  		return false, err
   205  	}
   206  
   207  	if !strings.Contains(string(b), target) {
   208  		return false, nil
   209  	}
   210  	return true, nil
   211  }
   212  
   213  // ReplaceInFile replaces all instances of old with new in the file at path.
   214  func ReplaceInFile(path, old, new string) error {
   215  	info, err := os.Stat(path)
   216  	if err != nil {
   217  		return err
   218  	}
   219  	// false positive
   220  	// nolint:gosec
   221  	b, err := os.ReadFile(path)
   222  	if err != nil {
   223  		return err
   224  	}
   225  	if !strings.Contains(string(b), old) {
   226  		return errors.New("unable to find the content to be replaced")
   227  	}
   228  	s := strings.Replace(string(b), old, new, -1)
   229  	err = os.WriteFile(path, []byte(s), info.Mode())
   230  	if err != nil {
   231  		return err
   232  	}
   233  	return nil
   234  }
   235  
   236  // ReplaceRegexInFile finds all strings that match `match` and replaces them
   237  // with `replace` in the file at path.
   238  func ReplaceRegexInFile(path, match, replace string) error {
   239  	matcher, err := regexp.Compile(match)
   240  	if err != nil {
   241  		return err
   242  	}
   243  	info, err := os.Stat(path)
   244  	if err != nil {
   245  		return err
   246  	}
   247  	// false positive
   248  	// nolint:gosec
   249  	b, err := os.ReadFile(path)
   250  	if err != nil {
   251  		return err
   252  	}
   253  	s := matcher.ReplaceAllString(string(b), replace)
   254  	if s == string(b) {
   255  		return errors.New("unable to find the content to be replaced")
   256  	}
   257  	err = os.WriteFile(path, []byte(s), info.Mode())
   258  	if err != nil {
   259  		return err
   260  	}
   261  	return nil
   262  }
   263  
   264  // HasFileContentWith check if given `text` can be found in file
   265  func HasFileContentWith(path, text string) (bool, error) {
   266  	// nolint:gosec
   267  	contents, err := os.ReadFile(path)
   268  	if err != nil {
   269  		return false, err
   270  	}
   271  
   272  	return strings.Contains(string(contents), text), nil
   273  }