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 }