github.com/operator-framework/operator-lifecycle-manager@v0.30.0/pkg/controller/registry/registry_client.go (about)

     1  package registry
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"io"
     7  
     8  	"google.golang.org/grpc"
     9  
    10  	registryapi "github.com/operator-framework/operator-registry/pkg/api"
    11  	"github.com/operator-framework/operator-registry/pkg/client"
    12  	opregistry "github.com/operator-framework/operator-registry/pkg/registry"
    13  )
    14  
    15  // ChannelEntryStream interface
    16  type ChannelEntryStream interface {
    17  	Recv() (*registryapi.ChannelEntry, error)
    18  }
    19  
    20  // ClientInterface that extends client.Interface
    21  type ClientInterface interface {
    22  	client.Interface
    23  	FindBundleThatProvides(ctx context.Context, group, version, kind string, excludedPackages map[string]struct{}) (*registryapi.Bundle, error)
    24  	GetLatestChannelEntriesThatProvide(ctx context.Context, group, version, kind string) (*ChannelEntryIterator, error)
    25  }
    26  
    27  // ChannelEntryIterator struct
    28  type ChannelEntryIterator struct {
    29  	stream ChannelEntryStream
    30  	error  error
    31  }
    32  
    33  // NewChannelEntryIterator returns a new ChannelEntryIterator
    34  func NewChannelEntryIterator(stream ChannelEntryStream) *ChannelEntryIterator {
    35  	return &ChannelEntryIterator{stream: stream}
    36  }
    37  
    38  // Next returns the next Channel Entry in the grpc stream
    39  func (ceit *ChannelEntryIterator) Next() *registryapi.ChannelEntry {
    40  	if ceit.error != nil {
    41  		return nil
    42  	}
    43  	next, err := ceit.stream.Recv()
    44  	if err == io.EOF {
    45  		return nil
    46  	}
    47  	if err != nil {
    48  		ceit.error = err
    49  	}
    50  	return next
    51  }
    52  
    53  func (ceit *ChannelEntryIterator) Error() error {
    54  	return ceit.error
    55  }
    56  
    57  // Client struct with a registry client embedded
    58  type Client struct {
    59  	*client.Client
    60  }
    61  
    62  // NewClientFromConn returns the next Channel Entry in the grpc stream
    63  func NewClientFromConn(conn *grpc.ClientConn) *Client {
    64  	return &Client{
    65  		Client: client.NewClientFromConn(conn),
    66  	}
    67  }
    68  
    69  var _ ClientInterface = &Client{}
    70  
    71  // GetLatestChannelEntriesThatProvide uses registry client to get a list of
    72  // latest channel entries that provide the requested API (via an iterator)
    73  func (rc *Client) GetLatestChannelEntriesThatProvide(ctx context.Context, group, version, kind string) (*ChannelEntryIterator, error) {
    74  	stream, err := rc.Client.Registry.GetLatestChannelEntriesThatProvide(ctx, &registryapi.GetLatestProvidersRequest{Group: group, Version: version, Kind: kind})
    75  	if err != nil {
    76  		return nil, err
    77  	}
    78  	return NewChannelEntryIterator(stream), nil
    79  }
    80  
    81  // FindBundleThatProvides returns a bundle that provides the request API and
    82  // doesn't belong to the provided package
    83  func (rc *Client) FindBundleThatProvides(ctx context.Context, group, version, kind string, excludedPackages map[string]struct{}) (*registryapi.Bundle, error) {
    84  	it, err := rc.GetLatestChannelEntriesThatProvide(ctx, group, version, kind)
    85  	if err != nil {
    86  		return nil, err
    87  	}
    88  	entry := rc.filterChannelEntries(ctx, it, excludedPackages)
    89  	if entry == nil {
    90  		return nil, fmt.Errorf("unable to find a channel entry that satisfies the requirements")
    91  	}
    92  	bundle, err := rc.Client.Registry.GetBundle(ctx, &registryapi.GetBundleRequest{PkgName: entry.PackageName, ChannelName: entry.ChannelName, CsvName: entry.BundleName})
    93  	if err != nil {
    94  		return nil, err
    95  	}
    96  	return bundle, nil
    97  }
    98  
    99  // FilterChannelEntries filters out channel entries that provide the requested
   100  // API and come from the same package with original operator and returns the
   101  // first entry on the list from the default channel of that package
   102  func (rc *Client) filterChannelEntries(ctx context.Context, it *ChannelEntryIterator, excludedPackages map[string]struct{}) *opregistry.ChannelEntry {
   103  	defChannels := make(map[string]string)
   104  
   105  	var entries []*opregistry.ChannelEntry
   106  	for e := it.Next(); e != nil; e = it.Next() {
   107  		if _, ok := excludedPackages[e.PackageName]; !ok {
   108  			// keep track of the default channel for each package
   109  			if _, ok := defChannels[e.PackageName]; !ok {
   110  				defChannel, err := rc.getDefaultPackageChannel(ctx, e.PackageName)
   111  				if err != nil {
   112  					continue
   113  				}
   114  				defChannels[e.PackageName] = defChannel
   115  			}
   116  
   117  			// only add entry to the list if the entry is in the default channel
   118  			if e.ChannelName == defChannels[e.PackageName] {
   119  				entry := &opregistry.ChannelEntry{
   120  					PackageName: e.PackageName,
   121  					ChannelName: e.ChannelName,
   122  					BundleName:  e.BundleName,
   123  					Replaces:    e.Replaces,
   124  				}
   125  				entries = append(entries, entry)
   126  			}
   127  		}
   128  	}
   129  
   130  	if len(entries) > 0 {
   131  		return entries[0]
   132  	}
   133  	return nil
   134  }
   135  
   136  // GetDefaultPackageChannel uses registry client to get the default
   137  // channel name for a given package name
   138  func (rc *Client) getDefaultPackageChannel(ctx context.Context, pkgName string) (string, error) {
   139  	pkg, err := rc.Client.Registry.GetPackage(ctx, &registryapi.GetPackageRequest{Name: pkgName})
   140  	if err != nil {
   141  		return "", err
   142  	}
   143  	if pkg == nil {
   144  		return "", fmt.Errorf("package %s not found in registry", pkgName)
   145  	}
   146  
   147  	return pkg.DefaultChannelName, nil
   148  }