Skip to content

Commit b4872af

Browse files
authored
Implement WriteIndex for cluster summaries (#70)
* Implement WriteIndex for cluster summaries Signed-off-by: Charlie Egan <[email protected]> * Remove unreachable code Signed-off-by: Charlie Egan <[email protected]> * Dry up azblob output Signed-off-by: Charlie Egan <[email protected]>
1 parent d66977f commit b4872af

File tree

18 files changed

+523
-2
lines changed

18 files changed

+523
-2
lines changed

cmd/check.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"path/filepath"
1111
"time"
1212

13+
"github.com/jetstack/preflight/api"
1314
"github.com/jetstack/preflight/pkg/datagatherer"
1415
"github.com/jetstack/preflight/pkg/datagatherer/aks"
1516
"github.com/jetstack/preflight/pkg/datagatherer/eks"
@@ -322,6 +323,7 @@ func check() {
322323
}
323324

324325
missingRules := false
326+
packageReports := []api.Report{}
325327
for _, enabledPackage := range enabledPackages {
326328
// Make sure we loaded the package for this.
327329
pkg := packages[enabledPackage.ID]
@@ -360,6 +362,13 @@ func check() {
360362
log.Fatalf("Cannot marshal intermediate result: %v", err)
361363
}
362364

365+
// build a report to build the updated context for the report index
366+
report, err := reports.NewReport(manifest, rc)
367+
if err != nil {
368+
log.Fatalf("Cannot generate report for results: %v", err)
369+
}
370+
packageReports = append(packageReports, report)
371+
363372
for _, output := range outputs {
364373
err := output.Write(ctx, manifest, intermediateBytes, rc, clusterName, checkTime)
365374
if err != nil {
@@ -373,6 +382,19 @@ func check() {
373382
} else {
374383
log.Printf("Done.")
375384
}
385+
386+
clusterSummary, err := reports.NewClusterSummary(packageReports)
387+
if err != nil {
388+
log.Fatalf("Cannot generate index of reports: %v", err)
389+
}
390+
for _, output := range outputs {
391+
err := output.WriteIndex(ctx, clusterName, checkTime, &clusterSummary)
392+
if err != nil {
393+
log.Fatalf("failed to output index: %s", err)
394+
}
395+
}
396+
397+
log.Printf("Done.")
376398
}
377399

378400
func homeDir() string {

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ require (
1010
github.com/aws/aws-sdk-go v1.25.30
1111
github.com/blang/semver v3.5.1+incompatible
1212
github.com/gomarkdown/markdown v0.0.0-20191104174740-4d42851d4d5a
13+
github.com/google/go-cmp v0.3.0
1314
github.com/gookit/color v1.2.0
1415
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6 // indirect
1516
github.com/juju/errors v0.0.0-20190930114154-d42613fe1ab9

pkg/exporter/cli.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88

99
"github.com/gookit/color"
1010

11+
"github.com/jetstack/preflight/api"
1112
"github.com/jetstack/preflight/pkg/packaging"
1213
"github.com/jetstack/preflight/pkg/results"
1314
"github.com/jetstack/preflight/pkg/rules"
@@ -95,6 +96,28 @@ func (e *CLIExporter) Export(ctx context.Context, policyManifest *packaging.Poli
9596
return writer, nil
9697
}
9798

99+
// ExportIndex formats the supplied cluster summary
100+
func (e *CLIExporter) ExportIndex(ctx context.Context, clusterSummary *api.ClusterSummary) (*bytes.Buffer, error) {
101+
lines := []string{
102+
"Summary",
103+
"-------",
104+
fmt.Sprintf("cluster: %s", clusterSummary.Cluster),
105+
fmt.Sprintf("failures: %d", clusterSummary.LatestReportSet.FailureCount),
106+
fmt.Sprintf("successes: %d", clusterSummary.LatestReportSet.SuccessCount),
107+
"reports:",
108+
}
109+
110+
for _, r := range clusterSummary.LatestReportSet.Reports {
111+
lines = append(lines, []string{
112+
fmt.Sprintf(" package: %s", r.Package),
113+
fmt.Sprintf(" failures: %d", r.FailureCount),
114+
fmt.Sprintf(" successes: %d", r.SuccessCount)}...)
115+
}
116+
lines = append(lines, "")
117+
118+
return bytes.NewBuffer([]byte(strings.Join(lines, "\n"))), nil
119+
}
120+
98121
// FileExtension returns the file extension for this exporter's format
99122
func (e *CLIExporter) FileExtension() string {
100123
return ""

pkg/exporter/cli_test.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package exporter
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
"github.com/google/go-cmp/cmp"
8+
"github.com/jetstack/preflight/api"
9+
)
10+
11+
func TestCLIExportIndex(t *testing.T) {
12+
clusterSummary := api.ClusterSummary{
13+
Cluster: "exampleCluster",
14+
LatestReportSet: &api.ReportSet{
15+
Cluster: "exampleCluster",
16+
Timestamp: api.Time{},
17+
FailureCount: 4,
18+
SuccessCount: 1,
19+
Reports: []*api.ReportSummary{
20+
&api.ReportSummary{
21+
ID: "exampleReport1",
22+
Package: "examplePackage.ID.1",
23+
Cluster: "exampleCluster",
24+
Timestamp: api.Time{},
25+
FailureCount: 2,
26+
SuccessCount: 1,
27+
},
28+
&api.ReportSummary{
29+
ID: "exampleReport2",
30+
Package: "examplePackage.ID.2",
31+
Cluster: "exampleCluster",
32+
Timestamp: api.Time{},
33+
FailureCount: 2,
34+
SuccessCount: 0,
35+
},
36+
},
37+
},
38+
}
39+
40+
exporter := NewCLIExporter()
41+
var ctx context.Context
42+
43+
got, err := exporter.ExportIndex(ctx, &clusterSummary)
44+
if err != nil {
45+
t.Errorf("error exporting: %v", err)
46+
}
47+
48+
want := `Summary
49+
-------
50+
cluster: exampleCluster
51+
failures: 4
52+
successes: 1
53+
reports:
54+
package: examplePackage.ID.1
55+
failures: 2
56+
successes: 1
57+
package: examplePackage.ID.2
58+
failures: 2
59+
successes: 0
60+
`
61+
62+
if diff := cmp.Diff(want, got.String()); diff != "" {
63+
t.Errorf("ExportIndex diff (-want +got):\n%s", diff)
64+
}
65+
}

pkg/exporter/exporter.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@ import (
44
"bytes"
55
"context"
66

7+
"github.com/jetstack/preflight/api"
78
"github.com/jetstack/preflight/pkg/packaging"
89
"github.com/jetstack/preflight/pkg/results"
910
)
1011

1112
// Exporter consumes policy manifests, intermediate JSON, and results, and exports them to a buffer in a certain format
1213
type Exporter interface {
1314
Export(ctx context.Context, policyManifest *packaging.PolicyManifest, intermediateJSON []byte, results *results.ResultCollection) (*bytes.Buffer, error)
15+
ExportIndex(ctx context.Context, clusterIndex *api.ClusterSummary) (*bytes.Buffer, error)
1416
FileExtension() string
1517
}
1618

pkg/exporter/html.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"context"
66

77
"github.com/gomarkdown/markdown"
8+
"github.com/jetstack/preflight/api"
89
"github.com/jetstack/preflight/pkg/packaging"
910
"github.com/jetstack/preflight/pkg/results"
1011
)
@@ -35,6 +36,23 @@ func (e *HTMLExporter) Export(ctx context.Context, policyManifest *packaging.Pol
3536
return writer, nil
3637
}
3738

39+
// ExportIndex formats the supplied cluster summary
40+
func (e *HTMLExporter) ExportIndex(ctx context.Context, clusterSummary *api.ClusterSummary) (*bytes.Buffer, error) {
41+
mdExporter := NewMarkdownExporter()
42+
md, err := mdExporter.ExportIndex(ctx, clusterSummary)
43+
if err != nil {
44+
return nil, err
45+
}
46+
htmlBytes := markdown.ToHTML(md.Bytes(), nil, nil)
47+
writer := bytes.NewBuffer([]byte{})
48+
_, err = writer.Write(htmlBytes)
49+
if err != nil {
50+
return nil, err
51+
}
52+
53+
return writer, nil
54+
}
55+
3856
// FileExtension returns the file extension for this exporter's format
3957
func (e *HTMLExporter) FileExtension() string {
4058
return ".html"

pkg/exporter/intermediate.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"bytes"
55
"context"
66

7+
"github.com/jetstack/preflight/api"
78
"github.com/jetstack/preflight/pkg/packaging"
89
"github.com/jetstack/preflight/pkg/results"
910
)
@@ -27,6 +28,11 @@ func (e *IntermediateExporter) Export(ctx context.Context, policyManifest *packa
2728
return writer, nil
2829
}
2930

31+
// ExportIndex formats the supplied cluster summary
32+
func (e *IntermediateExporter) ExportIndex(ctx context.Context, clusterSummary *api.ClusterSummary) (*bytes.Buffer, error) {
33+
return bytes.NewBuffer([]byte("there is no intermediateJSON for cluster report summaries")), nil
34+
}
35+
3036
// FileExtension returns the file extension for this exporter's format
3137
func (e *IntermediateExporter) FileExtension() string {
3238
return ".intermediate.json"

pkg/exporter/json.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"context"
66
"encoding/json"
77

8+
"github.com/jetstack/preflight/api"
89
"github.com/jetstack/preflight/pkg/packaging"
910
"github.com/jetstack/preflight/pkg/reports"
1011
"github.com/jetstack/preflight/pkg/results"
@@ -37,6 +38,16 @@ func (e *JSONExporter) Export(ctx context.Context, policyManifest *packaging.Pol
3738
return bytes.NewBuffer(b), missingRuleError
3839
}
3940

41+
// ExportIndex formats the supplied cluster summary
42+
func (e *JSONExporter) ExportIndex(ctx context.Context, clusterSummary *api.ClusterSummary) (*bytes.Buffer, error) {
43+
b, err := json.Marshal(clusterSummary)
44+
if err != nil {
45+
return nil, err
46+
}
47+
48+
return bytes.NewBuffer(b), nil
49+
}
50+
4051
// FileExtension returns the file extension for this exporter's format
4152
func (e *JSONExporter) FileExtension() string {
4253
return ".json"

pkg/exporter/json_test.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"github.com/yudai/gojsondiff"
99
"github.com/yudai/gojsondiff/formatter"
1010

11+
"github.com/jetstack/preflight/api"
1112
"github.com/jetstack/preflight/pkg/packaging"
1213
"github.com/jetstack/preflight/pkg/reports"
1314
"github.com/jetstack/preflight/pkg/results"
@@ -334,3 +335,85 @@ func TestJSONExportBackwardsCompatibility(t *testing.T) {
334335
t.Fatalf("got != want: %v", differences)
335336
}
336337
}
338+
339+
func TestJSONExportIndex(t *testing.T) {
340+
clusterSummary := api.ClusterSummary{
341+
Cluster: "exampleCluster",
342+
LatestReportSet: &api.ReportSet{
343+
Cluster: "exampleCluster",
344+
Timestamp: api.Time{},
345+
FailureCount: 4,
346+
SuccessCount: 1,
347+
Reports: []*api.ReportSummary{
348+
&api.ReportSummary{
349+
ID: "exampleReport1",
350+
Package: "examplePackage.ID.1",
351+
Cluster: "exampleCluster",
352+
Timestamp: api.Time{},
353+
FailureCount: 2,
354+
SuccessCount: 1,
355+
},
356+
&api.ReportSummary{
357+
ID: "exampleReport2",
358+
Package: "examplePackage.ID.2",
359+
Cluster: "exampleCluster",
360+
Timestamp: api.Time{},
361+
FailureCount: 2,
362+
SuccessCount: 0,
363+
},
364+
},
365+
},
366+
}
367+
368+
exporter := NewJSONExporter()
369+
var ctx context.Context
370+
371+
actualJSON, err := exporter.ExportIndex(ctx, &clusterSummary)
372+
if err != nil {
373+
t.Errorf("error exporting: %v", err)
374+
}
375+
376+
expectedJSON := `{
377+
"cluster": "exampleCluster",
378+
"latestReportSet": {
379+
"timestamp": "0001-01-01T00:00:00Z",
380+
"failureCount": 4,
381+
"successCount": 1,
382+
"reports": [
383+
{
384+
"id": "exampleReport1",
385+
"package": "examplePackage.ID.1",
386+
"failureCount": 2,
387+
"successCount": 1
388+
},
389+
{
390+
"id": "exampleReport2",
391+
"package": "examplePackage.ID.2",
392+
"failureCount": 2,
393+
"successCount": 0
394+
}
395+
]
396+
}
397+
}`
398+
399+
var got, want map[string]interface{}
400+
401+
if err = json.Unmarshal([]byte(expectedJSON), &want); err != nil {
402+
t.Fatalf("%+v", err)
403+
}
404+
405+
if err = json.Unmarshal(actualJSON.Bytes(), &got); err != nil {
406+
t.Fatalf("%+v", err)
407+
}
408+
diff := gojsondiff.New().CompareObjects(want, got)
409+
410+
if diff.Modified() {
411+
f := formatter.NewAsciiFormatter(want, formatter.AsciiFormatterConfig{ShowArrayIndex: true, Coloring: true})
412+
differences, err := f.Format(diff)
413+
if err != nil {
414+
t.Errorf("could not format diff: %+v", err)
415+
}
416+
417+
t.Fatalf("got != want: %v", differences)
418+
}
419+
}

pkg/exporter/markdown.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"fmt"
77
"strings"
88

9+
"github.com/jetstack/preflight/api"
910
"github.com/jetstack/preflight/pkg/packaging"
1011
"github.com/jetstack/preflight/pkg/results"
1112
"github.com/jetstack/preflight/pkg/rules"
@@ -86,6 +87,26 @@ func (e *MarkdownExporter) Export(ctx context.Context, policyManifest *packaging
8687
return writer, nil
8788
}
8889

90+
// ExportIndex formats the supplied cluster summary
91+
func (e *MarkdownExporter) ExportIndex(ctx context.Context, clusterSummary *api.ClusterSummary) (*bytes.Buffer, error) {
92+
lines := []string{
93+
"# Summary",
94+
fmt.Sprintf("**cluster:** %s", clusterSummary.Cluster),
95+
fmt.Sprintf("**failures:** %d", clusterSummary.LatestReportSet.FailureCount),
96+
fmt.Sprintf("**successes:** %d\n", clusterSummary.LatestReportSet.SuccessCount),
97+
"## Report Breakdown\n",
98+
}
99+
100+
for _, r := range clusterSummary.LatestReportSet.Reports {
101+
lines = append(lines, []string{
102+
fmt.Sprintf("* **%s**\n", r.Package),
103+
fmt.Sprintf(" * **failures:** %d", r.FailureCount),
104+
fmt.Sprintf(" * **successes:** %d\n", r.SuccessCount)}...)
105+
}
106+
107+
return bytes.NewBuffer([]byte(strings.Join(lines, "\n"))), nil
108+
}
109+
89110
// FileExtension returns the file extension for this exporter's format
90111
func (e *MarkdownExporter) FileExtension() string {
91112
return ".md"

0 commit comments

Comments
 (0)