From 0939b160eb1a46623c1035a0a9eff76e7a49752b Mon Sep 17 00:00:00 2001 From: Stepan Stipl Date: Thu, 1 Dec 2022 11:32:44 +0100 Subject: [PATCH] feat: Disable any logging from go-client's klog This PR disables warnings from go-client. Unfortunately this is not sufficient to also supress the warning about deprecated auth plugins, therefore the need for klog output code too. But in general direct logging from 3rd-party libs is undesirable, and this configures K8S's go-client klog to discard any logs. I've added some tests to cover some unexpected 3rd-party output, but as this depends on the environment and kubeconfig config, incl. providers, and is noteasily reproducible, this should be covered by integration test, as the current test do not fully cover this. Opened #406. fixes #402 --- cmd/kubent/main.go | 10 +++++++++ cmd/kubent/main_test.go | 46 ++++++++++++++++++++++++-------------- go.mod | 4 ++-- go.sum | 8 +++---- pkg/collector/kube.go | 2 ++ pkg/collector/kube_test.go | 15 +++++++++++++ 6 files changed, 62 insertions(+), 23 deletions(-) diff --git a/cmd/kubent/main.go b/cmd/kubent/main.go index ef3fdd95..03216a4b 100644 --- a/cmd/kubent/main.go +++ b/cmd/kubent/main.go @@ -1,7 +1,9 @@ package main import ( + "flag" "fmt" + "io" "os" "github.com/doitintl/kube-no-trouble/pkg/collector" @@ -16,6 +18,7 @@ import ( _ "k8s.io/client-go/plugin/pkg/client/auth/azure" _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" _ "k8s.io/client-go/plugin/pkg/client/auth/oidc" + "k8s.io/klog/v2" ) var ( @@ -97,6 +100,13 @@ func main() { log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}) + // disable any logging from K8S go-client + // unfortunately restConfig.WarningHandler does not handle auth plugins + klog.SetOutput(io.Discard) + flags := &flag.FlagSet{} + klog.InitFlags(flags) + flags.Set("logtostderr", "false") + config, err := config.NewFromFlags() if err != nil { log.Fatal().Err(err).Msg("failed to parse config flags") diff --git a/cmd/kubent/main_test.go b/cmd/kubent/main_test.go index 4491f97b..4285acdc 100644 --- a/cmd/kubent/main_test.go +++ b/cmd/kubent/main_test.go @@ -117,20 +117,22 @@ func TestMainExitCodes(t *testing.T) { expected int // expected exit code stdout string // expected stdout outFileName string + emptyStderr bool }{ - {"success", []string{clusterFlagDisabled, helm3FlagDisabled}, 0, "", ""}, - {"errorBadFlag", []string{"-c=not-boolean"}, 2, "", ""}, - {"successFound", []string{"-o=json", clusterFlagDisabled, helm3FlagDisabled, "-f=" + filepath.Join(FIXTURES_DIR, "deployment-v1beta1.yaml")}, 0, string(expectedJsonOutput), ""}, - {"exitErrorFlagNone", []string{clusterFlagDisabled, helm3FlagDisabled, "-e"}, 0, "", ""}, - {"exitErrorFlagFound", []string{clusterFlagDisabled, helm3FlagDisabled, "-e", "-f=" + filepath.Join(FIXTURES_DIR, "deployment-v1beta1.yaml")}, 200, "", ""}, - {"version short flag set", []string{"-v"}, 0, "", ""}, - {"version long flag set", []string{"--version"}, 0, "", ""}, - {"empty text output", []string{clusterFlagDisabled, helm3FlagDisabled}, 0, "", ""}, - {"empty json output", []string{"-o=json", clusterFlagDisabled, helm3FlagDisabled}, 0, "[]\n", ""}, - {"json-file", []string{"-o=json", clusterFlagDisabled, helm3FlagDisabled, "-f=" + filepath.Join(FIXTURES_DIR, "deployment-v1beta1.yaml")}, 0, "", filepath.Join(tmpDir, "json-file.out")}, - {"text-file", []string{"-o=json", clusterFlagDisabled, helm3FlagDisabled, "-f=" + filepath.Join(FIXTURES_DIR, "deployment-v1beta1.yaml")}, 0, "", filepath.Join(tmpDir, "text-file.out")}, - {"json-stdout", []string{"-o=json", clusterFlagDisabled, helm3FlagDisabled, "-f=" + filepath.Join(FIXTURES_DIR, "deployment-v1beta1.yaml")}, 0, string(expectedJsonOutput), "-"}, - {"error-bad-file", []string{clusterFlagDisabled, helm3FlagDisabled}, 1, "", "/this/dir/is/unlikely/to/exist"}, + {"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}, + {"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}, + {"version long flag set", []string{"--version"}, 0, "", "", false}, + {"empty text output", []string{clusterFlagDisabled, helm3FlagDisabled}, 0, "", "", false}, + {"empty json output", []string{"-o=json", clusterFlagDisabled, helm3FlagDisabled}, 0, "[]\n", "", false}, + {"json-file", []string{"-o=json", clusterFlagDisabled, helm3FlagDisabled, "-f=" + filepath.Join(FIXTURES_DIR, "deployment-v1beta1.yaml")}, 0, "", filepath.Join(tmpDir, "json-file.out"), false}, + {"text-file", []string{"-o=text", clusterFlagDisabled, helm3FlagDisabled, "-f=" + filepath.Join(FIXTURES_DIR, "deployment-v1beta1.yaml")}, 0, "", filepath.Join(tmpDir, "text-file.out"), false}, + {"json-stdout", []string{"-o=json", clusterFlagDisabled, helm3FlagDisabled, "-f=" + filepath.Join(FIXTURES_DIR, "deployment-v1beta1.yaml")}, 0, string(expectedJsonOutput), "-", false}, + {"error-bad-file", []string{clusterFlagDisabled, helm3FlagDisabled}, 1, "", "/this/dir/is/unlikely/to/exist", false}, + {"no-3rdparty-output", []string{clusterFlagDisabled, helm3FlagDisabled, "-l=disabled"}, 0, "", "", true}, } if os.Getenv("TEST_EXIT_CODE") == "1" { @@ -150,11 +152,18 @@ func TestMainExitCodes(t *testing.T) { } base64Args, _ := encodeBase64(tc.args) + var stdout, stderr bytes.Buffer + cmd := exec.Command(os.Args[0], "-test.run=TestMainExitCodes") cmd.Env = append(os.Environ(), "TEST_EXIT_CODE=1", "TEST_ARGS="+base64Args) - out, err := cmd.Output() + cmd.Stdout = &stdout + cmd.Stderr = &stderr + err := cmd.Run() + + outStr := stdout.String() + errStr := stderr.String() if tc.expected == 0 && err != nil { t.Fatalf("expected to succeed with exit code %d, failed with %v", tc.expected, err) @@ -171,16 +180,19 @@ func TestMainExitCodes(t *testing.T) { t.Fatalf("expected to get exit code %d, failed with %v", tc.expected, err) } } - if tc.expected == 0 && err == nil && tc.stdout != string(out) { - t.Fatalf("expected to get stdout as %s, instead got %s", tc.stdout, out) + if tc.expected == 0 && err == nil && tc.stdout != outStr { + t.Fatalf("expected to get stdout as %s, instead got %s", tc.stdout, outStr) } if tc.expected == 0 && err == nil && tc.outFileName != "" && tc.outFileName != "-" { if fs, err := os.Stat(tc.outFileName); err != nil || fs.Size() == 0 { - t.Fatalf("expected non-empty outputdile: %s, got error: %v", tc.outFileName, err) + t.Fatalf("expected non-empty outputfile: %s, got error: %v", tc.outFileName, err) } } + if tc.emptyStderr && errStr != "" { + t.Fatalf("expected empty stderr, got: %s", errStr) + } }) } } diff --git a/go.mod b/go.mod index ceeb7078..38d8c1d4 100644 --- a/go.mod +++ b/go.mod @@ -6,11 +6,12 @@ require ( github.com/ghodss/yaml v1.0.0 github.com/hashicorp/go-version v1.3.0 github.com/open-policy-agent/opa v0.34.2 - github.com/rs/zerolog v1.21.1-0.20210413053206-582f0cf0e39b + github.com/rs/zerolog v1.26.1 github.com/spf13/pflag v1.0.5 helm.sh/helm/v3 v3.10.2 k8s.io/apimachinery v0.25.4 k8s.io/client-go v0.25.4 + k8s.io/klog/v2 v2.70.1 ) require ( @@ -75,7 +76,6 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/api v0.25.4 // indirect k8s.io/apiextensions-apiserver v0.25.2 // indirect - k8s.io/klog/v2 v2.70.1 // indirect k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed // indirect sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect diff --git a/go.sum b/go.sum index 4fe90cf1..7783b349 100644 --- a/go.sum +++ b/go.sum @@ -114,7 +114,6 @@ github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= @@ -447,9 +446,9 @@ github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0/go.mod h1:bCqn github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= -github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= -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/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.26.1 h1:/ihwxqH+4z8UxyI70wM1z9yCvkWcfz/a3mj48k/Zngc= +github.com/rs/zerolog v1.26.1/go.mod h1:/wSSJWX7lVrsOwlbyTRSOJvqRlc+WjWlfes+CiJ+tmc= github.com/rubenv/sql-migrate v1.1.2 h1:9M6oj4e//owVVHYrFISmY9LBRw6gzkCNmD9MV36tZeQ= github.com/rubenv/sql-migrate v1.1.2/go.mod h1:/7TZymwxN8VWumcIxw1jjHEcR1djpdkMHQPT4FWdnbQ= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= @@ -539,6 +538,7 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM= golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= diff --git a/pkg/collector/kube.go b/pkg/collector/kube.go index f6e102e9..e4b3bc11 100644 --- a/pkg/collector/kube.go +++ b/pkg/collector/kube.go @@ -38,6 +38,7 @@ func newClientRestConfig(kubeconfig string, kubecontext string, inClusterFn func if kubeconfig == "" { if restConfig, err := inClusterFn(); err == nil { restConfig.UserAgent = userAgent + restConfig.WarningHandler = rest.NoWarnings{} return restConfig, nil } } @@ -64,6 +65,7 @@ func newClientRestConfig(kubeconfig string, kubecontext string, inClusterFn func } restConfig.UserAgent = userAgent + restConfig.WarningHandler = rest.NoWarnings{} return restConfig, nil } diff --git a/pkg/collector/kube_test.go b/pkg/collector/kube_test.go index 3a2100f8..26baceac 100644 --- a/pkg/collector/kube_test.go +++ b/pkg/collector/kube_test.go @@ -3,6 +3,7 @@ package collector import ( "os" "path/filepath" + "reflect" "testing" "k8s.io/client-go/rest" @@ -141,6 +142,14 @@ func TestNewClientRestConfigWithContext(t *testing.T) { if config.Host != expectedHost { t.Fatalf("Expected host from context %s to be: %s, got %s instead", expectedContext, expectedHost, config.Host) } + + if config.UserAgent != USER_AGENT { + t.Fatalf("Expected %s UserAgent, instead got: %s", USER_AGENT, config.UserAgent) + } + + if _, ok := config.WarningHandler.(rest.NoWarnings); !ok { + t.Fatalf("Expected NoWarnings warnings handler, instead got: %s", reflect.TypeOf(config.WarningHandler).Name()) + } } func TestNewClientRestConfigContextMissing(t *testing.T) { @@ -165,6 +174,12 @@ func TestNewClientRestConfigInCluster(t *testing.T) { if cfg.Host != expectedHost { t.Fatalf("Expected %s host, instead got: %s", expectedHost, cfg.Host) } + if cfg.UserAgent != USER_AGENT { + t.Fatalf("Expected %s UserAgent, instead got: %s", USER_AGENT, cfg.UserAgent) + } + if _, ok := cfg.WarningHandler.(rest.NoWarnings); !ok { + t.Fatalf("Expected NoWarnings warnings handler, instead got: %s", reflect.TypeOf(cfg.WarningHandler).Name()) + } } type env struct {