github.com/operator-framework/operator-lifecycle-manager@v0.30.0/pkg/controller/operators/internal/alongside/alongside.go (about)

     1  // Package alongside provides a mechanism for recording the fact that
     2  // one object was installed alongside another object as part of the
     3  // installation of the same operator version.
     4  package alongside
     5  
     6  import (
     7  	"fmt"
     8  	"hash/fnv"
     9  	"strings"
    10  )
    11  
    12  const (
    13  	AnnotationPrefix = "operatorframework.io/installed-alongside-"
    14  )
    15  
    16  // NamespacedName is a reference to an object by namespace and name.
    17  type NamespacedName struct {
    18  	Namespace string
    19  	Name      string
    20  }
    21  
    22  type Annotatable interface {
    23  	GetAnnotations() map[string]string
    24  	SetAnnotations(map[string]string)
    25  }
    26  
    27  // Annotator translates installed-alongside references to and from
    28  // object annotations.
    29  type Annotator struct{}
    30  
    31  // FromObject returns a slice containing each namespaced name
    32  // referenced by an alongside annotation on the provided Object.
    33  func (a Annotator) FromObject(o Annotatable) []NamespacedName {
    34  	var result []NamespacedName
    35  	for k, v := range o.GetAnnotations() {
    36  		if !strings.HasPrefix(k, AnnotationPrefix) {
    37  			continue
    38  		}
    39  		tokens := strings.Split(v, "/")
    40  		if len(tokens) != 2 {
    41  			continue
    42  		}
    43  		result = append(result, NamespacedName{
    44  			Namespace: tokens[0],
    45  			Name:      tokens[1],
    46  		})
    47  	}
    48  	return result
    49  }
    50  
    51  // ToObject removes all existing alongside annotations on the provided
    52  // Object and adds one new annotation per entry in the provided slice
    53  // of namespaced names.
    54  func (a Annotator) ToObject(o Annotatable, nns []NamespacedName) {
    55  	annotations := o.GetAnnotations()
    56  
    57  	for key := range annotations {
    58  		if strings.HasPrefix(key, AnnotationPrefix) {
    59  			delete(annotations, key)
    60  		}
    61  	}
    62  
    63  	if len(nns) == 0 {
    64  		if len(annotations) == 0 {
    65  			annotations = nil
    66  		}
    67  		o.SetAnnotations(annotations)
    68  		return
    69  	}
    70  
    71  	if annotations == nil {
    72  		annotations = make(map[string]string, len(nns))
    73  	}
    74  	for _, nn := range nns {
    75  		annotations[key(nn)] = fmt.Sprintf("%s/%s", nn.Namespace, nn.Name)
    76  	}
    77  	o.SetAnnotations(annotations)
    78  }
    79  
    80  func key(n NamespacedName) string {
    81  	hasher := fnv.New64a()
    82  	hasher.Write([]byte(n.Namespace))
    83  	hasher.Write([]byte{'/'})
    84  	hasher.Write([]byte(n.Name))
    85  	return fmt.Sprintf("%s%x", AnnotationPrefix, hasher.Sum64())
    86  }