@@ -24,6 +24,7 @@ import (
24
24
"os/exec"
25
25
"path/filepath"
26
26
"runtime"
27
+ "strings"
27
28
28
29
. "github.com/onsi/ginkgo/v2"
29
30
. "github.com/onsi/gomega"
@@ -33,12 +34,14 @@ import (
33
34
)
34
35
35
36
const (
36
- fromVersion = "v4.5.2"
37
- toVersion = "v4.6.0"
37
+ fromVersion = "v4.5.2"
38
+ toVersion = "v4.6.0"
39
+ toVersionWithConflict = "v4.7.0"
38
40
39
41
// Binary patterns for cleanup
40
- binFromVersionPath = "/tmp/kubebuilder" + fromVersion + "-*"
41
- pathBinToVersion = "/tmp/kubebuilder" + toVersion + "-*"
42
+ binFromVersionPath = "/tmp/kubebuilder" + fromVersion + "-*"
43
+ pathBinToVersion = "/tmp/kubebuilder" + toVersion + "-*"
44
+ pathBinToVersionWithConflict = "/tmp/kubebuilder" + toVersionWithConflict + "-*"
42
45
43
46
controllerImplementation = `// Fetch the TestOperator instance
44
47
testOperator := &webappv1.TestOperator{}
@@ -88,6 +91,7 @@ var _ = Describe("kubebuilder", func() {
88
91
binaryPatterns := []string {
89
92
pathBinFromVersion ,
90
93
pathBinToVersion ,
94
+ pathBinToVersionWithConflict ,
91
95
}
92
96
93
97
for _ , pattern := range binaryPatterns {
@@ -116,33 +120,78 @@ var _ = Describe("kubebuilder", func() {
116
120
117
121
By ("validating custom code preservation" )
118
122
validateCustomCodePreservation (mockProjectDir )
123
+
124
+ By ("validating no conflict markers are present" )
125
+ validateConflictMarkers (mockProjectDir , false )
126
+ })
127
+
128
+ It ("should update project from v4.5.2 to v4.7.0 with --force flag and create conflict markers" , func () {
129
+ By ("creating mock project with kubebuilder v4.5.2" )
130
+ createMockProject (mockProjectDir , pathBinFromVersion )
131
+
132
+ By ("adding custom code in API and controller" )
133
+ updateAPI (mockProjectDir )
134
+ updateController (mockProjectDir )
135
+
136
+ By ("initializing git repository and committing mock project" )
137
+ initializeGitRepo (mockProjectDir )
138
+
139
+ By ("running alpha update from v4.5.2 to v4.7.0 with --force flag" )
140
+ runAlphaUpdateWithForce (mockProjectDir , kbc )
141
+
142
+ By ("validating conflict markers are present" )
143
+ validateConflictMarkers (mockProjectDir , true )
144
+ })
145
+
146
+ It ("should stop when updating the project from v4.5.2 to v4.7.0 without the flag force " +
147
+ "to allow manual conflicts resolution" , func () {
148
+ By ("creating mock project with kubebuilder v4.5.2" )
149
+ createMockProject (mockProjectDir , pathBinFromVersion )
150
+
151
+ By ("adding custom code in API and controller" )
152
+ updateAPI (mockProjectDir )
153
+ updateController (mockProjectDir )
154
+
155
+ By ("initializing git repository and committing mock project" )
156
+ initializeGitRepo (mockProjectDir )
157
+
158
+ By ("running alpha update from v4.5.2 to v4.7.0 without --force flag" )
159
+ runAlphaUpdateWithoutForce (mockProjectDir , kbc )
160
+
161
+ By ("validating merge stopped in conflict state for manual resolution" )
162
+ validateConflictState (mockProjectDir )
119
163
})
120
164
})
121
165
})
122
166
123
167
// downloadKubebuilder downloads the --from-version kubebuilder binary to a temporary directory
124
168
func downloadKubebuilder () (string , error ) {
125
- binaryDir , err := os .MkdirTemp ("" , "kubebuilder-v4.5.2-" )
169
+ return downloadKubebuilderVersion (fromVersion )
170
+ }
171
+
172
+ // downloadKubebuilderVersion downloads a specific kubebuilder version binary to a temporary directory
173
+ func downloadKubebuilderVersion (version string ) (string , error ) {
174
+ binaryDir , err := os .MkdirTemp ("" , "kubebuilder-" + version + "-" )
126
175
if err != nil {
127
176
return "" , fmt .Errorf ("failed to create binary directory: %w" , err )
128
177
}
129
178
130
179
url := fmt .Sprintf (
131
180
"https://github.com/kubernetes-sigs/kubebuilder/releases/download/%s/kubebuilder_%s_%s" ,
132
- fromVersion ,
181
+ version ,
133
182
runtime .GOOS ,
134
183
runtime .GOARCH ,
135
184
)
136
185
binaryPath := filepath .Join (binaryDir , "kubebuilder" )
137
186
138
187
resp , err := http .Get (url )
139
188
if err != nil {
140
- return "" , fmt .Errorf ("failed to download kubebuilder %s: %w" , fromVersion , err )
189
+ return "" , fmt .Errorf ("failed to download kubebuilder %s: %w" , version , err )
141
190
}
142
191
defer func () { _ = resp .Body .Close () }()
143
192
144
193
if resp .StatusCode != http .StatusOK {
145
- return "" , fmt .Errorf ("failed to download kubebuilder %s: HTTP %d" , fromVersion , resp .StatusCode )
194
+ return "" , fmt .Errorf ("failed to download kubebuilder %s: HTTP %d" , version , resp .StatusCode )
146
195
}
147
196
148
197
file , err := os .Create (binaryPath )
@@ -165,13 +214,10 @@ func downloadKubebuilder() (string, error) {
165
214
}
166
215
167
216
func createMockProject (projectDir , binaryPath string ) {
168
- err := os .Chdir (projectDir )
169
- Expect (err ).NotTo (HaveOccurred ())
170
-
171
217
By ("running kubebuilder init" )
172
218
cmd := exec .Command (binaryPath , "init" , "--domain" , "example.com" , "--repo" , "github.com/example/test-operator" )
173
219
cmd .Dir = projectDir
174
- _ , err = cmd .CombinedOutput ()
220
+ _ , err : = cmd .CombinedOutput ()
175
221
Expect (err ).NotTo (HaveOccurred ())
176
222
177
223
By ("running kubebuilder create api" )
@@ -268,17 +314,30 @@ func initializeGitRepo(projectDir string) {
268
314
}
269
315
270
316
func runAlphaUpdate (projectDir string , kbc * utils.TestContext ) {
271
- err := os .Chdir (projectDir )
272
- Expect (err ).NotTo (HaveOccurred ())
273
-
274
- // Use TestContext to run alpha update command
275
317
cmd := exec .Command (kbc .BinaryName , "alpha" , "update" ,
276
318
"--from-version" , fromVersion , "--to-version" , toVersion , "--from-branch" , "main" )
277
319
cmd .Dir = projectDir
278
320
output , err := cmd .CombinedOutput ()
279
321
Expect (err ).NotTo (HaveOccurred (), fmt .Sprintf ("Alpha update failed: %s" , string (output )))
280
322
}
281
323
324
+ func runAlphaUpdateWithForce (projectDir string , kbc * utils.TestContext ) {
325
+ cmd := exec .Command (kbc .BinaryName , "alpha" , "update" , "--from-version" , fromVersion ,
326
+ "--to-version" , toVersionWithConflict , "--from-branch" , "main" , "--force" )
327
+ cmd .Dir = projectDir
328
+ output , err := cmd .CombinedOutput ()
329
+ Expect (err ).NotTo (HaveOccurred (), fmt .Sprintf ("Alpha update with force failed: %s" , string (output )))
330
+ }
331
+
332
+ func runAlphaUpdateWithoutForce (projectDir string , kbc * utils.TestContext ) {
333
+ cmd := exec .Command (kbc .BinaryName , "alpha" , "update" ,
334
+ "--from-version" , fromVersion , "--to-version" , toVersionWithConflict , "--from-branch" , "main" )
335
+ cmd .Dir = projectDir
336
+ output , err := cmd .CombinedOutput ()
337
+ Expect (err ).To (HaveOccurred ())
338
+ Expect (string (output )).To (ContainSubstring ("merge stopped due to conflicts" ))
339
+ }
340
+
282
341
func validateCustomCodePreservation (projectDir string ) {
283
342
By ("validating the API" )
284
343
typesFile := filepath .Join (projectDir , "api" , "v1" , "testoperator_types.go" )
@@ -295,3 +354,48 @@ func validateCustomCodePreservation(projectDir string) {
295
354
Expect (err ).NotTo (HaveOccurred ())
296
355
Expect (string (content )).To (ContainSubstring (controllerImplementation ))
297
356
}
357
+
358
+ func validateConflictMarkers (projectDir string , expectMarkers bool ) {
359
+ if expectMarkers {
360
+ By ("validating conflict markers are present" )
361
+ } else {
362
+ By ("validating no conflict markers are present" )
363
+ }
364
+
365
+ filesToCheck := []string {
366
+ filepath .Join (projectDir , "api" , "v1" , "testoperator_types.go" ),
367
+ filepath .Join (projectDir , "internal" , "controller" , "testoperator_controller.go" ),
368
+ }
369
+
370
+ conflictMarkersFound := false
371
+ for _ , file := range filesToCheck {
372
+ content , err := os .ReadFile (file )
373
+ if err != nil {
374
+ continue
375
+ }
376
+ fileContent := string (content )
377
+ if strings .Contains (fileContent , "<<<<<<<" ) && strings .Contains (fileContent , "=======" ) &&
378
+ strings .Contains (fileContent , ">>>>>>>" ) {
379
+ conflictMarkersFound = true
380
+ break
381
+ }
382
+ }
383
+
384
+ if expectMarkers {
385
+ Expect (conflictMarkersFound ).To (BeTrue (), "Expected to find conflict markers in at least one file" )
386
+ } else {
387
+ Expect (conflictMarkersFound ).To (BeFalse (), "Expected no conflict markers, but found them in files" )
388
+ }
389
+ }
390
+
391
+ func validateConflictState (projectDir string ) {
392
+ By ("validating merge stopped with conflicts requiring manual resolution" )
393
+ cmd := exec .Command ("git" , "status" , "--porcelain" )
394
+ cmd .Dir = projectDir
395
+ output , err := cmd .CombinedOutput ()
396
+ Expect (err ).NotTo (HaveOccurred ())
397
+ statusOutput := strings .TrimSpace (string (output ))
398
+ Expect (statusOutput ).NotTo (BeEmpty (), "Working directory should have uncommitted changes from merge conflict" )
399
+
400
+ validateConflictMarkers (projectDir , true )
401
+ }
0 commit comments