Skip to content

Commit 3b76e67

Browse files
committed
fix(resolver): don't pick operator from same package when resolve requiredAPI
When resolver looks for an operator that provides requiredAPIs, it doesn't exclude operators from the same package that the original operator that requires those APIs. As a result, if there is an existing version of that operator now provides those requiredAPIs (due to API ownership transfer), resolver will select an older or newer version of the same operator to fulfill that requiredAPIs which leads to API ownership conflicts on providedAPIs. The resolver will look up all latest channel entries that provides the necessary requiredAPIs and filter out those from the same package before go on with the operator selection. Signed-off-by: Vu Dinh <[email protected]>
1 parent d23b7ab commit 3b76e67

File tree

4 files changed

+101
-16
lines changed

4 files changed

+101
-16
lines changed

pkg/controller/registry/grpc/source.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -190,8 +190,9 @@ func (s *SourceStore) Remove(key resolver.CatalogKey) error {
190190
return source.Conn.Close()
191191
}
192192

193-
func (s *SourceStore) AsClients(namespaces ...string) map[resolver.CatalogKey]client.Interface {
194-
refs := map[resolver.CatalogKey]client.Interface{}
193+
func (s *SourceStore) AsClients(namespaces ...string) (map[resolver.CatalogKey]client.Interface, map[resolver.CatalogKey]*client.Client) {
194+
refsInterface := map[resolver.CatalogKey]client.Interface{}
195+
refsClient := map[resolver.CatalogKey]*client.Client{}
195196
s.sourcesLock.RLock()
196197
defer s.sourcesLock.RUnlock()
197198
for key, source := range s.sources {
@@ -200,11 +201,12 @@ func (s *SourceStore) AsClients(namespaces ...string) map[resolver.CatalogKey]cl
200201
}
201202
for _, namespace := range namespaces {
202203
if key.Namespace == namespace {
203-
refs[key] = client.NewClientFromConn(source.Conn)
204+
refsInterface[key] = client.NewClientFromConn(source.Conn)
205+
refsClient[key] = client.NewClientFromConn(source.Conn)
204206
}
205207
}
206208
}
207209

208210
// TODO : remove unhealthy
209-
return refs
211+
return refsInterface, refsClient
210212
}

pkg/controller/registry/resolver/evolver.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ func (e *NamespaceGenerationEvolver) queryForRequiredAPIs() error {
134134
}
135135

