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

openshift-kni / cluster-group-upgrades-operator / #3

20 Nov 2025 11:55AM UTC coverage: 61.674% (+17.2%) from 44.472%
#3

push

web-flow
chore(deps): update topology-aware-lifecycle-manager-precache-4-21 to e280b86 (#5031)

Image created from 'https://github.com/openshift-kni/cluster-group-upgrades-operator?rev=e0953ccb4'

Signed-off-by: red-hat-konflux <126015336+red-hat-konflux[bot]@users.noreply.github.com>
Co-authored-by: red-hat-konflux[bot] <126015336+red-hat-konflux[bot]@users.noreply.github.com>

4039 of 6549 relevant lines covered (61.67%)

0.69 hits per line

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

40.22
/controllers/utils/common.go
1
package utils
2

3
import (
4
        "fmt"
5
        "regexp"
6
        "sort"
7
        "strings"
8
        "unicode/utf8"
9

10
        "github.com/openshift-kni/cluster-group-upgrades-operator/pkg/api/clustergroupupgrades/v1alpha1"
11
        ranv1alpha1 "github.com/openshift-kni/cluster-group-upgrades-operator/pkg/api/clustergroupupgrades/v1alpha1"
12
        lcav1 "github.com/openshift-kni/lifecycle-agent/api/imagebasedupgrade/v1"
13
        corev1 "k8s.io/api/core/v1"
14
        rbac "k8s.io/api/rbac/v1"
15
        "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
16
        "k8s.io/apimachinery/pkg/runtime"
17
        "k8s.io/apimachinery/pkg/util/rand"
18
        mwv1alpha1 "open-cluster-management.io/api/work/v1alpha1"
19
)
20

21
// GetMinOf3 return the minimum of 3 numbers.
22
func GetMinOf3(number1, number2, number3 int) int {
×
23
        // nolint: gocritic
×
24
        if number1 <= number2 && number1 <= number3 {
×
25
                return number1
×
26
        } else if number2 <= number1 && number2 <= number3 {
×
27
                return number2
×
28
        } else {
×
29
                return number3
×
30
        }
×
31
}
32

33
// FindStringInSlice checks if a given string is in the slice, and returns true along with its index if it's found
34
func FindStringInSlice(a []string, s string) (int, bool) {
1✔
35
        for i, e := range a {
2✔
36
                if e == s {
2✔
37
                        return i, true
1✔
38
                }
1✔
39
        }
40
        return -1, false
1✔
41
}
42

43
// GetSafeResourceName returns the safename if already allocated in the map and creates a new one if not
44
func GetSafeResourceName(name, namespace string, clusterGroupUpgrade *ranv1alpha1.ClusterGroupUpgrade, maxLength int) string {
1✔
45
        if clusterGroupUpgrade.Status.SafeResourceNames == nil {
2✔
46
                clusterGroupUpgrade.Status.SafeResourceNames = make(map[string]string)
1✔
47
        }
1✔
48
        safeName, ok := clusterGroupUpgrade.Status.SafeResourceNames[PrefixNameWithNamespace(namespace, name)]
1✔
49

1✔
50
        if !ok {
2✔
51
                safeName = NewSafeResourceName(name, namespace, clusterGroupUpgrade.GetAnnotations()[NameSuffixAnnotation], maxLength)
1✔
52
                clusterGroupUpgrade.Status.SafeResourceNames[PrefixNameWithNamespace(namespace, name)] = safeName
1✔
53
        }
1✔
54
        return safeName
1✔
55
}
56

57
const (
58
        finalDashLength = 1
59
)
60

61
// NewSafeResourceName creates a safe name to use with random suffix and possible truncation based on limits passed in
62
func NewSafeResourceName(name, namespace, suffix string, maxLength int) (safename string) {
1✔
63
        if suffix == "" {
1✔
64
                suffix = rand.String(RandomNameSuffixLength)
×
65
        }
×
66
        suffixLength := utf8.RuneCountInString(suffix)
1✔
67
        maxGeneratedNameLength := maxLength - suffixLength - utf8.RuneCountInString(namespace) - finalDashLength
1✔
68
        var base string
1✔
69
        if len(name) > maxGeneratedNameLength {
1✔
70
                base = name[:maxGeneratedNameLength]
×
71
        } else {
1✔
72
                base = name
1✔
73
        }
1✔
74

75
        // Make sure base ends in '-' or an alphanumerical character.
76
        for !regexp.MustCompile(`^[a-zA-Z0-9-]*$`).MatchString(base[utf8.RuneCountInString(base)-1:]) {
1✔
77
                base = base[:utf8.RuneCountInString(base)-1]
×
78
        }
×
79

80
        // The newSafeResourceName should match regex
81
        // `[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*` as per
82
        // https://kubernetes.io/docs/concepts/overview/working-with-objects/names/
83
        return fmt.Sprintf("%s-%s", base, suffix)
1✔
84
}
85

86
// PrefixNameWithNamespace Prefixes the passed name with the passed namespace. Use '/' as a separator
87
func PrefixNameWithNamespace(namespace, name string) string {
1✔
88
        return namespace + "/" + name
1✔
89
}
1✔
90

91
// ContainsTemplates checks if the string contains some templatized parts
92
func ContainsTemplates(s string) bool {
1✔
93
        // This expression matches all template types
1✔
94
        regexpAllTemplates := regexp.MustCompile(`{{.*}}`)
1✔
95

1✔
96
        return regexpAllTemplates.MatchString(s)
1✔
97
}
1✔
98

99
// Difference returns the elements in `a` that aren't in `b`.
100
func Difference(a, b []string) []string {
×
101
        mb := make(map[string]struct{}, len(b))
×
102
        for _, x := range b {
×
103
                mb[x] = struct{}{}
×
104
        }
×
105
        var diff []string
×
106
        for _, x := range a {
×
107
                if _, found := mb[x]; !found {
×
108
                        diff = append(diff, x)
×
109
                }
×
110
        }
111
        return diff
×
112
}
113

114
func ObjectToJSON(obj runtime.Object) (string, error) {
×
115
        scheme := runtime.NewScheme()
×
116
        mwv1alpha1.AddToScheme(scheme)
×
117
        v1alpha1.AddToScheme(scheme)
×
118
        corev1.AddToScheme(scheme)
×
119
        rbac.AddToScheme(scheme)
×
120
        lcav1.AddToScheme(scheme)
×
121
        outUnstructured := &unstructured.Unstructured{}
×
122
        scheme.Convert(obj, outUnstructured, nil)
×
123
        json, err := outUnstructured.MarshalJSON()
×
124
        return string(json), err
×
125
}
×
126

127
func ObjectToByteArray(obj runtime.Object) ([]byte, error) {
×
128
        json, err := ObjectToJSON(obj)
×
129
        return []byte(json), err
×
130
}
×
131

132
// Contains check if str is in slice
133
// can be replaced by slices.Contains when golang is updated to 1.21
134
func Contains(s []string, str string) bool {
×
135
        for _, v := range s {
×
136
                if v == str {
×
137
                        return true
×
138
                }
×
139
        }
140

141
        return false
×
142
}
143

144
// SortCGUListByPlanIndex orders the CGUs by the last action in the name of cgu
145
// with following order prep-upgrade-rollback-finalize-abort
146
func SortCGUListByPlanIndex(cguList *ranv1alpha1.ClusterGroupUpgradeList) {
×
147
        sort.Slice(cguList.Items, func(i, j int) bool {
×
148
                iSplitted := strings.Split(cguList.Items[i].GetName(), "-")
×
149
                jSplitted := strings.Split(cguList.Items[j].GetName(), "-")
×
150
                iLast := iSplitted[len(iSplitted)-1]
×
151
                jLast := jSplitted[len(jSplitted)-1]
×
152
                return iLast < jLast
×
153
        })
×
154
}
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

© 2026 Coveralls, Inc