Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add CSV output option #279

Merged
merged 8 commits into from
Feb 25, 2022
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
3 changes: 2 additions & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ var outputOptions = []string{
"wide",
"custom",
"markdown",
"csv",
}

func init() {
Expand All @@ -63,7 +64,7 @@ func init() {
rootCmd.PersistentFlags().BoolVarP(&onlyShowRemoved, "only-show-removed", "r", false, "Only display the apiVersions that have been removed in the target version.")
rootCmd.PersistentFlags().StringVarP(&additionalVersionsFile, "additional-versions", "f", "", "Additional deprecated versions file to add to the list. Cannot contain any existing versions")
rootCmd.PersistentFlags().StringToStringVarP(&targetVersions, "target-versions", "t", targetVersions, "A map of targetVersions to use. This flag supersedes all defaults in version files.")
rootCmd.PersistentFlags().StringVarP(&outputFormat, "output", "o", "normal", "The output format to use. (normal|wide|custom|json|yaml|markdown)")
rootCmd.PersistentFlags().StringVarP(&outputFormat, "output", "o", "normal", "The output format to use. (normal|wide|custom|json|yaml|markdown|csv)")
rootCmd.PersistentFlags().StringSliceVar(&customColumns, "columns", nil, "A list of columns to print. Mandatory when using --output custom, optional with --output markdown")
rootCmd.PersistentFlags().StringSliceVar(&componentsFromUser, "components", nil, "A list of components to run checks for. If nil, will check for all found in versions.")

Expand Down
14 changes: 14 additions & 0 deletions docs/advanced.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,20 @@ pluto detect-files -o markdown --columns NAMESPACE,NAME,DEPRECATED IN,DEPRECATED
| some name one | pluto-namespace | Deployment | extensions/v1beta1 | apps/v1 | true | v1.9.0 | foo | path-to-file |
| some name two | <UNKNOWN> | Deployment | extensions/v1beta1 | apps/v1 | true | v1.9.0 | foo | <UNKNOWN> |
```

### CSV

```
pluto detect-helm -o csv
NAME,NAMESPACE,KIND,VERSION,REPLACEMENT,DEPRECATED,DEPRECATED IN,REMOVED,REMOVED IN
some name one,pluto-namespace,Deployment,extensions/v1beta1,apps/v1,true,v1.9.0,true,v1.16.0
some name two,<UNKNOWN>,Deployment,extensions/v1beta1,apps/v1,true,v1.9.0,true,v1.16.0

pluto detect-helm -o csv --columns "NAMESPACE,NAME,DEPRECATED IN,DEPRECATED,REPLACEMENT,VERSION,KIND,COMPONENT,FILEPATH"
NAME,NAMESPACE,KIND,VERSION,REPLACEMENT,DEPRECATED,DEPRECATED IN,COMPONENT,FILEPATH
some name one,pluto-namespace,Deployment,extensions/v1beta1,apps/v1,true,v1.9.0,foo,path-to-file
some name two,<UNKNOWN>,Deployment,extensions/v1beta1,apps/v1,true,v1.9.0,foo,<UNKNOWN>
```
## CI Pipelines

Pluto has specific exit codes that is uses to indicate certain results:
Expand Down
62 changes: 60 additions & 2 deletions pkg/api/output.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package api

import (
"encoding/csv"
"encoding/json"
"fmt"
"github.com/olekukonko/tablewriter"
"os"
"sort"
"text/tabwriter"

"github.com/olekukonko/tablewriter"

"gopkg.in/yaml.v3"
)

Expand Down Expand Up @@ -105,6 +107,24 @@ func (instance *Instance) DisplayOutput() error {
t.SetCenterSeparator("|")
t.Render()
}
case "csv":
var c columnList
if len(instance.CustomColumns) >= 1 {
c = instance.customColumns()
} else {
c = instance.wideColumns()
}
csvWriter, err := instance.csvOut(c)
if err != nil {
return err
}

csvWriter.Flush()

err = csvWriter.Error()
if err != nil {
return err
}
}
return nil
}
Expand Down Expand Up @@ -214,6 +234,45 @@ func (instance *Instance) markdownOut(columns columnList) *tablewriter.Table {
return table
}

func (instance *Instance) csvOut(columns columnList) (*csv.Writer, error) {
csvWriter := csv.NewWriter(os.Stdout)

if len(instance.Outputs) == 0 {
_, _ = fmt.Println("No output to display")
}

columnIndexes := make([]int, 0, len(columns))
for k := range columns {
columnIndexes = append(columnIndexes, k)
}
sort.Ints(columnIndexes)

var csvData [][]string

var headers []string
for _, k := range columnIndexes {
headers = append(headers, columns[k].header())
}

csvData = append(csvData, headers)

for _, o := range instance.Outputs {
var row []string
for _, k := range columnIndexes {
row = append(row, columns[k].value(o))
}
csvData = append(csvData, row)
}

for i := range csvData {
if err := csvWriter.Write(csvData[i]); err != nil {
return nil, err
}
}

return csvWriter, nil
}

// GetReturnCode checks for deprecated versions and returns a code.
// takes a boolean to ignore any errors.
// exit 2 - version deprecated
Expand All @@ -224,7 +283,6 @@ func (instance *Instance) GetReturnCode() int {
var removals int
for _, output := range instance.Outputs {
if output.APIVersion.isRemovedIn(instance.TargetVersions) {

removals = removals + 1
}
if output.APIVersion.isDeprecatedIn(instance.TargetVersions) {
Expand Down
45 changes: 43 additions & 2 deletions pkg/api/output_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,8 @@ func ExampleInstance_DisplayOutput_markdown_customcolumns() {
testOutput1,
testOutput2,
},
OutputFormat: "markdown",
Components: []string{"foo"},
OutputFormat: "markdown",
Components: []string{"foo"},
CustomColumns: []string{"NAMESPACE", "NAME", "DEPRECATED IN", "DEPRECATED", "REPLACEMENT", "VERSION", "KIND", "COMPONENT", "FILEPATH"},
}
_ = instance.DisplayOutput()
Expand Down Expand Up @@ -261,6 +261,47 @@ func ExampleInstance_DisplayOutput_yaml() {
// foo: v1.16.0
}

func ExampleInstance_DisplayOutput_csv() {
instance := &Instance{
TargetVersions: map[string]string{
"foo": "v1.16.0",
},
Outputs: []*Output{
testOutput1,
testOutput2,
},
Components: []string{"foo"},
OutputFormat: "csv",
}
_ = instance.DisplayOutput()

// Output:
// NAME,NAMESPACE,KIND,VERSION,REPLACEMENT,DEPRECATED,DEPRECATED IN,REMOVED,REMOVED IN
// some name one,pluto-namespace,Deployment,extensions/v1beta1,apps/v1,true,v1.9.0,true,v1.16.0
// some name two,<UNKNOWN>,Deployment,extensions/v1beta1,apps/v1,true,v1.9.0,true,v1.16.0
}

func ExampleInstance_DisplayOutput_csv_customcolumns() {
instance := &Instance{
TargetVersions: map[string]string{
"foo": "v1.16.0",
},
Outputs: []*Output{
testOutput1,
testOutput2,
},
Components: []string{"foo"},
OutputFormat: "csv",
CustomColumns: []string{"NAMESPACE", "NAME", "DEPRECATED IN", "DEPRECATED", "REPLACEMENT", "VERSION", "KIND", "COMPONENT", "FILEPATH"},
}
_ = instance.DisplayOutput()

// Output:
// NAME,NAMESPACE,KIND,VERSION,REPLACEMENT,DEPRECATED,DEPRECATED IN,COMPONENT,FILEPATH
// some name one,pluto-namespace,Deployment,extensions/v1beta1,apps/v1,true,v1.9.0,foo,path-to-file
// some name two,<UNKNOWN>,Deployment,extensions/v1beta1,apps/v1,true,v1.9.0,foo,<UNKNOWN>
}

func ExampleInstance_DisplayOutput_noOutput() {
instance := &Instance{
TargetVersions: map[string]string{
Expand Down