From 20d2c76daf181cf20bddd567cebb04659090795d 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 34adb5f3..12eabe6d 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.46.1 - 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 ( @@ -77,7 +78,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 f43ba2ed..9104af5c 100644 --- a/go.sum +++ b/go.sum @@ -104,7 +104,6 @@ github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnht github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 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/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -393,9 +392,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/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -470,6 +469,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 {