Skip to content

Commit

Permalink
Add a minimal status option to the report generating command (#418)
Browse files Browse the repository at this point in the history
* Add a function that discards all check results that fall below the minStatus

* Add a --min-status flag to the Cobra generate comand

* Add tests to validate the filtering function

* Typo

* Add suggested changes

* Move method

* Correct a typo and add sorted explanation
  • Loading branch information
georgibaltiev authored Jan 9, 2025
1 parent ecdacfe commit f65eae4
Show file tree
Hide file tree
Showing 3 changed files with 208 additions and 0 deletions.
13 changes: 13 additions & 0 deletions cmd/diki/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"log/slog"
"os"
"path/filepath"
"slices"

"github.com/spf13/cobra"
"gopkg.in/yaml.v3"
Expand All @@ -22,6 +23,7 @@ import (
"github.com/gardener/diki/pkg/config"
"github.com/gardener/diki/pkg/provider"
"github.com/gardener/diki/pkg/report"
"github.com/gardener/diki/pkg/rule"
"github.com/gardener/diki/pkg/ruleset"
)

Expand Down Expand Up @@ -144,6 +146,7 @@ func addRunFlags(cmd *cobra.Command, opts *runOptions) {
func addReportGenerateFlags(cmd *cobra.Command, opts *generateOptions) {
cmd.PersistentFlags().Var(cliflag.NewMapStringString(&opts.distinctBy), "distinct-by", "If set generates a merged report. The keys are the IDs for the providers which the merged report will include and the values are distinct metadata attributes to be used as IDs for the different reports.")
cmd.PersistentFlags().StringVar(&opts.format, "format", "html", "Format for the output report. Format can be one of 'html' or 'json'.")
cmd.PersistentFlags().StringVar(&opts.minStatus, "min-status", "Passed", "If set specifies the minimal status that will be included in the generated report. Ordered from lowest to highest priority, Status can be one of 'Passed', 'Skipped', 'Accepted', 'Warning', 'Failed', 'Errored' or 'NotImplemented'")
}

func addReportDiffFlags(cmd *cobra.Command, opts *diffOptions) {
Expand Down Expand Up @@ -263,6 +266,14 @@ func generateCmd(args []string, rootOpts reportOptions, opts generateOptions, lo
return errors.New("generate command requires a single filepath argument when the distinct-by flag is not set")
}

minStatus := rule.Passed
if len(opts.minStatus) != 0 {
minStatus = rule.Status(opts.minStatus)
if !slices.Contains(rule.Statuses(), minStatus) {
return fmt.Errorf("not defined status: %s", minStatus)
}
}

var reports []*report.Report
for _, arg := range args {
fileData, err := os.ReadFile(filepath.Clean(arg))
Expand All @@ -276,6 +287,7 @@ func generateCmd(args []string, rootOpts reportOptions, opts generateOptions, lo
return fmt.Errorf("failed to unmarshal data: %w", err)
}

rep.SetMinStatus(minStatus)
reports = append(reports, rep)
}

Expand Down Expand Up @@ -455,6 +467,7 @@ type runOptions struct {
type generateOptions struct {
distinctBy map[string]string
format string
minStatus string
}

type generateDiffOptions struct {
Expand Down
20 changes: 20 additions & 0 deletions pkg/report/report.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,26 @@ func (r *Report) WriteToFile(filePath string) error {
return os.WriteFile(filePath, data, 0600)
}

// SetMinStatus sets minStatus of the report. It also removes all checks with status less than the specified one.
// If the new minStatus is less than the original one, nothing is changed.
func (r *Report) SetMinStatus(minStatus rule.Status) {
if minStatus.Less(r.MinStatus) {
return
}

r.MinStatus = minStatus
for _, provider := range r.Providers {
for _, ruleset := range provider.Rulesets {
for ruleIdx := range ruleset.Rules {
filteredChecks := slices.DeleteFunc(ruleset.Rules[ruleIdx].Checks, func(check Check) bool {
return check.Status.Less(minStatus)
})
ruleset.Rules[ruleIdx].Checks = filteredChecks
}
}
}
}

// rulesetSummaryText returns a summary string with the number of rules with results per status.
func rulesetSummaryText(ruleset *Ruleset) string {
statuses := rule.Statuses()
Expand Down
175 changes: 175 additions & 0 deletions pkg/report/report_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
// SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and Gardener contributors
//
// SPDX-License-Identifier: Apache-2.0

package report_test

import (
"time"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"

"github.com/gardener/diki/pkg/report"
"github.com/gardener/diki/pkg/rule"
)

var _ = Describe("report", func() {
Describe("Report", func() {
var (
reportTime time.Time
providerID = "provider-foo"
providerName = "Provider Foo"
rulesetID = "ruleset-foo"
rulesetName = "Ruleset Foo"
rulesetVersion = "v1"
simpleReport report.Report
)
BeforeEach(func() {
reportTime = time.Date(2000, time.January, 1, 0, 0, 0, 0, time.Local)
simpleReport = report.Report{
Time: reportTime,
DikiVersion: "1",
MinStatus: rule.Accepted,
Providers: []report.Provider{
{
ID: providerID,
Name: providerName,
Rulesets: []report.Ruleset{
{
ID: rulesetID,
Name: rulesetName,
Version: rulesetVersion,
Rules: []report.Rule{
{
ID: "1",
Name: "1",
Severity: rule.SeverityHigh,
Checks: []report.Check{
{
Status: "Accepted",
Message: "foo",
Targets: []rule.Target{},
},
{
Status: "Failed",
Message: "bar",
Targets: []rule.Target{},
},
{
Status: "Skipped",
Message: "baz",
Targets: []rule.Target{},
},
},
},
{
ID: "2",
Name: "2",
Severity: rule.SeverityLow,
Checks: []report.Check{
{
Status: "Accepted",
Message: "foo",
Targets: []rule.Target{},
},
{
Status: "Accepted",
Message: "bar",
Targets: []rule.Target{},
},
},
},
{
ID: "3",
Name: "3",
Severity: rule.SeverityLow,
Checks: []report.Check{
{
Status: "Failed",
Message: "foo",
Targets: []rule.Target{},
},
{
Status: "Failed",
Message: "bar",
Targets: []rule.Target{},
},
},
},
},
},
},
},
},
}
})

It("should correctly remove ruleset checks that are below the minStatus", func() {
expectedReportResult := report.Report{
Time: reportTime,
DikiVersion: "1",
MinStatus: rule.Failed,
Providers: []report.Provider{
{
ID: providerID,
Name: providerName,
Rulesets: []report.Ruleset{
{
ID: rulesetID,
Name: rulesetName,
Version: rulesetVersion,
Rules: []report.Rule{
{
ID: "1",
Name: "1",
Severity: rule.SeverityHigh,
Checks: []report.Check{
{
Status: "Failed",
Message: "bar",
Targets: []rule.Target{},
},
},
},
{
ID: "2",
Name: "2",
Severity: rule.SeverityLow,
Checks: []report.Check{},
},
{
ID: "3",
Name: "3",
Severity: rule.SeverityLow,
Checks: []report.Check{
{
Status: "Failed",
Message: "foo",
Targets: []rule.Target{},
},
{
Status: "Failed",
Message: "bar",
Targets: []rule.Target{},
},
},
},
},
},
},
},
},
}
simpleReport.SetMinStatus(rule.Failed)
Expect(simpleReport).To(Equal(expectedReportResult))
})

It("should not alter the report when the passed minStatus is not lower the report's minStatus", func() {
expectedReport := simpleReport
simpleReport.SetMinStatus(rule.Passed)
Expect(simpleReport).To(Equal(expectedReport))
})
})

})

0 comments on commit f65eae4

Please sign in to comment.