github.com/operator-framework/operator-lifecycle-manager@v0.30.0/pkg/controller/operators/olm/groups.go (about) 1 package olm 2 3 import ( 4 "strings" 5 6 operatorsv1 "github.com/operator-framework/api/pkg/operators/v1" 7 "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/cache" 8 ) 9 10 type NamespaceSet map[string]struct{} 11 12 func NewNamespaceSet(namespaces []string) NamespaceSet { 13 set := make(NamespaceSet) 14 for _, namespace := range namespaces { 15 set[namespace] = struct{}{} 16 } 17 18 return set 19 } 20 21 // NewNamespaceSetFromString creates a namespace set from a comma-delimited list of namespaces 22 func NewNamespaceSetFromString(namespaces string) NamespaceSet { 23 return NewNamespaceSet(strings.Split(namespaces, ",")) 24 } 25 26 func (n NamespaceSet) Peek() string { 27 for namespace := range n { 28 return namespace 29 } 30 31 return "" 32 } 33 34 func (n NamespaceSet) Intersection(set NamespaceSet) NamespaceSet { 35 intersection := make(NamespaceSet) 36 // Handle special NamespaceAll cases 37 if n.IsAllNamespaces() { 38 for namespace := range set { 39 intersection[namespace] = struct{}{} 40 } 41 return intersection 42 } 43 if set.IsAllNamespaces() { 44 for namespace := range n { 45 intersection[namespace] = struct{}{} 46 } 47 return intersection 48 } 49 50 for namespace := range n { 51 if _, ok := set[namespace]; ok { 52 intersection[namespace] = struct{}{} 53 } 54 } 55 56 return intersection 57 } 58 59 func (n NamespaceSet) Union(set NamespaceSet) NamespaceSet { 60 // Handle special NamespaceAll cases 61 if n.IsAllNamespaces() { 62 return n 63 } 64 if set.IsAllNamespaces() { 65 return set 66 } 67 union := make(NamespaceSet) 68 for namespace := range n { 69 union[namespace] = struct{}{} 70 } 71 for namespace := range set { 72 union[namespace] = struct{}{} 73 } 74 return union 75 } 76 77 func (n NamespaceSet) Contains(namespace string) bool { 78 if n.IsAllNamespaces() { 79 return true 80 } 81 _, ok := n[namespace] 82 return ok 83 } 84 85 func (n NamespaceSet) IsAllNamespaces() bool { 86 if len(n) == 1 && n.Peek() == "" { 87 return true 88 } 89 return false 90 } 91 92 type OperatorGroupSurface interface { 93 Identifier() string 94 Namespace() string 95 Targets() NamespaceSet 96 ProvidedAPIs() cache.APISet 97 GroupIntersection(groups ...OperatorGroupSurface) []OperatorGroupSurface 98 } 99 100 var _ OperatorGroupSurface = &OperatorGroup{} 101 102 type OperatorGroup struct { 103 namespace string 104 name string 105 targets NamespaceSet 106 providedAPIs cache.APISet 107 } 108 109 func NewOperatorGroup(group *operatorsv1.OperatorGroup) *OperatorGroup { 110 // Add operatorgroup namespace if not NamespaceAll 111 namespaces := group.Status.Namespaces 112 if len(namespaces) >= 1 && namespaces[0] != "" { 113 namespaces = append(namespaces, group.GetNamespace()) 114 } 115 // TODO: Sanitize OperatorGroup if len(namespaces) > 1 and contains "" 116 gvksStr := group.GetAnnotations()[operatorsv1.OperatorGroupProvidedAPIsAnnotationKey] 117 118 return &OperatorGroup{ 119 namespace: group.GetNamespace(), 120 name: group.GetName(), 121 targets: NewNamespaceSet(namespaces), 122 providedAPIs: cache.GVKStringToProvidedAPISet(gvksStr), 123 } 124 } 125 126 func NewOperatorGroupSurfaces(groups ...operatorsv1.OperatorGroup) []OperatorGroupSurface { 127 operatorGroups := make([]OperatorGroupSurface, len(groups)) 128 for i, group := range groups { 129 operatorGroups[i] = NewOperatorGroup(&group) 130 } 131 132 return operatorGroups 133 } 134 135 func (g *OperatorGroup) Identifier() string { 136 return g.name + "/" + g.namespace 137 } 138 139 func (g *OperatorGroup) Namespace() string { 140 return g.namespace 141 } 142 143 func (g *OperatorGroup) Targets() NamespaceSet { 144 return g.targets 145 } 146 147 func (g *OperatorGroup) ProvidedAPIs() cache.APISet { 148 return g.providedAPIs 149 } 150 151 func (g *OperatorGroup) GroupIntersection(groups ...OperatorGroupSurface) []OperatorGroupSurface { 152 intersection := []OperatorGroupSurface{} 153 for _, group := range groups { 154 if group.Identifier() == g.Identifier() { 155 // Skip self if present 156 continue 157 } 158 if len(g.targets.Intersection(group.Targets())) > 0 { 159 // TODO: This uses tons of space - maps are copied every time 160 intersection = append(intersection, group) 161 } 162 } 163 164 return intersection 165 } 166 167 type APIReconciliationResult int 168 169 const ( 170 RemoveAPIs APIReconciliationResult = iota 171 AddAPIs 172 APIConflict 173 NoAPIConflict 174 ) 175 176 type APIIntersectionReconciler interface { 177 Reconcile(add cache.APISet, group OperatorGroupSurface, otherGroups ...OperatorGroupSurface) APIReconciliationResult 178 } 179 180 type APIIntersectionReconcileFunc func(add cache.APISet, group OperatorGroupSurface, otherGroups ...OperatorGroupSurface) APIReconciliationResult 181 182 func (a APIIntersectionReconcileFunc) Reconcile(add cache.APISet, group OperatorGroupSurface, otherGroups ...OperatorGroupSurface) APIReconciliationResult { 183 return a(add, group, otherGroups...) 184 } 185 186 func ReconcileAPIIntersection(add cache.APISet, group OperatorGroupSurface, otherGroups ...OperatorGroupSurface) APIReconciliationResult { 187 groupIntersection := group.GroupIntersection(otherGroups...) 188 providedAPIIntersection := make(cache.APISet) 189 for _, g := range groupIntersection { 190 providedAPIIntersection = providedAPIIntersection.Union(g.ProvidedAPIs()) 191 } 192 193 intersecting := len(add.Intersection(providedAPIIntersection)) > 0 194 subset := add.IsSubset(group.ProvidedAPIs()) 195 196 if subset && intersecting { 197 return RemoveAPIs 198 } 199 200 if !subset && intersecting { 201 return APIConflict 202 } 203 204 if !subset { 205 return AddAPIs 206 } 207 208 return NoAPIConflict 209 }