diff --git a/pkg/package-server/apis/operators/packagemanifest_types.go b/pkg/package-server/apis/operators/packagemanifest_types.go index d31b8d68bd..e07615576a 100644 --- a/pkg/package-server/apis/operators/packagemanifest_types.go +++ b/pkg/package-server/apis/operators/packagemanifest_types.go @@ -1,10 +1,9 @@ package operators import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "github.com/operator-framework/api/pkg/lib/version" operatorv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) // PackageManifestList is a list of PackageManifest objects. @@ -81,6 +80,18 @@ type PackageChannel struct { // CurrentCSVSpec holds the spec of the current CSV CurrentCSVDesc CSVDescription + + // Entries lists all CSVs in the channel. + Entries []ChannelEntry +} + +// ChannelEntry defines a member of a package channel. +type ChannelEntry struct { + // Name is the name of the bundle for this entry. + Name string + + // Version is the version of the bundle for this entry. + Version string } // CSVDescription defines a description of a CSV diff --git a/pkg/package-server/apis/operators/v1/packagemanifest_types.go b/pkg/package-server/apis/operators/v1/packagemanifest_types.go index 6ad6949c55..c1c24576c7 100644 --- a/pkg/package-server/apis/operators/v1/packagemanifest_types.go +++ b/pkg/package-server/apis/operators/v1/packagemanifest_types.go @@ -1,10 +1,9 @@ package v1 import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "github.com/operator-framework/api/pkg/lib/version" operatorv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) // PackageManifestList is a list of PackageManifest objects. @@ -81,6 +80,18 @@ type PackageChannel struct { // CurrentCSVSpec holds the spec of the current CSV CurrentCSVDesc CSVDescription `json:"currentCSVDesc,omitempty"` + + // Entries lists all CSVs in the channel, with their upgrade edges. + Entries []ChannelEntry `json:"entries"` +} + +// ChannelEntry defines a member of a package channel. +type ChannelEntry struct { + // Name is the name of the bundle for this entry. + Name string `json:"name"` + + // Version is the version of the bundle for this entry. + Version string `json:"version,omitempty"` } // CSVDescription defines a description of a CSV diff --git a/pkg/package-server/apis/operators/v1/zz_generated.conversion.go b/pkg/package-server/apis/operators/v1/zz_generated.conversion.go index b87fb8266c..2bff55779b 100644 --- a/pkg/package-server/apis/operators/v1/zz_generated.conversion.go +++ b/pkg/package-server/apis/operators/v1/zz_generated.conversion.go @@ -58,6 +58,16 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddGeneratedConversionFunc((*ChannelEntry)(nil), (*operators.ChannelEntry)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1_ChannelEntry_To_operators_ChannelEntry(a.(*ChannelEntry), b.(*operators.ChannelEntry), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*operators.ChannelEntry)(nil), (*ChannelEntry)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_operators_ChannelEntry_To_v1_ChannelEntry(a.(*operators.ChannelEntry), b.(*ChannelEntry), scope) + }); err != nil { + return err + } if err := s.AddGeneratedConversionFunc((*Icon)(nil), (*operators.Icon)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1_Icon_To_operators_Icon(a.(*Icon), b.(*operators.Icon), scope) }); err != nil { @@ -207,6 +217,28 @@ func Convert_operators_CSVDescription_To_v1_CSVDescription(in *operators.CSVDesc return autoConvert_operators_CSVDescription_To_v1_CSVDescription(in, out, s) } +func autoConvert_v1_ChannelEntry_To_operators_ChannelEntry(in *ChannelEntry, out *operators.ChannelEntry, s conversion.Scope) error { + out.Name = in.Name + out.Version = in.Version + return nil +} + +// Convert_v1_ChannelEntry_To_operators_ChannelEntry is an autogenerated conversion function. +func Convert_v1_ChannelEntry_To_operators_ChannelEntry(in *ChannelEntry, out *operators.ChannelEntry, s conversion.Scope) error { + return autoConvert_v1_ChannelEntry_To_operators_ChannelEntry(in, out, s) +} + +func autoConvert_operators_ChannelEntry_To_v1_ChannelEntry(in *operators.ChannelEntry, out *ChannelEntry, s conversion.Scope) error { + out.Name = in.Name + out.Version = in.Version + return nil +} + +// Convert_operators_ChannelEntry_To_v1_ChannelEntry is an autogenerated conversion function. +func Convert_operators_ChannelEntry_To_v1_ChannelEntry(in *operators.ChannelEntry, out *ChannelEntry, s conversion.Scope) error { + return autoConvert_operators_ChannelEntry_To_v1_ChannelEntry(in, out, s) +} + func autoConvert_v1_Icon_To_operators_Icon(in *Icon, out *operators.Icon, s conversion.Scope) error { out.Base64Data = in.Base64Data out.Mediatype = in.Mediatype @@ -257,6 +289,7 @@ func autoConvert_v1_PackageChannel_To_operators_PackageChannel(in *PackageChanne if err := Convert_v1_CSVDescription_To_operators_CSVDescription(&in.CurrentCSVDesc, &out.CurrentCSVDesc, s); err != nil { return err } + out.Entries = *(*[]operators.ChannelEntry)(unsafe.Pointer(&in.Entries)) return nil } @@ -271,6 +304,7 @@ func autoConvert_operators_PackageChannel_To_v1_PackageChannel(in *operators.Pac if err := Convert_operators_CSVDescription_To_v1_CSVDescription(&in.CurrentCSVDesc, &out.CurrentCSVDesc, s); err != nil { return err } + out.Entries = *(*[]ChannelEntry)(unsafe.Pointer(&in.Entries)) return nil } diff --git a/pkg/package-server/apis/operators/v1/zz_generated.deepcopy.go b/pkg/package-server/apis/operators/v1/zz_generated.deepcopy.go index 3949989895..7147c0955e 100644 --- a/pkg/package-server/apis/operators/v1/zz_generated.deepcopy.go +++ b/pkg/package-server/apis/operators/v1/zz_generated.deepcopy.go @@ -105,6 +105,22 @@ func (in *CSVDescription) DeepCopy() *CSVDescription { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ChannelEntry) DeepCopyInto(out *ChannelEntry) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ChannelEntry. +func (in *ChannelEntry) DeepCopy() *ChannelEntry { + if in == nil { + return nil + } + out := new(ChannelEntry) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Icon) DeepCopyInto(out *Icon) { *out = *in @@ -141,6 +157,11 @@ func (in *Maintainer) DeepCopy() *Maintainer { func (in *PackageChannel) DeepCopyInto(out *PackageChannel) { *out = *in in.CurrentCSVDesc.DeepCopyInto(&out.CurrentCSVDesc) + if in.Entries != nil { + in, out := &in.Entries, &out.Entries + *out = make([]ChannelEntry, len(*in)) + copy(*out, *in) + } return } diff --git a/pkg/package-server/apis/operators/zz_generated.deepcopy.go b/pkg/package-server/apis/operators/zz_generated.deepcopy.go index c7fd15e884..e3b4d8f1ba 100644 --- a/pkg/package-server/apis/operators/zz_generated.deepcopy.go +++ b/pkg/package-server/apis/operators/zz_generated.deepcopy.go @@ -105,6 +105,22 @@ func (in *CSVDescription) DeepCopy() *CSVDescription { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ChannelEntry) DeepCopyInto(out *ChannelEntry) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ChannelEntry. +func (in *ChannelEntry) DeepCopy() *ChannelEntry { + if in == nil { + return nil + } + out := new(ChannelEntry) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Icon) DeepCopyInto(out *Icon) { *out = *in @@ -141,6 +157,11 @@ func (in *Maintainer) DeepCopy() *Maintainer { func (in *PackageChannel) DeepCopyInto(out *PackageChannel) { *out = *in in.CurrentCSVDesc.DeepCopyInto(&out.CurrentCSVDesc) + if in.Entries != nil { + in, out := &in.Entries, &out.Entries + *out = make([]ChannelEntry, len(*in)) + copy(*out, *in) + } return } diff --git a/pkg/package-server/client/fakes/fake_list_bundles_client.go b/pkg/package-server/client/fakes/fake_list_bundles_client.go new file mode 100644 index 0000000000..5f530a7534 --- /dev/null +++ b/pkg/package-server/client/fakes/fake_list_bundles_client.go @@ -0,0 +1,515 @@ +// Code generated by counterfeiter. DO NOT EDIT. +package fakes + +import ( + "context" + "sync" + + "github.com/operator-framework/operator-registry/pkg/api" + "google.golang.org/grpc/metadata" +) + +type FakeRegistry_ListBundlesClient struct { + CloseSendStub func() error + closeSendMutex sync.RWMutex + closeSendArgsForCall []struct { + } + closeSendReturns struct { + result1 error + } + closeSendReturnsOnCall map[int]struct { + result1 error + } + ContextStub func() context.Context + contextMutex sync.RWMutex + contextArgsForCall []struct { + } + contextReturns struct { + result1 context.Context + } + contextReturnsOnCall map[int]struct { + result1 context.Context + } + HeaderStub func() (metadata.MD, error) + headerMutex sync.RWMutex + headerArgsForCall []struct { + } + headerReturns struct { + result1 metadata.MD + result2 error + } + headerReturnsOnCall map[int]struct { + result1 metadata.MD + result2 error + } + RecvStub func() (*api.Bundle, error) + recvMutex sync.RWMutex + recvArgsForCall []struct { + } + recvReturns struct { + result1 *api.Bundle + result2 error + } + recvReturnsOnCall map[int]struct { + result1 *api.Bundle + result2 error + } + RecvMsgStub func(interface{}) error + recvMsgMutex sync.RWMutex + recvMsgArgsForCall []struct { + arg1 interface{} + } + recvMsgReturns struct { + result1 error + } + recvMsgReturnsOnCall map[int]struct { + result1 error + } + SendMsgStub func(interface{}) error + sendMsgMutex sync.RWMutex + sendMsgArgsForCall []struct { + arg1 interface{} + } + sendMsgReturns struct { + result1 error + } + sendMsgReturnsOnCall map[int]struct { + result1 error + } + TrailerStub func() metadata.MD + trailerMutex sync.RWMutex + trailerArgsForCall []struct { + } + trailerReturns struct { + result1 metadata.MD + } + trailerReturnsOnCall map[int]struct { + result1 metadata.MD + } + invocations map[string][][]interface{} + invocationsMutex sync.RWMutex +} + +func (fake *FakeRegistry_ListBundlesClient) CloseSend() error { + fake.closeSendMutex.Lock() + ret, specificReturn := fake.closeSendReturnsOnCall[len(fake.closeSendArgsForCall)] + fake.closeSendArgsForCall = append(fake.closeSendArgsForCall, struct { + }{}) + fake.recordInvocation("CloseSend", []interface{}{}) + fake.closeSendMutex.Unlock() + if fake.CloseSendStub != nil { + return fake.CloseSendStub() + } + if specificReturn { + return ret.result1 + } + fakeReturns := fake.closeSendReturns + return fakeReturns.result1 +} + +func (fake *FakeRegistry_ListBundlesClient) CloseSendCallCount() int { + fake.closeSendMutex.RLock() + defer fake.closeSendMutex.RUnlock() + return len(fake.closeSendArgsForCall) +} + +func (fake *FakeRegistry_ListBundlesClient) CloseSendCalls(stub func() error) { + fake.closeSendMutex.Lock() + defer fake.closeSendMutex.Unlock() + fake.CloseSendStub = stub +} + +func (fake *FakeRegistry_ListBundlesClient) CloseSendReturns(result1 error) { + fake.closeSendMutex.Lock() + defer fake.closeSendMutex.Unlock() + fake.CloseSendStub = nil + fake.closeSendReturns = struct { + result1 error + }{result1} +} + +func (fake *FakeRegistry_ListBundlesClient) CloseSendReturnsOnCall(i int, result1 error) { + fake.closeSendMutex.Lock() + defer fake.closeSendMutex.Unlock() + fake.CloseSendStub = nil + if fake.closeSendReturnsOnCall == nil { + fake.closeSendReturnsOnCall = make(map[int]struct { + result1 error + }) + } + fake.closeSendReturnsOnCall[i] = struct { + result1 error + }{result1} +} + +func (fake *FakeRegistry_ListBundlesClient) Context() context.Context { + fake.contextMutex.Lock() + ret, specificReturn := fake.contextReturnsOnCall[len(fake.contextArgsForCall)] + fake.contextArgsForCall = append(fake.contextArgsForCall, struct { + }{}) + fake.recordInvocation("Context", []interface{}{}) + fake.contextMutex.Unlock() + if fake.ContextStub != nil { + return fake.ContextStub() + } + if specificReturn { + return ret.result1 + } + fakeReturns := fake.contextReturns + return fakeReturns.result1 +} + +func (fake *FakeRegistry_ListBundlesClient) ContextCallCount() int { + fake.contextMutex.RLock() + defer fake.contextMutex.RUnlock() + return len(fake.contextArgsForCall) +} + +func (fake *FakeRegistry_ListBundlesClient) ContextCalls(stub func() context.Context) { + fake.contextMutex.Lock() + defer fake.contextMutex.Unlock() + fake.ContextStub = stub +} + +func (fake *FakeRegistry_ListBundlesClient) ContextReturns(result1 context.Context) { + fake.contextMutex.Lock() + defer fake.contextMutex.Unlock() + fake.ContextStub = nil + fake.contextReturns = struct { + result1 context.Context + }{result1} +} + +func (fake *FakeRegistry_ListBundlesClient) ContextReturnsOnCall(i int, result1 context.Context) { + fake.contextMutex.Lock() + defer fake.contextMutex.Unlock() + fake.ContextStub = nil + if fake.contextReturnsOnCall == nil { + fake.contextReturnsOnCall = make(map[int]struct { + result1 context.Context + }) + } + fake.contextReturnsOnCall[i] = struct { + result1 context.Context + }{result1} +} + +func (fake *FakeRegistry_ListBundlesClient) Header() (metadata.MD, error) { + fake.headerMutex.Lock() + ret, specificReturn := fake.headerReturnsOnCall[len(fake.headerArgsForCall)] + fake.headerArgsForCall = append(fake.headerArgsForCall, struct { + }{}) + fake.recordInvocation("Header", []interface{}{}) + fake.headerMutex.Unlock() + if fake.HeaderStub != nil { + return fake.HeaderStub() + } + if specificReturn { + return ret.result1, ret.result2 + } + fakeReturns := fake.headerReturns + return fakeReturns.result1, fakeReturns.result2 +} + +func (fake *FakeRegistry_ListBundlesClient) HeaderCallCount() int { + fake.headerMutex.RLock() + defer fake.headerMutex.RUnlock() + return len(fake.headerArgsForCall) +} + +func (fake *FakeRegistry_ListBundlesClient) HeaderCalls(stub func() (metadata.MD, error)) { + fake.headerMutex.Lock() + defer fake.headerMutex.Unlock() + fake.HeaderStub = stub +} + +func (fake *FakeRegistry_ListBundlesClient) HeaderReturns(result1 metadata.MD, result2 error) { + fake.headerMutex.Lock() + defer fake.headerMutex.Unlock() + fake.HeaderStub = nil + fake.headerReturns = struct { + result1 metadata.MD + result2 error + }{result1, result2} +} + +func (fake *FakeRegistry_ListBundlesClient) HeaderReturnsOnCall(i int, result1 metadata.MD, result2 error) { + fake.headerMutex.Lock() + defer fake.headerMutex.Unlock() + fake.HeaderStub = nil + if fake.headerReturnsOnCall == nil { + fake.headerReturnsOnCall = make(map[int]struct { + result1 metadata.MD + result2 error + }) + } + fake.headerReturnsOnCall[i] = struct { + result1 metadata.MD + result2 error + }{result1, result2} +} + +func (fake *FakeRegistry_ListBundlesClient) Recv() (*api.Bundle, error) { + fake.recvMutex.Lock() + ret, specificReturn := fake.recvReturnsOnCall[len(fake.recvArgsForCall)] + fake.recvArgsForCall = append(fake.recvArgsForCall, struct { + }{}) + fake.recordInvocation("Recv", []interface{}{}) + fake.recvMutex.Unlock() + if fake.RecvStub != nil { + return fake.RecvStub() + } + if specificReturn { + return ret.result1, ret.result2 + } + fakeReturns := fake.recvReturns + return fakeReturns.result1, fakeReturns.result2 +} + +func (fake *FakeRegistry_ListBundlesClient) RecvCallCount() int { + fake.recvMutex.RLock() + defer fake.recvMutex.RUnlock() + return len(fake.recvArgsForCall) +} + +func (fake *FakeRegistry_ListBundlesClient) RecvCalls(stub func() (*api.Bundle, error)) { + fake.recvMutex.Lock() + defer fake.recvMutex.Unlock() + fake.RecvStub = stub +} + +func (fake *FakeRegistry_ListBundlesClient) RecvReturns(result1 *api.Bundle, result2 error) { + fake.recvMutex.Lock() + defer fake.recvMutex.Unlock() + fake.RecvStub = nil + fake.recvReturns = struct { + result1 *api.Bundle + result2 error + }{result1, result2} +} + +func (fake *FakeRegistry_ListBundlesClient) RecvReturnsOnCall(i int, result1 *api.Bundle, result2 error) { + fake.recvMutex.Lock() + defer fake.recvMutex.Unlock() + fake.RecvStub = nil + if fake.recvReturnsOnCall == nil { + fake.recvReturnsOnCall = make(map[int]struct { + result1 *api.Bundle + result2 error + }) + } + fake.recvReturnsOnCall[i] = struct { + result1 *api.Bundle + result2 error + }{result1, result2} +} + +func (fake *FakeRegistry_ListBundlesClient) RecvMsg(arg1 interface{}) error { + fake.recvMsgMutex.Lock() + ret, specificReturn := fake.recvMsgReturnsOnCall[len(fake.recvMsgArgsForCall)] + fake.recvMsgArgsForCall = append(fake.recvMsgArgsForCall, struct { + arg1 interface{} + }{arg1}) + fake.recordInvocation("RecvMsg", []interface{}{arg1}) + fake.recvMsgMutex.Unlock() + if fake.RecvMsgStub != nil { + return fake.RecvMsgStub(arg1) + } + if specificReturn { + return ret.result1 + } + fakeReturns := fake.recvMsgReturns + return fakeReturns.result1 +} + +func (fake *FakeRegistry_ListBundlesClient) RecvMsgCallCount() int { + fake.recvMsgMutex.RLock() + defer fake.recvMsgMutex.RUnlock() + return len(fake.recvMsgArgsForCall) +} + +func (fake *FakeRegistry_ListBundlesClient) RecvMsgCalls(stub func(interface{}) error) { + fake.recvMsgMutex.Lock() + defer fake.recvMsgMutex.Unlock() + fake.RecvMsgStub = stub +} + +func (fake *FakeRegistry_ListBundlesClient) RecvMsgArgsForCall(i int) interface{} { + fake.recvMsgMutex.RLock() + defer fake.recvMsgMutex.RUnlock() + argsForCall := fake.recvMsgArgsForCall[i] + return argsForCall.arg1 +} + +func (fake *FakeRegistry_ListBundlesClient) RecvMsgReturns(result1 error) { + fake.recvMsgMutex.Lock() + defer fake.recvMsgMutex.Unlock() + fake.RecvMsgStub = nil + fake.recvMsgReturns = struct { + result1 error + }{result1} +} + +func (fake *FakeRegistry_ListBundlesClient) RecvMsgReturnsOnCall(i int, result1 error) { + fake.recvMsgMutex.Lock() + defer fake.recvMsgMutex.Unlock() + fake.RecvMsgStub = nil + if fake.recvMsgReturnsOnCall == nil { + fake.recvMsgReturnsOnCall = make(map[int]struct { + result1 error + }) + } + fake.recvMsgReturnsOnCall[i] = struct { + result1 error + }{result1} +} + +func (fake *FakeRegistry_ListBundlesClient) SendMsg(arg1 interface{}) error { + fake.sendMsgMutex.Lock() + ret, specificReturn := fake.sendMsgReturnsOnCall[len(fake.sendMsgArgsForCall)] + fake.sendMsgArgsForCall = append(fake.sendMsgArgsForCall, struct { + arg1 interface{} + }{arg1}) + fake.recordInvocation("SendMsg", []interface{}{arg1}) + fake.sendMsgMutex.Unlock() + if fake.SendMsgStub != nil { + return fake.SendMsgStub(arg1) + } + if specificReturn { + return ret.result1 + } + fakeReturns := fake.sendMsgReturns + return fakeReturns.result1 +} + +func (fake *FakeRegistry_ListBundlesClient) SendMsgCallCount() int { + fake.sendMsgMutex.RLock() + defer fake.sendMsgMutex.RUnlock() + return len(fake.sendMsgArgsForCall) +} + +func (fake *FakeRegistry_ListBundlesClient) SendMsgCalls(stub func(interface{}) error) { + fake.sendMsgMutex.Lock() + defer fake.sendMsgMutex.Unlock() + fake.SendMsgStub = stub +} + +func (fake *FakeRegistry_ListBundlesClient) SendMsgArgsForCall(i int) interface{} { + fake.sendMsgMutex.RLock() + defer fake.sendMsgMutex.RUnlock() + argsForCall := fake.sendMsgArgsForCall[i] + return argsForCall.arg1 +} + +func (fake *FakeRegistry_ListBundlesClient) SendMsgReturns(result1 error) { + fake.sendMsgMutex.Lock() + defer fake.sendMsgMutex.Unlock() + fake.SendMsgStub = nil + fake.sendMsgReturns = struct { + result1 error + }{result1} +} + +func (fake *FakeRegistry_ListBundlesClient) SendMsgReturnsOnCall(i int, result1 error) { + fake.sendMsgMutex.Lock() + defer fake.sendMsgMutex.Unlock() + fake.SendMsgStub = nil + if fake.sendMsgReturnsOnCall == nil { + fake.sendMsgReturnsOnCall = make(map[int]struct { + result1 error + }) + } + fake.sendMsgReturnsOnCall[i] = struct { + result1 error + }{result1} +} + +func (fake *FakeRegistry_ListBundlesClient) Trailer() metadata.MD { + fake.trailerMutex.Lock() + ret, specificReturn := fake.trailerReturnsOnCall[len(fake.trailerArgsForCall)] + fake.trailerArgsForCall = append(fake.trailerArgsForCall, struct { + }{}) + fake.recordInvocation("Trailer", []interface{}{}) + fake.trailerMutex.Unlock() + if fake.TrailerStub != nil { + return fake.TrailerStub() + } + if specificReturn { + return ret.result1 + } + fakeReturns := fake.trailerReturns + return fakeReturns.result1 +} + +func (fake *FakeRegistry_ListBundlesClient) TrailerCallCount() int { + fake.trailerMutex.RLock() + defer fake.trailerMutex.RUnlock() + return len(fake.trailerArgsForCall) +} + +func (fake *FakeRegistry_ListBundlesClient) TrailerCalls(stub func() metadata.MD) { + fake.trailerMutex.Lock() + defer fake.trailerMutex.Unlock() + fake.TrailerStub = stub +} + +func (fake *FakeRegistry_ListBundlesClient) TrailerReturns(result1 metadata.MD) { + fake.trailerMutex.Lock() + defer fake.trailerMutex.Unlock() + fake.TrailerStub = nil + fake.trailerReturns = struct { + result1 metadata.MD + }{result1} +} + +func (fake *FakeRegistry_ListBundlesClient) TrailerReturnsOnCall(i int, result1 metadata.MD) { + fake.trailerMutex.Lock() + defer fake.trailerMutex.Unlock() + fake.TrailerStub = nil + if fake.trailerReturnsOnCall == nil { + fake.trailerReturnsOnCall = make(map[int]struct { + result1 metadata.MD + }) + } + fake.trailerReturnsOnCall[i] = struct { + result1 metadata.MD + }{result1} +} + +func (fake *FakeRegistry_ListBundlesClient) Invocations() map[string][][]interface{} { + fake.invocationsMutex.RLock() + defer fake.invocationsMutex.RUnlock() + fake.closeSendMutex.RLock() + defer fake.closeSendMutex.RUnlock() + fake.contextMutex.RLock() + defer fake.contextMutex.RUnlock() + fake.headerMutex.RLock() + defer fake.headerMutex.RUnlock() + fake.recvMutex.RLock() + defer fake.recvMutex.RUnlock() + fake.recvMsgMutex.RLock() + defer fake.recvMsgMutex.RUnlock() + fake.sendMsgMutex.RLock() + defer fake.sendMsgMutex.RUnlock() + fake.trailerMutex.RLock() + defer fake.trailerMutex.RUnlock() + copiedInvocations := map[string][][]interface{}{} + for key, value := range fake.invocations { + copiedInvocations[key] = value + } + return copiedInvocations +} + +func (fake *FakeRegistry_ListBundlesClient) recordInvocation(key string, args []interface{}) { + fake.invocationsMutex.Lock() + defer fake.invocationsMutex.Unlock() + if fake.invocations == nil { + fake.invocations = map[string][][]interface{}{} + } + if fake.invocations[key] == nil { + fake.invocations[key] = [][]interface{}{} + } + fake.invocations[key] = append(fake.invocations[key], args) +} + +var _ api.Registry_ListBundlesClient = new(FakeRegistry_ListBundlesClient) diff --git a/pkg/package-server/client/openapi/zz_generated.openapi.go b/pkg/package-server/client/openapi/zz_generated.openapi.go index baba7556f6..e4fcd10386 100644 --- a/pkg/package-server/client/openapi/zz_generated.openapi.go +++ b/pkg/package-server/client/openapi/zz_generated.openapi.go @@ -62,6 +62,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/operator-framework/api/pkg/operators/v1alpha1.WebhookDescription": schema_api_pkg_operators_v1alpha1_WebhookDescription(ref), "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/operators/v1.AppLink": schema_package_server_apis_operators_v1_AppLink(ref), "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/operators/v1.CSVDescription": schema_package_server_apis_operators_v1_CSVDescription(ref), + "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/operators/v1.ChannelEntry": schema_package_server_apis_operators_v1_ChannelEntry(ref), "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/operators/v1.Icon": schema_package_server_apis_operators_v1_Icon(ref), "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/operators/v1.Maintainer": schema_package_server_apis_operators_v1_Maintainer(ref), "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/operators/v1.PackageChannel": schema_package_server_apis_operators_v1_PackageChannel(ref), @@ -1927,6 +1928,35 @@ func schema_package_server_apis_operators_v1_CSVDescription(ref common.Reference } } +func schema_package_server_apis_operators_v1_ChannelEntry(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ChannelEntry defines a member of a package channel.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name is the name of the bundle for this entry.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "version": { + SchemaProps: spec.SchemaProps{ + Description: "Version is the version of the bundle for this entry.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"name"}, + }, + }, + } +} + func schema_package_server_apis_operators_v1_Icon(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -2007,12 +2037,26 @@ func schema_package_server_apis_operators_v1_PackageChannel(ref common.Reference Ref: ref("github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/operators/v1.CSVDescription"), }, }, + "entries": { + SchemaProps: spec.SchemaProps{ + Description: "Entries lists all CSVs in the channel, with their upgrade edges.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/operators/v1.ChannelEntry"), + }, + }, + }, + }, + }, }, - Required: []string{"name", "currentCSV"}, + Required: []string{"name", "currentCSV", "entries"}, }, }, Dependencies: []string{ - "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/operators/v1.CSVDescription"}, + "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/operators/v1.CSVDescription", "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/operators/v1.ChannelEntry"}, } } diff --git a/pkg/package-server/provider/registry.go b/pkg/package-server/provider/registry.go index 0a959b8c09..498f480e8a 100644 --- a/pkg/package-server/provider/registry.go +++ b/pkg/package-server/provider/registry.go @@ -5,12 +5,14 @@ import ( "encoding/json" "fmt" "io" + "sort" "strings" "sync" "time" - "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry" - + "github.com/blang/semver/v4" + operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" + "github.com/operator-framework/operator-registry/pkg/api" "github.com/sirupsen/logrus" "google.golang.org/grpc" "google.golang.org/grpc/connectivity" @@ -20,16 +22,15 @@ import ( utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/client-go/tools/cache" - operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned" "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/informers/externalversions" operatorslisters "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/listers/operators/v1alpha1" + "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry" registrygrpc "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/grpc" utillabels "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/kubernetes/pkg/util/labels" "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/queueinformer" "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/operators" pkglisters "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/client/listers/operators/internalversion" - "github.com/operator-framework/operator-registry/pkg/api" ) const ( @@ -277,12 +278,53 @@ func (p *RegistryProvider) refreshCache(ctx context.Context, client *registryCli "source": key, }) + bundleStream, err := client.ListBundles(ctx, &api.ListBundlesRequest{}) + if err != nil { + logger.WithField("err", err.Error()).Warnf("error getting bundle stream") + return nil + } + + bundles := map[string]map[string][]operators.ChannelEntry{} + for { + bundle, err := bundleStream.Recv() + if err == io.EOF { + break + } + if err != nil { + logger.WithField("err", err.Error()).Warnf("error getting bundle data") + break + } + if _, ok := bundles[bundle.PackageName]; !ok { + bundles[bundle.PackageName] = map[string][]operators.ChannelEntry{} + } + bundles[bundle.PackageName][bundle.ChannelName] = append(bundles[bundle.PackageName][bundle.ChannelName], operators.ChannelEntry{ + Name: bundle.CsvName, + Version: bundle.Version, + }) + } + stream, err := client.ListPackages(ctx, &api.ListPackageRequest{}) if err != nil { - logger.WithField("err", err.Error()).Warnf("error getting stream") + logger.WithField("err", err.Error()).Warnf("error getting package stream") return nil } + for pkgName := range bundles { + for chName := range bundles[pkgName] { + sort.Slice(bundles[pkgName][chName], func(i, j int) bool { + iV, err := semver.Parse(bundles[pkgName][chName][i].Version) + if err != nil { + iV = semver.Version{} + } + jV, err := semver.Parse(bundles[pkgName][chName][j].Version) + if err != nil { + jV = semver.Version{} + } + return iV.GT(jV) + }) + } + } + var ( added = map[string]struct{}{} mu sync.Mutex @@ -294,7 +336,7 @@ func (p *RegistryProvider) refreshCache(ctx context.Context, client *registryCli break } if err != nil { - logger.WithField("err", err.Error()).Warnf("error getting data") + logger.WithField("err", err.Error()).Warnf("error getting package name data") break } @@ -307,7 +349,7 @@ func (p *RegistryProvider) refreshCache(ctx context.Context, client *registryCli return } - newPkg, err := newPackageManifest(ctx, logger, pkg, client) + newPkg, err := newPackageManifest(ctx, logger, pkg, client, bundles[pkg.GetName()]) if err != nil { logger.WithField("err", err.Error()).Warnf("eliding package: error converting to packagemanifest") return @@ -454,7 +496,7 @@ func (p *RegistryProvider) List(namespace string, selector labels.Selector) (*op return pkgList, nil } -func newPackageManifest(ctx context.Context, logger *logrus.Entry, pkg *api.Package, client *registryClient) (*operators.PackageManifest, error) { +func newPackageManifest(ctx context.Context, logger *logrus.Entry, pkg *api.Package, client *registryClient, entriesByChannel map[string][]operators.ChannelEntry) (*operators.PackageManifest, error) { pkgChannels := pkg.GetChannels() catsrc := client.catsrc manifest := &operators.PackageManifest{ @@ -503,6 +545,7 @@ func newPackageManifest(ctx context.Context, logger *logrus.Entry, pkg *api.Pack Name: pkgChannel.GetName(), CurrentCSV: csv.GetName(), CurrentCSVDesc: operators.CreateCSVDescription(&csv, bundle.GetCsvJson()), + Entries: entriesByChannel[pkgChannel.GetName()], }) if manifest.Status.DefaultChannel != "" && pkgChannel.GetName() == manifest.Status.DefaultChannel || !providerSet { diff --git a/pkg/package-server/provider/registry_test.go b/pkg/package-server/provider/registry_test.go index 339094d761..190555f205 100644 --- a/pkg/package-server/provider/registry_test.go +++ b/pkg/package-server/provider/registry_test.go @@ -1,5 +1,6 @@ //go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 -o ../client/fakes/fake_registry_client.go github.com/operator-framework/operator-registry/pkg/api.RegistryClient //go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 -o ../client/fakes/fake_list_packages_client.go github.com/operator-framework/operator-registry/pkg/api.Registry_ListPackagesClient +//go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 -o ../client/fakes/fake_list_bundles_client.go github.com/operator-framework/operator-registry/pkg/api.Registry_ListBundlesClient package provider import ( @@ -14,8 +15,7 @@ import ( "testing" "time" - "k8s.io/apimachinery/pkg/selection" - + operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" "github.com/operator-framework/operator-registry/pkg/api" registryserver "github.com/operator-framework/operator-registry/pkg/server" "github.com/operator-framework/operator-registry/pkg/sqlite" @@ -25,9 +25,9 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/selection" k8sfake "k8s.io/client-go/kubernetes/fake" - operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned/fake" "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient" "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/queueinformer" @@ -133,6 +133,36 @@ var ( etcdUpgradesCRDJSON = "{\"apiVersion\":\"apiextensions.k8s.io/v1beta1\",\"kind\":\"CustomResourceDefinition\",\"metadata\":{\"name\":\"etcdclusters.etcd.database.coreos.com\"},\"spec\":{\"group\":\"etcd.database.coreos.com\",\"names\":{\"kind\":\"EtcdCluster\",\"listKind\":\"EtcdClusterList\",\"plural\":\"etcdclusters\",\"shortNames\":[\"etcdclus\",\"etcd\"],\"singular\":\"etcdcluster\"},\"scope\":\"Namespaced\",\"version\":\"v1beta2\"}}" etcdRestoresCRDJSON = "{\"apiVersion\":\"apiextensions.k8s.io/v1beta1\",\"kind\":\"CustomResourceDefinition\",\"metadata\":{\"name\":\"etcdrestores.etcd.database.coreos.com\"},\"spec\":{\"group\":\"etcd.database.coreos.com\",\"names\":{\"kind\":\"EtcdRestore\",\"listKind\":\"EtcdRestoreList\",\"plural\":\"etcdrestores\",\"singular\":\"etcdrestore\"},\"scope\":\"Namespaced\",\"version\":\"v1beta2\"}}" prometheusCSVJSON = `{"apiVersion":"operators.coreos.com/v1alpha1","kind":"ClusterServiceVersion","metadata":{"annotations":{"alm-examples":"[{\"apiVersion\":\"monitoring.coreos.com/v1\",\"kind\":\"Prometheus\",\"metadata\":{\"name\":\"example\",\"labels\":{\"prometheus\":\"k8s\"}},\"spec\":{\"replicas\":2,\"version\":\"v2.3.2\",\"serviceAccountName\":\"prometheus-k8s\",\"securityContext\": {}, \"serviceMonitorSelector\":{\"matchExpressions\":[{\"key\":\"k8s-app\",\"operator\":\"Exists\"}]},\"ruleSelector\":{\"matchLabels\":{\"role\":\"prometheus-rulefiles\",\"prometheus\":\"k8s\"}},\"alerting\":{\"alertmanagers\":[{\"namespace\":\"monitoring\",\"name\":\"alertmanager-main\",\"port\":\"web\"}]}}},{\"apiVersion\":\"monitoring.coreos.com/v1\",\"kind\":\"ServiceMonitor\",\"metadata\":{\"name\":\"example\",\"labels\":{\"k8s-app\":\"prometheus\"}},\"spec\":{\"selector\":{\"matchLabels\":{\"k8s-app\":\"prometheus\"}},\"endpoints\":[{\"port\":\"web\",\"interval\":\"30s\"}]}},{\"apiVersion\":\"monitoring.coreos.com/v1\",\"kind\":\"Alertmanager\",\"metadata\":{\"name\":\"alertmanager-main\"},\"spec\":{\"replicas\":3, \"securityContext\": {}}}]"},"name":"prometheusoperator.0.22.2","namespace":"placeholder"},"spec":{"customresourcedefinitions":{"owned":[{"description":"A running Prometheus instance","displayName":"Prometheus","kind":"Prometheus","name":"prometheuses.monitoring.coreos.com","resources":[{"kind":"StatefulSet","version":"v1beta2"},{"kind":"Pod","version":"v1"}],"specDescriptors":[{"description":"Desired number of Pods for the cluster","displayName":"Size","path":"replicas","x-descriptors":["urn:alm:descriptor:com.tectonic.ui:podCount"]},{"description":"A selector for the ConfigMaps from which to load rule files","displayName":"Rule Config Map Selector","path":"ruleSelector","x-descriptors":["urn:alm:descriptor:com.tectonic.ui:selector:core:v1:ConfigMap"]},{"description":"ServiceMonitors to be selected for target discovery","displayName":"Service Monitor Selector","path":"serviceMonitorSelector","x-descriptors":["urn:alm:descriptor:com.tectonic.ui:selector:monitoring.coreos.com:v1:ServiceMonitor"]},{"description":"The ServiceAccount to use to run the Prometheus pods","displayName":"Service Account","path":"serviceAccountName","x-descriptors":["urn:alm:descriptor:io.kubernetes:ServiceAccount"]},{"description":"Limits describes the minimum/maximum amount of compute resources required/allowed","displayName":"Resource Requirements","path":"resources","x-descriptors":["urn:alm:descriptor:com.tectonic.ui:resourceRequirements"]}],"version":"v1"},{"description":"A Prometheus Rule configures groups of sequentially evaluated recording and alerting rules.","displayName":"Prometheus Rule","kind":"PrometheusRule","name":"prometheusrules.monitoring.coreos.com","version":"v1"},{"description":"Configures prometheus to monitor a particular k8s service","displayName":"Service Monitor","kind":"ServiceMonitor","name":"servicemonitors.monitoring.coreos.com","resources":[{"kind":"Pod","version":"v1"}],"specDescriptors":[{"description":"The label to use to retrieve the job name from","displayName":"Job Label","path":"jobLabel","x-descriptors":["urn:alm:descriptor:com.tectonic.ui:label"]},{"description":"A list of endpoints allowed as part of this ServiceMonitor","displayName":"Endpoints","path":"endpoints","x-descriptors":["urn:alm:descriptor:com.tectonic.ui:endpointList"]}],"version":"v1"},{"description":"Configures an Alertmanager for the namespace","displayName":"Alertmanager","kind":"Alertmanager","name":"alertmanagers.monitoring.coreos.com","resources":[{"kind":"StatefulSet","version":"v1beta2"},{"kind":"Pod","version":"v1"}],"specDescriptors":[{"description":"Desired number of Pods for the cluster","displayName":"Size","path":"replicas","x-descriptors":["urn:alm:descriptor:com.tectonic.ui:podCount"]},{"description":"Limits describes the minimum/maximum amount of compute resources required/allowed","displayName":"Resource Requirements","path":"resources","x-descriptors":["urn:alm:descriptor:com.tectonic.ui:resourceRequirements"]}],"version":"v1"}]},"description":"The Prometheus Operator for Kubernetes provides easy monitoring definitions for Kubernetes services and deployment and management of Prometheus instances.\n\nOnce installed, the Prometheus Operator provides the following features:\n\n* **Create/Destroy**: Easily launch a Prometheus instance for your Kubernetes namespace, a specific application or team easily using the Operator.\n\n* **Simple Configuration**: Configure the fundamentals of Prometheus like versions, persistence, retention policies, and replicas from a native Kubernetes resource.\n\n* **Target Services via Labels**: Automatically generate monitoring target configurations based on familiar Kubernetes label queries; no need to learn a Prometheus specific configuration language.\n\n### Other Supported Features\n\n**High availability**\n\nMultiple instances are run across failure zones and data is replicated. This keeps your monitoring available during an outage, when you need it most.\n\n**Updates via automated operations**\n\nNew Prometheus versions are deployed using a rolling update with no downtime, making it easy to stay up to date.\n\n**Handles the dynamic nature of containers**\n\nAlerting rules are attached to groups of containers instead of individual instances, which is ideal for the highly dynamic nature of container deployment.\n","displayName":"Prometheus Operator","icon":[{"base64data":"PHN2ZyB3aWR0aD0iMjQ5MCIgaGVpZ2h0PSIyNTAwIiB2aWV3Qm94PSIwIDAgMjU2IDI1NyIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCI+PHBhdGggZD0iTTEyOC4wMDEuNjY3QzU3LjMxMS42NjcgMCA1Ny45NzEgMCAxMjguNjY0YzAgNzAuNjkgNTcuMzExIDEyNy45OTggMTI4LjAwMSAxMjcuOTk4UzI1NiAxOTkuMzU0IDI1NiAxMjguNjY0QzI1NiA1Ny45NyAxOTguNjg5LjY2NyAxMjguMDAxLjY2N3ptMCAyMzkuNTZjLTIwLjExMiAwLTM2LjQxOS0xMy40MzUtMzYuNDE5LTMwLjAwNGg3Mi44MzhjMCAxNi41NjYtMTYuMzA2IDMwLjAwNC0zNi40MTkgMzAuMDA0em02MC4xNTMtMzkuOTRINjcuODQyVjE3OC40N2gxMjAuMzE0djIxLjgxNmgtLjAwMnptLS40MzItMzMuMDQ1SDY4LjE4NWMtLjM5OC0uNDU4LS44MDQtLjkxLTEuMTg4LTEuMzc1LTEyLjMxNS0xNC45NTQtMTUuMjE2LTIyLjc2LTE4LjAzMi0zMC43MTYtLjA0OC0uMjYyIDE0LjkzMyAzLjA2IDI1LjU1NiA1LjQ1IDAgMCA1LjQ2NiAxLjI2NSAxMy40NTggMi43MjItNy42NzMtOC45OTQtMTIuMjMtMjAuNDI4LTEyLjIzLTMyLjExNiAwLTI1LjY1OCAxOS42OC00OC4wNzkgMTIuNTgtNjYuMjAxIDYuOTEuNTYyIDE0LjMgMTQuNTgzIDE0LjggMzYuNTA1IDcuMzQ2LTEwLjE1MiAxMC40Mi0yOC42OSAxMC40Mi00MC4wNTYgMC0xMS43NjkgNy43NTUtMjUuNDQgMTUuNTEyLTI1LjkwNy02LjkxNSAxMS4zOTYgMS43OSAyMS4xNjUgOS41MyA0NS40IDIuOTAyIDkuMTAzIDIuNTMyIDI0LjQyMyA0Ljc3MiAzNC4xMzguNzQ0LTIwLjE3OCA0LjIxMy00OS42MiAxNy4wMTQtNTkuNzg0LTUuNjQ3IDEyLjguODM2IDI4LjgxOCA1LjI3IDM2LjUxOCA3LjE1NCAxMi40MjQgMTEuNDkgMjEuODM2IDExLjQ5IDM5LjYzOCAwIDExLjkzNi00LjQwNyAyMy4xNzMtMTEuODQgMzEuOTU4IDguNDUyLTEuNTg2IDE0LjI4OS0zLjAxNiAxNC4yODktMy4wMTZsMjcuNDUtNS4zNTVjLjAwMi0uMDAyLTMuOTg3IDE2LjQwMS0xOS4zMTQgMzIuMTk3eiIgZmlsbD0iI0RBNEUzMSIvPjwvc3ZnPg==","mediatype":"image/svg+xml"}],"install":{"spec":{"deployments":[{"name":"prometheus-operator","spec":{"replicas":1,"selector":{"matchLabels":{"k8s-app":"prometheus-operator"}},"template":{"metadata":{"labels":{"k8s-app":"prometheus-operator"}},"spec":{"containers":[{"args":["-namespace=$(K8S_NAMESPACE)","-manage-crds=false","-logtostderr=true","--config-reloader-image=quay.io/coreos/configmap-reload:v0.0.1","--prometheus-config-reloader=quay.io/coreos/prometheus-config-reloader:v0.22.2"],"env":[{"name":"K8S_NAMESPACE","valueFrom":{"fieldRef":{"fieldPath":"metadata.namespace"}}}],"image":"quay.io/coreos/prometheus-operator@sha256:3daa69a8c6c2f1d35dcf1fe48a7cd8b230e55f5229a1ded438f687debade5bcf","name":"prometheus-operator","ports":[{"containerPort":8080,"name":"http"}],"resources":{"limits":{"cpu":"200m","memory":"100Mi"},"requests":{"cpu":"100m","memory":"50Mi"}},"securityContext":{"allowPrivilegeEscalation":false,"readOnlyRootFilesystem":true}}],"nodeSelector":{"kubernetes.io/os":"linux"},"serviceAccount":"prometheus-operator-0-22-2"}}}}],"permissions":[{"rules":[{"apiGroups":[""],"resources":["nodes","services","endpoints","pods"],"verbs":["get","list","watch"]},{"apiGroups":[""],"resources":["configmaps"],"verbs":["get"]}],"serviceAccountName":"prometheus-k8s"},{"rules":[{"apiGroups":["apiextensions.k8s.io"],"resources":["customresourcedefinitions"],"verbs":["*"]},{"apiGroups":["monitoring.coreos.com"],"resources":["alertmanagers","prometheuses","prometheuses/finalizers","alertmanagers/finalizers","servicemonitors","prometheusrules"],"verbs":["*"]},{"apiGroups":["apps"],"resources":["statefulsets"],"verbs":["*"]},{"apiGroups":[""],"resources":["configmaps","secrets"],"verbs":["*"]},{"apiGroups":[""],"resources":["pods"],"verbs":["list","delete"]},{"apiGroups":[""],"resources":["services","endpoints"],"verbs":["get","create","update"]},{"apiGroups":[""],"resources":["nodes"],"verbs":["list","watch"]},{"apiGroups":[""],"resources":["namespaces"],"verbs":["list","watch"]}],"serviceAccountName":"prometheus-operator-0-22-2"}]},"strategy":"deployment"},"keywords":["prometheus","monitoring","tsdb","alerting"],"labels":{"alm-owner-prometheus":"prometheusoperator","alm-status-descriptors":"prometheusoperator.0.22.2"},"links":[{"name":"Prometheus","url":"https://www.prometheus.io/"},{"name":"Documentation","url":"https://coreos.com/operators/prometheus/docs/latest/"},{"name":"Prometheus Operator","url":"https://github.com/coreos/prometheus-operator"}],"maintainers":[{"email":"openshift-operators@redhat.com","name":"Red Hat"}],"maturity":"beta","provider":{"name":"Red Hat"},"replaces":"prometheusoperator.0.15.0","selector":{"matchLabels":{"alm-owner-prometheus":"prometheusoperator"}},"version":"0.22.2"}}` + + etcdChannelEntries = []operators.ChannelEntry{ + { + Name: "etcdoperator.v0.9.2", + Version: "0.9.2", + }, + { + Name: "etcdoperator.v0.9.0", + Version: "0.9.0", + }, + { + Name: "etcdoperator.v0.6.1", + Version: "0.6.1", + }, + } + + prometheusChannelEntries = []operators.ChannelEntry{ + { + Name: "prometheusoperator.0.22.2", + Version: "0.22.2", + }, + { + Name: "prometheusoperator.0.15.0", + Version: "0.15.0", + }, + { + Name: "prometheusoperator.0.14.0", + Version: "0.14.0", + }, + } ) func TestToPackageManifest(t *testing.T) { @@ -429,7 +459,7 @@ func TestToPackageManifest(t *testing.T) { catsrc: test.catalogSource, } - packageManifest, err := newPackageManifest(context.Background(), logrus.NewEntry(logrus.New()), test.apiPkg, client) + packageManifest, err := newPackageManifest(context.Background(), logrus.NewEntry(logrus.New()), test.apiPkg, client, nil) if test.expectedErr != "" { require.Error(t, err) require.Equal(t, test.expectedErr, err.Error()) @@ -512,6 +542,7 @@ func TestRegistryProviderGet(t *testing.T) { require.NoError(t, json.Unmarshal([]byte(etcdCSVJSON), &csv)) return operators.CreateCSVDescription(&csv, etcdCSVJSON) }(), + Entries: etcdChannelEntries, }, }, }, @@ -561,6 +592,7 @@ func TestRegistryProviderGet(t *testing.T) { require.NoError(t, json.Unmarshal([]byte(etcdCSVJSON), &csv)) return operators.CreateCSVDescription(&csv, etcdCSVJSON) }(), + Entries: etcdChannelEntries, }, }, }, @@ -609,6 +641,7 @@ func TestRegistryProviderGet(t *testing.T) { require.NoError(t, json.Unmarshal([]byte(etcdCSVJSON), &csv)) return operators.CreateCSVDescription(&csv, etcdCSVJSON) }(), + Entries: etcdChannelEntries, }, }, }, @@ -696,6 +729,7 @@ func TestRegistryProviderList(t *testing.T) { require.NoError(t, json.Unmarshal([]byte(prometheusCSVJSON), &csv)) return operators.CreateCSVDescription(&csv, prometheusCSVJSON) }(), + Entries: prometheusChannelEntries, }, }, }, @@ -730,6 +764,7 @@ func TestRegistryProviderList(t *testing.T) { require.NoError(t, json.Unmarshal([]byte(etcdCSVJSON), &csv)) return operators.CreateCSVDescription(&csv, etcdCSVJSON) }(), + Entries: etcdChannelEntries, }, }, }, @@ -776,6 +811,7 @@ func TestRegistryProviderList(t *testing.T) { require.NoError(t, json.Unmarshal([]byte(prometheusCSVJSON), &csv)) return operators.CreateCSVDescription(&csv, prometheusCSVJSON) }(), + Entries: prometheusChannelEntries, }, }, }, @@ -810,6 +846,7 @@ func TestRegistryProviderList(t *testing.T) { require.NoError(t, json.Unmarshal([]byte(etcdCSVJSON), &csv)) return operators.CreateCSVDescription(&csv, etcdCSVJSON) }(), + Entries: etcdChannelEntries, }, }, }, @@ -856,6 +893,7 @@ func TestRegistryProviderList(t *testing.T) { require.NoError(t, json.Unmarshal([]byte(prometheusCSVJSON), &csv)) return operators.CreateCSVDescription(&csv, prometheusCSVJSON) }(), + Entries: prometheusChannelEntries, }, }, }, @@ -890,6 +928,7 @@ func TestRegistryProviderList(t *testing.T) { require.NoError(t, json.Unmarshal([]byte(etcdCSVJSON), &csv)) return operators.CreateCSVDescription(&csv, etcdCSVJSON) }(), + Entries: etcdChannelEntries, }, }, }, @@ -924,6 +963,7 @@ func TestRegistryProviderList(t *testing.T) { require.NoError(t, json.Unmarshal([]byte(prometheusCSVJSON), &csv)) return operators.CreateCSVDescription(&csv, prometheusCSVJSON) }(), + Entries: prometheusChannelEntries, }, }, }, @@ -958,6 +998,7 @@ func TestRegistryProviderList(t *testing.T) { require.NoError(t, json.Unmarshal([]byte(etcdCSVJSON), &csv)) return operators.CreateCSVDescription(&csv, etcdCSVJSON) }(), + Entries: etcdChannelEntries, }, }, }, @@ -970,12 +1011,23 @@ func TestRegistryProviderList(t *testing.T) { registryClients: []*registryClient{ func() *registryClient { catsrc := catalogSource("cool-operators", "ns") + + listBundlesFake := &fakes.FakeRegistry_ListBundlesClient{} + listBundlesFake.RecvReturnsOnCall(0, &api.Bundle{ + PackageName: "has-bundle", + ChannelName: "alpha", + CsvName: "etcdoperator.v0.9.2", + Version: "0.9.2", + }, nil) + listBundlesFake.RecvReturnsOnCall(1, nil, io.EOF) + listFake := &fakes.FakeRegistry_ListPackagesClient{} listFake.RecvReturnsOnCall(0, &api.PackageName{Name: "no-bundle"}, nil) listFake.RecvReturnsOnCall(1, &api.PackageName{Name: "has-bundle"}, nil) listFake.RecvReturnsOnCall(2, nil, io.EOF) clientFake := &fakes.FakeRegistryClient{} + clientFake.ListBundlesReturns(listBundlesFake, nil) clientFake.ListPackagesReturns(listFake, nil) clientFake.GetPackageReturnsOnCall(0, &api.Package{ Name: "no-bundle", @@ -1047,6 +1099,12 @@ func TestRegistryProviderList(t *testing.T) { require.NoError(t, json.Unmarshal([]byte(etcdCSVJSON), &csv)) return operators.CreateCSVDescription(&csv, etcdCSVJSON) }(), + Entries: []operators.ChannelEntry{ + { + Name: "etcdoperator.v0.9.2", + Version: "0.9.2", + }, + }, }, }, }, @@ -1139,6 +1197,7 @@ func TestRegistryProviderListLabels(t *testing.T) { require.NoError(t, json.Unmarshal([]byte(prometheusCSVJSON), &csv)) return operators.CreateCSVDescription(&csv, prometheusCSVJSON) }(), + Entries: prometheusChannelEntries, }, }, }, @@ -1173,6 +1232,7 @@ func TestRegistryProviderListLabels(t *testing.T) { require.NoError(t, json.Unmarshal([]byte(etcdCSVJSON), &csv)) return operators.CreateCSVDescription(&csv, etcdCSVJSON) }(), + Entries: etcdChannelEntries, }, }, }, @@ -1222,6 +1282,7 @@ func TestRegistryProviderListLabels(t *testing.T) { require.NoError(t, json.Unmarshal([]byte(prometheusCSVJSON), &csv)) return operators.CreateCSVDescription(&csv, prometheusCSVJSON) }(), + Entries: prometheusChannelEntries, }, }, }, @@ -1256,6 +1317,7 @@ func TestRegistryProviderListLabels(t *testing.T) { require.NoError(t, json.Unmarshal([]byte(etcdCSVJSON), &csv)) return operators.CreateCSVDescription(&csv, etcdCSVJSON) }(), + Entries: etcdChannelEntries, }, }, }, @@ -1315,6 +1377,7 @@ func TestRegistryProviderListLabels(t *testing.T) { require.NoError(t, json.Unmarshal([]byte(prometheusCSVJSON), &csv)) return operators.CreateCSVDescription(&csv, prometheusCSVJSON) }(), + Entries: prometheusChannelEntries, }, }, }, @@ -1349,6 +1412,7 @@ func TestRegistryProviderListLabels(t *testing.T) { require.NoError(t, json.Unmarshal([]byte(etcdCSVJSON), &csv)) return operators.CreateCSVDescription(&csv, etcdCSVJSON) }(), + Entries: etcdChannelEntries, }, }, }, diff --git a/test/e2e/packagemanifest_e2e_test.go b/test/e2e/packagemanifest_e2e_test.go index 577ee73e06..68157ba2db 100644 --- a/test/e2e/packagemanifest_e2e_test.go +++ b/test/e2e/packagemanifest_e2e_test.go @@ -8,12 +8,12 @@ import ( "github.com/blang/semver/v4" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + opver "github.com/operator-framework/api/pkg/lib/version" + "github.com/operator-framework/api/pkg/operators/v1alpha1" "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/rest" - opver "github.com/operator-framework/api/pkg/lib/version" - "github.com/operator-framework/api/pkg/operators/v1alpha1" "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned" "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry" "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient" @@ -140,11 +140,27 @@ var _ = Describe("Package Manifest API lists available Operators from Catalog So Name: alphaChannel, CurrentCSV: packageAlpha, CurrentCSVDesc: packagev1.CreateCSVDescription(&csvAlpha, string(csvAlphaJSON)), + Entries: []packagev1.ChannelEntry{ + { + Name: csvAlpha.Name, + Version: csvAlpha.Spec.Version.String(), + }, + { + Name: csv.Name, + Version: csv.Spec.Version.String(), + }, + }, }, { Name: stableChannel, CurrentCSV: packageStable, CurrentCSVDesc: packagev1.CreateCSVDescription(&csv, string(csvJSON)), + Entries: []packagev1.ChannelEntry{ + { + Name: csv.Name, + Version: csv.Spec.Version.String(), + }, + }, }, }, DefaultChannel: stableChannel,