github.com/operator-framework/operator-lifecycle-manager@v0.30.0/pkg/controller/registry/resolver/fail_forward.go (about) 1 package resolver 2 3 import ( 4 "fmt" 5 6 operatorsv1 "github.com/operator-framework/api/pkg/operators/v1" 7 operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" 8 v1listers "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/listers/operators/v1" 9 "k8s.io/apimachinery/pkg/labels" 10 ) 11 12 // IsFailForwardEnabled takes a namespaced operatorGroup lister and returns 13 // True if an operatorGroup exists in the namespace and its upgradeStrategy 14 // is set to UnsafeFailForward and false otherwise. An error is returned if 15 // an more than one operatorGroup exists in the namespace. 16 // No error is returned if no OperatorGroups are found to keep the resolver 17 // backwards compatible. 18 func IsFailForwardEnabled(ogLister v1listers.OperatorGroupNamespaceLister) (bool, error) { 19 ogs, err := ogLister.List(labels.Everything()) 20 if err != nil { 21 return false, err 22 } 23 if len(ogs) != 1 { 24 return false, fmt.Errorf("found %d operatorGroups, expected 1", len(ogs)) 25 } 26 return ogs[0].UpgradeStrategy() == operatorsv1.UpgradeStrategyUnsafeFailForward, nil 27 } 28 29 type walkOption func(csv *operatorsv1alpha1.ClusterServiceVersion) error 30 31 // WithCSVPhase returns an error if the CSV is not in the given phase. 32 func WithCSVPhase(phase operatorsv1alpha1.ClusterServiceVersionPhase) walkOption { 33 return func(csv *operatorsv1alpha1.ClusterServiceVersion) error { 34 if csv == nil || csv.Status.Phase != phase { 35 return fmt.Errorf("csv %s/%s in phase %s instead of %s", csv.GetNamespace(), csv.GetName(), csv.Status.Phase, phase) 36 } 37 return nil 38 } 39 } 40 41 // WithUniqueCSVs returns an error if the CSV has been seen before. 42 func WithUniqueCSVs() walkOption { 43 visited := map[string]struct{}{} 44 return func(csv *operatorsv1alpha1.ClusterServiceVersion) error { 45 // Check if we have visited the CSV before 46 if _, ok := visited[csv.GetName()]; ok { 47 return fmt.Errorf("csv %s/%s has already been seen", csv.GetNamespace(), csv.GetName()) 48 } 49 50 visited[csv.GetName()] = struct{}{} 51 return nil 52 } 53 } 54 55 // WalkReplacementChain walks along the chain of clusterServiceVersions being replaced and returns 56 // the last clusterServiceVersions in the replacement chain. An error is returned if any of the 57 // clusterServiceVersions before the last is not in the replaces phase or if an infinite replacement 58 // chain is detected. 59 func WalkReplacementChain(csv *operatorsv1alpha1.ClusterServiceVersion, csvToReplacement map[string]*operatorsv1alpha1.ClusterServiceVersion, options ...walkOption) (*operatorsv1alpha1.ClusterServiceVersion, error) { 60 if csv == nil { 61 return nil, fmt.Errorf("csv cannot be nil") 62 } 63 64 for { 65 // Check if there is a CSV that replaces this CSVs 66 next, ok := csvToReplacement[csv.GetName()] 67 if !ok { 68 break 69 } 70 71 // Check walk options 72 for _, o := range options { 73 if err := o(csv); err != nil { 74 return nil, err 75 } 76 } 77 78 // Move along replacement chain. 79 csv = next 80 } 81 return csv, nil 82 } 83 84 // isReplacementChainThatEndsInFailure returns true if the last CSV in the chain is in the failed phase and all other 85 // CSVs are in the replacing phase. 86 func isReplacementChainThatEndsInFailure(csv *operatorsv1alpha1.ClusterServiceVersion, csvToReplacement map[string]*operatorsv1alpha1.ClusterServiceVersion) (bool, error) { 87 lastCSV, err := WalkReplacementChain(csv, csvToReplacement, WithCSVPhase(operatorsv1alpha1.CSVPhaseReplacing), WithUniqueCSVs()) 88 if err != nil { 89 return false, err 90 } 91 return (lastCSV != nil && lastCSV.Status.Phase == operatorsv1alpha1.CSVPhaseFailed), nil 92 } 93 94 // ReplacementMapping takes a list of CSVs and returns a map that maps a CSV's name to the CSV that replaces it. 95 func ReplacementMapping(csvs []*operatorsv1alpha1.ClusterServiceVersion) map[string]*operatorsv1alpha1.ClusterServiceVersion { 96 replacementMapping := map[string]*operatorsv1alpha1.ClusterServiceVersion{} 97 for _, csv := range csvs { 98 if csv.Spec.Replaces != "" { 99 replacementMapping[csv.Spec.Replaces] = csv 100 } 101 } 102 return replacementMapping 103 }