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  }