Skip to content
Merged
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
53 changes: 49 additions & 4 deletions central/complianceoperator/v2/report/manager/format/formatter.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,18 @@ import (
"bytes"
"fmt"
"io"
"strings"

"github.com/pkg/errors"
"github.com/stackrox/rox/central/complianceoperator/v2/report"
"github.com/stackrox/rox/generated/storage"
"github.com/stackrox/rox/pkg/csv"
)

const (
EmptyValue = "Data not found for the cluster"
emptyValue = "Data not found for the cluster"
successfulClusterFmt = "cluster_%s.csv"
failedClusterFmt = "failed_cluster_%s.csv"
)

var (
Expand All @@ -27,6 +31,12 @@ var (
"Rationale",
"Instructions",
}
failedClusterCSVHeader = []string{
"Cluster ID",
"Cluster Name",
"Reason",
"Compliance Operator Version",
}
)

//go:generate mockgen-wrapper
Expand All @@ -53,7 +63,11 @@ func NewFormatter() *FormatterImpl {
}
}

func (f *FormatterImpl) FormatCSVReport(results map[string][]*report.ResultRow) (buffRet *bytes.Buffer, errRet error) {
// FormatCSVReport generates zip data containing CSV files (one per cluster).
// If a cluster fails, the generated CSV file will contain the reason for the reason but (no check results).
// If a cluster success, the generated CSV file will contain all the check results with enhanced information (e.g. remediation, associated profile, etc)
// The results parameter is expected to contain the clusters that succeed (no failed clusters should be passed in results).
func (f *FormatterImpl) FormatCSVReport(results map[string][]*report.ResultRow, failedClusters map[string]*storage.ComplianceOperatorReportSnapshotV2_FailedCluster) (buffRet *bytes.Buffer, errRet error) {
var buf bytes.Buffer
zipWriter := f.newZipWriter(&buf)
defer func() {
Expand All @@ -62,8 +76,18 @@ func (f *FormatterImpl) FormatCSVReport(results map[string][]*report.ResultRow)
errRet = errors.Wrap(err, "unable to create a zip file of the compliance report")
}
}()
for clusterID, failedCluster := range failedClusters {
fileName := fmt.Sprintf(failedClusterFmt, clusterID)
if err := f.createFailedClusterFileInZip(zipWriter, fileName, failedCluster); err != nil {
return nil, errors.Wrap(err, "error creating failed cluster report")
}
}
for clusterID, res := range results {
fileName := fmt.Sprintf("cluster_%s.csv", clusterID)
// We should not receive results from a failed cluster
if _, ok := failedClusters[clusterID]; ok {
continue
}
fileName := fmt.Sprintf(successfulClusterFmt, clusterID)
err := f.createCSVInZip(zipWriter, fileName, res)
if err != nil {
return nil, errors.Wrap(err, "error creating csv report")
Expand All @@ -83,12 +107,13 @@ func (f *FormatterImpl) createCSVInZip(zipWriter ZipWriter, filename string, clu
csvWriter.AddValue(generateRecord(checkRes))
}
} else {
csvWriter.AddValue([]string{EmptyValue})
csvWriter.AddValue([]string{emptyValue})
}
return csvWriter.WriteCSV(w)
}

func generateRecord(row *report.ResultRow) []string {
// The order in the slice needs to match the order defined in `csvHeader`
return []string{
row.ControlRef,
row.CheckName,
Expand All @@ -102,6 +127,26 @@ func generateRecord(row *report.ResultRow) []string {
}
}

func (f *FormatterImpl) createFailedClusterFileInZip(zipWriter ZipWriter, filename string, failedCluster *storage.ComplianceOperatorReportSnapshotV2_FailedCluster) error {
w, err := zipWriter.Create(filename)
if err != nil {
return err
}
csvWriter := f.newCSVWriter(failedClusterCSVHeader, true)
csvWriter.AddValue(generateFailRecord(failedCluster))
return csvWriter.WriteCSV(w)
}

func generateFailRecord(failedCluster *storage.ComplianceOperatorReportSnapshotV2_FailedCluster) []string {
// The order in the slice needs to match the order defined in `failedClusterCSVHeader`
return []string{
failedCluster.GetClusterId(),
failedCluster.GetClusterName(),
strings.Join(failedCluster.GetReasons(), ", "),
failedCluster.GetOperatorVersion(),
}
}

func createNewZipWriter(buf *bytes.Buffer) ZipWriter {
return zip.NewWriter(buf)
}
Expand Down
Loading
Loading