Skip to content

Bug 1834936: fix(resolver): don't pick operator from same package when resolve requiredAPI #1521

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8 changes: 4 additions & 4 deletions pkg/controller/registry/grpc/source.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import (
"google.golang.org/grpc"
"google.golang.org/grpc/connectivity"

"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry"
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver"
"github.com/operator-framework/operator-registry/pkg/client"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
Expand Down Expand Up @@ -190,8 +190,8 @@ func (s *SourceStore) Remove(key resolver.CatalogKey) error {
return source.Conn.Close()
}

func (s *SourceStore) AsClients(namespaces ...string) map[resolver.CatalogKey]client.Interface {
refs := map[resolver.CatalogKey]client.Interface{}
func (s *SourceStore) AsClients(namespaces ...string) map[resolver.CatalogKey]registry.ClientInterface {
refs := map[resolver.CatalogKey]registry.ClientInterface{}
s.sourcesLock.RLock()
defer s.sourcesLock.RUnlock()
for key, source := range s.sources {
Expand All @@ -200,7 +200,7 @@ func (s *SourceStore) AsClients(namespaces ...string) map[resolver.CatalogKey]cl
}
for _, namespace := range namespaces {
if key.Namespace == namespace {
refs[key] = client.NewClientFromConn(source.Conn)
refs[key] = registry.NewClientFromConn(source.Conn)
}
}
}
Expand Down
121 changes: 121 additions & 0 deletions pkg/controller/registry/registry_client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
//go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 -o resolver/fakes/fake_registry_client_interface.go . ClientInterface
package registry

import (
"context"
"fmt"
"io"

"google.golang.org/grpc"

registryapi "github.com/operator-framework/operator-registry/pkg/api"
"github.com/operator-framework/operator-registry/pkg/client"
opregistry "github.com/operator-framework/operator-registry/pkg/registry"
)

// ChannelEntryStream interface
type ChannelEntryStream interface {
Recv() (*registryapi.ChannelEntry, error)
}

// ClientInterface that extends client.Interface
type ClientInterface interface {
client.Interface
FindBundleThatProvides(ctx context.Context, group, version, kind, excludedPkgName string) (*registryapi.Bundle, error)
GetLatestChannelEntriesThatProvide(ctx context.Context, group, version, kind string) (*ChannelEntryIterator, error)
}

// ChannelEntryIterator struct
type ChannelEntryIterator struct {
stream ChannelEntryStream
error error
}

// NewChannelEntryIterator returns a new ChannelEntryIterator
func NewChannelEntryIterator(stream ChannelEntryStream) *ChannelEntryIterator {
return &ChannelEntryIterator{stream: stream}
}

// Next returns the next Channel Entry in the grpc stream
func (ceit *ChannelEntryIterator) Next() *registryapi.ChannelEntry {
if ceit.error != nil {
return nil
}
next, err := ceit.stream.Recv()
if err == io.EOF {
return nil
}
if err != nil {
ceit.error = err
}
return next
}

func (ceit *ChannelEntryIterator) Error() error {
return ceit.error
}

// Client struct with a registry client embedded
type Client struct {
*client.Client
}

// NewClientFromConn returns the next Channel Entry in the grpc stream
func NewClientFromConn(conn *grpc.ClientConn) *Client {
return &Client{
Client: client.NewClientFromConn(conn),
}
}

var _ ClientInterface = &Client{}

// GetLatestChannelEntriesThatProvide uses registry client to get a list of
// latest channel entries that provide the requested API (via an iterator)
func (rc *Client) GetLatestChannelEntriesThatProvide(ctx context.Context, group, version, kind string) (*ChannelEntryIterator, error) {
stream, err := rc.Client.Registry.GetLatestChannelEntriesThatProvide(ctx, &registryapi.GetLatestProvidersRequest{Group: group, Version: version, Kind: kind})
if err != nil {
return nil, err
}
return NewChannelEntryIterator(stream), nil
}

// FindBundleThatProvides returns a bundle that provides the request API and
// doesn't belong to the provided package
func (rc *Client) FindBundleThatProvides(ctx context.Context, group, version, kind, excludedPkgName string) (*registryapi.Bundle, error) {
it, err := rc.GetLatestChannelEntriesThatProvide(ctx, group, version, kind)
if err != nil {
return nil, err
}
entry := rc.filterChannelEntries(it, excludedPkgName)
if entry == nil {
return nil, fmt.Errorf("Unable to find a channel entry which doesn't belong to package %s", excludedPkgName)
}
bundle, err := rc.Client.Registry.GetBundle(ctx, &registryapi.GetBundleRequest{PkgName: entry.PackageName, ChannelName: entry.ChannelName, CsvName: entry.BundleName})
if err != nil {
return nil, err
}
return bundle, nil
}

// FilterChannelEntries filters out a channel entries that provide the requested
// API and come from the same package with original operator and returns the
// first entry on the list
func (rc *Client) filterChannelEntries(it *ChannelEntryIterator, excludedPkgName string) *opregistry.ChannelEntry {
var entries []*opregistry.ChannelEntry
for e := it.Next(); e != nil; e = it.Next() {
if e.PackageName != excludedPkgName {
entry := &opregistry.ChannelEntry{
PackageName: e.PackageName,
ChannelName: e.ChannelName,
BundleName: e.BundleName,
Replaces: e.Replaces,
}
entries = append(entries, entry)
}
}

if entries != nil {
return entries[0]
}
return nil
}
2 changes: 1 addition & 1 deletion pkg/controller/registry/resolver/evolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ func (e *NamespaceGenerationEvolver) queryForRequiredAPIs() error {
}

// attempt to find a bundle that provides that api
if bundle, key, err := e.querier.FindProvider(*api, initialSource.Catalog); err == nil {
if bundle, key, err := e.querier.FindProvider(*api, initialSource.Catalog, initialSource.Package); err == nil {
// add a bundle that provides the api to the generation
o, err := NewOperatorFromBundle(bundle, "", *key)
if err != nil {
Expand Down
Loading