Skip to content

Commit 014bd31

Browse files
Merge pull request #2260 from benluddy/name-multiple-replaces-chains
Explain unique replaces chain requirement in channel sort errors.
2 parents 8f09c66 + 6391025 commit 014bd31

File tree

2 files changed

+227
-81
lines changed

2 files changed

+227
-81
lines changed

pkg/controller/registry/resolver/resolver.go

+55-32
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"encoding/json"
66
"fmt"
77
"sort"
8+
"strings"
89

910
"github.com/blang/semver/v4"
1011
"github.com/sirupsen/logrus"
@@ -237,7 +238,7 @@ func (r *SatResolver) getSubscriptionInstallables(sub *v1alpha1.Subscription, cu
237238
if i != len(bundles) && bundles[i].bundle.ChannelName == lastChannel {
238239
continue
239240
}
240-
channel, err := r.sortChannel(bundles[lastIndex:i])
241+
channel, err := sortChannel(bundles[lastIndex:i])
241242
if err != nil {
242243
return nil, err
243244
}
@@ -610,7 +611,7 @@ func (r *SatResolver) sortBundles(bundles []*Operator) ([]*Operator, error) {
610611
return pi.Channel < pj.Channel
611612
})
612613
for channel := range partitionedBundles[catalog] {
613-
sorted, err := r.sortChannel(partitionedBundles[catalog][channel])
614+
sorted, err := sortChannel(partitionedBundles[catalog][channel])
614615
if err != nil {
615616
return nil, err
616617
}
@@ -626,13 +627,15 @@ func (r *SatResolver) sortBundles(bundles []*Operator) ([]*Operator, error) {
626627
return all, nil
627628
}
628629

629-
// sorts bundle in a channel by replaces
630-
func (r *SatResolver) sortChannel(bundles []*Operator) ([]*Operator, error) {
631-
if len(bundles) <= 1 {
630+
// Sorts bundle in a channel by replaces. All entries in the argument
631+
// are assumed to have the same Package and Channel.
632+
func sortChannel(bundles []*Operator) ([]*Operator, error) {
633+
if len(bundles) < 1 {
632634
return bundles, nil
633635
}
634636

635-
channel := []*Operator{}
637+
packageName := bundles[0].Package()
638+
channelName := bundles[0].Channel()
636639

637640
bundleLookup := map[string]*Operator{}
638641

@@ -660,44 +663,64 @@ func (r *SatResolver) sortChannel(bundles []*Operator) ([]*Operator, error) {
660663
}
661664
}
662665

663-
// a bundle without a replacement is a channel head, but if we find more than one of those something is weird
666+
// a bundle without a replacement is a channel head, but if we
667+
// find more than one of those something is weird
664668
headCandidates := []*Operator{}
665669
for _, b := range bundles {
666670
if _, ok := replacedBy[b]; !ok {
667671
headCandidates = append(headCandidates, b)
668672
}
669673
}
674+
if len(headCandidates) == 0 {
675+
return nil, fmt.Errorf("no channel heads (entries not replaced by another entry) found in channel %q of package %q", channelName, packageName)
676+
}
670677

671-
if len(headCandidates) > 1 {
672-
var names []string
673-
for _, v := range headCandidates {
674-
names = append(names, v.Identifier())
678+
var chains [][]*Operator
679+
for _, head := range headCandidates {
680+
var chain []*Operator
681+
visited := make(map[*Operator]struct{})
682+
current := head
683+
skip := false
684+
for {
685+
visited[current] = struct{}{}
686+
if !skip {
687+
chain = append(chain, current)
688+
}
689+
next, ok := replaces[current]
690+
if !ok {
691+
break
692+
}
693+
if _, ok := visited[next]; ok {
694+
return nil, fmt.Errorf("a cycle exists in the chain of replacement beginning with %q in channel %q of package %q", head.Identifier(), channelName, packageName)
695+
}
696+
if _, ok := skipped[current.Identifier()]; ok {
697+
skip = true
698+
}
699+
current = next
675700
}
676-
return nil, fmt.Errorf("found multiple channel heads: %v, please check the `replaces`/`skipRange` fields of the operator bundles", names)
677-
678-
} else if len(headCandidates) < 1 {
679-
return nil, fmt.Errorf("head of channel not found")
701+
chains = append(chains, chain)
680702
}
681703

682-
head := headCandidates[0]
683-
current := head
684-
skip := false
685-
for {
686-
if skip == false {
687-
channel = append(channel, current)
688-
}
689-
skip = false
690-
next, ok := replaces[current]
691-
if !ok {
692-
break
693-
}
694-
if _, ok := skipped[current.Identifier()]; ok {
695-
skip = true
704+
if len(chains) > 1 {
705+
schains := make([]string, len(chains))
706+
for i, chain := range chains {
707+
switch len(chain) {
708+
case 0:
709+
schains[i] = "[]" // Bug?
710+
case 1:
711+
schains[i] = chain[0].Identifier()
712+
default:
713+
schains[i] = fmt.Sprintf("%s...%s", chain[0].Identifier(), chain[len(chain)-1].Identifier())
714+
}
696715
}
697-
current = next
716+
return nil, fmt.Errorf("a unique replacement chain within a channel is required to determine the relative order between channel entries, but %d replacement chains were found in channel %q of package %q: %s", len(schains), channelName, packageName, strings.Join(schains, ", "))
698717
}
699718

700-
// TODO: do we care if the channel doesn't include every bundle in the input?
719+
if len(chains) == 0 {
720+
// Bug?
721+
return nil, fmt.Errorf("found no replacement chains in channel %q of package %q", channelName, packageName)
722+
}
701723

702-
return channel, nil
724+
// TODO: do we care if the channel doesn't include every bundle in the input?
725+
return chains[0], nil
703726
}

0 commit comments

Comments
 (0)