From 5ee08b5a932558041ecf07ecb6151281557e4a76 Mon Sep 17 00:00:00 2001 From: Michael Kelly Date: Fri, 15 Nov 2024 17:51:41 -0800 Subject: [PATCH] Add allow-list support to resolve and rpmtree In addition to allowing a set of packages that are not allowed, we should allow the scope to be narrowed to a set of packages that is allowed. This change adds the `--only-allow` option to both `resolve` and `rpmtree` commands to achieve this outcome. --- cmd/resolve.go | 4 +++- cmd/rpmtree.go | 4 +++- pkg/sat/sat.go | 32 +++++++++++++++++++++++++------- pkg/sat/sat_test.go | 6 +++--- 4 files changed, 34 insertions(+), 12 deletions(-) diff --git a/cmd/resolve.go b/cmd/resolve.go index 2f8ff40..8d8a12c 100644 --- a/cmd/resolve.go +++ b/cmd/resolve.go @@ -20,6 +20,7 @@ type resolveOpts struct { baseSystem string repofiles []string forceIgnoreRegex []string + onlyAllowRegex []string } var resolveopts = resolveOpts{} @@ -52,7 +53,7 @@ func NewResolveCmd() *cobra.Command { } solver := sat.NewResolver(resolveopts.nobest) logrus.Info("Loading involved packages into the resolver.") - err = solver.LoadInvolvedPackages(involved, resolveopts.forceIgnoreRegex) + err = solver.LoadInvolvedPackages(involved, resolveopts.forceIgnoreRegex, resolveopts.onlyAllowRegex) if err != nil { return err } @@ -79,6 +80,7 @@ func NewResolveCmd() *cobra.Command { resolveCmd.Flags().BoolVarP(&resolveopts.nobest, "nobest", "n", false, "allow picking versions which are not the newest") resolveCmd.Flags().StringArrayVarP(&resolveopts.repofiles, "repofile", "r", []string{"repo.yaml"}, "repository information file. Can be specified multiple times. Will be used by default if no explicit inputs are provided.") resolveCmd.Flags().StringArrayVar(&resolveopts.forceIgnoreRegex, "force-ignore-with-dependencies", []string{}, "Packages matching these regex patterns will not be installed. Allows force-removing unwanted dependencies. Be careful, this can lead to hidden missing dependencies.") + resolveCmd.Flags().StringArrayVar(&resolveopts.onlyAllowRegex, "only-allow", []string{}, "Packages matching these regex patterns may be installed. Allows scoping dependencies. Be careful, this can lead to hidden missing dependencies.") // deprecated options resolveCmd.Flags().StringVarP(&resolveopts.baseSystem, "fedora-base-system", "f", "fedora-release-container", "base system to use (e.g. fedora-release-server, centos-stream-release, ...)") resolveCmd.Flags().MarkDeprecated("fedora-base-system", "use --basesystem instead") diff --git a/cmd/rpmtree.go b/cmd/rpmtree.go index c34ffe0..f213049 100644 --- a/cmd/rpmtree.go +++ b/cmd/rpmtree.go @@ -25,6 +25,7 @@ type rpmtreeOpts struct { name string public bool forceIgnoreRegex []string + onlyAllowRegex []string } var rpmtreeopts = rpmtreeOpts{} @@ -54,7 +55,7 @@ func NewRpmTreeCmd() *cobra.Command { } solver := sat.NewResolver(rpmtreeopts.nobest) logrus.Info("Loading involved packages into the rpmtreer.") - err = solver.LoadInvolvedPackages(involved, rpmtreeopts.forceIgnoreRegex) + err = solver.LoadInvolvedPackages(involved, rpmtreeopts.forceIgnoreRegex, rpmtreeopts.onlyAllowRegex) if err != nil { return err } @@ -138,6 +139,7 @@ func NewRpmTreeCmd() *cobra.Command { rpmtreeCmd.Flags().StringVarP(&rpmtreeopts.buildfile, "buildfile", "b", "rpm/BUILD.bazel", "Build file for RPMs") rpmtreeCmd.Flags().StringVar(&rpmtreeopts.name, "name", "", "rpmtree rule name") rpmtreeCmd.Flags().StringArrayVar(&rpmtreeopts.forceIgnoreRegex, "force-ignore-with-dependencies", []string{}, "Packages matching these regex patterns will not be installed. Allows force-removing unwanted dependencies. Be careful, this can lead to hidden missing dependencies.") + rpmtreeCmd.Flags().StringArrayVar(&rpmtreeopts.onlyAllowRegex, "only-allow", []string{}, "Packages matching these regex patterns may be installed. Allows scoping dependencies. Be careful, this can lead to hidden missing dependencies.") rpmtreeCmd.MarkFlagRequired("name") // deprecated options rpmtreeCmd.Flags().StringVarP(&rpmtreeopts.baseSystem, "fedora-base-system", "f", "fedora-release-container", "base system to use (e.g. fedora-release-server, centos-stream-release, ...)") diff --git a/pkg/sat/sat.go b/pkg/sat/sat.go index 1a2f9ca..b84fb36 100644 --- a/pkg/sat/sat.go +++ b/pkg/sat/sat.go @@ -105,9 +105,10 @@ func (r *Resolver) ticket() string { } // LoadInvolvedPackages takes a list of all involved packages to install, as well as a list of regular -// expressions which denoe packages which should be taken into account for solving the problem, but they +// expressions which denote packages which should be taken into account for solving the problem, but they // should then be ignored together with their requirements in the provided list of installed packages. -func (r *Resolver) LoadInvolvedPackages(packages []*api.Package, ignoreRegex []string) error { +// Additionally, we can take a list of packages that the maximum selection desired. +func (r *Resolver) LoadInvolvedPackages(packages []*api.Package, ignoreRegex []string, allowRegex []string) error { // Deduplicate and detect excludes deduplicated := map[string]*api.Package{} for i, pkg := range packages { @@ -116,16 +117,33 @@ func (r *Resolver) LoadInvolvedPackages(packages []*api.Package, ignoreRegex []s } fullName := pkg.String() if _, exists := deduplicated[fullName]; !exists { - for _, rex := range ignoreRegex { + matchedAllow := len(allowRegex) == 0 + + for _, rex := range allowRegex { if match, err := regexp.MatchString(rex, fullName); err != nil { return fmt.Errorf("failed to match package with regex '%v': %v", rex, err) } else if match { - packages[i].Format.Requires.Entries = nil - logrus.Warnf("Package %v is forcefully ignored by regex '%v'.", pkg.String(), rex) - r.forceIgnoreWithDependencies[pkg.String()] = packages[i] - break + matchedAllow = true } } + + if !matchedAllow { + packages[i].Format.Requires.Entries = nil + logrus.Warnf("Package %v does not match allow list", pkg.String()) + r.forceIgnoreWithDependencies[pkg.String()] = packages[i] + } else { + for _, rex := range ignoreRegex { + if match, err := regexp.MatchString(rex, fullName); err != nil { + return fmt.Errorf("failed to match package with regex '%v': %v", rex, err) + } else if match { + packages[i].Format.Requires.Entries = nil + logrus.Warnf("Package %v is forcefully ignored by regex '%v'.", pkg.String(), rex) + r.forceIgnoreWithDependencies[pkg.String()] = packages[i] + break + } + } + } + deduplicated[pkg.String()] = packages[i] } } diff --git a/pkg/sat/sat_test.go b/pkg/sat/sat_test.go index f371aaa..72d39da 100644 --- a/pkg/sat/sat_test.go +++ b/pkg/sat/sat_test.go @@ -27,7 +27,7 @@ func TestRecursive(t *testing.T) { for i, _ := range repo.Packages { packages = append(packages, &repo.Packages[i]) } - err = resolver.LoadInvolvedPackages(packages, nil) + err = resolver.LoadInvolvedPackages(packages, nil, nil) g.Expect(err).ToNot(HaveOccurred()) err = resolver.ConstructRequirements([]string{pkg.Name}) g.Expect(err).ToNot(HaveOccurred()) @@ -1229,7 +1229,7 @@ func Test(t *testing.T) { for i, _ := range repo.Packages { packages = append(packages, &repo.Packages[i]) } - err = resolver.LoadInvolvedPackages(packages, nil) + err = resolver.LoadInvolvedPackages(packages, nil, nil) g.Expect(err).ToNot(HaveOccurred()) err = resolver.ConstructRequirements(tt.requires) g.Expect(err).ToNot(HaveOccurred()) @@ -1359,7 +1359,7 @@ func TestNewResolver(t *testing.T) { } t.Run(tt.name, func(t *testing.T) { resolver := NewResolver(tt.nobest) - err := resolver.LoadInvolvedPackages(tt.packages, nil) + err := resolver.LoadInvolvedPackages(tt.packages, nil, nil) if err != nil { t.Fail() }