Repository: doitintl/kube-no-trouble
Pull request #127 (2021-04-08 14:00:24)

feat: Configurable log level
Signed-off-by: david-doit-intl <david@doit-intl.com>

david-doit-intl: @stepanstipl done.
david-doit-intl: I just thought the string one was uglier, but I agree, its more user friendlier this way.
sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug.png' alt='Bug' width='16' height='16' />](https://sonarcloud.io/project/issues?id=doitintl_kube-no-trouble&pullRequest=127&resolved=false&types=BUG) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=doitintl_kube-no-trouble&pullRequest=127&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=doitintl_kube-no-trouble&pullRequest=127&resolved=false&types=BUG)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability.png' alt='Vulnerability' width='16' height='16' />](https://sonarcloud.io/project/issues?id=doitintl_kube-no-trouble&pullRequest=127&resolved=false&types=VULNERABILITY) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=doitintl_kube-no-trouble&pullRequest=127&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=doitintl_kube-no-trouble&pullRequest=127&resolved=false&types=VULNERABILITY)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot.png' alt='Security Hotspot' width='16' height='16' />](https://sonarcloud.io/project/security_hotspots?id=doitintl_kube-no-trouble&pullRequest=127&resolved=false&types=SECURITY_HOTSPOT) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/security_hotspots?id=doitintl_kube-no-trouble&pullRequest=127&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=doitintl_kube-no-trouble&pullRequest=127&resolved=false&types=SECURITY_HOTSPOT)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell.png' alt='Code Smell' width='16' height='16' />](https://sonarcloud.io/project/issues?id=doitintl_kube-no-trouble&pullRequest=127&resolved=false&types=CODE_SMELL) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=doitintl_kube-no-trouble&pullRequest=127&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=doitintl_kube-no-trouble&pullRequest=127&resolved=false&types=CODE_SMELL)

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo.png' alt='No Coverage information' width='16' height='16' />](https://sonarcloud.io/component_measures?id=doitintl_kube-no-trouble&pullRequest=127) No Coverage information  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3.png' alt='0.0%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=doitintl_kube-no-trouble&pullRequest=127&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=doitintl_kube-no-trouble&pullRequest=127&metric=new_duplicated_lines_density&view=list)


david-doit-intl: @stepanstipl I have updated

Patch:
diff --git a/README.md b/README.md
index 7473981..6d3fe0d 100644
--- a/README.md
+++ b/README.md
@@ -78,12 +78,12 @@ $./kubent -h
 Usage of ./kubent:
   -a, --additional-kind strings   additional kinds of resources to report in Kind.version.group.com format
   -c, --cluster                   enable Cluster collector (default true)
-  -d, --debug                     enable debug logging
   -e, --exit-error                exit with non-zero code when issues are found
   -f, --filename strings          manifests to check, use - for stdin
       --helm2                     enable Helm v2 collector (default true)
       --helm3                     enable Helm v3 collector (default true)
   -k, --kubeconfig string         path to the kubeconfig file (default "/Users/stepan/.kube/config")
+  -l, --loglevel string           set log level (trace, debug, info, warn, error, fatal, panic, disabled)
   -o, --output string             output format - [text|json] (default "text")
 ```
 
diff --git a/cmd/kubent/main.go b/cmd/kubent/main.go
index e3a9ce4..46e9e6f 100644
--- a/cmd/kubent/main.go
+++ b/cmd/kubent/main.go
@@ -81,16 +81,13 @@ func main() {
 	exitCode := EXIT_CODE_FAIL_GENERIC
 
 	log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
-	zerolog.SetGlobalLevel(zerolog.InfoLevel)
 
 	config, err := config.NewFromFlags()
 	if err != nil {
 		log.Fatal().Err(err).Msg("failed to parse config flags")
 	}
 
-	if config.Debug {
-		zerolog.SetGlobalLevel(zerolog.DebugLevel)
-	}
+	zerolog.SetGlobalLevel(zerolog.Level(config.LogLevel))
 
 	log.Info().Msg(">>> Kube No Trouble `kubent` <<<")
 	log.Info().Msgf("version %s (git sha %s)", version, gitSha)
diff --git a/go.mod b/go.mod
index 6e45693..a5ed225 100644
--- a/go.mod
+++ b/go.mod
@@ -10,7 +10,7 @@ require (
 	github.com/jmoiron/sqlx v1.2.0 // indirect
 	github.com/lib/pq v1.3.0 // indirect
 	github.com/open-policy-agent/opa v0.27.1
-	github.com/rs/zerolog v1.21.0
+	github.com/rs/zerolog v1.21.1-0.20210413053206-582f0cf0e39b
 	github.com/rubenv/sql-migrate v0.0.0-20200402132117-435005d389bc // indirect
 	github.com/spf13/pflag v1.0.5
 	helm.sh/helm v2.17.0+incompatible
diff --git a/go.sum b/go.sum
index 3baf8e0..9c1b5ae 100644
--- a/go.sum
+++ b/go.sum
@@ -532,6 +532,8 @@ github.com/rogpeppe/go-internal v1.4.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE
 github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
 github.com/rs/zerolog v1.21.0 h1:Q3vdXlfLNT+OftyBHsU0Y445MD+8m8axjKgf2si0QcM=
 github.com/rs/zerolog v1.21.0/go.mod h1:ZPhntP/xmq1nnND05hhpAh2QMhSsA4UN3MGZ6O2J3hM=
+github.com/rs/zerolog v1.21.1-0.20210413053206-582f0cf0e39b h1:lZjlZ6/ExLhIqPOmlh0dVJnBbiKokcjffr022PlkSSo=
+github.com/rs/zerolog v1.21.1-0.20210413053206-582f0cf0e39b/go.mod h1:ZPhntP/xmq1nnND05hhpAh2QMhSsA4UN3MGZ6O2J3hM=
 github.com/rubenv/sql-migrate v0.0.0-20200402132117-435005d389bc h1:+2DdDcxVYlarHjYcZTt8dZ4Ec8cXZirzL5ko0mkKPjU=
 github.com/rubenv/sql-migrate v0.0.0-20200402132117-435005d389bc/go.mod h1:DCgfY80j8GYL7MLEfvcpSFvjD0L5yZq/aZUJmhZklyg=
 github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
diff --git a/pkg/config/config.go b/pkg/config/config.go
index c5e33de..f19d468 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -7,36 +7,55 @@ import (
 	"strings"
 	"unicode"
 
-	"github.com/rs/zerolog/log"
+	"github.com/rs/zerolog"
 	flag "github.com/spf13/pflag"
 	"k8s.io/client-go/util/homedir"
 )
 
+type ZeroLogLevel int8
+
+func (l ZeroLogLevel) String() string {
+	return zerolog.Level(l).String()
+}
+
+func (l *ZeroLogLevel) Set(s string) error {
+	level, err := zerolog.ParseLevel(s)
+	if err != nil {
+		return err
+	}
+	*l = ZeroLogLevel(level)
+	return nil
+}
+
+func (l ZeroLogLevel) Type() string {
+	return "string"
+}
+
 type Config struct {
 	AdditionalKinds []string
 	Cluster         bool
-	Debug           bool
 	ExitError       bool
 	Filenames       []string
 	Helm2           bool
 	Helm3           bool
 	Kubeconfig      string
+	LogLevel        ZeroLogLevel
 	Output          string
 }
 
 func NewFromFlags() (*Config, error) {
-	config := Config{}
+	config := Config{LogLevel: ZeroLogLevel(zerolog.InfoLevel)}
 
 	home := homedir.HomeDir()
 	flag.StringSliceVarP(&config.AdditionalKinds, "additional-kind", "a", []string{}, "additional kinds of resources to report in Kind.version.group.com format")
 	flag.BoolVarP(&config.Cluster, "cluster", "c", true, "enable Cluster collector")
-	flag.BoolVarP(&config.Debug, "debug", "d", false, "enable debug logging")
 	flag.BoolVarP(&config.ExitError, "exit-error", "e", false, "exit with non-zero code when issues are found")
 	flag.BoolVar(&config.Helm2, "helm2", true, "enable Helm v2 collector")
 	flag.BoolVar(&config.Helm3, "helm3", true, "enable Helm v3 collector")
 	flag.StringSliceVarP(&config.Filenames, "filename", "f", []string{}, "manifests to check, use - for stdin")
 	flag.StringVarP(&config.Kubeconfig, "kubeconfig", "k", envOrString("KUBECONFIG", filepath.Join(home, ".kube", "config")), "path to the kubeconfig file")
 	flag.StringVarP(&config.Output, "output", "o", "text", "output format - [text|json]")
+	flag.VarP(&config.LogLevel, "log-level", "l", "set log level (trace, debug, info, warn, error, fatal, panic, disabled)")
 
 	flag.Parse()
 
@@ -60,7 +79,6 @@ func envOrString(env string, def string) string {
 func validateAdditionalResources(resources []string) error {
 	for _, r := range resources {
 		parts := strings.Split(r, ".")
-		log.Debug().Msgf("parts: %+v", parts)
 		if len(parts) < 4 {
 			return fmt.Errorf("failed to parse additional Kind, full form Kind.version.group.com is expected, instead got: %s", r)
 		}


Test patch:
diff --git a/cmd/kubent/main_test.go b/cmd/kubent/main_test.go
index 78d1da7..98b5419 100644
--- a/cmd/kubent/main_test.go
+++ b/cmd/kubent/main_test.go
@@ -9,16 +9,17 @@ import (
 
 	"github.com/doitintl/kube-no-trouble/pkg/collector"
 	"github.com/doitintl/kube-no-trouble/pkg/config"
+	"github.com/rs/zerolog"
 )
 
 func TestInitCollectors(t *testing.T) {
 	testConfig := config.Config{
 		Filenames:  []string{"../../fixtures/deployment-v1beta1.yaml"},
 		Cluster:    false,
-		Debug:      false,
 		Helm2:      false,
 		Helm3:      false,
 		Kubeconfig: "test",
+		LogLevel:   config.ZeroLogLevel(zerolog.ErrorLevel),
 		Output:     "test",
 	}
 
diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go
index cc8ec00..1b366db 100644
--- a/pkg/config/config_test.go
+++ b/pkg/config/config_test.go
@@ -8,6 +8,39 @@ import (
 	"k8s.io/client-go/util/homedir"
 )
 
+func TestValidLogLevelFromFlags(t *testing.T) {
+	oldArgs := os.Args[1]
+	defer func() { os.Args[1] = oldArgs }()
+
+	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()
+
+		if err != nil {
+			t.Errorf("Flags parsing failed %s", err)
+		}
+
+		expected := ZeroLogLevel(i - 1)
+		actual := config.LogLevel
+
+		if actual != expected {
+			t.Errorf("Config not parsed correctly: %s \nactual %d, expected %d", level, actual, expected)
+		}
+	}
+}
+
+func TestInvalidLogLevelFromFlags(t *testing.T) {
+	var testLevel ZeroLogLevel
+
+	if err := testLevel.Set("bad"); err == nil {
+		t.Errorf("Should not parse invalid flag")
+	}
+}
+
 func TestNewFromFlags(t *testing.T) {
 	// reset for testing
 	pflag.CommandLine = pflag.NewFlagSet(os.Args[0], pflag.ExitOnError)
@@ -50,7 +83,6 @@ func TestNewFromFlagsKubeconfigHome(t *testing.T) {
 	pflag.CommandLine = pflag.NewFlagSet(os.Args[0], pflag.ExitOnError)
 
 	expected := homedir.HomeDir() + "/.kube/config"
-
 	err := os.Unsetenv("KUBECONFIG")
 	if err != nil {
 		t.Errorf("failed to unset KUBECONFIG env variable: %s", err)
@@ -75,7 +107,7 @@ func TestEnvOrStringVariable(t *testing.T) {
 
 	i := envOrString("FOO", "default")
 	if i != "1" {
-		t.Errorf("Expected to get env variable, got %s insteadt", i)
+		t.Errorf("Expected to get env variable, got %s instead", i)
 	}
 }