diff --git a/cmd/flux/get.go b/cmd/flux/get.go index 53c4721f..46371bd3 100644 --- a/cmd/flux/get.go +++ b/cmd/flux/get.go @@ -68,6 +68,7 @@ type GetFlags struct { statusSelector string labelSelector string watch bool + outputFormat string } var getArgs GetFlags @@ -81,6 +82,7 @@ func init() { "specify the status condition name and the desired state to filter the get result, e.g. ready=false") getCmd.PersistentFlags().StringVarP(&getArgs.labelSelector, "label-selector", "l", "", "filter objects by label selector") + getCmd.PersistentFlags().StringVarP(&getArgs.outputFormat, "output", "o", "table", "Output format. One of: (table, yaml), default: table") rootCmd.AddCommand(getCmd) } @@ -168,6 +170,26 @@ func (get getCommand) run(cmd *cobra.Command, args []string) error { }) } + outputFormat := getArgs.outputFormat + + switch outputFormat { + case "yaml": + case "table": + case "": + outputFormat = "table" + default: + return fmt.Errorf("unknown output format: '%s'", outputFormat) + } + + if outputFormat != "table" { + if getArgs.noHeader { + return fmt.Errorf("cannot set no-header in %s output", outputFormat) + } + if getArgs.watch { + return fmt.Errorf("cannot set watch mode in %s output", outputFormat) + } + } + getAll := cmd.Use == "all" if getArgs.watch { @@ -198,25 +220,31 @@ func (get getCommand) run(cmd *cobra.Command, args []string) error { return nil } + displayNamespaces := getArgs.allNamespaces || outputFormat != "table" + var header []string if !getArgs.noHeader { - header = get.list.headers(getArgs.allNamespaces) + header = get.list.headers(displayNamespaces) } - rows, err := getRowsToPrint(getAll, get.list) + rows, err := getRowsToPrint(getAll, displayNamespaces, get.list) if err != nil { return err } - err = printers.TablePrinter(header).Print(cmd.OutOrStdout(), rows) + switch outputFormat { + case "yaml": + err = printers.YamlPrinter(header).Print(cmd.OutOrStdout(), rows) + case "table": + err = printers.TablePrinter(header).Print(cmd.OutOrStdout(), rows) + if getAll && err != nil { + fmt.Println() + } + } if err != nil { return err } - if getAll { - fmt.Println() - } - return nil } @@ -227,7 +255,7 @@ func namespaceNameOrAny(allNamespaces bool, namespaceName string) string { return fmt.Sprintf("%q", namespaceName) } -func getRowsToPrint(getAll bool, list summarisable) ([][]string, error) { +func getRowsToPrint(getAll bool, displayNamespace bool, list summarisable) ([][]string, error) { noFilter := true var conditionType, conditionStatus string if getArgs.statusSelector != "" { @@ -242,7 +270,7 @@ func getRowsToPrint(getAll bool, list summarisable) ([][]string, error) { var rows [][]string for i := 0; i < list.len(); i++ { if noFilter || list.statusSelectorMatches(i, conditionType, conditionStatus) { - row := list.summariseItem(i, getArgs.allNamespaces, getAll) + row := list.summariseItem(i, displayNamespace, getAll) rows = append(rows, row) } } @@ -277,7 +305,7 @@ func watchUntil(ctx context.Context, w watch.Interface, get *getCommand) (bool, if !getArgs.noHeader { header = sink.headers(getArgs.allNamespaces) } - rows, err := getRowsToPrint(false, sink) + rows, err := getRowsToPrint(false, getArgs.allNamespaces, sink) if err != nil { return false, err } diff --git a/cmd/flux/get_test.go b/cmd/flux/get_test.go index 30d74ab2..49f1df63 100644 --- a/cmd/flux/get_test.go +++ b/cmd/flux/get_test.go @@ -49,6 +49,11 @@ func Test_GetCmd(t *testing.T) { args: `-l "sharding.fluxcd.io/key notin (shard1, shard2)"`, expected: "testdata/get/get_label_two.golden", }, + { + name: "yaml output", + args: `--output yaml`, + expected: "testdata/get/get_yaml.golden", + }, } for _, tt := range tests { diff --git a/cmd/flux/testdata/get/get_yaml.golden b/cmd/flux/testdata/get/get_yaml.golden new file mode 100644 index 00000000..2de202ec --- /dev/null +++ b/cmd/flux/testdata/get/get_yaml.golden @@ -0,0 +1,18 @@ +- Message: 'Fetched revision: main@sha1:696f056d' + Name: podinfo + Namespace: flux-system-15 + Ready: true + Revision: main@sha1:696f056d + Suspended: false +- Message: 'Fetched revision: main@sha1:696f056d' + Name: podinfo-shard1 + Namespace: flux-system-15 + Ready: true + Revision: main@sha1:696f056d + Suspended: false +- Message: 'Fetched revision: main@sha1:696f056d' + Name: podinfo-shard2 + Namespace: flux-system-15 + Ready: true + Revision: main@sha1:696f056d + Suspended: false diff --git a/pkg/printers/yaml_printer.go b/pkg/printers/yaml_printer.go new file mode 100644 index 00000000..7dd6e43c --- /dev/null +++ b/pkg/printers/yaml_printer.go @@ -0,0 +1,70 @@ +/* +Copyright 2022 The Flux authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package printers + +import ( + "fmt" + "io" + + "sigs.k8s.io/yaml" +) + +// YamlPrinter is a printer that prints Flux cmd outputs as yaml. +func YamlPrinter(header []string) PrinterFunc { + return func(w io.Writer, args ...interface{}) error { + // args is 2d array of string + // headers is 1d array of string + var rows [][]string + for _, arg := range args { + switch arg := arg.(type) { + case []interface{}: + for _, v := range arg { + s, ok := v.([][]string) + if !ok { + return fmt.Errorf("unsupported type %T", v) + } + rows = append(rows, s...) + } + default: + return fmt.Errorf("unsupported type %T", arg) + } + } + + var entries []map[string]interface{} + for _, row := range rows { + entry := make(map[string]interface{}) + for j, h := range header { + if row[j] == "True" || row[j] == "False" { + entry[h] = row[j] == "True" + } else { + entry[h] = row[j] + } + } + entries = append(entries, entry) + } + + if len(entries) > 0 { + y, err := yaml.Marshal(entries) + if err != nil { + return fmt.Errorf("error marshalling yaml %w", err) + } + fmt.Fprint(w, string(y)) + } + + return nil + } +}