Skip to content

Commit 0f23717

Browse files
kevinrizzaopenshift-cherrypick-robot
authored andcommitted
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 b57391e commit 0f23717

File tree

2 files changed

+47
-13
lines changed

2 files changed

+47
-13
lines changed

pkg/controller/registry/registry_client.go

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,9 @@ 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 := rc.filterChannelEntries(ctx, it, excludedPkgName)
9090
if entry == nil {
91-
return nil, fmt.Errorf("Unable to find a channel entry which doesn't belong to package %s", excludedPkgName)
91+
return nil, fmt.Errorf("Unable to find a channel entry which doesn't belong to package %s's default channel", excludedPkgName)
9292
}
9393
bundle, err := rc.Client.Registry.GetBundle(ctx, &registryapi.GetBundleRequest{PkgName: entry.PackageName, ChannelName: entry.ChannelName, CsvName: entry.BundleName})
9494
if err != nil {
@@ -97,25 +97,53 @@ func (rc *Client) FindBundleThatProvides(ctx context.Context, group, version, ki
9797
return bundle, nil
9898
}
9999

100-
// FilterChannelEntries filters out a channel entries that provide the requested
100+
// FilterChannelEntries filters out channel entries that provide the requested
101101
// 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 {
102+
// first entry on the list from the default channel of that package
103+
func (rc *Client) filterChannelEntries(ctx context.Context, it *ChannelEntryIterator, excludedPkgName string) *opregistry.ChannelEntry {
104+
defChannels := make(map[string]string, 0)
105+
104106
var entries []*opregistry.ChannelEntry
105107
for e := it.Next(); e != nil; e = it.Next() {
106108
if e.PackageName != excludedPkgName {
107-
entry := &opregistry.ChannelEntry{
108-
PackageName: e.PackageName,
109-
ChannelName: e.ChannelName,
110-
BundleName: e.BundleName,
111-
Replaces: e.Replaces,
109+
// keep track of the default channel for each package
110+
if _, ok := defChannels[e.PackageName]; !ok {
111+
defChannel, err := rc.getDefaultPackageChannel(ctx, e.PackageName)
112+
if err != nil {
113+
continue
114+
}
115+
defChannels[e.PackageName] = defChannel
116+
}
117+
118+
// only add entry to the list if the entry is in the default channel
119+
if e.ChannelName == defChannels[e.PackageName] {
120+
entry := &opregistry.ChannelEntry{
121+
PackageName: e.PackageName,
122+
ChannelName: e.ChannelName,
123+
BundleName: e.BundleName,
124+
Replaces: e.Replaces,
125+
}
126+
entries = append(entries, entry)
112127
}
113-
entries = append(entries, entry)
114128
}
115129
}
116130

117-
if entries != nil {
131+
if len(entries) > 0 {
118132
return entries[0]
119133
}
120134
return nil
121135
}
136+
137+
// GetDefaultPackageChannel uses registry client to get the default
138+
// channel name for a given package name
139+
func (rc *Client) getDefaultPackageChannel(ctx context.Context, pkgName string) (string, error) {
140+
pkg, err := rc.Client.Registry.GetPackage(ctx, &registryapi.GetPackageRequest{Name: pkgName})
141+
if err != nil {
142+
return "", err
143+
}
144+
if pkg == nil {
145+
return "", fmt.Errorf("package %s not found in registry", pkgName)
146+
}
147+
148+
return pkg.DefaultChannelName, nil
149+
}

test/e2e/subscription_e2e_test.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1497,6 +1497,8 @@ func TestCreateNewSubscriptionWithDependenciesSamePackage(t *testing.T) {
14971497
csvB := newCSV("nginx-b-dep", testNamespace, "", semver.MustParse("0.1.0"), []apiextensions.CustomResourceDefinition{crd}, nil, depNamedStrategy)
14981498
// csvC provides CRD2 in the different catalogsource with csvA (apackage)
14991499
csvC := newCSV("nginx-c-dep", testNamespace, "", semver.MustParse("0.1.0"), []apiextensions.CustomResourceDefinition{crd2}, nil, depNamedStrategy2)
1500+
// csvD provides CRD1 in the same catalogsource with csvA (apackage)
1501+
csvD := newCSV("nginx-d-dep", testNamespace, "", semver.MustParse("0.1.0"), []apiextensions.CustomResourceDefinition{crd}, nil, depNamedStrategy)
15001502

15011503
// Create PackageManifests 1
15021504
// Contain csvA, ABC and B
@@ -1512,6 +1514,7 @@ func TestCreateNewSubscriptionWithDependenciesSamePackage(t *testing.T) {
15121514
{
15131515
PackageName: packageName2,
15141516
Channels: []registry.PackageChannel{
1517+
{Name: alphaChannel, CurrentCSVName: csvD.GetName()},
15151518
{Name: stableChannel, CurrentCSVName: csvB.GetName()},
15161519
},
15171520
DefaultChannelName: stableChannel,
@@ -1531,7 +1534,7 @@ func TestCreateNewSubscriptionWithDependenciesSamePackage(t *testing.T) {
15311534
}
15321535

15331536
catalogSourceName := genName("catsrc")
1534-
catsrc, cleanup := createInternalCatalogSource(t, kubeClient, crClient, catalogSourceName, testNamespace, manifests, []apiextensions.CustomResourceDefinition{crd, crd2}, []v1alpha1.ClusterServiceVersion{csvA, csvABC, csvB})
1537+
catsrc, cleanup := createInternalCatalogSource(t, kubeClient, crClient, catalogSourceName, testNamespace, manifests, []apiextensions.CustomResourceDefinition{crd, crd2}, []v1alpha1.ClusterServiceVersion{csvA, csvABC, csvB, csvD})
15351538
defer cleanup()
15361539

15371540
// Ensure that the catalog source is resolved before we create a subscription.
@@ -1577,6 +1580,9 @@ func TestCreateNewSubscriptionWithDependenciesSamePackage(t *testing.T) {
15771580
// Ensure csvABC is not installed
15781581
_, err = crClient.OperatorsV1alpha1().ClusterServiceVersions(testNamespace).Get(csvABC.Name, metav1.GetOptions{})
15791582
require.Error(t, err)
1583+
// Ensure csvD is not installed -- this implies the dependent subscription selected the default channel
1584+
_, err = crClient.OperatorsV1alpha1().ClusterServiceVersions(testNamespace).Get(csvD.Name, metav1.GetOptions{})
1585+
require.Error(t, err)
15801586
}
15811587

15821588
func checkDeploymentWithPodConfiguration(t *testing.T, client operatorclient.ClientInterface, csv *v1alpha1.ClusterServiceVersion, envVar []corev1.EnvVar, volumes []corev1.Volume, volumeMounts []corev1.VolumeMount) {

0 commit comments

Comments
 (0)