• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

kubernetes-sigs / kubebuilder / 16522004449

25 Jul 2025 12:30PM UTC coverage: 64.151%. Remained the same
16522004449

Pull #4898

github

web-flow
Merge branch 'master' into version/refactor-add-tests
Pull Request #4898: ✨ Improve version command output: add runtime fallbacks and unit tests.

2627 of 4095 relevant lines covered (64.15%)

13.59 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

70.65
/pkg/plugin/util/util.go
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) {
3✔
38
        source := []rune("abcdefghijklmnopqrstuvwxyz")
3✔
39
        res := make([]rune, 4)
3✔
40
        for i := range res {
15✔
41
                bi := new(big.Int)
12✔
42
                r, err := rand.Int(rand.Reader, bi.SetInt64(int64(len(source))))
12✔
43
                if err != nil {
12✔
44
                        return "", fmt.Errorf("failed to generate random number: %w", err)
×
45
                }
×
46
                res[i] = source[r.Int64()]
12✔
47
        }
48
        return string(res), nil
3✔
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 {
3✔
54
        var res []string
3✔
55
        elements := strings.Split(output, "\n")
3✔
56
        for _, element := range elements {
11✔
57
                if element != "" {
12✔
58
                        res = append(res, element)
4✔
59
                }
4✔
60
        }
61

62
        return res
3✔
63
}
64

65
// InsertCode searches target content in the file and insert `toInsert` after the target.
66
func InsertCode(filename, target, code string) error {
3✔
67
        //nolint:gosec // false positive
3✔
68
        contents, err := os.ReadFile(filename)
3✔
69
        if err != nil {
3✔
70
                return fmt.Errorf("failed to read file %q: %w", filename, err)
×
71
        }
×
72
        idx := strings.Index(string(contents), target)
3✔
73
        if idx == -1 {
4✔
74
                return fmt.Errorf("string %s not found in %s", target, string(contents))
1✔
75
        }
1✔
76
        out := string(contents[:idx+len(target)]) + code + string(contents[idx+len(target):])
2✔
77
        //nolint:gosec // false positive
2✔
78
        if errWriteFile := os.WriteFile(filename, []byte(out), 0o644); errWriteFile != nil {
2✔
79
                return fmt.Errorf("failed to write file %q: %w", filename, errWriteFile)
×
80
        }
×
81

82
        return nil
2✔
83
}
84

85
// InsertCodeIfNotExist insert code if it does not already exist
86
func InsertCodeIfNotExist(filename, target, code string) error {
2✔
87
        //nolint:gosec // false positive
2✔
88
        contents, err := os.ReadFile(filename)
2✔
89
        if err != nil {
2✔
90
                return fmt.Errorf("failed to read file %q: %w", filename, err)
×
91
        }
×
92

93
        idx := strings.Index(string(contents), code)
2✔
94
        if idx != -1 {
3✔
95
                return nil
1✔
96
        }
1✔
97

98
        return InsertCode(filename, target, code)
1✔
99
}
100

101
// AppendCodeIfNotExist checks if the code does not already exist in the file, and if not, appends it to the end.
102
func AppendCodeIfNotExist(filename, code string) error {
2✔
103
        contents, err := os.ReadFile(filename)
2✔
104
        if err != nil {
2✔
105
                return fmt.Errorf("failed to read file %q: %w", filename, err)
×
106
        }
×
107

108
        if strings.Contains(string(contents), code) {
3✔
109
                return nil // Code already exists, no need to append.
1✔
110
        }
1✔
111

112
        return AppendCodeAtTheEnd(filename, code)
1✔
113
}
114

115
// AppendCodeAtTheEnd appends the given code at the end of the file.
116
func AppendCodeAtTheEnd(filename, code string) error {
1✔
117
        f, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, 0o644)
1✔
118
        if err != nil {
1✔
119
                return fmt.Errorf("failed to open file %q: %w", filename, err)
×
120
        }
×
121
        defer func() {
2✔
122
                if err = f.Close(); err != nil {
1✔
123
                        return
×
124
                }
×
125
        }()
126

127
        if _, errWriteString := f.WriteString(code); errWriteString != nil {
1✔
128
                return fmt.Errorf("failed to write to file %q: %w", filename, errWriteString)
×
129
        }
×
130

131
        return nil
1✔
132
}
133

