From 59e6fafe261e02611e50e451be8fa8fb4880a345 Mon Sep 17 00:00:00 2001 From: Felipe Amaral Date: Fri, 27 Sep 2024 15:48:30 +0100 Subject: [PATCH] add labels flag --- cmd/kubent/main.go | 6 +-- cmd/kubent/main_test.go | 2 +- pkg/printer/csv.go | 62 +++++++++++++++++++++--------- pkg/printer/csv_test.go | 35 ++++++++++++++++- pkg/printer/json.go | 2 +- pkg/printer/json_test.go | 44 ++++++++++++++++++++- pkg/printer/printer.go | 2 +- pkg/printer/printer_helper.go | 17 ++++++++ pkg/printer/printer_helper_test.go | 36 +++++++++++++++++ pkg/printer/text.go | 24 ++++++------ pkg/printer/text_test.go | 35 ++++++++++++++++- 11 files changed, 225 insertions(+), 40 deletions(-) create mode 100644 pkg/printer/printer_helper.go create mode 100644 pkg/printer/printer_helper_test.go diff --git a/cmd/kubent/main.go b/cmd/kubent/main.go index fc54bc0c..a2c1a8fe 100644 --- a/cmd/kubent/main.go +++ b/cmd/kubent/main.go @@ -156,7 +156,7 @@ func main() { log.Fatal().Err(err).Str("name", "Rego").Msg("Failed to filter results") } - err = outputResults(results, config.Output, config.OutputFile) + err = outputResults(results, config.Output, config.OutputFile, config.Labels) if err != nil { log.Fatal().Err(err).Msgf("Failed to output results") } @@ -180,14 +180,14 @@ func configureGlobalLogging() { log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}) } -func outputResults(results []judge.Result, outputType string, outputFile string) error { +func outputResults(results []judge.Result, outputType string, outputFile string, labels bool) error { printer, err := printer.NewPrinter(outputType, outputFile) if err != nil { return fmt.Errorf("failed to create printer: %v", err) } defer printer.Close() - err = printer.Print(results) + err = printer.Print(results, labels) if err != nil { return fmt.Errorf("failed to print results: %v", err) } diff --git a/cmd/kubent/main_test.go b/cmd/kubent/main_test.go index fe81a8de..1c1395c6 100644 --- a/cmd/kubent/main_test.go +++ b/cmd/kubent/main_test.go @@ -288,7 +288,7 @@ func Test_outputResults(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if err := outputResults(tt.args.results, tt.args.outputType, tt.args.outputFile); (err != nil) != tt.wantErr { + if err := outputResults(tt.args.results, tt.args.outputType, tt.args.outputFile, false); (err != nil) != tt.wantErr { t.Errorf("unexpected error - got: %v, wantErr: %v", err, tt.wantErr) } }) diff --git a/pkg/printer/csv.go b/pkg/printer/csv.go index 3ea7d334..19c9ca6a 100644 --- a/pkg/printer/csv.go +++ b/pkg/printer/csv.go @@ -29,7 +29,7 @@ func (c *csvPrinter) Close() error { } // Print will print results in CSV format -func (c *csvPrinter) Print(results []judge.Result) error { +func (c *csvPrinter) Print(results []judge.Result, labels bool) error { sort.Slice(results, func(i, j int) bool { return results[i].Name < results[j].Name @@ -46,26 +46,50 @@ func (c *csvPrinter) Print(results []judge.Result) error { w := csv.NewWriter(c.commonPrinter.outputFile) - w.Write([]string{ - "api_version", - "kind", - "namespace", - "name", - "replace_with", - "since", - "rule_set", - }) - - for _, r := range results { + if labels { + w.Write([]string{ + "api_version", + "kind", + "namespace", + "name", + "replace_with", + "since", + "rule_set", + "labels", + }) + for _, r := range results { + w.Write([]string{ + r.ApiVersion, + r.Kind, + r.Namespace, + r.Name, + r.ReplaceWith, + r.Since.String(), + r.RuleSet, + mapToCommaSeparatedString(r.Labels), + }) + } + } else { w.Write([]string{ - r.ApiVersion, - r.Kind, - r.Namespace, - r.Name, - r.ReplaceWith, - r.Since.String(), - r.RuleSet, + "api_version", + "kind", + "namespace", + "name", + "replace_with", + "since", + "rule_set", }) + for _, r := range results { + w.Write([]string{ + r.ApiVersion, + r.Kind, + r.Namespace, + r.Name, + r.ReplaceWith, + r.Since.String(), + r.RuleSet, + }) + } } w.Flush() diff --git a/pkg/printer/csv_test.go b/pkg/printer/csv_test.go index 5a5c0758..efce248e 100644 --- a/pkg/printer/csv_test.go +++ b/pkg/printer/csv_test.go @@ -62,7 +62,40 @@ func TestCSVPrinterPrint(t *testing.T) { Labels: map[string]interface{}{"app": "php-apache-app"}, }} - if err := tp.Print(results); err != nil { + if err := tp.Print(results, true); err != nil { + t.Fatalf("unexpected error: %v", err) + } + + fi, _ := tmpFile.Stat() + if fi.Size() == 0 { + t.Fatalf("expected non-zero size output file: %v", err) + } +} + +func TestCSVPrinterPrintNoLabels(t *testing.T) { + tmpFile, err := ioutil.TempFile(os.TempDir(), tempFilePrefix) + if err != nil { + t.Fatalf(tempFileCreateFailureMessage, err) + } + defer os.Remove(tmpFile.Name()) + + tp := &csvPrinter{ + commonPrinter: &commonPrinter{tmpFile}, + } + + version, _ := judge.NewVersion("1.2.3") + results := []judge.Result{{ + Name: "Name", + Namespace: "Namespace", + Kind: "Kind", + ApiVersion: "1.2.3", + RuleSet: "Test", + ReplaceWith: "4.5.6", + Since: version, + Labels: map[string]interface{}{}, + }} + + if err := tp.Print(results, false); err != nil { t.Fatalf("unexpected error: %v", err) } diff --git a/pkg/printer/json.go b/pkg/printer/json.go index e51756d7..12910710 100644 --- a/pkg/printer/json.go +++ b/pkg/printer/json.go @@ -29,7 +29,7 @@ func (c *jsonPrinter) Close() error { } // Print will print results in text format -func (c *jsonPrinter) Print(results []judge.Result) error { +func (c *jsonPrinter) Print(results []judge.Result, labels bool) error { writer := bufio.NewWriter(c.commonPrinter.outputFile) defer writer.Flush() diff --git a/pkg/printer/json_test.go b/pkg/printer/json_test.go index c6708141..254591ff 100644 --- a/pkg/printer/json_test.go +++ b/pkg/printer/json_test.go @@ -64,7 +64,49 @@ func Test_jsonPrinter_Print(t *testing.T) { Labels: map[string]interface{}{"label1": "value1", "label2": "value2"}, }} - if err := c.Print(results); err != nil { + if err := c.Print(results, true); err != nil { + t.Fatalf("unexpected error: %v", err) + } + + tmpFile.Seek(0, 0) + + var readResults []judge.Result + readBytes, err := ioutil.ReadAll(tmpFile) + if err != nil { + t.Fatalf("unexpected error reading back the file: %v", err) + } + if err := json.Unmarshal(readBytes, &readResults); err != nil { + t.Fatalf("unexpected error unmarshalling the previously written file: %v", err) + } + if !reflect.DeepEqual(readResults, results) { + t.Fatalf("written and read result do not seem to be equal") + } +} + +func Test_jsonPrinter_PrintNoLabel(t *testing.T) { + tmpFile, err := ioutil.TempFile(os.TempDir(), tempFilePrefix) + if err != nil { + t.Fatalf(tempFileCreateFailureMessage, err) + } + defer os.Remove(tmpFile.Name()) + + c := &jsonPrinter{ + commonPrinter: &commonPrinter{tmpFile}, + } + + version, _ := judge.NewVersion("1.2.3") + results := []judge.Result{{ + Name: "Name", + Namespace: "Namespace", + Kind: "Kind", + ApiVersion: "1.2.3", + RuleSet: "Test", + ReplaceWith: "4.5.6", + Since: version, + Labels: map[string]interface{}{}, + }} + + if err := c.Print(results, false); err != nil { t.Fatalf("unexpected error: %v", err) } diff --git a/pkg/printer/printer.go b/pkg/printer/printer.go index 31ab2e58..1b40bbc3 100644 --- a/pkg/printer/printer.go +++ b/pkg/printer/printer.go @@ -14,7 +14,7 @@ var printers = map[string]func(string) (Printer, error){ } type Printer interface { - Print([]judge.Result) error + Print([]judge.Result, bool) error Close() error } diff --git a/pkg/printer/printer_helper.go b/pkg/printer/printer_helper.go new file mode 100644 index 00000000..051e8d87 --- /dev/null +++ b/pkg/printer/printer_helper.go @@ -0,0 +1,17 @@ +package printer + +import ( + "fmt" + "strings" +) + +func mapToCommaSeparatedString(m map[string]interface{}) string { + var sb strings.Builder + for k, v := range m { + if sb.Len() > 0 { + sb.WriteString(", ") + } + sb.WriteString(fmt.Sprintf("%s:%v", k, v)) + } + return sb.String() +} diff --git a/pkg/printer/printer_helper_test.go b/pkg/printer/printer_helper_test.go new file mode 100644 index 00000000..808fc332 --- /dev/null +++ b/pkg/printer/printer_helper_test.go @@ -0,0 +1,36 @@ +package printer + +import ( + "testing" +) + +func TestMapToCommaSeparatedString(t *testing.T) { + tests := []struct { + input map[string]interface{} + expected string + }{ + { + input: map[string]interface{}{}, + expected: "", + }, + { + input: map[string]interface{}{"key1": "value1"}, + expected: "key1:value1", + }, + { + input: map[string]interface{}{"key1": "value1", "key2": "value2"}, + expected: "key1:value1, key2:value2", + }, + { + input: map[string]interface{}{"key1": 123, "key2": true, "key3": 45.67}, + expected: "key1:123, key2:true, key3:45.67", + }, + } + + for _, test := range tests { + result := mapToCommaSeparatedString(test.input) + if result != test.expected { + t.Errorf("For input %v, expected %s but got %s", test.input, test.expected, result) + } + } +} diff --git a/pkg/printer/text.go b/pkg/printer/text.go index f8e95b15..7eba9f7e 100644 --- a/pkg/printer/text.go +++ b/pkg/printer/text.go @@ -30,7 +30,7 @@ func (c *textPrinter) Close() error { return c.commonPrinter.Close() } -func (c *textPrinter) Print(results []judge.Result) error { +func (c *textPrinter) Print(results []judge.Result, labels bool) error { sort.Slice(results, func(i, j int) bool { return results[i].Name < results[j].Name @@ -54,19 +54,19 @@ func (c *textPrinter) Print(results []judge.Result) error { fmt.Fprintf(w, "%s\n", strings.Repeat("_", 90)) fmt.Fprintf(w, ">>> %s <<<\n", ruleSet) fmt.Fprintf(w, "%s\n", strings.Repeat("-", 90)) - fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s \t(%s) \t%s\n", "KIND", "NAMESPACE", "NAME", "API_VERSION", "REPLACE_WITH", "SINCE", "LABELS") + if labels { + fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s \t(%s) \t%s\n", "KIND", "NAMESPACE", "NAME", "API_VERSION", "REPLACE_WITH", "SINCE", "LABELS") + } else { + fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s \t(%s)\n", "KIND", "NAMESPACE", "NAME", "API_VERSION", "REPLACE_WITH", "SINCE") + } + + } + if labels { + fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s \t(%s) \t%s\n", r.Kind, r.Namespace, r.Name, r.ApiVersion, r.ReplaceWith, r.Since, mapToCommaSeparatedString(r.Labels)) + } else { + fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s \t(%s) \n", r.Kind, r.Namespace, r.Name, r.ApiVersion, r.ReplaceWith, r.Since) } - fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s \t(%s) \t%s\n", r.Kind, r.Namespace, r.Name, r.ApiVersion, r.ReplaceWith, r.Since, mapToCommaSeparatedString(r.Labels)) } w.Flush() return nil } - -// Helper function to convert labels map to comma-separated string -func mapToCommaSeparatedString(m map[string]interface{}) string { - var parts []string - for key, value := range m { - parts = append(parts, fmt.Sprintf("%s:%s", key, value)) - } - return strings.Join(parts, ", ") -} diff --git a/pkg/printer/text_test.go b/pkg/printer/text_test.go index d1689bd5..6505d5a5 100644 --- a/pkg/printer/text_test.go +++ b/pkg/printer/text_test.go @@ -62,7 +62,40 @@ func Test_textPrinter_Print(t *testing.T) { Labels: map[string]interface{}{"label1": "value1", "label2": "value2"}, }} - if err := tp.Print(results); err != nil { + if err := tp.Print(results, false); err != nil { + t.Fatalf("unexpected error: %v", err) + } + + fi, _ := tmpFile.Stat() + if fi.Size() == 0 { + t.Fatalf("expected non-zero size output file: %v", err) + } +} + +func Test_textPrinter_PrintNoLabel(t *testing.T) { + tmpFile, err := ioutil.TempFile(os.TempDir(), tempFilePrefix) + if err != nil { + t.Fatalf(tempFileCreateFailureMessage, err) + } + defer os.Remove(tmpFile.Name()) + + tp := &textPrinter{ + commonPrinter: &commonPrinter{tmpFile}, + } + + version, _ := judge.NewVersion("1.2.3") + results := []judge.Result{{ + Name: "Name", + Namespace: "Namespace", + Kind: "Kind", + ApiVersion: "1.2.3", + RuleSet: "Test", + ReplaceWith: "4.5.6", + Since: version, + Labels: map[string]interface{}{}, + }} + + if err := tp.Print(results, false); err != nil { t.Fatalf("unexpected error: %v", err) }