Skip to content

Commit 0866a6d

Browse files
committed
Bug 1847540: resolve only default channels
Non deterministic behavior was introduced where, in some cases, the dependency resolver would select a channel entry from the registry that was not part of the default channel and create a subscription on that channel. This bugfix updates the registry client's filter function to ignore non default channel entries.
1 parent 21d67c1 commit 0866a6d

File tree

2 files changed

+54
-14
lines changed

2 files changed

+54
-14
lines changed

pkg/controller/registry/registry_client.go

Lines changed: 47 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,12 @@ func (rc *Client) FindBundleThatProvides(ctx context.Context, group, version, ki
8686
if err != nil {
8787
return nil, err
8888
}
89-
entry := rc.filterChannelEntries(it, excludedPkgName)
89+
entry, err := rc.filterChannelEntries(ctx, it, excludedPkgName)
90+
if err != nil {
91+
return nil, err
92+
}
9093
if entry == nil {
91-
return nil, fmt.Errorf("Unable to find a channel entry which doesn't belong to package %s", excludedPkgName)
94+
return nil, fmt.Errorf("Unable to find a channel entry which doesn't belong to package %s's default channel", excludedPkgName)
9295
}
9396
bundle, err := rc.Client.Registry.GetBundle(ctx, &registryapi.GetBundleRequest{PkgName: entry.PackageName, ChannelName: entry.ChannelName, CsvName: entry.BundleName})
9497
if err != nil {
@@ -97,25 +100,56 @@ func (rc *Client) FindBundleThatProvides(ctx context.Context, group, version, ki
97100
return bundle, nil
98101
}
99102

100-
// FilterChannelEntries filters out a channel entries that provide the requested
103+
// FilterChannelEntries filters out channel entries that provide the requested
101104
// API and come from the same package with original operator and returns the
102-
// first entry on the list
103-
func (rc *Client) filterChannelEntries(it *ChannelEntryIterator, excludedPkgName string) *opregistry.ChannelEntry {
105+
// first entry on the list from the default channel of that package
106+
func (rc *Client) filterChannelEntries(ctx context.Context, it *ChannelEntryIterator, excludedPkgName string) (*opregistry.ChannelEntry, error) {
107+
defChannels := make(map[string]string, 0)
108+
104109
var entries []*opregistry.ChannelEntry
105110
for e := it.Next(); e != nil; e = it.Next() {
106111
if e.PackageName != excludedPkgName {
107-
entry := &opregistry.ChannelEntry{
108-
PackageName: e.PackageName,
109-
ChannelName: e.ChannelName,
110-
BundleName: e.BundleName,
111-
Replaces: e.Replaces,
112+
// keep track of the default channel for each package
113+
if _, ok := defChannels[e.PackageName]; !ok {
114+
defChannel, err := rc.getDefaultPackageChannel(ctx, e.PackageName)
115+
if err != nil {
116+
return nil, err
117+
}
118+
defChannels[e.PackageName] = defChannel
119+
}
120+
121+
// only add entry to the list if the entry is in the default channel
122+
if e.ChannelName == defChannels[e.PackageName] {
123+
entry := &opregistry.ChannelEntry{
124+
PackageName: e.PackageName,
125+
ChannelName: e.ChannelName,
126+
BundleName: e.BundleName,
127+
Replaces: e.Replaces,
128+
}
129+
entries = append(entries, entry)
112130
}
113-
entries = append(entries, entry)
114131
}
115132
}
116133

117134
if entries != nil {
118-
return entries[0]
135+
return entries[0], nil
136+
}
137+
return nil, nil
138+
}
139+
140+
// GetDefaultPackageChannel uses registry client to get the default
141+
// channel name for a given package name
142+
func (rc *Client) getDefaultPackageChannel(ctx context.Context, pkgName string) (string, error) {
143+
pkg, err := rc.Client.Registry.GetPackage(ctx, &registryapi.GetPackageRequest{Name: pkgName})
144+
if err != nil {
145+
return "", err
146+
}
147+
148+
var defChan string
149+
if pkg != nil {
150+
defChan = pkg.DefaultChannelName
151+
} else {
152+
return "", fmt.Errorf("package %s not found in registry", pkgName)
119153
}
120-
return nil
154+
return defChan, nil
121155
}

test/e2e/subscription_e2e_test.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1349,6 +1349,8 @@ var _ = Describe("Subscription", func() {
13491349
csvB := newCSV("nginx-b-dep", testNamespace, "", semver.MustParse("0.1.0"), []apiextensions.CustomResourceDefinition{crd}, nil, depNamedStrategy)
13501350
// csvC provides CRD2 in the different catalogsource with csvA (apackage)
13511351
csvC := newCSV("nginx-c-dep", testNamespace, "", semver.MustParse("0.1.0"), []apiextensions.CustomResourceDefinition{crd2}, nil, depNamedStrategy2)
1352+
// csvD provides CRD1 in the same catalogsource with csvA (apackage)
1353+
csvD := newCSV("nginx-d-dep", testNamespace, "", semver.MustParse("0.1.0"), []apiextensions.CustomResourceDefinition{crd}, nil, depNamedStrategy)
13521354

13531355
// Create PackageManifests 1
13541356
// Contain csvA, ABC and B
@@ -1364,6 +1366,7 @@ var _ = Describe("Subscription", func() {
13641366
{
13651367
PackageName: packageName2,
13661368
Channels: []registry.PackageChannel{
1369+
{Name: alphaChannel, CurrentCSVName: csvD.GetName()},
13671370
{Name: stableChannel, CurrentCSVName: csvB.GetName()},
13681371
},
13691372
DefaultChannelName: stableChannel,
@@ -1383,7 +1386,7 @@ var _ = Describe("Subscription", func() {
13831386
}
13841387

13851388
catalogSourceName := genName("catsrc")
1386-
catsrc, cleanup := createInternalCatalogSource(kubeClient, crClient, catalogSourceName, testNamespace, manifests, []apiextensions.CustomResourceDefinition{crd, crd2}, []v1alpha1.ClusterServiceVersion{csvA, csvABC, csvB})
1389+
catsrc, cleanup := createInternalCatalogSource(kubeClient, crClient, catalogSourceName, testNamespace, manifests, []apiextensions.CustomResourceDefinition{crd, crd2}, []v1alpha1.ClusterServiceVersion{csvA, csvABC, csvB, csvD})
13871390
defer cleanup()
13881391

13891392
// Ensure that the catalog source is resolved before we create a subscription.
@@ -1429,6 +1432,9 @@ var _ = Describe("Subscription", func() {
14291432
// Ensure csvABC is not installed
14301433
_, err = crClient.OperatorsV1alpha1().ClusterServiceVersions(testNamespace).Get(context.TODO(), csvABC.Name, metav1.GetOptions{})
14311434
require.Error(GinkgoT(), err)
1435+
// Ensure csvD is not installed -- this implies the dependent subscription selected the default channel
1436+
_, err = crClient.OperatorsV1alpha1().ClusterServiceVersions(testNamespace).Get(context.TODO(), csvD.Name, metav1.GetOptions{})
1437+
require.Error(GinkgoT(), err)
14321438
})
14331439
})
14341440

0 commit comments

Comments
 (0)