134
// UncommentCode searches for target in the file and remove the comment prefix
135
// of the target content. The target content may span multiple lines.
136
func UncommentCode(filename, target, prefix string) error {
2✔
137
        //nolint:gosec // false positive
2✔
138
        content, err := os.ReadFile(filename)
2✔
139
        if err != nil {
2✔
140
                return fmt.Errorf("failed to read file %q: %w", filename, err)
×
141
        }
×
142
        strContent := string(content)
2✔
143

2✔
144
        idx := strings.Index(strContent, target)
2✔
145
        if idx < 0 {
3✔
146
                return fmt.Errorf("unable to find the code %q to be uncomment", target)
1✔
147
        }
1✔
148

149
        out := new(bytes.Buffer)
1✔
150
        _, err = out.Write(content[:idx])
1✔
151
        if err != nil {
1✔
152
                return fmt.Errorf("failed to write to file %q: %w", filename, err)
×
153
        }
×
154

155
        scanner := bufio.NewScanner(bytes.NewBufferString(target))
1✔
156
        if !scanner.Scan() {
1✔
157
                return nil
×
158
        }
×
159
        for {
3✔
160
                if _, err = out.WriteString(strings.TrimPrefix(scanner.Text(), prefix)); err != nil {
2✔
161
                        return fmt.Errorf("failed to write to file %q: %w", filename, err)
×
162
                }
×
163
                // Avoid writing a newline in case the previous line was the last in target.
164
                if !scanner.Scan() {
3✔
165
                        break
1✔
166
                }
167
                if _, err = out.WriteString("\n"); err != nil {
1✔
168
                        return fmt.Errorf("failed to write to file %q: %w", filename, err)
×
169
                }
×
170
        }
171

172
        if _, err = out.Write(content[idx+len(target):]); err != nil {
1✔
173
                return fmt.Errorf("failed to write to file %q: %w", filename, err)
×
174
        }
×
175
        //nolint:gosec // false positive
176
        if err = os.WriteFile(filename, out.Bytes(), 0o644); err != nil {
1✔
177
                return fmt.Errorf("failed to write file %q: %w", filename, err)
×
178
        }
×
179

180
        return nil
1✔
181
}
182

183
// CommentCode searches for target in the file and adds the comment prefix
184
// to the target content. The target content may span multiple lines.
185
func CommentCode(filename, target, prefix string) error {
2✔
186
        // Read the file content
2✔
187
        content, err := os.ReadFile(filename)
2✔
188
        if err != nil {
2✔
189
                return fmt.Errorf("failed to read file %q: %w", filename, err)
×
190
        }
×
191
        strContent := string(content)
2✔
192

2✔
193
        // Find the target code to be commented
2✔
194
        idx := strings.Index(strContent, target)
2✔
195
        if idx < 0 {
3✔
196
                return fmt.Errorf("unable to find the code %q to be commented", target)
1✔
197
        }
1✔
198

199
        // Create a buffer to hold the modified content
200
        out := new(bytes.Buffer)
1✔
201
        if _, err = out.Write(content[:idx]); err != nil {
1✔
202
                return fmt.Errorf("failed to write to file %q: %w", filename, err)
×
203
        }
×
204

205
        // Add the comment prefix to each line of the target code
206
        scanner := bufio.NewScanner(bytes.NewBufferString(target))
1✔
207
        for scanner.Scan() {
3✔
208
                if _, err = out.WriteString(prefix + scanner.Text() + "\n"); err != nil {
2✔
209
                        return fmt.Errorf("failed to write to file %q: %w", filename, err)
×
210
                }
×
211
        }
212

213
        // Write the rest of the file content
214
        if _, err = out.Write(content[idx+len(target):]); err != nil {
1✔
215
                return fmt.Errorf("failed to write to file %q: %w", filename, err)
×
216
        }
×
217

218
        // Write the modified content back to the file
219
        if err = os.WriteFile(filename, out.Bytes(), 0o644); err != nil {
1✔
220
                return fmt.Errorf("failed to write file %q: %w", filename, err)
×
221
        }
×
222

223
        return nil
1✔
224
}
225

