@@ -4,10 +4,13 @@ import (
44 "encoding/json"
55 "fmt"
66 "log"
7+ "os"
78 "slices"
89 "strings"
910 "time"
1011
12+ "regexp"
13+
1114 "github.com/getgauge-contrib/gauge-go/testsuit"
1215 "github.com/openshift-pipelines/release-tests/pkg/cmd"
1316 "github.com/openshift-pipelines/release-tests/pkg/config"
@@ -169,3 +172,276 @@ func CopySecret(secretName string, sourceNamespace string, destNamespace string)
169172 cmd .MustSucceed ("bash" , "-c" , fmt .Sprintf (`echo '%s' | kubectl apply -n %s -f -` , cmdOutput , destNamespace ))
170173 log .Printf ("Successfully copied secret %s from %s to %s" , secretName , sourceNamespace , destNamespace )
171174}
175+
176+ func FetchOlmSkipRange () (map [string ]string , error ) {
177+ skipRangesJson := cmd .MustSucceed ("bash" , "-c" , `oc get packagemanifests openshift-pipelines-operator-rh -n openshift-marketplace -o json | jq -r '.status.channels[].currentCSVDesc.annotations["olm.skipRange"]'` ).Stdout ()
178+ skipRanges := strings .Split (strings .TrimSpace (skipRangesJson ), "\n " )
179+ channelsJson := cmd .MustSucceed ("bash" , "-c" , `oc get packagemanifests openshift-pipelines-operator-rh -n openshift-marketplace -o json | jq -r '.status.channels[].name'` ).Stdout ()
180+ channels := strings .Split (strings .TrimSpace (channelsJson ), "\n " )
181+
182+ if len (channels ) != len (skipRanges ) {
183+ return nil , fmt .Errorf ("mismatch between number of channels (%d) and skipRanges (%d)" , len (channels ), len (skipRanges ))
184+ }
185+
186+ channelSkipRangeMap := make (map [string ]string )
187+ for i , channel := range channels {
188+ if skipRanges [i ] != "null" && skipRanges [i ] != "" {
189+ channelSkipRangeMap [channel ] = skipRanges [i ]
190+ }
191+ }
192+
193+ if len (channelSkipRangeMap ) == 0 {
194+ return nil , fmt .Errorf ("no valid OLM Skip Ranges found" )
195+ }
196+ return channelSkipRangeMap , nil
197+ }
198+
199+ func GetOlmSkipRange (upgradeType , fieldName , fileName string ) {
200+ skipRangeMap , err := FetchOlmSkipRange ()
201+ if err != nil {
202+ log .Printf ("Error fetching OLM Skip Range: %v" , err )
203+ return
204+ }
205+ file , err := os .OpenFile (config .Path (fileName ), os .O_RDWR , 0644 )
206+ if err != nil {
207+ log .Printf ("Error opening file %s: %v" , fileName , err )
208+ return
209+ }
210+ defer file .Close ()
211+ var existingData map [string ]interface {}
212+ if err := json .NewDecoder (file ).Decode (& existingData ); err != nil {
213+ log .Printf ("Error decoding existing data from file %s: %v" , fileName , err )
214+ return
215+ }
216+ switch upgradeType {
217+ case "pre-upgrade" :
218+ existingData ["pre-upgrade-olm-skip-range" ] = skipRangeMap
219+ log .Printf ("Pre-upgrade OLM Skip Range is stored as: %+v" , skipRangeMap )
220+ case "post-upgrade" :
221+ existingData ["post-upgrade-olm-skip-range" ] = skipRangeMap
222+ log .Printf ("Post-upgrade OLM Skip Range is stored as: %+v" , skipRangeMap )
223+ }
224+ if _ , err := file .Seek (0 , 0 ); err != nil {
225+ log .Printf ("Error seeking file %s: %v" , fileName , err )
226+ return
227+ }
228+ encoder := json .NewEncoder (file )
229+ encoder .SetIndent ("" , " " ) // Pretty-print the JSON output
230+ if err := encoder .Encode (existingData ); err != nil {
231+ log .Printf ("Error writing updated data to file %s: %v" , fileName , err )
232+ return
233+ }
234+ log .Printf ("OLM Skip Range for '%s' has been saved to file %s" , fieldName , fileName )
235+ }
236+
237+ func ValidateOlmSkipRange () {
238+ skipRangeMap , err := FetchOlmSkipRange ()
239+ if err != nil {
240+ log .Printf ("Error fetching OLM Skip Range: %v" , err )
241+ return
242+ }
243+
244+ ospVersion := os .Getenv ("OSP_VERSION" )
245+ log .Printf ("Validating OSP_VERSION: %s" , ospVersion )
246+ found := false
247+
248+ if ospVersion == "5.0.5" {
249+ log .Printf ("Detected nightly build (OSP_VERSION=5.0.5), validating only skipRange, not channel name" )
250+ for channel , skipRange := range skipRangeMap {
251+ if channel == "latest" {
252+ log .Printf ("Skipping 'latest' channel as requested" )
253+ continue
254+ }
255+ skipRangeContainsVersion := strings .Contains (skipRange , ospVersion )
256+ log .Printf ("Channel: %s, SkipRange: %s" , channel , skipRange )
257+ log .Printf (" - SkipRange contains OSP_VERSION '%s': %v" , ospVersion , skipRangeContainsVersion )
258+
259+ if skipRangeContainsVersion {
260+ log .Printf ("Success: OSP_VERSION '%s' found in skipRange for channel '%s': '%s'" , ospVersion , channel , skipRange )
261+ found = true
262+ break
263+ }
264+ }
265+ } else {
266+ log .Printf ("Regular release build, validating both channel name and skipRange" )
267+ for channel , skipRange := range skipRangeMap {
268+ if channel == "latest" {
269+ log .Printf ("Skipping 'latest' channel as requested" )
270+ continue
271+ }
272+ channelContainsVersion := strings .Contains (channel , ospVersion )
273+ skipRangeContainsVersion := strings .Contains (skipRange , ospVersion )
274+ log .Printf ("Channel: %s, SkipRange: %s" , channel , skipRange )
275+ log .Printf (" - Channel contains OSP_VERSION '%s': %v" , ospVersion , channelContainsVersion )
276+ log .Printf (" - SkipRange contains OSP_VERSION '%s': %v" , ospVersion , skipRangeContainsVersion )
277+ if channelContainsVersion && skipRangeContainsVersion {
278+ log .Printf ("Success: OSP_VERSION '%s' found in both channel '%s' and its skipRange '%s'" , ospVersion , channel , skipRange )
279+ found = true
280+ break
281+ }
282+ }
283+ }
284+
285+ if ! found {
286+ log .Printf ("Available channels and their skipRanges:" )
287+ for channel , skipRange := range skipRangeMap {
288+ if channel != "latest" {
289+ log .Printf (" - Channel: %s, SkipRange: %s" , channel , skipRange )
290+ }
291+ }
292+
293+ if ospVersion == "5.0.5" {
294+ testsuit .T .Fail (fmt .Errorf ("Error: OSP_VERSION '%s' not found in skipRange for any non-latest channel" , ospVersion ))
295+ } else {
296+ testsuit .T .Fail (fmt .Errorf ("Error: OSP_VERSION '%s' not found in both channel name and skipRange for any non-latest channel" , ospVersion ))
297+ }
298+ }
299+ }
300+
301+ func isValidOspVersionPatchUpdate (preSkipRange , postSkipRange string ) bool {
302+ ospVersion := os .Getenv ("OSP_VERSION" )
303+
304+ if ! skipRangeContainsVersion (postSkipRange , ospVersion ) {
305+ log .Printf ("Post-upgrade skip range '%s' does not contain OSP_VERSION '%s'" , postSkipRange , ospVersion )
306+ return false
307+ }
308+
309+ rangeRegex := regexp .MustCompile (`>=(\d+\.\d+\.\d+)\s*<(\d+\.\d+\.\d+)` )
310+ preMatches := rangeRegex .FindStringSubmatch (preSkipRange )
311+ postMatches := rangeRegex .FindStringSubmatch (postSkipRange )
312+
313+ preUpperBound := preMatches [2 ] // Upper bound from pre-upgrade
314+ postUpperBound := postMatches [2 ] // Upper bound from post-upgrade
315+
316+ versionRegex := regexp .MustCompile (`^(\d+)\.(\d+)\.(\d+)$` )
317+ preUpperMatches := versionRegex .FindStringSubmatch (preUpperBound )
318+ postUpperMatches := versionRegex .FindStringSubmatch (postUpperBound )
319+
320+ preUpperPatch := preUpperMatches [3 ]
321+ postUpperPatch := postUpperMatches [3 ]
322+
323+ var preUpperPatchInt , postUpperPatchInt int
324+ fmt .Sscanf (preUpperPatch , "%d" , & preUpperPatchInt )
325+ fmt .Sscanf (postUpperPatch , "%d" , & postUpperPatchInt )
326+
327+ if postUpperPatchInt <= preUpperPatchInt {
328+ log .Printf ("Version did not increase: %s -> %s" , preUpperBound , postUpperBound )
329+ return false
330+ }
331+
332+ log .Printf ("Valid OSP version-based patch update detected: %s -> %s (OSP_VERSION: %s)" ,
333+ preUpperBound , postUpperBound , ospVersion )
334+ return true
335+ }
336+
337+ func skipRangeContainsVersion (skipRange , version string ) bool {
338+ return strings .Contains (skipRange , version )
339+ }
340+
341+ func ValidateOlmSkipRangeDiff (fileName string , preUpgradeSkipRange string , postUpgradeSkipRange string ) {
342+ file , err := os .Open (config .Path (fileName ))
343+ if err != nil {
344+ log .Printf ("Error opening file %s: %v" , fileName , err )
345+ testsuit .T .Fail (fmt .Errorf ("Error opening file %s: %v" , fileName , err ))
346+ return
347+ }
348+ defer file .Close ()
349+ var skipRangeData map [string ]interface {}
350+ decoder := json .NewDecoder (file )
351+ if err := decoder .Decode (& skipRangeData ); err != nil {
352+ log .Printf ("Error decoding JSON from file %s: %v" , fileName , err )
353+ testsuit .T .Fail (fmt .Errorf ("Error decoding JSON from file %s: %v" , fileName , err ))
354+ return
355+ }
356+ preUpgradeData , preExists := skipRangeData [preUpgradeSkipRange ]
357+ postUpgradeData , postExists := skipRangeData [postUpgradeSkipRange ]
358+ if ! preExists || ! postExists {
359+ log .Printf ("Error: One of the skip ranges is missing. Pre-Upgrade exists: %v, Post-Upgrade exists: %v" , preExists , postExists )
360+ testsuit .T .Fail (fmt .Errorf ("One of the skip ranges is missing. Pre-Upgrade exists: %v, Post-Upgrade exists: %v" , preExists , postExists ))
361+ return
362+ }
363+
364+ preUpgradeMap , ok1 := preUpgradeData .(map [string ]interface {})
365+ postUpgradeMap , ok2 := postUpgradeData .(map [string ]interface {})
366+
367+ if ! ok1 || ! ok2 {
368+ log .Printf ("Error: Skip range data is not in expected map format" )
369+ testsuit .T .Fail (fmt .Errorf ("Skip range data is not in expected map format" ))
370+ return
371+ }
372+
373+ log .Printf ("Pre-Upgrade Skip Range: %+v" , preUpgradeMap )
374+ log .Printf ("Post-Upgrade Skip Range: %+v" , postUpgradeMap )
375+
376+ validationErrors := []string {}
377+
378+ log .Printf ("Validating that all pre-upgrade channels are preserved in post-upgrade (ignoring 'latest' channel)" )
379+
380+ // Check each channel from pre-upgrade data
381+ for preChannel , preSkipRange := range preUpgradeMap {
382+ // Skip 'latest' channel from pre-upgrade validation
383+ if preChannel == "latest" {
384+ log .Printf ("Skipping 'latest' channel from pre-upgrade data as requested" )
385+ continue
386+ }
387+
388+ if postSkipRange , exists := postUpgradeMap [preChannel ]; exists {
389+ if preSkipRange == postSkipRange {
390+ log .Printf ("✅ Success: Channel '%s' preserved with skipRange: %v" , preChannel , preSkipRange )
391+ } else {
392+ // There's a skipRange mismatch - check if it's related to current OSP_VERSION
393+ preSkipRangeStr , ok1 := preSkipRange .(string )
394+ postSkipRangeStr , ok2 := postSkipRange .(string )
395+
396+ if ok1 && ok2 {
397+ ospVersion := os .Getenv ("OSP_VERSION" )
398+
399+ if ospVersion != "" && (strings .Contains (preSkipRangeStr , ospVersion ) || strings .Contains (postSkipRangeStr , ospVersion )) {
400+ log .Printf ("ℹ️ SkipRange mismatch involves current OSP_VERSION '%s', applying OSP_VERSION-based validation" , ospVersion )
401+
402+ if isValidOspVersionPatchUpdate (preSkipRangeStr , postSkipRangeStr ) {
403+ log .Printf ("✅ Success: Channel '%s' updated with valid OSP_VERSION-based patch release: %v -> %v" , preChannel , preSkipRange , postSkipRange )
404+ } else {
405+ validationErrors = append (validationErrors , fmt .Sprintf ("Channel '%s' skipRange changed from '%v' to '%v' (not a valid OSP_VERSION-based patch update for OSP_VERSION '%s')" , preChannel , preSkipRange , postSkipRange , ospVersion ))
406+ }
407+ } else {
408+ if ospVersion == "" {
409+ log .Printf ("ℹ️ OSP_VERSION not set, applying standard validation (no changes allowed)" )
410+ } else {
411+ log .Printf ("ℹ️ SkipRange mismatch does not involve current OSP_VERSION '%s', applying standard validation (no changes allowed)" , ospVersion )
412+ }
413+ validationErrors = append (validationErrors , fmt .Sprintf ("Channel '%s' skipRange changed from '%v' to '%v' (should remain unchanged)" , preChannel , preSkipRange , postSkipRange ))
414+ }
415+ } else {
416+ validationErrors = append (validationErrors , fmt .Sprintf ("Channel '%s' skipRange changed from '%v' to '%v' (invalid format)" , preChannel , preSkipRange , postSkipRange ))
417+ }
418+ }
419+ } else {
420+ validationErrors = append (validationErrors , fmt .Sprintf ("Pre-upgrade channel '%s' is missing in post-upgrade data" , preChannel ))
421+ }
422+ }
423+
424+ log .Printf ("Additional channels found in post-upgrade data:" )
425+ for postChannel , postSkipRange := range postUpgradeMap {
426+ if _ , existedInPre := preUpgradeMap [postChannel ]; existedInPre {
427+ continue
428+ }
429+
430+ if postChannel == "latest" {
431+ log .Printf (" - Ignoring 'latest' channel in post-upgrade as requested" )
432+ continue
433+ }
434+
435+ log .Printf (" - New channel '%s' with skipRange: %v" , postChannel , postSkipRange )
436+ }
437+
438+ if len (validationErrors ) > 0 {
439+ log .Printf ("❌ OLM Skip Range validation failed with errors:" )
440+ for _ , err := range validationErrors {
441+ log .Printf (" - %s" , err )
442+ }
443+ testsuit .T .Fail (fmt .Errorf ("OLM Skip Range validation failed: %v" , strings .Join (validationErrors , "; " )))
444+ } else {
445+ log .Printf ("✅ Success: OLM Skip Range validation passed - all expected changes detected correctly" )
446+ }
447+ }
0 commit comments