Skip to content
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

Trivy scan meddling #482

Closed
wants to merge 13 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions changelog/v0.21.27/scan-updates.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
changelog:
- type: FIX
description: Trivy scanning option to only update github issues for Max Patch in a majorminor combo
issueLink: https://github.com/solo-io/go-utils/issues/469

137 changes: 130 additions & 7 deletions githubutils/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,17 +145,100 @@ func GetRawGitFile(ctx context.Context, client *github.Client, content *github.R
return byt, err
}

// A RepoFilter can check if a release shold be filtered out based on a condition.
// In general filters may be stateful and be a StatefulRepoFilter
type RepoFilter interface {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is this different that a RepositoryReleasePredicate? It has the same interface, so I don't quite follow how each are intended to be used

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You had stated that you wanted the LTS determining filter to exist in the same place as our existing predicates.

Predicate becomes a bad name for a repofiltering mechanism once you are no longer filtering purely on predicates.

// Apply the filter and return true if it conforms to the flter and should
// not be removed.
Apply(release *github.RepositoryRelease) bool
}

// A StatefulRepoFilter has a stateful prerun check and along with a way to determine
// if a release conforms to our desired filter.
type StatefulRepoFilter interface {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't fully understand how a StatefulFilter works. To me it seems like the stateful filter PreFilterCheck is a way of defining the state on a Filter (to be used during the filter logic).
I wonder if instead, we just stateful component should just be handled at construction time. That way, if we create a RepoFilter (Or RepositoryReleasePredicate), we can instantiate it to behave a certain way (ie filter only latest release), and then the code doesn't need to be aware that it has any internal state.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this is the crux of the issue (other than the related issue where in issue updates we retrun too early which breaks things)

Namely we only get releases in go-utils today.
So if we want to only upload issues for the most recent LTS branch then we need to be able to determine that either here or leak repository retrieval info to another library. This is true because one can only know what an LTS branch is by inspection of the other existing releases.

// Apply the filter and return true if it conforms to the flter and should
// not be removed.
Apply(release *github.RepositoryRelease) bool
// PreFilterCheck is a preprocessing state that updates the internal state
// of the filter. It should not have sideeffects.
PreFilterCheck(release *github.RepositoryRelease)
}

// RetrievalOption applies the option the passed in set of retrieval options.
type RetrievalOption func(*RetrievalOptions)

// RetrievalOptions are the list of optional flags on repo-retrieval.
type RetrievalOptions struct {
Sort bool
MaxReleases int
Filters []RepoFilter
}

// NewCombinedFilter returns a nice combined filter from all the current
// repofilters that have been set.
func NewCombinedFilter(filters ...RepoFilter) StatefulRepoFilter {
cf := combinedFilter{}

for _, rf := range filters {
if srf, ok := rf.(StatefulRepoFilter); ok {
cf.statefulFilters = append(cf.statefulFilters, srf)
} else {
cf.filters = append(cf.filters, rf)
}

}

return &cf
}

type combinedFilter struct {
filters []RepoFilter
statefulFilters []StatefulRepoFilter
}

// Apply all the subfilters and if any reject the release, return false.
func (c *combinedFilter) Apply(release *github.RepositoryRelease) bool {
for _, f := range c.filters {
if !f.Apply(release) {
return false
}
}
for _, f := range c.statefulFilters {
if !f.Apply(release) {
return false
}
}
return true
}

// PreFilterCheck for all the combined stateful filters.
func (c *combinedFilter) PreFilterCheck(release *github.RepositoryRelease) {
for _, f := range c.statefulFilters {
f.PreFilterCheck(release)
}
}

// RepositoryReleasePredicate is a non-stateful release contstraint
// that can be checked to see if a release should be filtered.
// The easiest form is to check whether a release has a given predicate.
type RepositoryReleasePredicate interface {
Apply(release *github.RepositoryRelease) bool
}

// AllReleasesPredicate is a filter that filters... well nothing.
type AllReleasesPredicate struct {
}

