cmd: silence warnings in kube-controller-manager/kube-apiserver, dedupe/color warnings in kubectl

This commit is contained in:
Jordan Liggitt 2020-06-08 15:20:19 -04:00
parent e4bb1daecf
commit 0d674c4edb
5 changed files with 72 additions and 1 deletions

View File

@ -53,6 +53,7 @@ import (
"k8s.io/apiserver/pkg/util/webhook"
clientgoinformers "k8s.io/client-go/informers"
clientgoclientset "k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/util/keyutil"
cloudprovider "k8s.io/cloud-provider"
cliflag "k8s.io/component-base/cli/flag"
@ -100,6 +101,12 @@ cluster's shared state through which all other components interact.`,
// stop printing usage when the command errors
SilenceUsage: true,
PersistentPreRunE: func(*cobra.Command, []string) error {
// silence client-go warnings.
// kube-apiserver loopback clients should not log self-issued warnings.
rest.SetDefaultWarningHandler(rest.NoWarnings{})
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
verflag.PrintAndExitIfRequested()
cliflag.PrintFlags(cmd.Flags())

View File

@ -104,6 +104,13 @@ state of the cluster through the apiserver and makes changes attempting to move
current state towards the desired state. Examples of controllers that ship with
Kubernetes today are the replication controller, endpoints controller, namespace
controller, and serviceaccounts controller.`,
PersistentPreRunE: func(*cobra.Command, []string) error {
// silence client-go warnings.
// kube-controller-manager generically watches APIs (including deprecated ones),
// and CI ensures it works properly against matching kube-apiserver versions.
restclient.SetDefaultWarningHandler(restclient.NoWarnings{})
return nil
},
Run: func(cmd *cobra.Command, args []string) {
verflag.PrintAndExitIfRequested()
cliflag.PrintFlags(cmd.Flags())

View File

@ -28,6 +28,7 @@ import (
"github.com/spf13/cobra"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
cliflag "k8s.io/component-base/cli/flag"
cmdpkg "k8s.io/kubectl/pkg/cmd"
@ -69,6 +70,7 @@ import (
"k8s.io/kubectl/pkg/cmd/wait"
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates"
"k8s.io/kubectl/pkg/util/term"
"k8s.io/kubernetes/pkg/kubectl/cmd/auth"
"k8s.io/kubernetes/pkg/kubectl/cmd/convert"
"k8s.io/kubernetes/pkg/kubectl/cmd/cp"
@ -428,6 +430,9 @@ func HandlePluginCommand(pluginHandler PluginHandler, cmdArgs []string) error {
// NewKubectlCommand creates the `kubectl` command and its nested children.
func NewKubectlCommand(in io.Reader, out, err io.Writer) *cobra.Command {
warningHandler := rest.NewWarningWriter(err, rest.WarningWriterOptions{Deduplicate: true, Color: term.AllowsColorOutput(err)})
warningsAsErrors := false
// Parent command to which all subcommands are added.
cmds := &cobra.Command{
Use: "kubectl",
@ -441,10 +446,25 @@ func NewKubectlCommand(in io.Reader, out, err io.Writer) *cobra.Command {
// Hook before and after Run initialize and write profiles to disk,
// respectively.
PersistentPreRunE: func(*cobra.Command, []string) error {
rest.SetDefaultWarningHandler(warningHandler)
return initProfiling()
},
PersistentPostRunE: func(*cobra.Command, []string) error {
return flushProfiling()
if err := flushProfiling(); err != nil {
return err
}
if warningsAsErrors {
count := warningHandler.WarningCount()
switch count {
case 0:
// no warnings
case 1:
return fmt.Errorf("%d warning received", count)
default:
return fmt.Errorf("%d warnings received", count)
}
}
return nil
},
BashCompletionFunction: bashCompletionFunc,
}
@ -458,6 +478,8 @@ func NewKubectlCommand(in io.Reader, out, err io.Writer) *cobra.Command {
addProfilingFlags(flags)
flags.BoolVar(&warningsAsErrors, "warnings-as-errors", warningsAsErrors, "Treat warnings received from the server as errors and exit with a non-zero exit code")
kubeConfigFlags := genericclioptions.NewConfigFlags(true).WithDeprecatedPasswordFlag()
kubeConfigFlags.AddFlags(flags)
matchVersionKubeConfigFlags := cmdutil.NewMatchVersionFlags(kubeConfigFlags)

View File

@ -19,6 +19,7 @@ package term
import (
"io"
"os"
"runtime"
"github.com/moby/term"
@ -70,6 +71,32 @@ func IsTerminal(i interface{}) bool {
return terminal
}
// AllowsColorOutput returns true if the specified writer is a terminal and
// the process environment indicates color output is supported and desired.
func AllowsColorOutput(w io.Writer) bool {
if !IsTerminal(w) {
return false
}
// https://en.wikipedia.org/wiki/Computer_terminal#Dumb_terminals
if os.Getenv("TERM") == "dumb" {
return false
}
// https://no-color.org/
if _, nocolor := os.LookupEnv("NO_COLOR"); nocolor {
return false
}
// On Windows WT_SESSION is set by the modern terminal component.
// Older terminals have poor support for UTF-8, VT escape codes, etc.
if runtime.GOOS == "windows" && os.Getenv("WT_SESSION") == "" {
return false
}
return true
}
// Safe invokes the provided function and will attempt to ensure that when the
// function returns (or a termination signal is sent) that the terminal state
// is reset to the condition it was in prior to the function being invoked. If

View File

@ -145,6 +145,14 @@ run_role_tests() {
create_and_use_new_namespace
kube::log::status "Testing role"
# Test deprecated API request output
# TODO(liggitt): switch this to a custom deprecated resource once CRDs support marking versions as deprecated
output_message=$(kubectl get roles.v1beta1.rbac.authorization.k8s.io 2>&1 "${kube_flags[@]}")
kube::test::if_has_string "${output_message}" 'Role is deprecated'
output_message=$(! kubectl get roles.v1beta1.rbac.authorization.k8s.io --warnings-as-errors 2>&1 "${kube_flags[@]}")
kube::test::if_has_string "${output_message}" 'Role is deprecated'
kube::test::if_has_string "${output_message}" 'Error: 1 warning received'
# Dry-run create
kubectl create "${kube_flags[@]}" role pod-admin --dry-run=client --verb=* --resource=pods
kubectl create "${kube_flags[@]}" role pod-admin --dry-run=server --verb=* --resource=pods