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

Allow checking a single file or stdin. Test output function and move it to API. #10

Merged
merged 4 commits into from
Mar 27, 2020
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
68 changes: 8 additions & 60 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ This is a very simple utility to help users find deprecated Kubernetes apiVersio

Install the binary from our [releases](https://github.com/FairwindsOps/pluto/releases) page.

### File Detection
### File Detection in a Directory

Run `pluto detect-files -d <DIRECTORY YOU WANT TO SCAN>`

Expand All @@ -25,7 +25,7 @@ Deployment extensions/v1beta1 true pkg/finder/testdata/deployment-ex

This indicates that we have two files in our directory that have deprecated apiVersions. This will need to be fixed prior to a 1.16 upgrade.

### Helm Detection
### Helm Detection (in-cluster)

```
$ pluto detect-helm --helm-version 3
Expand All @@ -35,66 +35,14 @@ StatefulSet apps/v1beta1 true audit-dashboard-prod-rabbitmq-ha

This indicates that the StatefulSet audit-dashboard-prod-rabbitmq-ha was deployed with apps/v1beta1 which is deprecated in 1.16

## Usage
### Helm Chart Checking (local files)

```
A tool to detect Kubernetes apiVersions

Usage:
pluto [flags]
pluto [command]

Available Commands:
detect-files detect-files
detect-helm detect-helm
help Help about any command
version Prints the current version of the tool.

Flags:
--add_dir_header If true, adds the file directory to the header
--alsologtostderr log to standard error as well as files
-h, --help help for pluto
--kubeconfig string Paths to a kubeconfig. Only required if out-of-cluster.
--log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0)
--log_dir string If non-empty, write log files in this directory
--log_file string If non-empty, use this log file
--log_file_max_size uint Defines the maximum size a log file can grow to. Unit is megabytes. If the value is 0, the maximum file size is unlimited. (default 1800)
--logtostderr log to standard error instead of files (default true)
--master --kubeconfig (Deprecated: switch to --kubeconfig) The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster.
--skip_headers If true, avoid header prefixes in the log messages
--skip_log_headers If true, avoid headers when opening log files
--stderrthreshold severity logs at or above this threshold go to stderr (default 2)
-v, --v Level number for the log level verbosity
--vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging

Use "pluto [command] --help" for more information about a command.
```
You can run `helm template <chart-dir> | pluto detect --show-non-deprecated -`

## Detect Files Options
This will output something like so:

```
Usage:
pluto detect-files [flags]

Flags:
-d, --directory string The directory to scan. If blank, defaults to current workding dir.
-h, --help help for detect-files
-o, --output string The output format to use. (tabular|json|yaml) (default "tabular")
--show-non-deprecated If enabled, will show files that have non-deprecated apiVersion. Only applies to tabular output.
```

## Detect Helm Options

NOTE: Only helm 3 is currently supported

```
Detect Kubernetes apiVersions in a helm release (in cluster)

Usage:
pluto detect-helm [flags]

Flags:
--helm-version string Helm version in current cluster (2|3) (default "3")
-h, --help help for detect-helm
--show-non-deprecated If enabled, will show files that have non-deprecated apiVersion. Only applies to tabular output.
KIND VERSION DEPRECATED RESOURCE NAME
Deployment apps/v1 false RELEASE-NAME-goldilocks-controller
Deployment apps/v1 false RELEASE-NAME-goldilocks-dashboard
```
116 changes: 64 additions & 52 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,10 @@
package cmd

import (
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"os"
"text/tabwriter"

"gopkg.in/yaml.v3"

"github.com/fairwindsops/pluto/pkg/api"
"github.com/fairwindsops/pluto/pkg/finder"
Expand All @@ -43,19 +40,22 @@ var (

func init() {
rootCmd.AddCommand(detectFilesCmd)
rootCmd.PersistentFlags().BoolVar(&showNonDeprecated, "show-non-deprecated", false, "If enabled, will show files that have non-deprecated apiVersion. Only applies to tabular output.")

detectFilesCmd.PersistentFlags().StringVarP(&directory, "directory", "d", "", "The directory to scan. If blank, defaults to current workding dir.")
detectFilesCmd.PersistentFlags().StringVarP(&outputFormat, "output", "o", "tabular", "The output format to use. (tabular|json|yaml)")
detectFilesCmd.PersistentFlags().BoolVar(&showNonDeprecated, "show-non-deprecated", false, "If enabled, will show files that have non-deprecated apiVersion. Only applies to tabular output.")

rootCmd.AddCommand(detectHelmCmd)
detectHelmCmd.PersistentFlags().StringVar(&helmVersion, "helm-version", "3", "Helm version in current cluster (2|3)")
detectHelmCmd.PersistentFlags().BoolVar(&showNonDeprecated, "show-non-deprecated", false, "If enabled, will show files that have non-deprecated apiVersion. Only applies to tabular output.")

rootCmd.AddCommand(detectCmd)

klog.InitFlags(nil)
flag.Parse()
pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
}

// Checker is an interface to find versions
type Checker interface {
FindVersions() error
}
Expand Down Expand Up @@ -91,7 +91,7 @@ var detectFilesCmd = &cobra.Command{
os.Exit(0)
}

err = parseOutput(dir.Outputs)
err = api.DisplayOutput(dir.Outputs, outputFormat, showNonDeprecated)
if err != nil {
fmt.Println("Error Parsing Output:", err)
os.Exit(1)
Expand All @@ -110,14 +110,62 @@ var detectHelmCmd = &cobra.Command{
fmt.Println("Error running helm-detect:", err)
os.Exit(1)
}
err = parseOutput(h.Outputs)
err = api.DisplayOutput(h.Outputs, outputFormat, showNonDeprecated)
if err != nil {
fmt.Println("Error Parsing Output:", err)
os.Exit(1)
}
},
}

var detectCmd = &cobra.Command{
Use: "detect [file to check or -]",
Short: "Checks a single file or stdin for deprecated apiVersions.",
Long: `Detect deprecated apiVersion in a specific file or other input. Accepts multi-document yaml files and/or - for stdin. Useful for helm testing.`,
Args: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("requires a file argument")
}
if isFileOrStdin(args[0]) {
return nil
}
return fmt.Errorf("invalid file specified: %s", args[0])
},
Run: func(cmd *cobra.Command, args []string) {
klog.V(3).Infof("arg0: %s", args[0])

if args[0] == "-" {
//stdin
fileData, err := ioutil.ReadAll(os.Stdin)
if err != nil {
fmt.Println("Error reading stdin:", err)
os.Exit(1)
}
output, err := api.IsVersioned(fileData)
if err != nil {
fmt.Println("Error checking for versions:", err)
os.Exit(1)
}
err = api.DisplayOutput(output, outputFormat, showNonDeprecated)
if err != nil {
fmt.Println("Error parsing output:", err)
os.Exit(1)
}
os.Exit(0)
}
output, err := finder.CheckForAPIVersion(args[0])
if err != nil {
fmt.Println("Error reading file:", err)
os.Exit(1)
}
err = api.DisplayOutput(output, outputFormat, showNonDeprecated)
if err != nil {
fmt.Println("Error parsing output:", err)
os.Exit(1)
}
},
}

// Execute the stuff
func Execute(VERSION string, COMMIT string) {
version = VERSION
Expand All @@ -128,49 +176,13 @@ func Execute(VERSION string, COMMIT string) {
}
}

func parseOutput(outputs []*api.Output) error {
var err error
var outData []byte
switch outputFormat {
case "tabular":
w := new(tabwriter.Writer)
w.Init(os.Stdout, 0, 8, 2, ' ', 0)
_, err = fmt.Fprintln(w, "KIND\t VERSION\t DEPRECATED\t RESOURCE NAME")
if err != nil {
return err
}
for _, output := range outputs {
// Don't show non-deprecated apis if we have them disabled
if !showNonDeprecated {
if !output.APIVersion.Deprecated {
continue
}
}
kind := output.APIVersion.Kind
deprecated := fmt.Sprintf("%t", output.APIVersion.Deprecated)
version := output.APIVersion.Name
fileName := output.Name

_, err = fmt.Fprintf(w, "%s\t %s\t %s\t %s\t\n", kind, version, deprecated, fileName)
if err != nil {
return err
}
}
err = w.Flush()
if err != nil {
return err
}
case "json":
outData, err = json.Marshal(outputs)
if err != nil {
return err
}
case "yaml":
outData, err = yaml.Marshal(outputs)
if err != nil {
return err
}
func isFileOrStdin(name string) bool {
if name == "-" {
return true
}
info, err := os.Stat(name)
if os.IsNotExist(err) {
return false
}
fmt.Println(string(outData))
return nil
return !info.IsDir()
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require (
github.com/spf13/cobra v0.0.6
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.4.0
gopkg.in/yaml.v2 v2.2.8
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c
k8s.io/apimachinery v0.17.4
k8s.io/client-go v0.17.4
Expand Down
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DM
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
Expand Down
73 changes: 73 additions & 0 deletions pkg/api/output.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package api

import (
"encoding/json"
"fmt"
"os"
"text/tabwriter"

"gopkg.in/yaml.v2"
)

// DisplayOutput prints the output based on desired variables
func DisplayOutput(outputs []*Output, outputFormat string, showNonDeprecated bool) error {
if len(outputs) == 0 {
fmt.Println("There were no apiVersions found that match our records.")
return nil
}
var err error
var outData []byte
var usableOutputs []*Output
switch outputFormat {
case "tabular":
if showNonDeprecated {
usableOutputs = outputs
} else {
for _, output := range outputs {
if output.APIVersion.Deprecated {
usableOutputs = append(usableOutputs, output)
}
}
}
if len(usableOutputs) == 0 {
fmt.Println("APIVersions were found, but none were deprecated. Try --show-non-deprecated.")
return nil
}
w := new(tabwriter.Writer)
w.Init(os.Stdout, 0, 8, 2, ' ', 0)
_, err = fmt.Fprintln(w, "KIND\t VERSION\t DEPRECATED\t RESOURCE NAME")
if err != nil {
return err
}
for _, output := range usableOutputs {
kind := output.APIVersion.Kind
deprecated := fmt.Sprintf("%t", output.APIVersion.Deprecated)
version := output.APIVersion.Name
fileName := output.Name

_, err = fmt.Fprintf(w, "%s\t %s\t %s\t %s\t\n", kind, version, deprecated, fileName)
if err != nil {
return err
}
}
err = w.Flush()
if err != nil {
return err
}
case "json":
outData, err = json.Marshal(outputs)
if err != nil {
return err
}
fmt.Println(string(outData))
case "yaml":
outData, err = yaml.Marshal(outputs)
if err != nil {
return err
}
fmt.Println(string(outData))
default:
fmt.Println("output format should be one of (json,yaml,tabular)")
}
return nil
}
Loading