136136
// attempt to find a bundle that provides that api
137-
if bundle, key, err := e.querier.FindProvider(*api, initialSource.Catalog); err == nil {
137+
if bundle, key, err := e.querier.FindProvider(*api, initialSource.Catalog, initialSource.Package); err == nil {
138138
// add a bundle that provides the api to the generation
139139
o, err := NewOperatorFromBundle(bundle, "", *key)
140140
if err != nil {

pkg/controller/registry/resolver/querier.go

Lines changed: 92 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@ package resolver
44
import (
55
"context"
66
"fmt"
7+
"io"
78

89
"github.com/blang/semver"
910
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1011
"k8s.io/apimachinery/pkg/util/errors"
1112

1213
"github.com/operator-framework/operator-registry/pkg/api"
14+
registryapi "github.com/operator-framework/operator-registry/pkg/api"
1315
"github.com/operator-framework/operator-registry/pkg/client"
1416
opregistry "github.com/operator-framework/operator-registry/pkg/registry"
1517
)
@@ -24,7 +26,7 @@ type SourceRef struct {
2426
}
2527

2628
type SourceQuerier interface {
27-
FindProvider(api opregistry.APIKey, initialSource CatalogKey) (*api.Bundle, *CatalogKey, error)
29+
FindProvider(api opregistry.APIKey, initialSource CatalogKey, pkgName string) (*api.Bundle, *CatalogKey, error)
2830
FindBundle(pkgName, channelName, bundleName string, initialSource CatalogKey) (*api.Bundle, *CatalogKey, error)
2931
FindLatestBundle(pkgName, channelName string, initialSource CatalogKey) (*api.Bundle, *CatalogKey, error)
3032
FindReplacement(currentVersion *semver.Version, bundleName, pkgName, channelName string, initialSource CatalogKey) (*api.Bundle, *CatalogKey, error)
@@ -33,13 +35,46 @@ type SourceQuerier interface {
3335

3436
type NamespaceSourceQuerier struct {
3537
sources map[CatalogKey]client.Interface
38+
clients map[CatalogKey]*client.Client
3639
}
3740

3841
var _ SourceQuerier = &NamespaceSourceQuerier{}
3942

40-
func NewNamespaceSourceQuerier(sources map[CatalogKey]client.Interface) *NamespaceSourceQuerier {
43+
type ChannelEntryStream interface {
44+
Recv() (*api.ChannelEntry, error)
45+
}
46+
47+
type ChannelEntryIterator struct {
48+
stream ChannelEntryStream
49+
error error
50+
}
51+
52+
func NewChannelEntryIterator(stream ChannelEntryStream) *ChannelEntryIterator {
53+
return &ChannelEntryIterator{stream: stream}
54+
}
55+
56+
func (ceit *ChannelEntryIterator) Next() *registryapi.ChannelEntry {
57+
if ceit.error != nil {
58+
return nil
59+
}
60+
next, err := ceit.stream.Recv()
61+
if err == io.EOF {
62+
return nil
63+
}
64+
if err != nil {
65+
ceit.error = err
66+
}
67+
return next
68+
}
69+
70+
func (ceit *ChannelEntryIterator) Error() error {
71+
return ceit.error
72+
}
73+
74+
func NewNamespaceSourceQuerier(sources map[CatalogKey]client.Interface, clients map[CatalogKey]*client.Client) *NamespaceSourceQuerier {
4175
return &NamespaceSourceQuerier{
4276
sources: sources,
77+
clients: clients,
4378
}
4479
}
4580

@@ -50,23 +85,23 @@ func (q *NamespaceSourceQuerier) Queryable() error {
5085
return nil
5186
}
5287

53-
func (q *NamespaceSourceQuerier) FindProvider(api opregistry.APIKey, initialSource CatalogKey) (*api.Bundle, *CatalogKey, error) {
88+
func (q *NamespaceSourceQuerier) FindProvider(api opregistry.APIKey, initialSource CatalogKey, pkgName string) (*registryapi.Bundle, *CatalogKey, error) {
5489
if initialSource.Name != "" && initialSource.Namespace != "" {
55-
source, ok := q.sources[initialSource]
90+
client, ok := q.clients[initialSource]
5691
if ok {
57-
if bundle, err := source.GetBundleThatProvides(context.TODO(), api.Group, api.Version, api.Kind); err == nil {
92+
if bundle, err := FindBundleThatProvides(context.TODO(), client, api.Group, api.Version, api.Kind, pkgName); err == nil {
5893
return bundle, &initialSource, nil
5994
}
60-
if bundle, err := source.GetBundleThatProvides(context.TODO(), api.Plural+"."+api.Group, api.Version, api.Kind); err == nil {
95+
if bundle, err := FindBundleThatProvides(context.TODO(), client, api.Plural+"."+api.Group, api.Version, api.Kind, pkgName); err == nil {
6196
return bundle, &initialSource, nil
6297
}
6398
}
6499
}
65-
for key, source := range q.sources {
66-
if bundle, err := source.GetBundleThatProvides(context.TODO(), api.Group, api.Version, api.Kind); err == nil {
100+
for key, client := range q.clients {
101+
if bundle, err := FindBundleThatProvides(context.TODO(), client, api.Group, api.Version, api.Kind, pkgName); err == nil {
67102
return bundle, &key, nil
68103
}
69-
if bundle, err := source.GetBundleThatProvides(context.TODO(), api.Plural+"."+api.Group, api.Version, api.Kind); err == nil {
104+
if bundle, err := FindBundleThatProvides(context.TODO(), client, api.Plural+"."+api.Group, api.Version, api.Kind, pkgName); err == nil {
70105
return bundle, &key, nil
71106
}
72107
}
@@ -191,3 +226,51 @@ func (q *NamespaceSourceQuerier) findChannelHead(currentVersion *semver.Version,
191226
}
192227
return nil, nil
193228
}
229+
230+
// GetLatestChannelEntriesThatProvide uses registry client to get a list of
231+
// latest channel entries that provide the requested API (via an iterator)
232+
func GetLatestChannelEntriesThatProvide(ctx context.Context, c *client.Client, group, version, kind string) (*ChannelEntryIterator, error) {
233+
stream, err := c.Registry.GetLatestChannelEntriesThatProvide(ctx, &registryapi.GetLatestProvidersRequest{Group: group, Version: version, Kind: kind})
234+
if err != nil {
235+
return nil, err
236+
}
237+
return NewChannelEntryIterator(stream), nil
238+
}
239+
240+
// FilterChannelEntries filters out a channel entries that provide the requested
241+
// API and come from the same package with original operator and returns the
242+
// first entry on the list
243+
func FilterChannelEntries(it *ChannelEntryIterator, pkgName string) *opregistry.ChannelEntry {
244+
var entry *opregistry.ChannelEntry
245+
for e := it.Next(); e != nil; e = it.Next() {
246+
if e.PackageName != pkgName {
247+
entry = &opregistry.ChannelEntry{
248+
PackageName: e.PackageName,
249+
ChannelName: e.ChannelName,
250+
BundleName: e.BundleName,
251+
Replaces: e.Replaces,
252+
}
253+
break
254+
}
255+
}
256+
return entry
257+
}
258+
259+
// FindBundleThatProvides returns a bundle that provides the request API and
260+
// doesn't belong to the provided package
261+
func FindBundleThatProvides(ctx context.Context, client *client.Client, group, version, kind, pkgName string) (*api.Bundle, error) {
262+
it, err := GetLatestChannelEntriesThatProvide(ctx, client, group, version, kind)
263+
if err != nil {
264+
return nil, err
265+
}
266+
267+
entry := FilterChannelEntries(it, pkgName)
268+
if entry != nil {
269+
return nil, fmt.Errorf("Unable to find a channel entry which doesn't belong to package %s", pkgName)
270+
}
271+
bundle, err := client.Registry.GetBundle(ctx, &registryapi.GetBundleRequest{PkgName: entry.PackageName, ChannelName: entry.ChannelName, CsvName: entry.BundleName})
272+
if err != nil {
273+
return nil, err
274+
}
275+
return bundle, nil
276+
}

pkg/controller/registry/resolver/querier_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ import (
1313
"github.com/stretchr/testify/require"
1414
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1515

16+
"github.com/operator-framework/api/pkg/lib/version"
1617
"github.com/operator-framework/api/pkg/operators/v1alpha1"
1718
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/fakes"
18-
"github.com/operator-framework/api/pkg/lib/version"
1919
)
2020

2121
func TestNewNamespaceSourceQuerier(t *testing.T) {
@@ -207,7 +207,7 @@ func TestNamespaceSourceQuerier_FindProvider(t *testing.T) {
207207
q := &NamespaceSourceQuerier{
208208
sources: tt.fields.sources,
209209
}
210-
bundle, key, err := q.FindProvider(tt.args.api, tt.args.catalogKey)
210+
bundle, key, err := q.FindProvider(tt.args.api, tt.args.catalogKey, "")
211211
require.Equal(t, tt.out.err, err)
212212
require.Equal(t, tt.out.bundle, bundle)
213213
require.Equal(t, tt.out.key, key)

0 commit comments

Comments
 (0)