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, ®istryapi.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, ®istryapi.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, ®istryapi.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 }