226
// EnsureExistAndReplace check if the content exists and then do the replacement
227
func EnsureExistAndReplace(input, match, replace string) (string, error) {
2✔
228
        if !strings.Contains(input, match) {
3✔
229
                return "", fmt.Errorf("can't find %q", match)
1✔
230
        }
1✔
231
        return strings.ReplaceAll(input, match, replace), nil
1✔
232
}
233

234
// ReplaceInFile replaces all instances of old with new in the file at path.
235
func ReplaceInFile(path, oldValue, newValue string) error {
2✔
236
        info, err := os.Stat(path)
2✔
237
        if err != nil {
2✔
238
                return fmt.Errorf("failed to stat file %q: %w", path, err)
×
239
        }
×
240
        //nolint:gosec // false positive
241
        b, err := os.ReadFile(path)
2✔
242
        if err != nil {
2✔
243
                return fmt.Errorf("failed to read file %q: %w", path, err)
×
244
        }
×
245
        if !strings.Contains(string(b), oldValue) {
3✔
246
                return errors.New("unable to find the content to be replaced")
1✔
247
        }
1✔
248
        s := strings.ReplaceAll(string(b), oldValue, newValue)
1✔
249
        if err = os.WriteFile(path, []byte(s), info.Mode()); err != nil {
1✔
250
                return fmt.Errorf("failed to write file %q: %w", path, err)
×
251
        }
×
252
        return nil
1✔
253
}
254

255
// ReplaceRegexInFile finds all strings that match `match` and replaces them
256
// with `replace` in the file at path.
257
//
258
// This function is currently unused in the Kubebuilder codebase,
259
// but is used by other projects and may be used in Kubebuilder in the future.
260
func ReplaceRegexInFile(path, match, replace string) error {
3✔
261
        matcher, err := regexp.Compile(match)
3✔
262
        if err != nil {
4✔
263
                return fmt.Errorf("failed to compile regular expression %q: %w", match, err)
1✔
264
        }
1✔
265
        info, err := os.Stat(path)
2✔
266
        if err != nil {
2✔
267
                return fmt.Errorf("failed to stat file %q: %w", path, err)
×
268
        }
×
269
        //nolint:gosec // false positive
270
        b, err := os.ReadFile(path)
2✔
271
        if err != nil {
2✔
272
                return fmt.Errorf("failed to read file %q: %w", path, err)
×
273
        }
×
274
        s := matcher.ReplaceAllString(string(b), replace)
2✔
275
        if s == string(b) {
3✔
276
                return errors.New("unable to find the content to be replaced")
1✔
277
        }
1✔
278

279
        if err = os.WriteFile(path, []byte(s), info.Mode()); err != nil {
1✔
280
                return fmt.Errorf("failed to write file %q: %w", path, err)
×
281
        }
×
282

283
        return nil
1✔
284
}
285

286
// HasFileContentWith check if given `text` can be found in file
287
func HasFileContentWith(path, text string) (bool, error) {
3✔
288
        //nolint:gosec
3✔
289
        contents, err := os.ReadFile(path)
3✔
290
        if err != nil {
3✔
291
                return false, fmt.Errorf("failed to read file %q: %w", path, err)
×
292
        }
×
293

294
        return strings.Contains(string(contents), text), nil
3✔
295
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc