Skip to content

Commit

Permalink
feat: Adding labels flag as optional flag
Browse files Browse the repository at this point in the history
  • Loading branch information
femrtnz committed Oct 10, 2024
1 parent 8582f7c commit c6f59f0
Show file tree
Hide file tree
Showing 31 changed files with 336 additions and 53 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ Otherwise there's `Makefile`
```sh
$ make
make
all Cean, build and pack
all Clean, build and pack
help Prints list of tasks
build Build binary
generate Go generate
Expand Down
11 changes: 7 additions & 4 deletions cmd/kubent/main.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"context"
"flag"
"fmt"
"io"
Expand Down Expand Up @@ -96,11 +97,13 @@ func getServerVersion(cv *judge.Version, collectors []collector.Collector) (*jud
}

func main() {
ctx := context.Background()
exitCode := EXIT_CODE_FAIL_GENERIC

configureGlobalLogging()

config, err := config.NewFromFlags()
config, ctx, err := config.NewFromFlags(ctx)

if err != nil {
log.Fatal().Err(err).Msg("failed to parse config flags")
}
Expand Down Expand Up @@ -156,7 +159,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, ctx)
if err != nil {
log.Fatal().Err(err).Msgf("Failed to output results")
}
Expand All @@ -180,14 +183,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, ctx context.Context) 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, ctx)
if err != nil {
return fmt.Errorf("failed to print results: %v", err)
}
Expand Down
9 changes: 7 additions & 2 deletions cmd/kubent/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"bytes"
"context"
"encoding/base64"
"encoding/json"
"errors"
Expand All @@ -13,6 +14,7 @@ import (

"github.com/doitintl/kube-no-trouble/pkg/collector"
"github.com/doitintl/kube-no-trouble/pkg/config"
ctxKey "github.com/doitintl/kube-no-trouble/pkg/context"
"github.com/doitintl/kube-no-trouble/pkg/judge"

"github.com/rs/zerolog"
Expand Down Expand Up @@ -120,7 +122,7 @@ func TestMainExitCodes(t *testing.T) {
}{
{"success", []string{clusterFlagDisabled, helm3FlagDisabled}, 0, "", "", false},
{"errorBadFlag", []string{"-c=not-boolean"}, 2, "", "", false},
{"successFound", []string{"-o=json", clusterFlagDisabled, helm3FlagDisabled, "-f=" + filepath.Join(FIXTURES_DIR, "deployment-v1beta1.yaml")}, 0, string(expectedJsonOutput), "", false},
{"successFound", []string{"--labels=true", "-o=json", clusterFlagDisabled, helm3FlagDisabled, "-f=" + filepath.Join(FIXTURES_DIR, "deployment-v1beta1.yaml")}, 0, string(expectedJsonOutput), "", false},
{"exitErrorFlagNone", []string{clusterFlagDisabled, helm3FlagDisabled, "-e"}, 0, "", "", false},
{"exitErrorFlagFound", []string{clusterFlagDisabled, helm3FlagDisabled, "-e", "-f=" + filepath.Join(FIXTURES_DIR, "deployment-v1beta1.yaml")}, 200, "", "", false},
{"version short flag set", []string{"-v"}, 0, "", "", false},
Expand Down Expand Up @@ -286,9 +288,12 @@ func Test_outputResults(t *testing.T) {
{"bad-new-printer-file", args{testResults, "text", "/unlikely/to/exist/dir"}, true},
}

labelsFlag := false
ctx := context.WithValue(context.Background(), ctxKey.LABELS_CTX_KEY, &labelsFlag)

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, ctx); (err != nil) != tt.wantErr {
t.Errorf("unexpected error - got: %v, wantErr: %v", err, tt.wantErr)
}
})
Expand Down
5 changes: 4 additions & 1 deletion fixtures/expected-json-output.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
"ApiVersion": "apps/v1beta1",
"RuleSet": "Deprecated APIs removed in 1.16",
"ReplaceWith": "apps/v1",
"Since": "1.9.0"
"Since": "1.9.0",
"Labels": {
"app": "nginx"
}
}
]
2 changes: 1 addition & 1 deletion pkg/collector/file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func TestFileCollectorGet(t *testing.T) {
t.Errorf("Expected to get %d, got %d", len(tc.expected), len(manifests))
}

for i, _ := range manifests {
for i := range manifests {
if manifests[i]["kind"] != tc.expected[i] {
t.Errorf("Expected to get %s, instead got: %s", tc.expected[i], manifests[i]["kind"])
}
Expand Down
37 changes: 30 additions & 7 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package config

import (
"context"
"errors"
"fmt"
"io/fs"
Expand All @@ -9,14 +10,20 @@ import (
"strings"
"unicode"

ctxKey "github.com/doitintl/kube-no-trouble/pkg/context"
"github.com/doitintl/kube-no-trouble/pkg/judge"
"github.com/doitintl/kube-no-trouble/pkg/printer"
"k8s.io/client-go/tools/clientcmd"

"github.com/rs/zerolog"
flag "github.com/spf13/pflag"
)

const (
JSON = "json"
TEXT = "text"
CSV = "csv"
)

type Config struct {
AdditionalKinds []string
AdditionalAnnotations []string
Expand All @@ -33,12 +40,14 @@ type Config struct {
KubentVersion bool
}

func NewFromFlags() (*Config, error) {
func NewFromFlags(ctx context.Context) (*Config, context.Context, error) {
config := Config{
LogLevel: ZeroLogLevel(zerolog.InfoLevel),
TargetVersion: &judge.Version{},
}

var labels bool

flag.StringSliceVarP(&config.AdditionalKinds, "additional-kind", "a", []string{}, "additional kinds of resources to report in Kind.version.group.com format")
flag.StringSliceVarP(&config.AdditionalAnnotations, "additional-annotation", "A", []string{}, "additional annotations that should be checked to determine the last applied config")
flag.BoolVarP(&config.Cluster, "cluster", "c", true, "enable Cluster collector")
Expand All @@ -52,19 +61,22 @@ func NewFromFlags() (*Config, error) {
flag.StringVarP(&config.OutputFile, "output-file", "O", "-", "output file, use - for stdout")
flag.VarP(&config.LogLevel, "log-level", "l", "set log level (trace, debug, info, warn, error, fatal, panic, disabled)")
flag.VarP(config.TargetVersion, "target-version", "t", "target K8s version in SemVer format (autodetected by default)")
flag.BoolVar(&labels, "labels", false, "print resource labels")

flag.Parse()

if _, err := printer.ParsePrinter(config.Output); err != nil {
return nil, fmt.Errorf("failed to validate argument output: %w", err)
newContext := context.WithValue(ctx, ctxKey.LABELS_CTX_KEY, &labels)

if !isValidOutputFormat(config.Output) {
return nil, nil, fmt.Errorf("failed to validate argument output: %s", config.Output)
}

if err := validateOutputFile(config.OutputFile); err != nil {
return nil, fmt.Errorf("failed to validate argument output-file: %w", err)
return nil, nil, fmt.Errorf("failed to validate argument output-file: %w", err)
}

if err := validateAdditionalResources(config.AdditionalKinds); err != nil {
return nil, fmt.Errorf("failed to validate arguments: %w", err)
return nil, nil, fmt.Errorf("failed to validate arguments: %w", err)
}

// This is a little ugly, but I think preferred to implementing
Expand All @@ -74,7 +86,18 @@ func NewFromFlags() (*Config, error) {
config.TargetVersion = nil
}

return &config, nil
return &config, newContext, nil
}

// Previuosly this was handled by a printer.go ParsePrinter function
// but we need to avoid cycle imports in order to inject the additional flags
func isValidOutputFormat(format string) bool {
switch format {
case JSON, TEXT, CSV:
return true
default:
return false
}
}

// validateAdditionalResources check that all resources are provided in full form
Expand Down
28 changes: 17 additions & 11 deletions pkg/config/config_test.go
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
package config

import (
goversion "github.com/hashicorp/go-version"
"context"
"os"
"testing"

goversion "github.com/hashicorp/go-version"

"github.com/spf13/pflag"
)

func TestValidLogLevelFromFlags(t *testing.T) {
oldArgs := os.Args[1]
defer func() { os.Args[1] = oldArgs }()
ctx := context.Background()

var validLevels = []string{"trace", "debug", "info", "warn", "error", "fatal", "panic", "", "disabled"}
for i, level := range validLevels {
// reset for testing
pflag.CommandLine = pflag.NewFlagSet(os.Args[0], pflag.ExitOnError)

os.Args[1] = "--log-level=" + level
config, err := NewFromFlags()

config, _, err := NewFromFlags(ctx)

if err != nil {
t.Errorf("Flags parsing failed %s", err)
Expand All @@ -44,8 +48,9 @@ func TestInvalidLogLevelFromFlags(t *testing.T) {
func TestNewFromFlags(t *testing.T) {
// reset for testing
pflag.CommandLine = pflag.NewFlagSet(os.Args[0], pflag.ExitOnError)
ctx := context.Background()

config, err := NewFromFlags()
config, _, err := NewFromFlags(ctx)

if err != nil {
t.Errorf("Flags parsing failed %s", err)
Expand All @@ -72,9 +77,9 @@ func TestValidateAdditionalResources(t *testing.T) {

func TestValidateAdditionalResourcesFail(t *testing.T) {
testCases := [][]string{
[]string{"abcdef"},
[]string{""},
[]string{"test.v1.com"},
{"abcdef"},
{""},
{"test.v1.com"},
}

for _, tc := range testCases {
Expand All @@ -90,6 +95,7 @@ func TestTargetVersion(t *testing.T) {
validVersions := []string{
"1.16", "1.16.3", "1.2.3",
}
ctx := context.Background()

oldArgs := os.Args[1]
defer func() { os.Args[1] = oldArgs }()
Expand All @@ -99,7 +105,7 @@ func TestTargetVersion(t *testing.T) {
pflag.CommandLine = pflag.NewFlagSet(os.Args[0], pflag.ExitOnError)

os.Args[1] = "--target-version=" + v
config, err := NewFromFlags()
config, _, err := NewFromFlags(ctx)

if err != nil {
t.Errorf("Flags parsing failed %s", err)
Expand All @@ -120,7 +126,7 @@ func TestTargetVersionInvalid(t *testing.T) {
invalidVersions := []string{
"1.blah", "nope",
}

ctx := context.Background()
oldArgs := os.Args[1]
defer func() { os.Args[1] = oldArgs }()

Expand All @@ -129,7 +135,7 @@ func TestTargetVersionInvalid(t *testing.T) {
pflag.CommandLine = pflag.NewFlagSet(os.Args[0], pflag.ContinueOnError)

os.Args[1] = "--target-version=" + v
config, _ := NewFromFlags()
config, _, _ := NewFromFlags(ctx)

if config.TargetVersion != nil {
t.Errorf("expected --target-version flag parsing to fail for: %s", v)
Expand All @@ -141,7 +147,7 @@ func TestContext(t *testing.T) {
validContexts := []string{
"my-context",
}

ctx := context.Background()
oldArgs := os.Args[1]
defer func() { os.Args[1] = oldArgs }()

Expand All @@ -150,7 +156,7 @@ func TestContext(t *testing.T) {
pflag.CommandLine = pflag.NewFlagSet(os.Args[0], pflag.ExitOnError)

os.Args[1] = "--context=" + context
config, err := NewFromFlags()
config, _, err := NewFromFlags(ctx)

if err != nil {
t.Errorf("Flags parsing failed %s", err)
Expand Down
5 changes: 5 additions & 0 deletions pkg/context/context-keys.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package context

type ctxKey string

const LABELS_CTX_KEY ctxKey = "labels"
1 change: 1 addition & 0 deletions pkg/judge/judge.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ type Result struct {
RuleSet string
ReplaceWith string
Since *Version
Labels map[string]interface{}
}

type Judge interface {
Expand Down
7 changes: 7 additions & 0 deletions pkg/judge/rego.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ func (j *RegoJudge) Eval(input []map[string]interface{}) ([]Result, error) {
m["Namespace"] = "<undefined>"
}

var labels map[string]interface{}
if v, ok := m["Labels"].(map[string]interface{}); ok {
labels = v
} else {
labels = make(map[string]interface{})
}
results = append(results, Result{
Name: m["Name"].(string),
Namespace: m["Namespace"].(string),
Expand All @@ -71,6 +77,7 @@ func (j *RegoJudge) Eval(input []map[string]interface{}) ([]Result, error) {
ReplaceWith: m["ReplaceWith"].(string),
RuleSet: m["RuleSet"].(string),
Since: since,
Labels: labels,
})
}
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/judge/rego_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ func TestEvalRules(t *testing.T) {
t.Errorf("expected %d findings, instead got: %d", len(tc.expected), len(results))
}

for i, _ := range results {
for i := range results {
if results[i].Kind != tc.expected[i] {
t.Errorf("expected to get %s finding, instead got: %s", tc.expected[i], results[i].Kind)
}
Expand Down
Loading

0 comments on commit c6f59f0

Please sign in to comment.