Skip to content

Commit

Permalink
feat: Disable any logging from go-client's klog
Browse files Browse the repository at this point in the history
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
  • Loading branch information
stepanstipl committed Dec 1, 2022
1 parent c2ba471 commit 0939b16
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 23 deletions.
10 changes: 10 additions & 0 deletions cmd/kubent/main.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package main

import (
"flag"
"fmt"
"io"
"os"

"github.com/doitintl/kube-no-trouble/pkg/collector"
Expand All @@ -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 (
Expand Down Expand Up @@ -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")
Expand Down
46 changes: 29 additions & 17 deletions cmd/kubent/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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" {
Expand All @@ -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)
Expand All @@ -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)
}
})
}
}
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down Expand Up @@ -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
Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand Down Expand Up @@ -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=
Expand Down Expand Up @@ -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=
Expand Down
2 changes: 2 additions & 0 deletions pkg/collector/kube.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
Expand All @@ -64,6 +65,7 @@ func newClientRestConfig(kubeconfig string, kubecontext string, inClusterFn func
}

restConfig.UserAgent = userAgent
restConfig.WarningHandler = rest.NoWarnings{}
return restConfig, nil
}

Expand Down
15 changes: 15 additions & 0 deletions pkg/collector/kube_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package collector
import (
"os"
"path/filepath"
"reflect"
"testing"

"k8s.io/client-go/rest"
Expand Down Expand Up @@ -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) {
Expand All @@ -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 {
Expand Down

0 comments on commit 0939b16

Please sign in to comment.