// Apply always returns that the release should not be filtered.
func (a *AllReleasesPredicate) Apply(_ *github.RepositoryRelease) bool {
return true
}

// PreFilterCheck for all releases is a no-op.
func (a *AllReleasesPredicate) PreFilterCheck(release *github.RepositoryRelease) {
return
}

func GetAllRepoReleases(ctx context.Context, client *github.Client, owner, repo string) ([]*github.RepositoryRelease, error) {
return GetAllRepoReleasesWithMax(ctx, client, owner, repo, math.MaxInt32)
}
Expand All @@ -165,8 +248,36 @@ func GetAllRepoReleasesWithMax(ctx context.Context, client *github.Client, owner
}

func GetRepoReleasesWithPredicateAndMax(ctx context.Context, client *github.Client, owner, repo string, predicate RepositoryReleasePredicate, maxReleases int) ([]*github.RepositoryRelease, error) {
opt := func(ro *RetrievalOptions) {
ro.MaxReleases = maxReleases
ro.Filters = append(ro.Filters, predicate)

}
return GetRepoReleases(ctx, client, owner, repo, opt)
}

// GetRepoReleases retrieves releases from a repository given a set of retrieval options.
func GetRepoReleases(ctx context.Context, client *github.Client, owner, repo string, opts ...RetrievalOption) ([]*github.RepositoryRelease, error) {
var allReleases []*github.RepositoryRelease
for i := MIN_GITHUB_PAGE_NUM; len(allReleases) < maxReleases; i += 1 {
ro := RetrievalOptions{}

for _, opt := range opts {
opt(&ro)
}

filter := NewCombinedFilter(ro.Filters...)
// uploadFilter := newCombinedFilter(ro.uploadFilters...)

// This is silly but we dont want to change the exported value
i := MIN_GITHUB_PAGE_NUM - 1

for {
// check to see if we have gotten enough releases
if len(allReleases) >= ro.MaxReleases {
// One more filtering where we check for
allReleases = FilterRepositoryReleases(allReleases, filter)
}

releases, _, err := client.Repositories.ListReleases(ctx, owner, repo, &github.ListOptions{
Page: i,
PerPage: MAX_GITHUB_RESULTS_PER_PAGE,
Expand All @@ -175,9 +286,12 @@ func GetRepoReleasesWithPredicateAndMax(ctx context.Context, client *github.Clie
return nil, err
}

// Only append releases if they match the predicate
// Only append releases if they match the filter (predicate or other functions)
// This is required since the Github API does not expose parameters to filter the RepositoryRelease list in the request
filteredReleases := FilterRepositoryReleases(releases, predicate)
filteredReleases := FilterRepositoryReleases(releases, filter)
if ro.Sort {
SortReleasesBySemver(filteredReleases)
}
allReleases = append(allReleases, filteredReleases...)

// If the number of releases on this page is less than the results per page,
Expand All @@ -188,16 +302,25 @@ func GetRepoReleasesWithPredicateAndMax(ctx context.Context, client *github.Clie
}

// Ensure that if we have exceeded the number of maxReleases, we truncate the list
if len(allReleases) > maxReleases {
allReleases = allReleases[:maxReleases]
if len(allReleases) > ro.MaxReleases {
allReleases = allReleases[:ro.MaxReleases]
}
return allReleases, nil
}

func FilterRepositoryReleases(releases []*github.RepositoryRelease, predicate RepositoryReleasePredicate) []*github.RepositoryRelease {
// FilterRepositoryReleases applys the filtering logic to rebuild the slice of
// releases that we care about.
func FilterRepositoryReleases(releases []*github.RepositoryRelease, filter RepoFilter) []*github.RepositoryRelease {
var filteredReleases []*github.RepositoryRelease
// in case the filter is stateful
if statefulF, ok := filter.(StatefulRepoFilter); ok {
for _, release := range releases {
statefulF.PreFilterCheck(release)
}
}

for _, release := range releases {
if predicate.Apply(release) {
if filter.Apply(release) {
filteredReleases = append(filteredReleases, release)
}
}
Expand Down
Loading