-
Couldn't load subscription status.
- Fork 68
[resolution pt.2] required package variable source #96
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| package required_package | ||
|
|
||
| import ( | ||
| "context" | ||
| "fmt" | ||
|
|
||
| "github.com/operator-framework/deppy/pkg/deppy" | ||
| "github.com/operator-framework/deppy/pkg/deppy/constraint" | ||
| "github.com/operator-framework/deppy/pkg/deppy/input" | ||
|
|
||
| olmentity "github.com/operator-framework/operator-controller/internal/resolution/variable_sources/entity" | ||
| "github.com/operator-framework/operator-controller/internal/resolution/variable_sources/utils/predicates" | ||
| "github.com/operator-framework/operator-controller/internal/resolution/variable_sources/utils/sort" | ||
| ) | ||
|
|
||
| type RequiredPackageVariable struct { | ||
| *input.SimpleVariable | ||
| bundleEntities []*olmentity.BundleEntity | ||
| } | ||
|
|
||
| func (r *RequiredPackageVariable) BundleEntities() []*olmentity.BundleEntity { | ||
| return r.bundleEntities | ||
| } | ||
|
|
||
| func NewRequiredPackageVariable(packageName string, bundleEntities []*olmentity.BundleEntity) *RequiredPackageVariable { | ||
| id := deppy.IdentifierFromString(fmt.Sprintf("required package %s", packageName)) | ||
| var entityIDs []deppy.Identifier | ||
| for _, bundle := range bundleEntities { | ||
| entityIDs = append(entityIDs, bundle.ID) | ||
| } | ||
| return &RequiredPackageVariable{ | ||
| SimpleVariable: input.NewSimpleVariable(id, constraint.Mandatory(), constraint.Dependency(entityIDs...)), | ||
| bundleEntities: bundleEntities, | ||
| } | ||
| } | ||
|
|
||
| var _ input.VariableSource = &RequiredPackageVariableSource{} | ||
|
|
||
| type RequiredPackageVariableSource struct { | ||
| packageName string | ||
| } | ||
|
|
||
| func NewRequiredPackage(packageName string) *RequiredPackageVariableSource { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If RequiredPackageVariableSource is exported, why do I need this function? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just being more idiomatic, I suppose. Plus if something changes in the initialization we don't need to refactor every call. |
||
| return &RequiredPackageVariableSource{ | ||
| packageName: packageName, | ||
| } | ||
| } | ||
|
|
||
| func (r *RequiredPackageVariableSource) GetVariables(ctx context.Context, entitySource input.EntitySource) ([]deppy.Variable, error) { | ||
| resultSet, err := entitySource.Filter(ctx, predicates.WithPackageName(r.packageName)) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| if len(resultSet) == 0 { | ||
| return nil, fmt.Errorf("package '%s' not found", r.packageName) | ||
| } | ||
| resultSet = resultSet.Sort(sort.ByChannelAndVersion) | ||
| var bundleEntities []*olmentity.BundleEntity | ||
| for i := 0; i < len(resultSet); i++ { | ||
| bundleEntities = append(bundleEntities, olmentity.NewBundleEntity(&resultSet[i])) | ||
| } | ||
| return []deppy.Variable{ | ||
| NewRequiredPackageVariable(r.packageName, bundleEntities), | ||
| }, nil | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,129 @@ | ||
| package required_package_test | ||
|
|
||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "testing" | ||
|
|
||
| . "github.com/onsi/ginkgo/v2" | ||
| . "github.com/onsi/gomega" | ||
| "github.com/operator-framework/deppy/pkg/deppy" | ||
| "github.com/operator-framework/deppy/pkg/deppy/input" | ||
| "github.com/operator-framework/operator-registry/alpha/property" | ||
|
|
||
| olmentity "github.com/operator-framework/operator-controller/internal/resolution/variable_sources/entity" | ||
| "github.com/operator-framework/operator-controller/internal/resolution/variable_sources/required_package" | ||
| ) | ||
|
|
||
| func TestRequiredPackage(t *testing.T) { | ||
| RegisterFailHandler(Fail) | ||
| RunSpecs(t, "RequiredPackageVariableSource Suite") | ||
| } | ||
|
|
||
| var _ = Describe("RequiredPackageVariable", func() { | ||
| var ( | ||
| rpv *required_package.RequiredPackageVariable | ||
| packageName string | ||
| bundleEntities []*olmentity.BundleEntity | ||
| ) | ||
|
|
||
| BeforeEach(func() { | ||
| packageName = "test-package" | ||
| bundleEntities = []*olmentity.BundleEntity{ | ||
| olmentity.NewBundleEntity(input.NewEntity("bundle-1", map[string]string{ | ||
| property.TypePackage: `{"packageName": "test-package", "version": "1.0.0"}`, | ||
| property.TypeChannel: `{"channelName":"stable","priority":0}`, | ||
| })), | ||
| olmentity.NewBundleEntity(input.NewEntity("bundle-2", map[string]string{ | ||
| property.TypePackage: `{"packageName": "test-package", "version": "2.0.0"}`, | ||
| property.TypeChannel: `{"channelName":"stable","priority":0}`, | ||
| })), | ||
| olmentity.NewBundleEntity(input.NewEntity("bundle-3", map[string]string{ | ||
| property.TypePackage: `{"packageName": "test-package", "version": "3.0.0"}`, | ||
| property.TypeChannel: `{"channelName":"stable","priority":0}`, | ||
| })), | ||
| } | ||
| rpv = required_package.NewRequiredPackageVariable(packageName, bundleEntities) | ||
| }) | ||
|
|
||
| It("should return the correct package name", func() { | ||
| Expect(rpv.Identifier()).To(Equal(deppy.IdentifierFromString(fmt.Sprintf("required package %s", packageName)))) | ||
| }) | ||
|
|
||
| It("should return the correct bundle entities", func() { | ||
| Expect(rpv.BundleEntities()).To(Equal(bundleEntities)) | ||
| }) | ||
|
|
||
| It("should contain both mandatory and dependency constraints", func() { | ||
| // TODO: add this test once https://github.com/operator-framework/deppy/pull/85 gets merged | ||
| // then we'll be able to inspect constraint types | ||
| }) | ||
| }) | ||
|
|
||
| var _ = Describe("RequiredPackageVariableSource", func() { | ||
| var ( | ||
| rpvs *required_package.RequiredPackageVariableSource | ||
| packageName string | ||
| mockEntitySource input.EntitySource | ||
| ) | ||
|
|
||
| BeforeEach(func() { | ||
| packageName = "test-package" | ||
| rpvs = required_package.NewRequiredPackage(packageName) | ||
| mockEntitySource = input.NewCacheQuerier(map[deppy.Identifier]input.Entity{ | ||
| "bundle-1": *input.NewEntity("bundle-1", map[string]string{ | ||
| property.TypePackage: `{"packageName": "test-package", "version": "1.0.0"}`, | ||
| property.TypeChannel: `{"channelName":"stable","priority":0}`, | ||
| }), | ||
| "bundle-2": *input.NewEntity("bundle-2", map[string]string{ | ||
| property.TypePackage: `{"packageName": "test-package", "version": "3.0.0"}`, | ||
| property.TypeChannel: `{"channelName":"stable","priority":0}`, | ||
| }), | ||
| "bundle-3": *input.NewEntity("bundle-3", map[string]string{ | ||
| property.TypePackage: `{"packageName": "test-package", "version": "2.0.0"}`, | ||
| property.TypeChannel: `{"channelName":"stable","priority":0}`, | ||
| }), | ||
|
|
||
| // add some bundles from a different package | ||
| "bundle-4": *input.NewEntity("bundle-4", map[string]string{ | ||
| property.TypePackage: `{"packageName": "test-package-2", "version": "1.0.0"}`, | ||
| property.TypeChannel: `{"channelName":"stable","priority":0}`, | ||
| }), | ||
| "bundle-5": *input.NewEntity("bundle-5", map[string]string{ | ||
| property.TypePackage: `{"packageName": "test-package-2", "version": "1.0.0"}`, | ||
| property.TypeChannel: `{"channelName":"stable","priority":0}`, | ||
| }), | ||
| }) | ||
| }) | ||
|
|
||
| It("should return the correct package variable", func() { | ||
| variables, err := rpvs.GetVariables(context.TODO(), mockEntitySource) | ||
| Expect(err).NotTo(HaveOccurred()) | ||
| Expect(len(variables)).To(Equal(1)) | ||
| reqPackageVar, ok := variables[0].(*required_package.RequiredPackageVariable) | ||
| Expect(ok).To(BeTrue()) | ||
| Expect(reqPackageVar.Identifier()).To(Equal(deppy.IdentifierFromString(fmt.Sprintf("required package %s", packageName)))) | ||
|
|
||
| // ensure bundle entities are in version order | ||
| Expect(reqPackageVar.BundleEntities()).To(Equal([]*olmentity.BundleEntity{ | ||
| olmentity.NewBundleEntity(input.NewEntity("bundle-2", map[string]string{ | ||
| property.TypePackage: `{"packageName": "test-package", "version": "3.0.0"}`, | ||
| property.TypeChannel: `{"channelName":"stable","priority":0}`, | ||
| })), | ||
| olmentity.NewBundleEntity(input.NewEntity("bundle-3", map[string]string{ | ||
| property.TypePackage: `{"packageName": "test-package", "version": "2.0.0"}`, | ||
| property.TypeChannel: `{"channelName":"stable","priority":0}`, | ||
| })), | ||
| olmentity.NewBundleEntity(input.NewEntity("bundle-1", map[string]string{ | ||
| property.TypePackage: `{"packageName": "test-package", "version": "1.0.0"}`, | ||
| property.TypeChannel: `{"channelName":"stable","priority":0}`})), | ||
| })) | ||
| }) | ||
|
|
||
| It("should return an error if package not found", func() { | ||
| mockEntitySource := input.NewCacheQuerier(map[deppy.Identifier]input.Entity{}) | ||
| _, err := rpvs.GetVariables(context.TODO(), mockEntitySource) | ||
| Expect(err).To(HaveOccurred()) | ||
| Expect(err.Error()).To(Equal("package 'test-package' not found")) | ||
| }) | ||
| }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure I get this construct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's ensuring that
RequiredPackageVariableSourcecorrectly implementsinput.VariableSource: https://go.dev/doc/effective_go#blank_implements