diff --git a/cmd/kube-apiserver/app/server.go b/cmd/kube-apiserver/app/server.go index 0815511707d..fc0d51d2eca 100644 --- a/cmd/kube-apiserver/app/server.go +++ b/cmd/kube-apiserver/app/server.go @@ -58,6 +58,7 @@ import ( cliflag "k8s.io/component-base/cli/flag" "k8s.io/component-base/cli/globalflag" "k8s.io/component-base/logs" + logsapi "k8s.io/component-base/logs/api/v1" _ "k8s.io/component-base/metrics/prometheus/workqueue" // for workqueue metric registration "k8s.io/component-base/term" "k8s.io/component-base/version" @@ -82,7 +83,7 @@ import ( ) func init() { - utilruntime.Must(logs.AddFeatureGates(utilfeature.DefaultMutableFeatureGate)) + utilruntime.Must(logsapi.AddFeatureGates(utilfeature.DefaultMutableFeatureGate)) } // NewAPIServerCommand creates a *cobra.Command object with default parameters diff --git a/cmd/kube-controller-manager/app/controllermanager.go b/cmd/kube-controller-manager/app/controllermanager.go index 443daab6f76..a0dd399972a 100644 --- a/cmd/kube-controller-manager/app/controllermanager.go +++ b/cmd/kube-controller-manager/app/controllermanager.go @@ -55,6 +55,7 @@ import ( "k8s.io/component-base/cli/globalflag" "k8s.io/component-base/configz" "k8s.io/component-base/logs" + logsapi "k8s.io/component-base/logs/api/v1" "k8s.io/component-base/term" "k8s.io/component-base/version" "k8s.io/component-base/version/verflag" @@ -75,7 +76,7 @@ import ( ) func init() { - utilruntime.Must(logs.AddFeatureGates(utilfeature.DefaultMutableFeatureGate)) + utilruntime.Must(logsapi.AddFeatureGates(utilfeature.DefaultMutableFeatureGate)) } const ( diff --git a/cmd/kube-scheduler/app/server.go b/cmd/kube-scheduler/app/server.go index 9826e3f67e5..93230343492 100644 --- a/cmd/kube-scheduler/app/server.go +++ b/cmd/kube-scheduler/app/server.go @@ -46,6 +46,7 @@ import ( "k8s.io/component-base/cli/globalflag" "k8s.io/component-base/configz" "k8s.io/component-base/logs" + logsapi "k8s.io/component-base/logs/api/v1" "k8s.io/component-base/metrics/legacyregistry" "k8s.io/component-base/term" "k8s.io/component-base/version" @@ -62,7 +63,7 @@ import ( ) func init() { - utilruntime.Must(logs.AddFeatureGates(utilfeature.DefaultMutableFeatureGate)) + utilruntime.Must(logsapi.AddFeatureGates(utilfeature.DefaultMutableFeatureGate)) } // Option configures a framework.Registry. diff --git a/cmd/kubelet/app/options/options.go b/cmd/kubelet/app/options/options.go index d72c4f40c47..12a16bdc340 100644 --- a/cmd/kubelet/app/options/options.go +++ b/cmd/kubelet/app/options/options.go @@ -28,7 +28,6 @@ import ( "k8s.io/apimachinery/pkg/util/sets" utilfeature "k8s.io/apiserver/pkg/util/feature" cliflag "k8s.io/component-base/cli/flag" - "k8s.io/component-base/logs" "k8s.io/kubelet/config/v1beta1" kubeletapis "k8s.io/kubelet/pkg/apis" "k8s.io/kubernetes/pkg/cluster/ports" @@ -500,7 +499,7 @@ func AddKubeletConfigFlags(mainfs *pflag.FlagSet, c *kubeletconfig.KubeletConfig fs.StringSliceVar(&c.EnforceNodeAllocatable, "enforce-node-allocatable", c.EnforceNodeAllocatable, "A comma separated list of levels of node allocatable enforcement to be enforced by kubelet. Acceptable options are 'none', 'pods', 'system-reserved', and 'kube-reserved'. If the latter two options are specified, '--system-reserved-cgroup' and '--kube-reserved-cgroup' must also be set, respectively. If 'none' is specified, no additional options should be set. See https://kubernetes.io/docs/tasks/administer-cluster/reserve-compute-resources/ for more details.") fs.StringVar(&c.SystemReservedCgroup, "system-reserved-cgroup", c.SystemReservedCgroup, "Absolute name of the top level cgroup that is used to manage non-kubernetes components for which compute resources were reserved via '--system-reserved' flag. Ex. '/system-reserved'. [default='']") fs.StringVar(&c.KubeReservedCgroup, "kube-reserved-cgroup", c.KubeReservedCgroup, "Absolute name of the top level cgroup that is used to manage kubernetes components for which compute resources were reserved via '--kube-reserved' flag. Ex. '/kube-reserved'. [default='']") - logs.BindLoggingFlags(&c.Logging, fs) + c.Logging.AddFlags(fs) // Memory Manager Flags fs.StringVar(&c.MemoryManagerPolicy, "memory-manager-policy", c.MemoryManagerPolicy, "Memory Manager policy to use. Possible values: 'None', 'Static'.") diff --git a/cmd/kubelet/app/server.go b/cmd/kubelet/app/server.go index faa83d469f9..8aaaeb50c77 100644 --- a/cmd/kubelet/app/server.go +++ b/cmd/kubelet/app/server.go @@ -47,6 +47,7 @@ import ( utilnet "k8s.io/apimachinery/pkg/util/net" utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/apimachinery/pkg/util/wait" genericapiserver "k8s.io/apiserver/pkg/server" "k8s.io/apiserver/pkg/server/healthz" @@ -65,6 +66,7 @@ import ( "k8s.io/component-base/configz" "k8s.io/component-base/featuregate" "k8s.io/component-base/logs" + logsapi "k8s.io/component-base/logs/api/v1" "k8s.io/component-base/metrics" "k8s.io/component-base/metrics/legacyregistry" "k8s.io/component-base/version" @@ -105,7 +107,7 @@ import ( ) func init() { - utilruntime.Must(logs.AddFeatureGates(utilfeature.DefaultMutableFeatureGate)) + utilruntime.Must(logsapi.AddFeatureGates(utilfeature.DefaultMutableFeatureGate)) } const ( @@ -213,9 +215,8 @@ HTTP server: The kubelet can also listen for HTTP and respond to a simple API // Config and flags parsed, now we can initialize logging. logs.InitLogs() - logOption := &logs.Options{Config: kubeletConfig.Logging} - if err := logOption.ValidateAndApply(utilfeature.DefaultFeatureGate); err != nil { - return fmt.Errorf("failed to initialize logging: %v", err) + if err := kubeletConfig.Logging.ValidateAndApplyAsField(utilfeature.DefaultFeatureGate, field.NewPath("logging")); err != nil { + return fmt.Errorf("initialize logging: %v", err) } cliflag.PrintFlags(cleanFlagSet) diff --git a/pkg/generated/openapi/zz_generated.openapi.go b/pkg/generated/openapi/zz_generated.openapi.go index e3bfd4aa195..74e4c1804ce 100644 --- a/pkg/generated/openapi/zz_generated.openapi.go +++ b/pkg/generated/openapi/zz_generated.openapi.go @@ -53339,7 +53339,7 @@ func schema_k8sio_kubelet_config_v1beta1_KubeletConfiguration(ref common.Referen SchemaProps: spec.SchemaProps{ Description: "logging specifies the options of logging. Refer to [Logs Options](https://github.com/kubernetes/component-base/blob/master/logs/options.go) for more information. Default:\n Format: text", Default: map[string]interface{}{}, - Ref: ref("k8s.io/component-base/config/v1alpha1.LoggingConfiguration"), + Ref: ref("k8s.io/component-base/logs/api/v1.LoggingConfiguration"), }, }, "enableSystemLogHandler": { @@ -53442,7 +53442,7 @@ func schema_k8sio_kubelet_config_v1beta1_KubeletConfiguration(ref common.Referen }, }, Dependencies: []string{ - "k8s.io/api/core/v1.Taint", "k8s.io/apimachinery/pkg/apis/meta/v1.Duration", "k8s.io/component-base/config/v1alpha1.LoggingConfiguration", "k8s.io/kubelet/config/v1beta1.KubeletAuthentication", "k8s.io/kubelet/config/v1beta1.KubeletAuthorization", "k8s.io/kubelet/config/v1beta1.MemoryReservation", "k8s.io/kubelet/config/v1beta1.MemorySwapConfiguration", "k8s.io/kubelet/config/v1beta1.ShutdownGracePeriodByPodPriority"}, + "k8s.io/api/core/v1.Taint", "k8s.io/apimachinery/pkg/apis/meta/v1.Duration", "k8s.io/component-base/logs/api/v1.LoggingConfiguration", "k8s.io/kubelet/config/v1beta1.KubeletAuthentication", "k8s.io/kubelet/config/v1beta1.KubeletAuthorization", "k8s.io/kubelet/config/v1beta1.MemoryReservation", "k8s.io/kubelet/config/v1beta1.MemorySwapConfiguration", "k8s.io/kubelet/config/v1beta1.ShutdownGracePeriodByPodPriority"}, } } diff --git a/pkg/kubelet/apis/config/register_test.go b/pkg/kubelet/apis/config/register_test.go index 87f7adbda15..b67fcb0df62 100644 --- a/pkg/kubelet/apis/config/register_test.go +++ b/pkg/kubelet/apis/config/register_test.go @@ -23,6 +23,7 @@ import ( v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" componentconfigtesting "k8s.io/component-base/config/testing" + logsapi "k8s.io/component-base/logs/api/v1" ) func TestComponentConfigSetup(t *testing.T) { @@ -32,10 +33,11 @@ func TestComponentConfigSetup(t *testing.T) { SchemeGroupVersion: SchemeGroupVersion, AddToScheme: AddToScheme, AllowedTags: map[reflect.Type]bool{ - reflect.TypeOf(metav1.TypeMeta{}): true, - reflect.TypeOf(metav1.Duration{}): true, - reflect.TypeOf(v1.NodeConfigSource{}): true, - reflect.TypeOf(v1.Taint{}): true, + reflect.TypeOf(logsapi.LoggingConfiguration{}): true, + reflect.TypeOf(metav1.Duration{}): true, + reflect.TypeOf(metav1.TypeMeta{}): true, + reflect.TypeOf(v1.NodeConfigSource{}): true, + reflect.TypeOf(v1.Taint{}): true, }, } diff --git a/pkg/kubelet/apis/config/types.go b/pkg/kubelet/apis/config/types.go index a2da50a2d0b..6c6377c500a 100644 --- a/pkg/kubelet/apis/config/types.go +++ b/pkg/kubelet/apis/config/types.go @@ -19,7 +19,7 @@ package config import ( v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - componentbaseconfig "k8s.io/component-base/config" + logsapi "k8s.io/component-base/logs/api/v1" ) // HairpinMode denotes how the kubelet should configure networking to handle @@ -384,7 +384,7 @@ type KubeletConfiguration struct { ShowHiddenMetricsForVersion string // Logging specifies the options of logging. // Refer [Logs Options](https://github.com/kubernetes/component-base/blob/master/logs/options.go) for more information. - Logging componentbaseconfig.LoggingConfiguration + Logging logsapi.LoggingConfiguration // EnableSystemLogHandler enables /logs handler. EnableSystemLogHandler bool // ShutdownGracePeriod specifies the total duration that the node should delay the shutdown and total grace period for pod termination during a node shutdown. diff --git a/pkg/kubelet/apis/config/v1beta1/defaults.go b/pkg/kubelet/apis/config/v1beta1/defaults.go index eee1d0945da..06ea4559e97 100644 --- a/pkg/kubelet/apis/config/v1beta1/defaults.go +++ b/pkg/kubelet/apis/config/v1beta1/defaults.go @@ -21,7 +21,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" kruntime "k8s.io/apimachinery/pkg/runtime" - componentbaseconfigv1alpha1 "k8s.io/component-base/config/v1alpha1" kubeletconfigv1beta1 "k8s.io/kubelet/config/v1beta1" // TODO: Cut references to k8s.io/kubernetes, eventually there should be none from this package @@ -245,7 +244,7 @@ func SetDefaults_KubeletConfiguration(obj *kubeletconfigv1beta1.KubeletConfigura obj.VolumePluginDir = DefaultVolumePluginDir } // Use the Default LoggingConfiguration option - componentbaseconfigv1alpha1.RecommendedLoggingConfiguration(&obj.Logging) + obj.Logging.SetRecommendedLoggingConfiguration() if obj.EnableSystemLogHandler == nil { obj.EnableSystemLogHandler = utilpointer.BoolPtr(true) } diff --git a/pkg/kubelet/apis/config/v1beta1/defaults_test.go b/pkg/kubelet/apis/config/v1beta1/defaults_test.go index aa835bd9811..59d2e35fd1f 100644 --- a/pkg/kubelet/apis/config/v1beta1/defaults_test.go +++ b/pkg/kubelet/apis/config/v1beta1/defaults_test.go @@ -24,7 +24,7 @@ import ( v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - componentbaseconfigv1alpha1 "k8s.io/component-base/config/v1alpha1" + logsapi "k8s.io/component-base/logs/api/v1" "k8s.io/kubelet/config/v1beta1" "k8s.io/kubernetes/pkg/cluster/ports" "k8s.io/kubernetes/pkg/kubelet/qos" @@ -111,7 +111,7 @@ func TestSetDefaultsKubeletConfiguration(t *testing.T) { ConfigMapAndSecretChangeDetectionStrategy: v1beta1.WatchChangeDetectionStrategy, EnforceNodeAllocatable: DefaultNodeAllocatableEnforcement, VolumePluginDir: DefaultVolumePluginDir, - Logging: componentbaseconfigv1alpha1.LoggingConfiguration{ + Logging: logsapi.LoggingConfiguration{ Format: "text", FlushFrequency: 5 * time.Second, }, @@ -232,7 +232,7 @@ func TestSetDefaultsKubeletConfiguration(t *testing.T) { VolumePluginDir: "", ProviderID: "", KernelMemcgNotification: false, - Logging: componentbaseconfigv1alpha1.LoggingConfiguration{ + Logging: logsapi.LoggingConfiguration{ Format: "", FlushFrequency: 5 * time.Second, }, @@ -329,7 +329,7 @@ func TestSetDefaultsKubeletConfiguration(t *testing.T) { EnforceNodeAllocatable: []string{}, AllowedUnsafeSysctls: []string{}, VolumePluginDir: DefaultVolumePluginDir, - Logging: componentbaseconfigv1alpha1.LoggingConfiguration{ + Logging: logsapi.LoggingConfiguration{ Format: "text", FlushFrequency: 5 * time.Second, }, @@ -468,7 +468,7 @@ func TestSetDefaultsKubeletConfiguration(t *testing.T) { VolumePluginDir: "volume-plugin-dir", ProviderID: "provider-id", KernelMemcgNotification: true, - Logging: componentbaseconfigv1alpha1.LoggingConfiguration{ + Logging: logsapi.LoggingConfiguration{ Format: "json", FlushFrequency: 5 * time.Second, }, @@ -611,7 +611,7 @@ func TestSetDefaultsKubeletConfiguration(t *testing.T) { VolumePluginDir: "volume-plugin-dir", ProviderID: "provider-id", KernelMemcgNotification: true, - Logging: componentbaseconfigv1alpha1.LoggingConfiguration{ + Logging: logsapi.LoggingConfiguration{ Format: "json", FlushFrequency: 5 * time.Second, }, @@ -705,7 +705,7 @@ func TestSetDefaultsKubeletConfiguration(t *testing.T) { ConfigMapAndSecretChangeDetectionStrategy: v1beta1.WatchChangeDetectionStrategy, EnforceNodeAllocatable: DefaultNodeAllocatableEnforcement, VolumePluginDir: DefaultVolumePluginDir, - Logging: componentbaseconfigv1alpha1.LoggingConfiguration{ + Logging: logsapi.LoggingConfiguration{ Format: "text", FlushFrequency: 5 * time.Second, }, diff --git a/pkg/kubelet/apis/config/v1beta1/zz_generated.conversion.go b/pkg/kubelet/apis/config/v1beta1/zz_generated.conversion.go index 5cc1efddcd7..0f50f13d291 100644 --- a/pkg/kubelet/apis/config/v1beta1/zz_generated.conversion.go +++ b/pkg/kubelet/apis/config/v1beta1/zz_generated.conversion.go @@ -28,7 +28,6 @@ import ( v1 "k8s.io/apimachinery/pkg/apis/meta/v1" conversion "k8s.io/apimachinery/pkg/conversion" runtime "k8s.io/apimachinery/pkg/runtime" - v1alpha1 "k8s.io/component-base/config/v1alpha1" v1beta1 "k8s.io/kubelet/config/v1beta1" config "k8s.io/kubernetes/pkg/kubelet/apis/config" ) @@ -485,9 +484,7 @@ func autoConvert_v1beta1_KubeletConfiguration_To_config_KubeletConfiguration(in out.VolumePluginDir = in.VolumePluginDir out.ProviderID = in.ProviderID out.KernelMemcgNotification = in.KernelMemcgNotification - if err := v1alpha1.Convert_v1alpha1_LoggingConfiguration_To_config_LoggingConfiguration(&in.Logging, &out.Logging, s); err != nil { - return err - } + out.Logging = in.Logging if err := v1.Convert_Pointer_bool_To_bool(&in.EnableSystemLogHandler, &out.EnableSystemLogHandler, s); err != nil { return err } @@ -661,9 +658,7 @@ func autoConvert_config_KubeletConfiguration_To_v1beta1_KubeletConfiguration(in out.EnforceNodeAllocatable = *(*[]string)(unsafe.Pointer(&in.EnforceNodeAllocatable)) out.ReservedSystemCPUs = in.ReservedSystemCPUs out.ShowHiddenMetricsForVersion = in.ShowHiddenMetricsForVersion - if err := v1alpha1.Convert_config_LoggingConfiguration_To_v1alpha1_LoggingConfiguration(&in.Logging, &out.Logging, s); err != nil { - return err - } + out.Logging = in.Logging if err := v1.Convert_bool_To_Pointer_bool(&in.EnableSystemLogHandler, &out.EnableSystemLogHandler, s); err != nil { return err } diff --git a/pkg/kubelet/apis/config/validation/validation.go b/pkg/kubelet/apis/config/validation/validation.go index 5e38bbfdd01..08425d25486 100644 --- a/pkg/kubelet/apis/config/validation/validation.go +++ b/pkg/kubelet/apis/config/validation/validation.go @@ -25,7 +25,6 @@ import ( utilvalidation "k8s.io/apimachinery/pkg/util/validation" "k8s.io/apimachinery/pkg/util/validation/field" utilfeature "k8s.io/apiserver/pkg/util/feature" - "k8s.io/component-base/logs" "k8s.io/component-base/metrics" "k8s.io/kubernetes/pkg/features" kubeletconfig "k8s.io/kubernetes/pkg/kubelet/apis/config" @@ -237,7 +236,7 @@ func ValidateKubeletConfiguration(kc *kubeletconfig.KubeletConfiguration) error } allErrors = append(allErrors, metrics.ValidateShowHiddenMetricsVersion(kc.ShowHiddenMetricsForVersion)...) - if errs := logs.ValidateLoggingConfiguration(&kc.Logging, field.NewPath("logging")); len(errs) > 0 { + if errs := kc.Logging.Validate(localFeatureGate, field.NewPath("logging")); len(errs) > 0 { allErrors = append(allErrors, errs.ToAggregate().Errors()...) } diff --git a/pkg/kubelet/apis/config/validation/validation_test.go b/pkg/kubelet/apis/config/validation/validation_test.go index 12a15e925dc..08057cf4458 100644 --- a/pkg/kubelet/apis/config/validation/validation_test.go +++ b/pkg/kubelet/apis/config/validation/validation_test.go @@ -23,7 +23,7 @@ import ( v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - componentbaseconfig "k8s.io/component-base/config" + logsapi "k8s.io/component-base/logs/api/v1" kubeletconfig "k8s.io/kubernetes/pkg/kubelet/apis/config" "k8s.io/kubernetes/pkg/kubelet/apis/config/validation" kubetypes "k8s.io/kubernetes/pkg/kubelet/types" @@ -68,7 +68,7 @@ var ( "GracefulNodeShutdown": true, "MemoryQoS": true, }, - Logging: componentbaseconfig.LoggingConfiguration{ + Logging: logsapi.LoggingConfiguration{ Format: "text", }, } diff --git a/staging/src/k8s.io/component-base/config/types.go b/staging/src/k8s.io/component-base/config/types.go index 4a4c13a5da6..aad605eeef8 100644 --- a/staging/src/k8s.io/component-base/config/types.go +++ b/staging/src/k8s.io/component-base/config/types.go @@ -17,14 +17,6 @@ limitations under the License. package config import ( - "fmt" - "strconv" - "strings" - "time" - - "github.com/spf13/pflag" - - "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -86,127 +78,3 @@ type DebuggingConfiguration struct { // enableProfiling is true. EnableContentionProfiling bool } - -// LoggingConfiguration contains logging options -// Refer [Logs Options](https://github.com/kubernetes/component-base/blob/master/logs/options.go) for more information. -type LoggingConfiguration struct { - // Format Flag specifies the structure of log messages. - // default value of format is `text` - Format string - // Maximum number of nanoseconds (i.e. 1s = 1000000000) between log - // flushes. Ignored if the selected logging backend writes log - // messages without buffering. - FlushFrequency time.Duration - // Verbosity is the threshold that determines which log messages are - // logged. Default is zero which logs only the most important - // messages. Higher values enable additional messages. Error messages - // are always logged. - Verbosity VerbosityLevel - // VModule overrides the verbosity threshold for individual files. - // Only supported for "text" log format. - VModule VModuleConfiguration - // [Experimental] Options holds additional parameters that are specific - // to the different logging formats. Only the options for the selected - // format get used, but all of them get validated. - Options FormatOptions -} - -// FormatOptions contains options for the different logging formats. -type FormatOptions struct { - // [Experimental] JSON contains options for logging format "json". - JSON JSONOptions -} - -// JSONOptions contains options for logging format "json". -type JSONOptions struct { - // [Experimental] SplitStream redirects error messages to stderr while - // info messages go to stdout, with buffering. The default is to write - // both to stdout, without buffering. - SplitStream bool - // [Experimental] InfoBufferSize sets the size of the info stream when - // using split streams. The default is zero, which disables buffering. - InfoBufferSize resource.QuantityValue -} - -// VModuleConfiguration is a collection of individual file names or patterns -// and the corresponding verbosity threshold. -type VModuleConfiguration []VModuleItem - -var _ pflag.Value = &VModuleConfiguration{} - -// VModuleItem defines verbosity for one or more files which match a certain -// glob pattern. -type VModuleItem struct { - // FilePattern is a base file name (i.e. minus the ".go" suffix and - // directory) or a "glob" pattern for such a name. It must not contain - // comma and equal signs because those are separators for the - // corresponding klog command line argument. - FilePattern string - // Verbosity is the threshold for log messages emitted inside files - // that match the pattern. - Verbosity VerbosityLevel -} - -// String returns the -vmodule parameter (comma-separated list of pattern=N). -func (vmodule *VModuleConfiguration) String() string { - var patterns []string - for _, item := range *vmodule { - patterns = append(patterns, fmt.Sprintf("%s=%d", item.FilePattern, item.Verbosity)) - } - return strings.Join(patterns, ",") -} - -// Set parses the -vmodule parameter (comma-separated list of pattern=N). -func (vmodule *VModuleConfiguration) Set(value string) error { - // This code mirrors https://github.com/kubernetes/klog/blob/9ad246211af1ed84621ee94a26fcce0038b69cd1/klog.go#L287-L313 - - for _, pat := range strings.Split(value, ",") { - if len(pat) == 0 { - // Empty strings such as from a trailing comma can be ignored. - continue - } - patLev := strings.Split(pat, "=") - if len(patLev) != 2 || len(patLev[0]) == 0 || len(patLev[1]) == 0 { - return fmt.Errorf("%q does not have the pattern=N format", pat) - } - pattern := patLev[0] - // 31 instead of 32 to ensure that it also fits into int32. - v, err := strconv.ParseUint(patLev[1], 10, 31) - if err != nil { - return fmt.Errorf("parsing verbosity in %q: %v", pat, err) - } - *vmodule = append(*vmodule, VModuleItem{FilePattern: pattern, Verbosity: VerbosityLevel(v)}) - } - return nil -} - -func (vmodule *VModuleConfiguration) Type() string { - return "pattern=N,..." -} - -// VerbosityLevel represents a klog or logr verbosity threshold. -type VerbosityLevel uint32 - -var _ pflag.Value = new(VerbosityLevel) - -func (l *VerbosityLevel) String() string { - return strconv.FormatInt(int64(*l), 10) -} - -func (l *VerbosityLevel) Get() interface{} { - return *l -} - -func (l *VerbosityLevel) Set(value string) error { - // Limited to int32 for compatibility with klog. - v, err := strconv.ParseUint(value, 10, 31) - if err != nil { - return err - } - *l = VerbosityLevel(v) - return nil -} - -func (l *VerbosityLevel) Type() string { - return "Level" -} diff --git a/staging/src/k8s.io/component-base/config/types_test.go b/staging/src/k8s.io/component-base/config/types_test.go deleted file mode 100644 index f35c2a1aa33..00000000000 --- a/staging/src/k8s.io/component-base/config/types_test.go +++ /dev/null @@ -1,119 +0,0 @@ -/* -Copyright 2021 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package config - -import ( - "fmt" - "math" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestVModule(t *testing.T) { - testcases := []struct { - arg string - expectError string - expectValue VModuleConfiguration - expectParam string - }{ - { - arg: "gopher*=1", - expectValue: VModuleConfiguration{ - { - FilePattern: "gopher*", - Verbosity: 1, - }, - }, - }, - { - arg: "foo=1,bar=2", - expectValue: VModuleConfiguration{ - { - FilePattern: "foo", - Verbosity: 1, - }, - { - FilePattern: "bar", - Verbosity: 2, - }, - }, - }, - { - arg: "foo=1,bar=2,", - expectValue: VModuleConfiguration{ - { - FilePattern: "foo", - Verbosity: 1, - }, - { - FilePattern: "bar", - Verbosity: 2, - }, - }, - expectParam: "foo=1,bar=2", - }, - { - arg: "gopher*", - expectError: `"gopher*" does not have the pattern=N format`, - }, - { - arg: "=1", - expectError: `"=1" does not have the pattern=N format`, - }, - { - arg: "foo=-1", - expectError: `parsing verbosity in "foo=-1": strconv.ParseUint: parsing "-1": invalid syntax`, - }, - { - arg: fmt.Sprintf("validint32=%d", math.MaxInt32), - expectValue: VModuleConfiguration{ - { - FilePattern: "validint32", - Verbosity: math.MaxInt32, - }, - }, - }, - { - arg: fmt.Sprintf("invalidint32=%d", math.MaxInt32+1), - expectError: `parsing verbosity in "invalidint32=2147483648": strconv.ParseUint: parsing "2147483648": value out of range`, - }, - } - - for _, test := range testcases { - t.Run(test.arg, func(t *testing.T) { - var actual VModuleConfiguration - err := actual.Set(test.arg) - if test.expectError != "" { - if err == nil { - t.Fatal("parsing should have failed") - } - assert.Equal(t, test.expectError, err.Error(), "parse error") - } else { - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - param := actual.String() - expectParam := test.expectParam - if expectParam == "" { - expectParam = test.arg - } - assert.Equal(t, expectParam, param, "encoded parameter value not identical") - } - }) - } -} diff --git a/staging/src/k8s.io/component-base/config/v1alpha1/conversion.go b/staging/src/k8s.io/component-base/config/v1alpha1/conversion.go index abf35c6d910..e2951e310d5 100644 --- a/staging/src/k8s.io/component-base/config/v1alpha1/conversion.go +++ b/staging/src/k8s.io/component-base/config/v1alpha1/conversion.go @@ -51,11 +51,3 @@ func Convert_v1alpha1_LeaderElectionConfiguration_To_config_LeaderElectionConfig func Convert_config_LeaderElectionConfiguration_To_v1alpha1_LeaderElectionConfiguration(in *config.LeaderElectionConfiguration, out *LeaderElectionConfiguration, s conversion.Scope) error { return autoConvert_config_LeaderElectionConfiguration_To_v1alpha1_LeaderElectionConfiguration(in, out, s) } - -func Convert_v1alpha1_LoggingConfiguration_To_config_LoggingConfiguration(in *LoggingConfiguration, out *config.LoggingConfiguration, s conversion.Scope) error { - return autoConvert_v1alpha1_LoggingConfiguration_To_config_LoggingConfiguration(in, out, s) -} - -func Convert_config_LoggingConfiguration_To_v1alpha1_LoggingConfiguration(in *config.LoggingConfiguration, out *LoggingConfiguration, s conversion.Scope) error { - return autoConvert_config_LoggingConfiguration_To_v1alpha1_LoggingConfiguration(in, out, s) -} diff --git a/staging/src/k8s.io/component-base/config/v1alpha1/defaults.go b/staging/src/k8s.io/component-base/config/v1alpha1/defaults.go index 57d066edacc..cd7f820e97e 100644 --- a/staging/src/k8s.io/component-base/config/v1alpha1/defaults.go +++ b/staging/src/k8s.io/component-base/config/v1alpha1/defaults.go @@ -19,7 +19,6 @@ package v1alpha1 import ( "time" - "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" utilpointer "k8s.io/utils/pointer" ) @@ -97,32 +96,3 @@ func NewRecommendedDebuggingConfiguration() *DebuggingConfiguration { RecommendedDebuggingConfiguration(ret) return ret } - -// RecommendedLoggingConfiguration defaults logging configuration. -// This will set the recommended default -// values, but they may be subject to change between API versions. This function -// is intentionally not registered in the scheme as a "normal" `SetDefaults_Foo` -// function to allow consumers of this type to set whatever defaults for their -// embedded configs. Forcing consumers to use these defaults would be problematic -// as defaulting in the scheme is done as part of the conversion, and there would -// be no easy way to opt-out. Instead, if you want to use this defaulting method -// run it in your wrapper struct of this type in its `SetDefaults_` method. -func RecommendedLoggingConfiguration(obj *LoggingConfiguration) { - if obj.Format == "" { - obj.Format = "text" - } - var empty resource.QuantityValue - if obj.Options.JSON.InfoBufferSize == empty { - obj.Options.JSON.InfoBufferSize = resource.QuantityValue{ - // This is similar, but not quite the same as a default - // constructed instance. - Quantity: *resource.NewQuantity(0, resource.DecimalSI), - } - // This sets the unexported Quantity.s which will be compared - // by reflect.DeepEqual in some tests. - _ = obj.Options.JSON.InfoBufferSize.String() - } - if obj.FlushFrequency == 0 { - obj.FlushFrequency = 5 * time.Second - } -} diff --git a/staging/src/k8s.io/component-base/config/v1alpha1/types.go b/staging/src/k8s.io/component-base/config/v1alpha1/types.go index 71a2e9e8875..c9d05525d43 100644 --- a/staging/src/k8s.io/component-base/config/v1alpha1/types.go +++ b/staging/src/k8s.io/component-base/config/v1alpha1/types.go @@ -17,9 +17,6 @@ limitations under the License. package v1alpha1 import ( - "time" - - "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -83,61 +80,3 @@ type ClientConnectionConfiguration struct { // burst allows extra queries to accumulate when a client is exceeding its rate. Burst int32 `json:"burst"` } - -// LoggingConfiguration contains logging options -// Refer [Logs Options](https://github.com/kubernetes/component-base/blob/master/logs/options.go) for more information. -type LoggingConfiguration struct { - // Format Flag specifies the structure of log messages. - // default value of format is `text` - Format string `json:"format,omitempty"` - // Maximum number of nanoseconds (i.e. 1s = 1000000000) between log - // flushes. Ignored if the selected logging backend writes log - // messages without buffering. - FlushFrequency time.Duration `json:"flushFrequency"` - // Verbosity is the threshold that determines which log messages are - // logged. Default is zero which logs only the most important - // messages. Higher values enable additional messages. Error messages - // are always logged. - Verbosity uint32 `json:"verbosity"` - // VModule overrides the verbosity threshold for individual files. - // Only supported for "text" log format. - VModule VModuleConfiguration `json:"vmodule,omitempty"` - // [Experimental] Options holds additional parameters that are specific - // to the different logging formats. Only the options for the selected - // format get used, but all of them get validated. - Options FormatOptions `json:"options,omitempty"` -} - -// FormatOptions contains options for the different logging formats. -type FormatOptions struct { - // [Experimental] JSON contains options for logging format "json". - JSON JSONOptions `json:"json,omitempty"` -} - -// JSONOptions contains options for logging format "json". -type JSONOptions struct { - // [Experimental] SplitStream redirects error messages to stderr while - // info messages go to stdout, with buffering. The default is to write - // both to stdout, without buffering. - SplitStream bool `json:"splitStream,omitempty"` - // [Experimental] InfoBufferSize sets the size of the info stream when - // using split streams. The default is zero, which disables buffering. - InfoBufferSize resource.QuantityValue `json:"infoBufferSize,omitempty"` -} - -// VModuleConfiguration is a collection of individual file names or patterns -// and the corresponding verbosity threshold. -type VModuleConfiguration []VModuleItem - -// VModuleItem defines verbosity for one or more files which match a certain -// glob pattern. -type VModuleItem struct { - // FilePattern is a base file name (i.e. minus the ".go" suffix and - // directory) or a "glob" pattern for such a name. It must not contain - // comma and equal signs because those are separators for the - // corresponding klog command line argument. - FilePattern string `json:"filePattern"` - // Verbosity is the threshold for log messages emitted inside files - // that match the pattern. - Verbosity uint32 `json:"verbosity"` -} diff --git a/staging/src/k8s.io/component-base/config/v1alpha1/zz_generated.conversion.go b/staging/src/k8s.io/component-base/config/v1alpha1/zz_generated.conversion.go index 4162e095dc9..a911bb50d8a 100644 --- a/staging/src/k8s.io/component-base/config/v1alpha1/zz_generated.conversion.go +++ b/staging/src/k8s.io/component-base/config/v1alpha1/zz_generated.conversion.go @@ -22,9 +22,6 @@ limitations under the License. package v1alpha1 import ( - time "time" - unsafe "unsafe" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" conversion "k8s.io/apimachinery/pkg/conversion" runtime "k8s.io/apimachinery/pkg/runtime" @@ -38,36 +35,6 @@ func init() { // RegisterConversions adds conversion functions to the given scheme. // Public to allow building arbitrary schemes. func RegisterConversions(s *runtime.Scheme) error { - if err := s.AddGeneratedConversionFunc((*FormatOptions)(nil), (*config.FormatOptions)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1alpha1_FormatOptions_To_config_FormatOptions(a.(*FormatOptions), b.(*config.FormatOptions), scope) - }); err != nil { - return err - } - if err := s.AddGeneratedConversionFunc((*config.FormatOptions)(nil), (*FormatOptions)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_config_FormatOptions_To_v1alpha1_FormatOptions(a.(*config.FormatOptions), b.(*FormatOptions), scope) - }); err != nil { - return err - } - if err := s.AddGeneratedConversionFunc((*JSONOptions)(nil), (*config.JSONOptions)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1alpha1_JSONOptions_To_config_JSONOptions(a.(*JSONOptions), b.(*config.JSONOptions), scope) - }); err != nil { - return err - } - if err := s.AddGeneratedConversionFunc((*config.JSONOptions)(nil), (*JSONOptions)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_config_JSONOptions_To_v1alpha1_JSONOptions(a.(*config.JSONOptions), b.(*JSONOptions), scope) - }); err != nil { - return err - } - if err := s.AddGeneratedConversionFunc((*VModuleItem)(nil), (*config.VModuleItem)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1alpha1_VModuleItem_To_config_VModuleItem(a.(*VModuleItem), b.(*config.VModuleItem), scope) - }); err != nil { - return err - } - if err := s.AddGeneratedConversionFunc((*config.VModuleItem)(nil), (*VModuleItem)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_config_VModuleItem_To_v1alpha1_VModuleItem(a.(*config.VModuleItem), b.(*VModuleItem), scope) - }); err != nil { - return err - } if err := s.AddConversionFunc((*config.ClientConnectionConfiguration)(nil), (*ClientConnectionConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_config_ClientConnectionConfiguration_To_v1alpha1_ClientConnectionConfiguration(a.(*config.ClientConnectionConfiguration), b.(*ClientConnectionConfiguration), scope) }); err != nil { @@ -83,11 +50,6 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddConversionFunc((*config.LoggingConfiguration)(nil), (*LoggingConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_config_LoggingConfiguration_To_v1alpha1_LoggingConfiguration(a.(*config.LoggingConfiguration), b.(*LoggingConfiguration), scope) - }); err != nil { - return err - } if err := s.AddConversionFunc((*ClientConnectionConfiguration)(nil), (*config.ClientConnectionConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1alpha1_ClientConnectionConfiguration_To_config_ClientConnectionConfiguration(a.(*ClientConnectionConfiguration), b.(*config.ClientConnectionConfiguration), scope) }); err != nil { @@ -103,11 +65,6 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddConversionFunc((*LoggingConfiguration)(nil), (*config.LoggingConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1alpha1_LoggingConfiguration_To_config_LoggingConfiguration(a.(*LoggingConfiguration), b.(*config.LoggingConfiguration), scope) - }); err != nil { - return err - } return nil } @@ -149,52 +106,6 @@ func autoConvert_config_DebuggingConfiguration_To_v1alpha1_DebuggingConfiguratio return nil } -func autoConvert_v1alpha1_FormatOptions_To_config_FormatOptions(in *FormatOptions, out *config.FormatOptions, s conversion.Scope) error { - if err := Convert_v1alpha1_JSONOptions_To_config_JSONOptions(&in.JSON, &out.JSON, s); err != nil { - return err - } - return nil -} - -// Convert_v1alpha1_FormatOptions_To_config_FormatOptions is an autogenerated conversion function. -func Convert_v1alpha1_FormatOptions_To_config_FormatOptions(in *FormatOptions, out *config.FormatOptions, s conversion.Scope) error { - return autoConvert_v1alpha1_FormatOptions_To_config_FormatOptions(in, out, s) -} - -func autoConvert_config_FormatOptions_To_v1alpha1_FormatOptions(in *config.FormatOptions, out *FormatOptions, s conversion.Scope) error { - if err := Convert_config_JSONOptions_To_v1alpha1_JSONOptions(&in.JSON, &out.JSON, s); err != nil { - return err - } - return nil -} - -// Convert_config_FormatOptions_To_v1alpha1_FormatOptions is an autogenerated conversion function. -func Convert_config_FormatOptions_To_v1alpha1_FormatOptions(in *config.FormatOptions, out *FormatOptions, s conversion.Scope) error { - return autoConvert_config_FormatOptions_To_v1alpha1_FormatOptions(in, out, s) -} - -func autoConvert_v1alpha1_JSONOptions_To_config_JSONOptions(in *JSONOptions, out *config.JSONOptions, s conversion.Scope) error { - out.SplitStream = in.SplitStream - out.InfoBufferSize = in.InfoBufferSize - return nil -} - -// Convert_v1alpha1_JSONOptions_To_config_JSONOptions is an autogenerated conversion function. -func Convert_v1alpha1_JSONOptions_To_config_JSONOptions(in *JSONOptions, out *config.JSONOptions, s conversion.Scope) error { - return autoConvert_v1alpha1_JSONOptions_To_config_JSONOptions(in, out, s) -} - -func autoConvert_config_JSONOptions_To_v1alpha1_JSONOptions(in *config.JSONOptions, out *JSONOptions, s conversion.Scope) error { - out.SplitStream = in.SplitStream - out.InfoBufferSize = in.InfoBufferSize - return nil -} - -// Convert_config_JSONOptions_To_v1alpha1_JSONOptions is an autogenerated conversion function. -func Convert_config_JSONOptions_To_v1alpha1_JSONOptions(in *config.JSONOptions, out *JSONOptions, s conversion.Scope) error { - return autoConvert_config_JSONOptions_To_v1alpha1_JSONOptions(in, out, s) -} - func autoConvert_v1alpha1_LeaderElectionConfiguration_To_config_LeaderElectionConfiguration(in *LeaderElectionConfiguration, out *config.LeaderElectionConfiguration, s conversion.Scope) error { if err := v1.Convert_Pointer_bool_To_bool(&in.LeaderElect, &out.LeaderElect, s); err != nil { return err @@ -220,47 +131,3 @@ func autoConvert_config_LeaderElectionConfiguration_To_v1alpha1_LeaderElectionCo out.ResourceNamespace = in.ResourceNamespace return nil } - -func autoConvert_v1alpha1_LoggingConfiguration_To_config_LoggingConfiguration(in *LoggingConfiguration, out *config.LoggingConfiguration, s conversion.Scope) error { - out.Format = in.Format - out.FlushFrequency = time.Duration(in.FlushFrequency) - out.Verbosity = config.VerbosityLevel(in.Verbosity) - out.VModule = *(*config.VModuleConfiguration)(unsafe.Pointer(&in.VModule)) - if err := Convert_v1alpha1_FormatOptions_To_config_FormatOptions(&in.Options, &out.Options, s); err != nil { - return err - } - return nil -} - -func autoConvert_config_LoggingConfiguration_To_v1alpha1_LoggingConfiguration(in *config.LoggingConfiguration, out *LoggingConfiguration, s conversion.Scope) error { - out.Format = in.Format - out.FlushFrequency = time.Duration(in.FlushFrequency) - out.Verbosity = uint32(in.Verbosity) - out.VModule = *(*VModuleConfiguration)(unsafe.Pointer(&in.VModule)) - if err := Convert_config_FormatOptions_To_v1alpha1_FormatOptions(&in.Options, &out.Options, s); err != nil { - return err - } - return nil -} - -func autoConvert_v1alpha1_VModuleItem_To_config_VModuleItem(in *VModuleItem, out *config.VModuleItem, s conversion.Scope) error { - out.FilePattern = in.FilePattern - out.Verbosity = config.VerbosityLevel(in.Verbosity) - return nil -} - -// Convert_v1alpha1_VModuleItem_To_config_VModuleItem is an autogenerated conversion function. -func Convert_v1alpha1_VModuleItem_To_config_VModuleItem(in *VModuleItem, out *config.VModuleItem, s conversion.Scope) error { - return autoConvert_v1alpha1_VModuleItem_To_config_VModuleItem(in, out, s) -} - -func autoConvert_config_VModuleItem_To_v1alpha1_VModuleItem(in *config.VModuleItem, out *VModuleItem, s conversion.Scope) error { - out.FilePattern = in.FilePattern - out.Verbosity = uint32(in.Verbosity) - return nil -} - -// Convert_config_VModuleItem_To_v1alpha1_VModuleItem is an autogenerated conversion function. -func Convert_config_VModuleItem_To_v1alpha1_VModuleItem(in *config.VModuleItem, out *VModuleItem, s conversion.Scope) error { - return autoConvert_config_VModuleItem_To_v1alpha1_VModuleItem(in, out, s) -} diff --git a/staging/src/k8s.io/component-base/config/v1alpha1/zz_generated.deepcopy.go b/staging/src/k8s.io/component-base/config/v1alpha1/zz_generated.deepcopy.go index 5503e7a869d..92176d9944c 100644 --- a/staging/src/k8s.io/component-base/config/v1alpha1/zz_generated.deepcopy.go +++ b/staging/src/k8s.io/component-base/config/v1alpha1/zz_generated.deepcopy.go @@ -63,40 +63,6 @@ func (in *DebuggingConfiguration) DeepCopy() *DebuggingConfiguration { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *FormatOptions) DeepCopyInto(out *FormatOptions) { - *out = *in - in.JSON.DeepCopyInto(&out.JSON) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FormatOptions. -func (in *FormatOptions) DeepCopy() *FormatOptions { - if in == nil { - return nil - } - out := new(FormatOptions) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *JSONOptions) DeepCopyInto(out *JSONOptions) { - *out = *in - in.InfoBufferSize.DeepCopyInto(&out.InfoBufferSize) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JSONOptions. -func (in *JSONOptions) DeepCopy() *JSONOptions { - if in == nil { - return nil - } - out := new(JSONOptions) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *LeaderElectionConfiguration) DeepCopyInto(out *LeaderElectionConfiguration) { *out = *in @@ -120,61 +86,3 @@ func (in *LeaderElectionConfiguration) DeepCopy() *LeaderElectionConfiguration { in.DeepCopyInto(out) return out } - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *LoggingConfiguration) DeepCopyInto(out *LoggingConfiguration) { - *out = *in - if in.VModule != nil { - in, out := &in.VModule, &out.VModule - *out = make(VModuleConfiguration, len(*in)) - copy(*out, *in) - } - in.Options.DeepCopyInto(&out.Options) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LoggingConfiguration. -func (in *LoggingConfiguration) DeepCopy() *LoggingConfiguration { - if in == nil { - return nil - } - out := new(LoggingConfiguration) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in VModuleConfiguration) DeepCopyInto(out *VModuleConfiguration) { - { - in := &in - *out = make(VModuleConfiguration, len(*in)) - copy(*out, *in) - return - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VModuleConfiguration. -func (in VModuleConfiguration) DeepCopy() VModuleConfiguration { - if in == nil { - return nil - } - out := new(VModuleConfiguration) - in.DeepCopyInto(out) - return *out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *VModuleItem) DeepCopyInto(out *VModuleItem) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VModuleItem. -func (in *VModuleItem) DeepCopy() *VModuleItem { - if in == nil { - return nil - } - out := new(VModuleItem) - in.DeepCopyInto(out) - return out -} diff --git a/staging/src/k8s.io/component-base/config/zz_generated.deepcopy.go b/staging/src/k8s.io/component-base/config/zz_generated.deepcopy.go index ede25cd32f3..fb0c1f1e6a8 100644 --- a/staging/src/k8s.io/component-base/config/zz_generated.deepcopy.go +++ b/staging/src/k8s.io/component-base/config/zz_generated.deepcopy.go @@ -53,40 +53,6 @@ func (in *DebuggingConfiguration) DeepCopy() *DebuggingConfiguration { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *FormatOptions) DeepCopyInto(out *FormatOptions) { - *out = *in - in.JSON.DeepCopyInto(&out.JSON) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FormatOptions. -func (in *FormatOptions) DeepCopy() *FormatOptions { - if in == nil { - return nil - } - out := new(FormatOptions) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *JSONOptions) DeepCopyInto(out *JSONOptions) { - *out = *in - in.InfoBufferSize.DeepCopyInto(&out.InfoBufferSize) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JSONOptions. -func (in *JSONOptions) DeepCopy() *JSONOptions { - if in == nil { - return nil - } - out := new(JSONOptions) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *LeaderElectionConfiguration) DeepCopyInto(out *LeaderElectionConfiguration) { *out = *in @@ -105,61 +71,3 @@ func (in *LeaderElectionConfiguration) DeepCopy() *LeaderElectionConfiguration { in.DeepCopyInto(out) return out } - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *LoggingConfiguration) DeepCopyInto(out *LoggingConfiguration) { - *out = *in - if in.VModule != nil { - in, out := &in.VModule, &out.VModule - *out = make(VModuleConfiguration, len(*in)) - copy(*out, *in) - } - in.Options.DeepCopyInto(&out.Options) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LoggingConfiguration. -func (in *LoggingConfiguration) DeepCopy() *LoggingConfiguration { - if in == nil { - return nil - } - out := new(LoggingConfiguration) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in VModuleConfiguration) DeepCopyInto(out *VModuleConfiguration) { - { - in := &in - *out = make(VModuleConfiguration, len(*in)) - copy(*out, *in) - return - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VModuleConfiguration. -func (in VModuleConfiguration) DeepCopy() VModuleConfiguration { - if in == nil { - return nil - } - out := new(VModuleConfiguration) - in.DeepCopyInto(out) - return *out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *VModuleItem) DeepCopyInto(out *VModuleItem) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VModuleItem. -func (in *VModuleItem) DeepCopy() *VModuleItem { - if in == nil { - return nil - } - out := new(VModuleItem) - in.DeepCopyInto(out) - return out -} diff --git a/staging/src/k8s.io/component-base/go.mod b/staging/src/k8s.io/component-base/go.mod index 57f319f59d2..f468c8e92fa 100644 --- a/staging/src/k8s.io/component-base/go.mod +++ b/staging/src/k8s.io/component-base/go.mod @@ -28,6 +28,7 @@ require ( k8s.io/client-go v0.0.0 k8s.io/klog/v2 v2.60.1 k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 + sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 ) require ( @@ -81,7 +82,6 @@ require ( gotest.tools/v3 v3.0.3 // indirect k8s.io/api v0.0.0 // indirect k8s.io/kube-openapi v0.0.0-20220603121420-31174f50af60 // indirect - sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect sigs.k8s.io/yaml v1.2.0 // indirect ) diff --git a/staging/src/k8s.io/component-base/logs/api/OWNERS b/staging/src/k8s.io/component-base/logs/api/OWNERS new file mode 100644 index 00000000000..2d5a80acad8 --- /dev/null +++ b/staging/src/k8s.io/component-base/logs/api/OWNERS @@ -0,0 +1,9 @@ +# Disable inheritance as this is an api owners file +options: + no_parent_owners: true +approvers: + - api-approvers +reviewers: + - api-reviewers +labels: + - kind/api-change diff --git a/staging/src/k8s.io/component-base/logs/api/v1/doc.go b/staging/src/k8s.io/component-base/logs/api/v1/doc.go new file mode 100644 index 00000000000..735aecb0c2e --- /dev/null +++ b/staging/src/k8s.io/component-base/logs/api/v1/doc.go @@ -0,0 +1,32 @@ +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// +k8s:deepcopy-gen=package + +// Package v1 contains the configuration API for logging. +// +// The intention is to only have a single version of this API, potentially with +// new fields added over time in a backwards-compatible manner. Fields for +// alpha or beta features are allowed as long as they are defined so that not +// changing the defaults leaves those features disabled. +// +// The "v1" package name is just a reminder that API compatibility rules apply, +// not an indication of the stability of all features covered by it. + +// The LoggingAlphaOptions and LoggingBetaOptions feature gates control whether +// these unstable features can get enabled. This can be used to ensure that +// command invocations do not accidentally rely on unstable features. +package v1 // import "k8s.io/component-base/logs/api/v1" diff --git a/staging/src/k8s.io/component-base/logs/api/v1/kube_features.go b/staging/src/k8s.io/component-base/logs/api/v1/kube_features.go index b8ddf09df3f..e9feaff9cd6 100644 --- a/staging/src/k8s.io/component-base/logs/api/v1/kube_features.go +++ b/staging/src/k8s.io/component-base/logs/api/v1/kube_features.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package logs +package v1 import ( "k8s.io/component-base/featuregate" diff --git a/staging/src/k8s.io/component-base/logs/api/v1/options.go b/staging/src/k8s.io/component-base/logs/api/v1/options.go index 91e602992f6..02b9cc4bc04 100644 --- a/staging/src/k8s.io/component-base/logs/api/v1/options.go +++ b/staging/src/k8s.io/component-base/logs/api/v1/options.go @@ -1,5 +1,5 @@ /* -Copyright 2020 The Kubernetes Authors. +Copyright 2021 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,33 +14,44 @@ See the License for the specific language governing permissions and limitations under the License. */ -package logs +package v1 import ( + "flag" "fmt" + "math" + "sort" + "strings" + "time" "github.com/spf13/pflag" - utilerrors "k8s.io/apimachinery/pkg/util/errors" - "k8s.io/component-base/config" - "k8s.io/component-base/config/v1alpha1" - "k8s.io/component-base/featuregate" - "k8s.io/component-base/logs/registry" "k8s.io/klog/v2" + + "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/apimachinery/pkg/util/validation/field" + cliflag "k8s.io/component-base/cli/flag" + "k8s.io/component-base/featuregate" ) -// Options has klog format parameters -type Options struct { - Config config.LoggingConfiguration -} +const ( + // LogFlushFreqDefault is the default for the corresponding command line + // parameter. + LogFlushFreqDefault = 5 * time.Second +) -// NewOptions return new klog options -func NewOptions() *Options { - c := v1alpha1.LoggingConfiguration{} - v1alpha1.RecommendedLoggingConfiguration(&c) - o := &Options{} - v1alpha1.Convert_v1alpha1_LoggingConfiguration_To_config_LoggingConfiguration(&c, &o.Config, nil) - return o +const ( + // LogFlushFreqFlagName is the name of the command line parameter. + // Depending on how flags get added, it is either a stand-alone + // value (logs.AddFlags) or part of LoggingConfiguration. + LogFlushFreqFlagName = "log-flush-frequency" +) + +// NewLoggingConfiguration returns a struct holding the default logging configuration. +func NewLoggingConfiguration() *LoggingConfiguration { + c := LoggingConfiguration{} + c.SetRecommendedLoggingConfiguration() + return &c } // ValidateAndApply combines validation and application of the logging configuration. @@ -50,58 +61,186 @@ func NewOptions() *Options { // // The optional FeatureGate controls logging features. If nil, the default for // these features is used. -func (o *Options) ValidateAndApply(featureGate featuregate.FeatureGate) error { - errs := o.validate() +func (c *LoggingConfiguration) ValidateAndApply(featureGate featuregate.FeatureGate) error { + return c.ValidateAndApplyAsField(featureGate, nil) +} + +// ValidateAndApplyAsField is a variant of ValidateAndApply that should be used +// when the LoggingConfiguration is embedded in some larger configuration +// structure. +func (c *LoggingConfiguration) ValidateAndApplyAsField(featureGate featuregate.FeatureGate, fldPath *field.Path) error { + errs := c.Validate(featureGate, fldPath) if len(errs) > 0 { - return utilerrors.NewAggregate(errs) + return errs.ToAggregate() } - o.apply(featureGate) - return nil + return c.apply(featureGate) } -// validate verifies if any unsupported flag is set -// for non-default logging format -func (o *Options) validate() []error { - errs := ValidateLoggingConfiguration(&o.Config, nil) - if len(errs) != 0 { - return errs.ToAggregate().Errors() +// Validate can be used to check for invalid settings without applying them. +// Most binaries should validate and apply the logging configuration as soon +// as possible via ValidateAndApply. The field path is optional: nil +// can be passed when the struct is not embedded in some larger struct. +func (c *LoggingConfiguration) Validate(featureGate featuregate.FeatureGate, fldPath *field.Path) field.ErrorList { + errs := field.ErrorList{} + if c.Format != DefaultLogFormat { + // WordSepNormalizeFunc is just a guess. Commands should use it, + // but we cannot know for sure. + allFlags := unsupportedLoggingFlags(cliflag.WordSepNormalizeFunc) + for _, f := range allFlags { + if f.DefValue != f.Value.String() { + errs = append(errs, field.Invalid(fldPath.Child("format"), c.Format, fmt.Sprintf("Non-default format doesn't honor flag: %s", f.Name))) + } + } } - return nil + if _, err := logRegistry.get(c.Format); err != nil { + errs = append(errs, field.Invalid(fldPath.Child("format"), c.Format, "Unsupported log format")) + } + + // The type in our struct is uint32, but klog only accepts positive int32. + if c.Verbosity > math.MaxInt32 { + errs = append(errs, field.Invalid(fldPath.Child("verbosity"), c.Verbosity, fmt.Sprintf("Must be <= %d", math.MaxInt32))) + } + vmoduleFldPath := fldPath.Child("vmodule") + if len(c.VModule) > 0 && c.Format != "" && c.Format != "text" { + errs = append(errs, field.Forbidden(vmoduleFldPath, "Only supported for text log format")) + } + for i, item := range c.VModule { + if item.FilePattern == "" { + errs = append(errs, field.Required(vmoduleFldPath.Index(i), "File pattern must not be empty")) + } + if strings.ContainsAny(item.FilePattern, "=,") { + errs = append(errs, field.Invalid(vmoduleFldPath.Index(i), item.FilePattern, "File pattern must not contain equal sign or comma")) + } + if item.Verbosity > math.MaxInt32 { + errs = append(errs, field.Invalid(vmoduleFldPath.Index(i), item.Verbosity, fmt.Sprintf("Must be <= %d", math.MaxInt32))) + } + } + + // Currently nothing to validate for c.Options. + return errs } -// AddFlags add logging-format flag. -// -// Programs using LoggingConfiguration must use SkipLoggingConfigurationFlags -// when calling AddFlags to avoid the duplicate registration of flags. -func (o *Options) AddFlags(fs *pflag.FlagSet) { - BindLoggingFlags(&o.Config, fs) -} - -// apply set klog logger from LogFormat type -func (o *Options) apply(featureGate featuregate.FeatureGate) { +func (c *LoggingConfiguration) apply(featureGate featuregate.FeatureGate) error { contextualLoggingEnabled := contextualLoggingDefault if featureGate != nil { contextualLoggingEnabled = featureGate.Enabled(ContextualLogging) } // if log format not exists, use nil loggr - factory, _ := registry.LogRegistry.Get(o.Config.Format) + factory, _ := logRegistry.get(c.Format) if factory == nil { klog.ClearLogger() } else { - // This logger will do its own verbosity checking, using the exact same - // configuration as klog itself. - log, flush := factory.Create(o.Config) - // Therefore it can get called directly. However, we only allow that - // when the feature is enabled. + log, flush := factory.Create(*c) klog.SetLoggerWithOptions(log, klog.ContextualLogger(contextualLoggingEnabled), klog.FlushLogger(flush)) } - if err := loggingFlags.Lookup("v").Value.Set(o.Config.Verbosity.String()); err != nil { - panic(fmt.Errorf("internal error while setting klog verbosity: %v", err)) + if err := loggingFlags.Lookup("v").Value.Set(VerbosityLevelPflag(&c.Verbosity).String()); err != nil { + return fmt.Errorf("internal error while setting klog verbosity: %v", err) } - if err := loggingFlags.Lookup("vmodule").Value.Set(o.Config.VModule.String()); err != nil { - panic(fmt.Errorf("internal error while setting klog vmodule: %v", err)) + if err := loggingFlags.Lookup("vmodule").Value.Set(VModuleConfigurationPflag(&c.VModule).String()); err != nil { + return fmt.Errorf("internal error while setting klog vmodule: %v", err) } - klog.StartFlushDaemon(o.Config.FlushFrequency) + klog.StartFlushDaemon(c.FlushFrequency) klog.EnableContextualLogging(contextualLoggingEnabled) + return nil +} + +// AddFlags adds command line flags for the configuration. +func (c *LoggingConfiguration) AddFlags(fs *pflag.FlagSet) { + // The help text is generated assuming that flags will eventually use + // hyphens, even if currently no normalization function is set for the + // flag set yet. + unsupportedFlags := strings.Join(unsupportedLoggingFlagNames(cliflag.WordSepNormalizeFunc), ", ") + formats := fmt.Sprintf(`"%s"`, strings.Join(logRegistry.list(), `", "`)) + fs.StringVar(&c.Format, "logging-format", c.Format, fmt.Sprintf("Sets the log format. Permitted formats: %s.\nNon-default formats don't honor these flags: %s.\nNon-default choices are currently alpha and subject to change without warning.", formats, unsupportedFlags)) + // No new log formats should be added after generation is of flag options + logRegistry.freeze() + + fs.DurationVar(&c.FlushFrequency, LogFlushFreqFlagName, c.FlushFrequency, "Maximum number of seconds between log flushes") + fs.VarP(VerbosityLevelPflag(&c.Verbosity), "v", "v", "number for the log level verbosity") + fs.Var(VModuleConfigurationPflag(&c.VModule), "vmodule", "comma-separated list of pattern=N settings for file-filtered logging (only works for text log format)") + + // JSON options. We only register them if "json" is a valid format. The + // config file API however always has them. + if _, err := logRegistry.get("json"); err == nil { + fs.BoolVar(&c.Options.JSON.SplitStream, "log-json-split-stream", false, "[Experimental] In JSON format, write error messages to stderr and info messages to stdout. The default is to write a single stream to stdout.") + fs.Var(&c.Options.JSON.InfoBufferSize, "log-json-info-buffer-size", "[Experimental] In JSON format with split output streams, the info messages can be buffered for a while to increase performance. The default value of zero bytes disables buffering. The size can be specified as number of bytes (512), multiples of 1000 (1K), multiples of 1024 (2Ki), or powers of those (3M, 4G, 5Mi, 6Gi).") + } +} + +// SetRecommendedLoggingConfiguration sets the default logging configuration +// for fields that are unset. +// +// Consumers who embed LoggingConfiguration in their own configuration structs +// may set custom defaults and then should call this function to add the +// global defaults. +func (c *LoggingConfiguration) SetRecommendedLoggingConfiguration() { + if c.Format == "" { + c.Format = "text" + } + if c.FlushFrequency == 0 { + c.FlushFrequency = LogFlushFreqDefault + } + var empty resource.QuantityValue + if c.Options.JSON.InfoBufferSize == empty { + c.Options.JSON.InfoBufferSize = resource.QuantityValue{ + // This is similar, but not quite the same as a default + // constructed instance. + Quantity: *resource.NewQuantity(0, resource.DecimalSI), + } + // This sets the unexported Quantity.s which will be compared + // by reflect.DeepEqual in some tests. + _ = c.Options.JSON.InfoBufferSize.String() + } +} + +// loggingFlags captures the state of the logging flags, in particular their default value +// before flag parsing. It is used by unsupportedLoggingFlags. +var loggingFlags pflag.FlagSet + +func init() { + var fs flag.FlagSet + klog.InitFlags(&fs) + loggingFlags.AddGoFlagSet(&fs) +} + +// List of logs (k8s.io/klog + k8s.io/component-base/logs) flags supported by all logging formats +var supportedLogsFlags = map[string]struct{}{ + "v": {}, + // TODO: support vmodule after 1.19 Alpha +} + +// unsupportedLoggingFlags lists unsupported logging flags. The normalize +// function is optional. +func unsupportedLoggingFlags(normalizeFunc func(f *pflag.FlagSet, name string) pflag.NormalizedName) []*pflag.Flag { + // k8s.io/component-base/logs and klog flags + pfs := &pflag.FlagSet{} + loggingFlags.VisitAll(func(flag *pflag.Flag) { + if _, found := supportedLogsFlags[flag.Name]; !found { + // Normalization changes flag.Name, so make a copy. + clone := *flag + pfs.AddFlag(&clone) + } + }) + + // Apply normalization. + pfs.SetNormalizeFunc(normalizeFunc) + + var allFlags []*pflag.Flag + pfs.VisitAll(func(flag *pflag.Flag) { + allFlags = append(allFlags, flag) + }) + return allFlags +} + +// unsupportedLoggingFlagNames lists unsupported logging flags by name, with +// optional normalization and sorted. +func unsupportedLoggingFlagNames(normalizeFunc func(f *pflag.FlagSet, name string) pflag.NormalizedName) []string { + unsupportedFlags := unsupportedLoggingFlags(normalizeFunc) + names := make([]string, 0, len(unsupportedFlags)) + for _, f := range unsupportedFlags { + names = append(names, "--"+f.Name) + } + sort.Strings(names) + return names } diff --git a/staging/src/k8s.io/component-base/logs/options_test.go b/staging/src/k8s.io/component-base/logs/api/v1/options_test.go similarity index 87% rename from staging/src/k8s.io/component-base/logs/options_test.go rename to staging/src/k8s.io/component-base/logs/api/v1/options_test.go index 1671bb87a00..2c2bbf15e2a 100644 --- a/staging/src/k8s.io/component-base/logs/options_test.go +++ b/staging/src/k8s.io/component-base/logs/api/v1/options_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package logs +package v1 import ( "bytes" @@ -32,10 +32,10 @@ import ( ) func TestFlags(t *testing.T) { - o := NewOptions() + c := NewLoggingConfiguration() fs := pflag.NewFlagSet("addflagstest", pflag.ContinueOnError) output := bytes.Buffer{} - o.AddFlags(fs) + c.AddFlags(fs) fs.SetOutput(&output) fs.PrintDefaults() want := ` --log-flush-frequency duration Maximum number of seconds between log flushes (default 5s) @@ -51,29 +51,30 @@ func TestFlags(t *testing.T) { } func TestOptions(t *testing.T) { - newOptions := NewOptions() + newOptions := NewLoggingConfiguration() testcases := []struct { name string args []string - want *Options + path *field.Path + want *LoggingConfiguration errs field.ErrorList }{ { name: "Default log format", - want: newOptions, + want: newOptions.DeepCopy(), }, { name: "Text log format", args: []string{"--logging-format=text"}, - want: newOptions, + want: newOptions.DeepCopy(), }, { name: "Unsupported log format", args: []string{"--logging-format=test"}, - want: func() *Options { - c := newOptions.Config.DeepCopy() + want: func() *LoggingConfiguration { + c := newOptions.DeepCopy() c.Format = "test" - return &Options{*c} + return c }(), errs: field.ErrorList{&field.Error{ Type: "FieldValueInvalid", @@ -86,19 +87,17 @@ func TestOptions(t *testing.T) { for _, tc := range testcases { t.Run(tc.name, func(t *testing.T) { - o := NewOptions() + c := NewLoggingConfiguration() fs := pflag.NewFlagSet("addflagstest", pflag.ContinueOnError) - o.AddFlags(fs) + c.AddFlags(fs) fs.Parse(tc.args) - if !assert.Equal(t, tc.want, o) { - t.Errorf("Wrong Validate() result for %q. expect %v, got %v", tc.name, tc.want, o) + if !assert.Equal(t, tc.want, c) { + t.Errorf("Wrong Validate() result for %q. expect %v, got %v", tc.name, tc.want, c) } - err := o.ValidateAndApply(nil /* We don't care about feature gates here. */) + errs := c.ValidateAndApply(nil /* We don't care about feature gates here. */) defer klog.StopFlushDaemon() - - if !assert.ElementsMatch(t, tc.errs.ToAggregate(), err) { - t.Errorf("Wrong Validate() result for %q.\n expect:\t%+v\n got:\t%+v", tc.name, tc.errs, err) - + if !assert.ElementsMatch(t, tc.errs, errs) { + t.Errorf("Wrong Validate() result for %q.\n expect:\t%+v\n got:\t%+v", tc.name, tc.errs, errs) } }) } @@ -117,12 +116,12 @@ func TestContextualLogging(t *testing.T) { func testContextualLogging(t *testing.T, enabled bool) { var err error - o := NewOptions() + c := NewLoggingConfiguration() featureGate := featuregate.NewFeatureGate() AddFeatureGates(featureGate) err = featureGate.SetFromMap(map[string]bool{string(ContextualLogging): enabled}) require.NoError(t, err) - err = o.ValidateAndApply(featureGate) + err = c.ValidateAndApply(featureGate) require.NoError(t, err) defer klog.StopFlushDaemon() defer klog.EnableContextualLogging(true) diff --git a/staging/src/k8s.io/component-base/logs/api/v1/pflags.go b/staging/src/k8s.io/component-base/logs/api/v1/pflags.go new file mode 100644 index 00000000000..36a98cc81cf --- /dev/null +++ b/staging/src/k8s.io/component-base/logs/api/v1/pflags.go @@ -0,0 +1,104 @@ +/* +Copyright 2021 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1 + +import ( + "fmt" + "strconv" + "strings" + + "github.com/spf13/pflag" +) + +// VModuleConfigurationPflag implements the pflag.Value interface for a +// VModuleConfiguration. The value pointer must not be nil. +func VModuleConfigurationPflag(value *VModuleConfiguration) pflag.Value { + return vmoduleConfigurationPFlag{value} +} + +type vmoduleConfigurationPFlag struct { + value *VModuleConfiguration +} + +// String returns the -vmodule parameter (comma-separated list of pattern=N). +func (wrapper vmoduleConfigurationPFlag) String() string { + var patterns []string + for _, item := range *wrapper.value { + patterns = append(patterns, fmt.Sprintf("%s=%d", item.FilePattern, item.Verbosity)) + } + return strings.Join(patterns, ",") +} + +// Set parses the -vmodule parameter (comma-separated list of pattern=N). +func (wrapper vmoduleConfigurationPFlag) Set(value string) error { + // This code mirrors https://github.com/kubernetes/klog/blob/9ad246211af1ed84621ee94a26fcce0038b69cd1/klog.go#L287-L313 + + for _, pat := range strings.Split(value, ",") { + if len(pat) == 0 { + // Empty strings such as from a trailing comma can be ignored. + continue + } + patLev := strings.Split(pat, "=") + if len(patLev) != 2 || len(patLev[0]) == 0 || len(patLev[1]) == 0 { + return fmt.Errorf("%q does not have the pattern=N format", pat) + } + pattern := patLev[0] + // 31 instead of 32 to ensure that it also fits into int32. + v, err := strconv.ParseUint(patLev[1], 10, 31) + if err != nil { + return fmt.Errorf("parsing verbosity in %q: %v", pat, err) + } + *wrapper.value = append(*wrapper.value, VModuleItem{FilePattern: pattern, Verbosity: VerbosityLevel(v)}) + } + return nil +} + +func (wrapper vmoduleConfigurationPFlag) Type() string { + return "pattern=N,..." +} + +// VerbosityLevelPflag implements the pflag.Value interface for a verbosity +// level value. +func VerbosityLevelPflag(value *VerbosityLevel) pflag.Value { + return verbosityLevelPflag{value} +} + +type verbosityLevelPflag struct { + value *VerbosityLevel +} + +func (wrapper verbosityLevelPflag) String() string { + return strconv.FormatInt(int64(*wrapper.value), 10) +} + +func (wrapper verbosityLevelPflag) Get() interface{} { + return *wrapper.value +} + +func (wrapper verbosityLevelPflag) Set(value string) error { + // Limited to int32 for compatibility with klog. + v, err := strconv.ParseUint(value, 10, 31) + if err != nil { + return err + } + *wrapper.value = VerbosityLevel(v) + return nil +} + +func (wrapper verbosityLevelPflag) Type() string { + return "Level" +} diff --git a/staging/src/k8s.io/component-base/logs/api/v1/registry.go b/staging/src/k8s.io/component-base/logs/api/v1/registry.go index be2c3260c9e..14995035092 100644 --- a/staging/src/k8s.io/component-base/logs/api/v1/registry.go +++ b/staging/src/k8s.io/component-base/logs/api/v1/registry.go @@ -14,22 +14,19 @@ See the License for the specific language governing permissions and limitations under the License. */ -package registry +package v1 import ( "fmt" "sort" "github.com/go-logr/logr" - - "k8s.io/component-base/config" ) -// LogRegistry is new init LogFormatRegistry struct -var LogRegistry = NewLogFormatRegistry() +var logRegistry = newLogFormatRegistry() -// LogFormatRegistry store klog format registry -type LogFormatRegistry struct { +// logFormatRegistry stores factories for all supported logging formats. +type logFormatRegistry struct { registry map[string]LogFormatFactory frozen bool } @@ -41,22 +38,28 @@ type LogFormatFactory interface { // Returning a flush function for the logger is optional. // If provided, the caller must ensure that it is called // periodically (if desired) and at program exit. - Create(c config.LoggingConfiguration) (log logr.Logger, flush func()) + Create(c LoggingConfiguration) (log logr.Logger, flush func()) } -// NewLogFormatRegistry return new init LogFormatRegistry struct -func NewLogFormatRegistry() *LogFormatRegistry { - return &LogFormatRegistry{ +// RegisterLogFormat registers support for a new logging format. This must be called +// before using any of the methods in LoggingConfiguration. +func RegisterLogFormat(name string, factory LogFormatFactory) error { + return logRegistry.register(name, factory) +} + +func newLogFormatRegistry() *logFormatRegistry { + registry := &logFormatRegistry{ registry: make(map[string]LogFormatFactory), frozen: false, } + registry.register("text", nil) + return registry } -// Register new log format registry to global logRegistry. -// nil is valid and selects the default klog output. -func (lfr *LogFormatRegistry) Register(name string, factory LogFormatFactory) error { +// register adds a new log format. It's an error to modify an existing one. +func (lfr *logFormatRegistry) register(name string, factory LogFormatFactory) error { if lfr.frozen { - return fmt.Errorf("log format is frozen, unable to register log format") + return fmt.Errorf("log format registry is frozen, unable to register log format %s", name) } if _, ok := lfr.registry[name]; ok { return fmt.Errorf("log format: %s already exists", name) @@ -65,8 +68,8 @@ func (lfr *LogFormatRegistry) Register(name string, factory LogFormatFactory) er return nil } -// Get specified log format logger -func (lfr *LogFormatRegistry) Get(name string) (LogFormatFactory, error) { +// get specified log format factory +func (lfr *logFormatRegistry) get(name string) (LogFormatFactory, error) { re, ok := lfr.registry[name] if !ok { return nil, fmt.Errorf("log format: %s does not exists", name) @@ -74,28 +77,8 @@ func (lfr *LogFormatRegistry) Get(name string) (LogFormatFactory, error) { return re, nil } -// Set specified log format logger -func (lfr *LogFormatRegistry) Set(name string, factory LogFormatFactory) error { - if lfr.frozen { - return fmt.Errorf("log format is frozen, unable to set log format") - } - - lfr.registry[name] = factory - return nil -} - -// Delete specified log format logger -func (lfr *LogFormatRegistry) Delete(name string) error { - if lfr.frozen { - return fmt.Errorf("log format is frozen, unable to delete log format") - } - - delete(lfr.registry, name) - return nil -} - -// List names of registered log formats (sorted) -func (lfr *LogFormatRegistry) List() []string { +// list names of registered log formats (sorted) +func (lfr *logFormatRegistry) list() []string { formats := make([]string, 0, len(lfr.registry)) for f := range lfr.registry { formats = append(formats, f) @@ -104,7 +87,7 @@ func (lfr *LogFormatRegistry) List() []string { return formats } -// Freeze freezes the log format registry -func (lfr *LogFormatRegistry) Freeze() { +// freeze prevents further modifications of the registered log formats. +func (lfr *logFormatRegistry) freeze() { lfr.frozen = true } diff --git a/staging/src/k8s.io/component-base/logs/api/v1/types.go b/staging/src/k8s.io/component-base/logs/api/v1/types.go new file mode 100644 index 00000000000..5f953932424 --- /dev/null +++ b/staging/src/k8s.io/component-base/logs/api/v1/types.go @@ -0,0 +1,92 @@ +/* +Copyright 2021 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1 + +import ( + "time" + + "k8s.io/apimachinery/pkg/api/resource" +) + +// Supported output formats. +const ( + // DefaultLogFormat is the traditional klog output format. + DefaultLogFormat = "text" + + // JSONLogFormat emits each log message as a JSON struct. + JSONLogFormat = "json" +) + +// LoggingConfiguration contains logging options. +type LoggingConfiguration struct { + // Format Flag specifies the structure of log messages. + // default value of format is `text` + Format string `json:"format,omitempty"` + // Maximum number of nanoseconds (i.e. 1s = 1000000000) between log + // flushes. Ignored if the selected logging backend writes log + // messages without buffering. + FlushFrequency time.Duration `json:"flushFrequency"` + // Verbosity is the threshold that determines which log messages are + // logged. Default is zero which logs only the most important + // messages. Higher values enable additional messages. Error messages + // are always logged. + Verbosity VerbosityLevel `json:"verbosity"` + // VModule overrides the verbosity threshold for individual files. + // Only supported for "text" log format. + VModule VModuleConfiguration `json:"vmodule,omitempty"` + // [Experimental] Options holds additional parameters that are specific + // to the different logging formats. Only the options for the selected + // format get used, but all of them get validated. + Options FormatOptions `json:"options,omitempty"` +} + +// FormatOptions contains options for the different logging formats. +type FormatOptions struct { + // [Experimental] JSON contains options for logging format "json". + JSON JSONOptions `json:"json,omitempty"` +} + +// JSONOptions contains options for logging format "json". +type JSONOptions struct { + // [Experimental] SplitStream redirects error messages to stderr while + // info messages go to stdout, with buffering. The default is to write + // both to stdout, without buffering. + SplitStream bool `json:"splitStream,omitempty"` + // [Experimental] InfoBufferSize sets the size of the info stream when + // using split streams. The default is zero, which disables buffering. + InfoBufferSize resource.QuantityValue `json:"infoBufferSize,omitempty"` +} + +// VModuleConfiguration is a collection of individual file names or patterns +// and the corresponding verbosity threshold. +type VModuleConfiguration []VModuleItem + +// VModuleItem defines verbosity for one or more files which match a certain +// glob pattern. +type VModuleItem struct { + // FilePattern is a base file name (i.e. minus the ".go" suffix and + // directory) or a "glob" pattern for such a name. It must not contain + // comma and equal signs because those are separators for the + // corresponding klog command line argument. + FilePattern string `json:"filePattern"` + // Verbosity is the threshold for log messages emitted inside files + // that match the pattern. + Verbosity VerbosityLevel `json:"verbosity"` +} + +// VerbosityLevel represents a klog or logr verbosity threshold. +type VerbosityLevel uint32 diff --git a/staging/src/k8s.io/component-base/logs/api/v1/types_test.go b/staging/src/k8s.io/component-base/logs/api/v1/types_test.go new file mode 100644 index 00000000000..100b1719df4 --- /dev/null +++ b/staging/src/k8s.io/component-base/logs/api/v1/types_test.go @@ -0,0 +1,266 @@ +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1 + +import ( + "fmt" + "math" + "reflect" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "k8s.io/apimachinery/pkg/api/resource" + "sigs.k8s.io/json" +) + +func TestVModule(t *testing.T) { + testcases := []struct { + arg string + expectError string + expectValue VModuleConfiguration + expectParam string + }{ + { + arg: "gopher*=1", + expectValue: VModuleConfiguration{ + { + FilePattern: "gopher*", + Verbosity: 1, + }, + }, + }, + { + arg: "foo=1,bar=2", + expectValue: VModuleConfiguration{ + { + FilePattern: "foo", + Verbosity: 1, + }, + { + FilePattern: "bar", + Verbosity: 2, + }, + }, + }, + { + arg: "foo=1,bar=2,", + expectValue: VModuleConfiguration{ + { + FilePattern: "foo", + Verbosity: 1, + }, + { + FilePattern: "bar", + Verbosity: 2, + }, + }, + expectParam: "foo=1,bar=2", + }, + { + arg: "gopher*", + expectError: `"gopher*" does not have the pattern=N format`, + }, + { + arg: "=1", + expectError: `"=1" does not have the pattern=N format`, + }, + { + arg: "foo=-1", + expectError: `parsing verbosity in "foo=-1": strconv.ParseUint: parsing "-1": invalid syntax`, + }, + { + arg: fmt.Sprintf("validint32=%d", math.MaxInt32), + expectValue: VModuleConfiguration{ + { + FilePattern: "validint32", + Verbosity: math.MaxInt32, + }, + }, + }, + { + arg: fmt.Sprintf("invalidint32=%d", math.MaxInt32+1), + expectError: `parsing verbosity in "invalidint32=2147483648": strconv.ParseUint: parsing "2147483648": value out of range`, + }, + } + + for _, test := range testcases { + t.Run(test.arg, func(t *testing.T) { + var actual VModuleConfiguration + value := VModuleConfigurationPflag(&actual) + err := value.Set(test.arg) + if test.expectError != "" { + if err == nil { + t.Fatal("parsing should have failed") + } + assert.Equal(t, test.expectError, err.Error(), "parse error") + } else { + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + param := value.String() + expectParam := test.expectParam + if expectParam == "" { + expectParam = test.arg + } + assert.Equal(t, expectParam, param, "encoded parameter value not identical") + } + }) + } +} + +// TestCompatibility ensures that a) valid JSON remains valid and has the same +// effect and b) that new fields are covered by the test data. +func TestCompatibility(t *testing.T) { + testcases := map[string]struct { + // fixture holds a representation of a LoggingConfiguration struct in JSON format. + fixture string + // baseConfig is the struct that Unmarshal writes into. + baseConfig LoggingConfiguration + // expectAllFields enables a reflection check to ensure that the + // result has all fields set. + expectAllFields bool + // expectConfig is the intended result. + expectConfig LoggingConfiguration + }{ + "defaults": { + // No changes when nothing is specified. + fixture: "{}", + baseConfig: *NewLoggingConfiguration(), + expectConfig: *NewLoggingConfiguration(), + }, + "all-fields": { + // The JSON fixture includes all fields. The result + // must have all fields as non-empty when starting with + // an empty base, otherwise the fixture is incomplete + // and must be updated for the test case to pass. + fixture: `{ + "format": "json", + "flushFrequency": 1, + "verbosity": 5, + "vmodule": [ + {"filePattern": "someFile", "verbosity": 10}, + {"filePattern": "anotherFile", "verbosity": 1} + ], + "options": { + "json": { + "splitStream": true, + "infoBufferSize": "1024" + } + } +} +`, + baseConfig: LoggingConfiguration{}, + expectAllFields: true, + expectConfig: LoggingConfiguration{ + Format: JSONLogFormat, + FlushFrequency: time.Nanosecond, + Verbosity: VerbosityLevel(5), + VModule: VModuleConfiguration{ + { + FilePattern: "someFile", + Verbosity: VerbosityLevel(10), + }, + { + FilePattern: "anotherFile", + Verbosity: VerbosityLevel(1), + }, + }, + Options: FormatOptions{ + JSON: JSONOptions{ + SplitStream: true, + InfoBufferSize: resource.QuantityValue{ + Quantity: *resource.NewQuantity(1024, resource.DecimalSI), + }, + }, + }, + }, + }, + } + + for name, tc := range testcases { + t.Run(name, func(t *testing.T) { + // Beware, not a deep copy. Different test cases must + // not share anything. + config := tc.baseConfig + if strictErr, err := json.UnmarshalStrict([]byte(tc.fixture), &config); err != nil { + t.Fatalf("unexpected unmarshal error: %v", err) + } else if strictErr != nil { + t.Fatalf("unexpected strict unmarshal error: %v", strictErr) + } + // This sets the internal "s" field just like unmarshaling does. + // Required for assert.Equal to pass. + _ = tc.expectConfig.Options.JSON.InfoBufferSize.String() + assert.Equal(t, tc.expectConfig, config) + if tc.expectAllFields { + notZeroRecursive(t, config, "LoggingConfiguration") + } + }) + } +} + +// notZero asserts that i is not the zero value for its type +// and repeats that check recursively for all pointers, +// structs, maps, arrays, and slices. +func notZeroRecursive(t *testing.T, i interface{}, path string) bool { + typeOfI := reflect.TypeOf(i) + + if i == nil || reflect.DeepEqual(i, reflect.Zero(typeOfI).Interface()) { + t.Errorf("%s: should not have been zero, but was %v", path, i) + return false + } + + valid := true + kind := typeOfI.Kind() + value := reflect.ValueOf(i) + switch kind { + case reflect.Ptr: + if !notZeroRecursive(t, value.Elem().Interface(), path) { + valid = false + } + case reflect.Struct: + for i := 0; i < typeOfI.NumField(); i++ { + if !typeOfI.Field(i).IsExported() { + // Cannot access value. + continue + } + if !notZeroRecursive(t, value.Field(i).Interface(), path+"."+typeOfI.Field(i).Name) { + valid = false + } + } + case reflect.Map: + iter := value.MapRange() + for iter.Next() { + k := iter.Key() + v := iter.Value() + if !notZeroRecursive(t, k.Interface(), path+"."+"") { + valid = false + } + if !notZeroRecursive(t, v.Interface(), path+"["+fmt.Sprintf("%v", k.Interface())+"]") { + valid = false + } + } + case reflect.Slice, reflect.Array: + for i := 0; i < value.Len(); i++ { + if !notZeroRecursive(t, value.Index(i).Interface(), path+"["+fmt.Sprintf("%d", i)+"]") { + valid = false + } + } + } + + return valid +} diff --git a/staging/src/k8s.io/component-base/logs/api/v1/validate.go b/staging/src/k8s.io/component-base/logs/api/v1/validate.go deleted file mode 100644 index 694e95d4681..00000000000 --- a/staging/src/k8s.io/component-base/logs/api/v1/validate.go +++ /dev/null @@ -1,70 +0,0 @@ -/* -Copyright 2021 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package logs - -import ( - "fmt" - "math" - "strings" - - "k8s.io/apimachinery/pkg/util/validation/field" - cliflag "k8s.io/component-base/cli/flag" - "k8s.io/component-base/config" - "k8s.io/component-base/logs/registry" -) - -func ValidateLoggingConfiguration(c *config.LoggingConfiguration, fldPath *field.Path) field.ErrorList { - errs := field.ErrorList{} - if c.Format != DefaultLogFormat { - // WordSepNormalizeFunc is just a guess. Commands should use it, - // but we cannot know for sure. - allFlags := UnsupportedLoggingFlags(cliflag.WordSepNormalizeFunc) - for _, f := range allFlags { - if f.DefValue != f.Value.String() { - errs = append(errs, field.Invalid(fldPath.Child("format"), c.Format, fmt.Sprintf("Non-default format doesn't honor flag: %s", f.Name))) - } - } - } - _, err := registry.LogRegistry.Get(c.Format) - if err != nil { - errs = append(errs, field.Invalid(fldPath.Child("format"), c.Format, "Unsupported log format")) - } - - // The type in our struct is uint32, but klog only accepts positive int32. - if c.Verbosity > math.MaxInt32 { - errs = append(errs, field.Invalid(fldPath.Child("verbosity"), c.Verbosity, fmt.Sprintf("Must be <= %d", math.MaxInt32))) - } - vmoduleFldPath := fldPath.Child("vmodule") - if len(c.VModule) > 0 && c.Format != "" && c.Format != "text" { - errs = append(errs, field.Forbidden(vmoduleFldPath, "Only supported for text log format")) - } - for i, item := range c.VModule { - if item.FilePattern == "" { - errs = append(errs, field.Required(vmoduleFldPath.Index(i), "File pattern must not be empty")) - } - if strings.ContainsAny(item.FilePattern, "=,") { - errs = append(errs, field.Invalid(vmoduleFldPath.Index(i), item.FilePattern, "File pattern must not contain equal sign or comma")) - } - if item.Verbosity > math.MaxInt32 { - errs = append(errs, field.Invalid(vmoduleFldPath.Index(i), item.Verbosity, fmt.Sprintf("Must be <= %d", math.MaxInt32))) - } - } - - // Currently nothing to validate for c.Options. - - return errs -} diff --git a/staging/src/k8s.io/component-base/logs/api/v1/validate_test.go b/staging/src/k8s.io/component-base/logs/api/v1/validate_test.go index cd386b6bd76..6cacb4e6d18 100644 --- a/staging/src/k8s.io/component-base/logs/api/v1/validate_test.go +++ b/staging/src/k8s.io/component-base/logs/api/v1/validate_test.go @@ -14,27 +14,27 @@ See the License for the specific language governing permissions and limitations under the License. */ -package logs +package v1 import ( "math" "testing" "github.com/stretchr/testify/assert" - - "k8s.io/component-base/config" + "k8s.io/apimachinery/pkg/util/validation/field" ) -func TestValidateLoggingConfiguration(t *testing.T) { +func TestValidation(t *testing.T) { testcases := map[string]struct { - config config.LoggingConfiguration + config LoggingConfiguration + path *field.Path expectErrors string }{ "okay": { - config: config.LoggingConfiguration{ + config: LoggingConfiguration{ Format: "text", Verbosity: 10, - VModule: config.VModuleConfiguration{ + VModule: VModuleConfiguration{ { FilePattern: "gopher*", Verbosity: 100, @@ -43,22 +43,29 @@ func TestValidateLoggingConfiguration(t *testing.T) { }, }, "wrong-format": { - config: config.LoggingConfiguration{ + config: LoggingConfiguration{ Format: "no-such-format", }, expectErrors: `format: Invalid value: "no-such-format": Unsupported log format`, }, + "embedded": { + config: LoggingConfiguration{ + Format: "no-such-format", + }, + path: field.NewPath("config"), + expectErrors: `config.format: Invalid value: "no-such-format": Unsupported log format`, + }, "verbosity-overflow": { - config: config.LoggingConfiguration{ + config: LoggingConfiguration{ Format: "text", Verbosity: math.MaxInt32 + 1, }, expectErrors: `verbosity: Invalid value: 0x80000000: Must be <= 2147483647`, }, "vmodule-verbosity-overflow": { - config: config.LoggingConfiguration{ + config: LoggingConfiguration{ Format: "text", - VModule: config.VModuleConfiguration{ + VModule: VModuleConfiguration{ { FilePattern: "gopher*", Verbosity: math.MaxInt32 + 1, @@ -68,9 +75,9 @@ func TestValidateLoggingConfiguration(t *testing.T) { expectErrors: `vmodule[0]: Invalid value: 0x80000000: Must be <= 2147483647`, }, "vmodule-empty-pattern": { - config: config.LoggingConfiguration{ + config: LoggingConfiguration{ Format: "text", - VModule: config.VModuleConfiguration{ + VModule: VModuleConfiguration{ { FilePattern: "", Verbosity: 1, @@ -80,9 +87,9 @@ func TestValidateLoggingConfiguration(t *testing.T) { expectErrors: `vmodule[0]: Required value: File pattern must not be empty`, }, "vmodule-pattern-with-special-characters": { - config: config.LoggingConfiguration{ + config: LoggingConfiguration{ Format: "text", - VModule: config.VModuleConfiguration{ + VModule: VModuleConfiguration{ { FilePattern: "foo,bar", Verbosity: 1, @@ -96,9 +103,9 @@ func TestValidateLoggingConfiguration(t *testing.T) { expectErrors: `[vmodule[0]: Invalid value: "foo,bar": File pattern must not contain equal sign or comma, vmodule[1]: Invalid value: "foo=bar": File pattern must not contain equal sign or comma]`, }, "vmodule-unsupported": { - config: config.LoggingConfiguration{ + config: LoggingConfiguration{ Format: "json", - VModule: config.VModuleConfiguration{ + VModule: VModuleConfiguration{ { FilePattern: "foo", Verbosity: 1, @@ -111,13 +118,13 @@ func TestValidateLoggingConfiguration(t *testing.T) { for name, test := range testcases { t.Run(name, func(t *testing.T) { - errs := ValidateLoggingConfiguration(&test.config, nil) - if len(errs) == 0 { + err := test.config.Validate(nil, test.path) + if len(err) == 0 { if test.expectErrors != "" { t.Fatalf("did not get expected error(s): %s", test.expectErrors) } } else { - assert.Equal(t, test.expectErrors, errs.ToAggregate().Error()) + assert.Equal(t, test.expectErrors, err.ToAggregate().Error()) } }) } diff --git a/staging/src/k8s.io/component-base/logs/api/v1/zz_generated.deepcopy.go b/staging/src/k8s.io/component-base/logs/api/v1/zz_generated.deepcopy.go new file mode 100644 index 00000000000..87ca10da1a3 --- /dev/null +++ b/staging/src/k8s.io/component-base/logs/api/v1/zz_generated.deepcopy.go @@ -0,0 +1,114 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package v1 + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FormatOptions) DeepCopyInto(out *FormatOptions) { + *out = *in + in.JSON.DeepCopyInto(&out.JSON) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FormatOptions. +func (in *FormatOptions) DeepCopy() *FormatOptions { + if in == nil { + return nil + } + out := new(FormatOptions) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *JSONOptions) DeepCopyInto(out *JSONOptions) { + *out = *in + in.InfoBufferSize.DeepCopyInto(&out.InfoBufferSize) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JSONOptions. +func (in *JSONOptions) DeepCopy() *JSONOptions { + if in == nil { + return nil + } + out := new(JSONOptions) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LoggingConfiguration) DeepCopyInto(out *LoggingConfiguration) { + *out = *in + if in.VModule != nil { + in, out := &in.VModule, &out.VModule + *out = make(VModuleConfiguration, len(*in)) + copy(*out, *in) + } + in.Options.DeepCopyInto(&out.Options) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LoggingConfiguration. +func (in *LoggingConfiguration) DeepCopy() *LoggingConfiguration { + if in == nil { + return nil + } + out := new(LoggingConfiguration) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in VModuleConfiguration) DeepCopyInto(out *VModuleConfiguration) { + { + in := &in + *out = make(VModuleConfiguration, len(*in)) + copy(*out, *in) + return + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VModuleConfiguration. +func (in VModuleConfiguration) DeepCopy() VModuleConfiguration { + if in == nil { + return nil + } + out := new(VModuleConfiguration) + in.DeepCopyInto(out) + return *out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VModuleItem) DeepCopyInto(out *VModuleItem) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VModuleItem. +func (in *VModuleItem) DeepCopy() *VModuleItem { + if in == nil { + return nil + } + out := new(VModuleItem) + in.DeepCopyInto(out) + return out +} diff --git a/staging/src/k8s.io/component-base/logs/config.go b/staging/src/k8s.io/component-base/logs/config.go deleted file mode 100644 index 4df04efe215..00000000000 --- a/staging/src/k8s.io/component-base/logs/config.go +++ /dev/null @@ -1,116 +0,0 @@ -/* -Copyright 2021 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package logs - -import ( - "flag" - "fmt" - "sort" - "strings" - - "github.com/spf13/pflag" - - cliflag "k8s.io/component-base/cli/flag" - "k8s.io/component-base/config" - "k8s.io/component-base/logs/registry" - "k8s.io/klog/v2" -) - -// Supported klog formats -const ( - DefaultLogFormat = "text" - JSONLogFormat = "json" -) - -// loggingFlags captures the state of the logging flags, in particular their default value -// before flag parsing. It is used by UnsupportedLoggingFlags. -var loggingFlags pflag.FlagSet - -func init() { - // Text format is default klog format - registry.LogRegistry.Register(DefaultLogFormat, nil) - - var fs flag.FlagSet - klog.InitFlags(&fs) - loggingFlags.AddGoFlagSet(&fs) -} - -// List of logs (k8s.io/klog + k8s.io/component-base/logs) flags supported by all logging formats -var supportedLogsFlags = map[string]struct{}{ - "v": {}, -} - -// BindLoggingFlags binds the Options struct fields to a flagset. -// -// Programs using LoggingConfiguration must use SkipLoggingConfigurationFlags -// when calling AddFlags to avoid the duplicate registration of flags. -func BindLoggingFlags(c *config.LoggingConfiguration, fs *pflag.FlagSet) { - // The help text is generated assuming that flags will eventually use - // hyphens, even if currently no normalization function is set for the - // flag set yet. - unsupportedFlags := strings.Join(unsupportedLoggingFlagNames(cliflag.WordSepNormalizeFunc), ", ") - formats := fmt.Sprintf(`"%s"`, strings.Join(registry.LogRegistry.List(), `", "`)) - fs.StringVar(&c.Format, "logging-format", c.Format, fmt.Sprintf("Sets the log format. Permitted formats: %s.\nNon-default formats don't honor these flags: %s.\nNon-default choices are currently alpha and subject to change without warning.", formats, unsupportedFlags)) - // No new log formats should be added after generation is of flag options - registry.LogRegistry.Freeze() - - fs.DurationVar(&c.FlushFrequency, logFlushFreqFlagName, logFlushFreq, "Maximum number of seconds between log flushes") - fs.VarP(&c.Verbosity, "v", "v", "number for the log level verbosity") - fs.Var(&c.VModule, "vmodule", "comma-separated list of pattern=N settings for file-filtered logging (only works for text log format)") - - // JSON options. We only register them if "json" is a valid format. The - // config file API however always has them. - if _, err := registry.LogRegistry.Get("json"); err == nil { - fs.BoolVar(&c.Options.JSON.SplitStream, "log-json-split-stream", false, "[Experimental] In JSON format, write error messages to stderr and info messages to stdout. The default is to write a single stream to stdout.") - fs.Var(&c.Options.JSON.InfoBufferSize, "log-json-info-buffer-size", "[Experimental] In JSON format with split output streams, the info messages can be buffered for a while to increase performance. The default value of zero bytes disables buffering. The size can be specified as number of bytes (512), multiples of 1000 (1K), multiples of 1024 (2Ki), or powers of those (3M, 4G, 5Mi, 6Gi).") - } -} - -// UnsupportedLoggingFlags lists unsupported logging flags. The normalize -// function is optional. -func UnsupportedLoggingFlags(normalizeFunc func(f *pflag.FlagSet, name string) pflag.NormalizedName) []*pflag.Flag { - // k8s.io/component-base/logs and klog flags - pfs := &pflag.FlagSet{} - loggingFlags.VisitAll(func(flag *pflag.Flag) { - if _, found := supportedLogsFlags[flag.Name]; !found { - // Normalization changes flag.Name, so make a copy. - clone := *flag - pfs.AddFlag(&clone) - } - }) - - // Apply normalization. - pfs.SetNormalizeFunc(normalizeFunc) - - var allFlags []*pflag.Flag - pfs.VisitAll(func(flag *pflag.Flag) { - allFlags = append(allFlags, flag) - }) - return allFlags -} - -// unsupportedLoggingFlagNames lists unsupported logging flags by name, with -// optional normalization and sorted. -func unsupportedLoggingFlagNames(normalizeFunc func(f *pflag.FlagSet, name string) pflag.NormalizedName) []string { - unsupportedFlags := UnsupportedLoggingFlags(normalizeFunc) - names := make([]string, 0, len(unsupportedFlags)) - for _, f := range unsupportedFlags { - names = append(names, "--"+f.Name) - } - sort.Strings(names) - return names -} diff --git a/staging/src/k8s.io/component-base/logs/example/cmd/logger.go b/staging/src/k8s.io/component-base/logs/example/cmd/logger.go index 4bf5639ac05..16d9f3b93cd 100644 --- a/staging/src/k8s.io/component-base/logs/example/cmd/logger.go +++ b/staging/src/k8s.io/component-base/logs/example/cmd/logger.go @@ -29,6 +29,7 @@ import ( "k8s.io/component-base/cli" "k8s.io/component-base/featuregate" "k8s.io/component-base/logs" + logsapi "k8s.io/component-base/logs/api/v1" "k8s.io/klog/v2" _ "k8s.io/component-base/logs/json/register" @@ -37,7 +38,7 @@ import ( var featureGate = featuregate.NewFeatureGate() func main() { - runtime.Must(logs.AddFeatureGates(featureGate)) + runtime.Must(logsapi.AddFeatureGates(featureGate)) command := NewLoggerCommand() // Intentionally broken: logging is not initialized yet. @@ -48,11 +49,11 @@ func main() { } func NewLoggerCommand() *cobra.Command { - o := logs.NewOptions() + c := logsapi.NewLoggingConfiguration() cmd := &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { logs.InitLogs() - if err := o.ValidateAndApply(featureGate); err != nil { + if err := c.ValidateAndApply(featureGate); err != nil { fmt.Fprintf(os.Stderr, "%v\n", err) os.Exit(1) } @@ -64,9 +65,9 @@ func NewLoggerCommand() *cobra.Command { runLogger(ctx) }, } - logs.AddFeatureGates(featureGate) + logsapi.AddFeatureGates(featureGate) featureGate.AddFlag(cmd.Flags()) - o.AddFlags(cmd.Flags()) + c.AddFlags(cmd.Flags()) return cmd } diff --git a/staging/src/k8s.io/component-base/logs/json/json.go b/staging/src/k8s.io/component-base/logs/json/json.go index 0171f66afb6..2876c602cda 100644 --- a/staging/src/k8s.io/component-base/logs/json/json.go +++ b/staging/src/k8s.io/component-base/logs/json/json.go @@ -26,8 +26,7 @@ import ( "go.uber.org/zap" "go.uber.org/zap/zapcore" - "k8s.io/component-base/config" - "k8s.io/component-base/logs/registry" + logsapi "k8s.io/component-base/logs/api/v1" ) var ( @@ -38,7 +37,7 @@ var ( // NewJSONLogger creates a new json logr.Logger and its associated // flush function. The separate error stream is optional and may be nil. // The encoder config is also optional. -func NewJSONLogger(v config.VerbosityLevel, infoStream, errorStream zapcore.WriteSyncer, encoderConfig *zapcore.EncoderConfig) (logr.Logger, func()) { +func NewJSONLogger(v logsapi.VerbosityLevel, infoStream, errorStream zapcore.WriteSyncer, encoderConfig *zapcore.EncoderConfig) (logr.Logger, func()) { // zap levels are inverted: everything with a verbosity >= threshold gets logged. zapV := -zapcore.Level(v) @@ -85,9 +84,9 @@ func epochMillisTimeEncoder(_ time.Time, enc zapcore.PrimitiveArrayEncoder) { // Factory produces JSON logger instances. type Factory struct{} -var _ registry.LogFormatFactory = Factory{} +var _ logsapi.LogFormatFactory = Factory{} -func (f Factory) Create(c config.LoggingConfiguration) (logr.Logger, func()) { +func (f Factory) Create(c logsapi.LoggingConfiguration) (logr.Logger, func()) { // We intentionally avoid all os.File.Sync calls. Output is unbuffered, // therefore we don't need to flush, and calling the underlying fsync // would just slow down writing. diff --git a/staging/src/k8s.io/component-base/logs/json/json_test.go b/staging/src/k8s.io/component-base/logs/json/json_test.go index aaffbe68094..44a077e3bcd 100644 --- a/staging/src/k8s.io/component-base/logs/json/json_test.go +++ b/staging/src/k8s.io/component-base/logs/json/json_test.go @@ -24,10 +24,10 @@ import ( "time" "github.com/stretchr/testify/assert" - "k8s.io/component-base/config" - "go.uber.org/zap" "go.uber.org/zap/zapcore" + + logsapi "k8s.io/component-base/logs/api/v1" ) // TestZapLoggerInfo test ZapLogger json info format @@ -112,7 +112,7 @@ func TestZapLoggerInfo(t *testing.T) { // TestZapLoggerEnabled test ZapLogger enabled func TestZapLoggerEnabled(t *testing.T) { verbosityLevel := 10 - sampleInfoLogger, _ := NewJSONLogger(config.VerbosityLevel(verbosityLevel), nil, nil, nil) + sampleInfoLogger, _ := NewJSONLogger(logsapi.VerbosityLevel(verbosityLevel), nil, nil, nil) for v := 0; v <= verbosityLevel; v++ { enabled := sampleInfoLogger.V(v).Enabled() expectEnabled := v <= verbosityLevel @@ -135,7 +135,7 @@ func TestZapLoggerV(t *testing.T) { for v := 0; v <= verbosityLevel; v++ { var buffer bytes.Buffer writer := zapcore.AddSync(&buffer) - sampleInfoLogger, _ := NewJSONLogger(config.VerbosityLevel(verbosityLevel), writer, nil, nil) + sampleInfoLogger, _ := NewJSONLogger(logsapi.VerbosityLevel(verbosityLevel), writer, nil, nil) sampleInfoLogger.V(v).Info("test", "ns", "default", "podnum", 2, "time", time.Microsecond) logStr := buffer.String() diff --git a/staging/src/k8s.io/component-base/logs/json/klog_test.go b/staging/src/k8s.io/component-base/logs/json/klog_test.go index 3358db88d45..05ae118dfdf 100644 --- a/staging/src/k8s.io/component-base/logs/json/klog_test.go +++ b/staging/src/k8s.io/component-base/logs/json/klog_test.go @@ -28,7 +28,7 @@ import ( "github.com/stretchr/testify/assert" "go.uber.org/zap/zapcore" - "k8s.io/component-base/config" + logsapi "k8s.io/component-base/logs/api/v1" "k8s.io/klog/v2" ) @@ -241,7 +241,7 @@ func TestKlogIntegration(t *testing.T) { var buffer bytes.Buffer writer := zapcore.AddSync(&buffer) // This level is high enough to enable all log messages from this test. - verbosityLevel := config.VerbosityLevel(100) + verbosityLevel := logsapi.VerbosityLevel(100) logger, _ := NewJSONLogger(verbosityLevel, writer, nil, nil) klog.SetLogger(logger) defer klog.ClearLogger() diff --git a/staging/src/k8s.io/component-base/logs/json/register/register.go b/staging/src/k8s.io/component-base/logs/json/register/register.go index dea55ec1e25..06dea3d34b7 100644 --- a/staging/src/k8s.io/component-base/logs/json/register/register.go +++ b/staging/src/k8s.io/component-base/logs/json/register/register.go @@ -17,12 +17,13 @@ limitations under the License. package register import ( - "k8s.io/component-base/logs" + logsapi "k8s.io/component-base/logs/api/v1" json "k8s.io/component-base/logs/json" - "k8s.io/component-base/logs/registry" ) func init() { // JSON format is optional klog format - registry.LogRegistry.Register(logs.JSONLogFormat, json.Factory{}) + if err := logsapi.RegisterLogFormat(logsapi.JSONLogFormat, json.Factory{}); err != nil { + panic(err) + } } diff --git a/staging/src/k8s.io/component-base/logs/json/register/register_test.go b/staging/src/k8s.io/component-base/logs/json/register/register_test.go index ebc71395b99..dae64494fdc 100644 --- a/staging/src/k8s.io/component-base/logs/json/register/register_test.go +++ b/staging/src/k8s.io/component-base/logs/json/register/register_test.go @@ -26,15 +26,15 @@ import ( "k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/component-base/featuregate" - "k8s.io/component-base/logs" + logsapi "k8s.io/component-base/logs/api/v1" "k8s.io/klog/v2" ) func TestJSONFlag(t *testing.T) { - o := logs.NewOptions() + c := logsapi.NewLoggingConfiguration() fs := pflag.NewFlagSet("addflagstest", pflag.ContinueOnError) output := bytes.Buffer{} - o.AddFlags(fs) + c.AddFlags(fs) fs.SetOutput(&output) fs.PrintDefaults() wantSubstring := `Permitted formats: "json", "text".` @@ -44,41 +44,41 @@ func TestJSONFlag(t *testing.T) { } func TestJSONFormatRegister(t *testing.T) { - newOptions := logs.NewOptions() + config := logsapi.NewLoggingConfiguration() klogr := klog.Background() testcases := []struct { name string args []string contextualLogging bool - want *logs.Options + want *logsapi.LoggingConfiguration errs field.ErrorList }{ { name: "JSON log format", args: []string{"--logging-format=json"}, - want: func() *logs.Options { - c := newOptions.Config.DeepCopy() - c.Format = logs.JSONLogFormat - return &logs.Options{*c} + want: func() *logsapi.LoggingConfiguration { + c := config.DeepCopy() + c.Format = logsapi.JSONLogFormat + return c }(), }, { name: "JSON direct", args: []string{"--logging-format=json"}, contextualLogging: true, - want: func() *logs.Options { - c := newOptions.Config.DeepCopy() - c.Format = logs.JSONLogFormat - return &logs.Options{*c} + want: func() *logsapi.LoggingConfiguration { + c := config.DeepCopy() + c.Format = logsapi.JSONLogFormat + return c }(), }, { name: "Unsupported log format", args: []string{"--logging-format=test"}, - want: func() *logs.Options { - c := newOptions.Config.DeepCopy() + want: func() *logsapi.LoggingConfiguration { + c := config.DeepCopy() c.Format = "test" - return &logs.Options{*c} + return c }(), errs: field.ErrorList{&field.Error{ Type: "FieldValueInvalid", @@ -91,18 +91,18 @@ func TestJSONFormatRegister(t *testing.T) { for _, tc := range testcases { t.Run(tc.name, func(t *testing.T) { - o := logs.NewOptions() + c := logsapi.NewLoggingConfiguration() fs := pflag.NewFlagSet("addflagstest", pflag.ContinueOnError) - o.AddFlags(fs) + c.AddFlags(fs) fs.Parse(tc.args) - if !assert.Equal(t, tc.want, o) { - t.Errorf("Wrong Validate() result for %q. expect %v, got %v", tc.name, tc.want, o) + if !assert.Equal(t, tc.want, c) { + t.Errorf("Wrong Validate() result for %q. expect %v, got %v", tc.name, tc.want, c) } featureGate := featuregate.NewFeatureGate() - logs.AddFeatureGates(featureGate) - err := featureGate.SetFromMap(map[string]bool{string(logs.ContextualLogging): tc.contextualLogging}) + logsapi.AddFeatureGates(featureGate) + err := featureGate.SetFromMap(map[string]bool{string(logsapi.ContextualLogging): tc.contextualLogging}) require.NoError(t, err) - errs := o.ValidateAndApply(featureGate) + errs := c.ValidateAndApply(featureGate) defer klog.ClearLogger() if !assert.ElementsMatch(t, tc.errs, errs) { t.Errorf("Wrong Validate() result for %q.\n expect:\t%+v\n got:\t%+v", tc.name, tc.errs, errs) diff --git a/staging/src/k8s.io/component-base/logs/logs.go b/staging/src/k8s.io/component-base/logs/logs.go index 9d66354a4c1..84d48f87738 100644 --- a/staging/src/k8s.io/component-base/logs/logs.go +++ b/staging/src/k8s.io/component-base/logs/logs.go @@ -26,10 +26,10 @@ import ( "time" "github.com/spf13/pflag" + logsapi "k8s.io/component-base/logs/api/v1" "k8s.io/klog/v2" ) -const logFlushFreqFlagName = "log-flush-frequency" const deprecated = "will be removed in a future release, see https://github.com/kubernetes/enhancements/tree/master/keps/sig-instrumentation/2845-deprecate-klog-specific-flags-in-k8s-components" // TODO (https://github.com/kubernetes/kubernetes/issues/105310): once klog @@ -48,7 +48,7 @@ var ( func init() { klog.InitFlags(packageFlags) - packageFlags.DurationVar(&logFlushFreq, logFlushFreqFlagName, 5*time.Second, "Maximum number of seconds between log flushes") + packageFlags.DurationVar(&logFlushFreq, logsapi.LogFlushFreqFlagName, logsapi.LogFlushFreqDefault, "Maximum number of seconds between log flushes") } type addFlagsOptions struct { @@ -66,6 +66,13 @@ func SkipLoggingConfigurationFlags() Option { } } +// Options is an alias for LoggingConfiguration to comply with component-base +// conventions. +type Options = logsapi.LoggingConfiguration + +// NewOptions is an alias for NewLoggingConfiguration. +var NewOptions = logsapi.NewLoggingConfiguration + // AddFlags registers this package's flags on arbitrary FlagSets. This includes // the klog flags, with the original underscore as separator between. If // commands want hyphens as separators, they can set @@ -94,7 +101,7 @@ func AddFlags(fs *pflag.FlagSet, opts ...Option) { if o.skipLoggingConfigurationFlags { return } - case logFlushFreqFlagName: + case logsapi.LogFlushFreqFlagName: // unchanged, potentially skip it if o.skipLoggingConfigurationFlags { return @@ -135,7 +142,7 @@ func AddGoFlags(fs *flag.FlagSet, opts ...Option) { if o.skipLoggingConfigurationFlags { return } - case logFlushFreqFlagName: + case logsapi.LogFlushFreqFlagName: // unchanged if o.skipLoggingConfigurationFlags { return diff --git a/staging/src/k8s.io/kube-controller-manager/go.mod b/staging/src/k8s.io/kube-controller-manager/go.mod index 2301a7462d6..93720b2dbbf 100644 --- a/staging/src/k8s.io/kube-controller-manager/go.mod +++ b/staging/src/k8s.io/kube-controller-manager/go.mod @@ -17,7 +17,6 @@ require ( github.com/json-iterator/go v1.1.12 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/spf13/pflag v1.0.5 // indirect golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect golang.org/x/text v0.3.7 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/staging/src/k8s.io/kube-controller-manager/go.sum b/staging/src/k8s.io/kube-controller-manager/go.sum index 0d674424a04..e46f68c6a0b 100644 --- a/staging/src/k8s.io/kube-controller-manager/go.sum +++ b/staging/src/k8s.io/kube-controller-manager/go.sum @@ -28,7 +28,6 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= diff --git a/staging/src/k8s.io/kube-proxy/go.mod b/staging/src/k8s.io/kube-proxy/go.mod index b5d0acebd06..14607b10098 100644 --- a/staging/src/k8s.io/kube-proxy/go.mod +++ b/staging/src/k8s.io/kube-proxy/go.mod @@ -17,7 +17,6 @@ require ( github.com/json-iterator/go v1.1.12 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/spf13/pflag v1.0.5 // indirect golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect golang.org/x/text v0.3.7 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/staging/src/k8s.io/kube-proxy/go.sum b/staging/src/k8s.io/kube-proxy/go.sum index ec22b4fb3c5..2659011b72a 100644 --- a/staging/src/k8s.io/kube-proxy/go.sum +++ b/staging/src/k8s.io/kube-proxy/go.sum @@ -29,7 +29,6 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= diff --git a/staging/src/k8s.io/kube-scheduler/go.mod b/staging/src/k8s.io/kube-scheduler/go.mod index a4262180243..bdf64b2572d 100644 --- a/staging/src/k8s.io/kube-scheduler/go.mod +++ b/staging/src/k8s.io/kube-scheduler/go.mod @@ -19,7 +19,6 @@ require ( github.com/json-iterator/go v1.1.12 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/spf13/pflag v1.0.5 // indirect golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect golang.org/x/text v0.3.7 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/staging/src/k8s.io/kube-scheduler/go.sum b/staging/src/k8s.io/kube-scheduler/go.sum index ec22b4fb3c5..2659011b72a 100644 --- a/staging/src/k8s.io/kube-scheduler/go.sum +++ b/staging/src/k8s.io/kube-scheduler/go.sum @@ -29,7 +29,6 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= diff --git a/staging/src/k8s.io/kubelet/config/v1beta1/types.go b/staging/src/k8s.io/kubelet/config/v1beta1/types.go index ef3f33ca644..16ecc805461 100644 --- a/staging/src/k8s.io/kubelet/config/v1beta1/types.go +++ b/staging/src/k8s.io/kubelet/config/v1beta1/types.go @@ -19,7 +19,7 @@ package v1beta1 import ( v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - componentbaseconfigv1alpha1 "k8s.io/component-base/config/v1alpha1" + logsapi "k8s.io/component-base/logs/api/v1" ) // HairpinMode denotes how the kubelet should configure networking to handle @@ -676,7 +676,7 @@ type KubeletConfiguration struct { // Default: // Format: text // + optional - Logging componentbaseconfigv1alpha1.LoggingConfiguration `json:"logging,omitempty"` + Logging logsapi.LoggingConfiguration `json:"logging,omitempty"` // enableSystemLogHandler enables system logs via web interface host:port/logs/ // Default: true // +optional diff --git a/staging/src/k8s.io/kubelet/go.mod b/staging/src/k8s.io/kubelet/go.mod index e3e6b44d5f7..2d02a6ccc58 100644 --- a/staging/src/k8s.io/kubelet/go.mod +++ b/staging/src/k8s.io/kubelet/go.mod @@ -18,9 +18,11 @@ require ( github.com/golang/protobuf v1.5.2 // indirect github.com/google/go-cmp v0.5.6 // indirect github.com/google/gofuzz v1.1.0 // indirect + github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/spf13/cobra v1.4.0 // indirect github.com/spf13/pflag v1.0.5 // indirect golang.org/x/sys v0.0.0-20220209214540-3681064d5158 // indirect golang.org/x/text v0.3.7 // indirect diff --git a/staging/src/k8s.io/kubelet/go.sum b/staging/src/k8s.io/kubelet/go.sum index 033780d674b..89a3600caea 100644 --- a/staging/src/k8s.io/kubelet/go.sum +++ b/staging/src/k8s.io/kubelet/go.sum @@ -12,6 +12,7 @@ github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -58,6 +59,8 @@ github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= @@ -75,7 +78,10 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q= +github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= diff --git a/test/integration/logs/benchmark/benchmark_test.go b/test/integration/logs/benchmark/benchmark_test.go index 157406b9935..91bc8348997 100644 --- a/test/integration/logs/benchmark/benchmark_test.go +++ b/test/integration/logs/benchmark/benchmark_test.go @@ -33,7 +33,7 @@ import ( "time" "github.com/go-logr/logr" - "k8s.io/component-base/logs" + logsapi "k8s.io/component-base/logs/api/v1" logsjson "k8s.io/component-base/logs/json" "k8s.io/klog/v2" ) @@ -175,7 +175,7 @@ func benchmarkOutputFormats(b *testing.B, config loadGeneratorConfig, discard bo generateOutput(b, config, nil, out) }) b.Run("JSON", func(b *testing.B) { - options := logs.NewOptions() + c := logsapi.NewLoggingConfiguration() var logger logr.Logger var flush func() var out1, out2 *os.File @@ -194,14 +194,14 @@ func benchmarkOutputFormats(b *testing.B, config loadGeneratorConfig, discard bo } b.Run("single-stream", func(b *testing.B) { if discard { - logger, flush = logsjson.NewJSONLogger(options.Config.Verbosity, logsjson.AddNopSync(&output), nil, nil) + logger, flush = logsjson.NewJSONLogger(c.Verbosity, logsjson.AddNopSync(&output), nil, nil) } else { stderr := os.Stderr os.Stderr = out1 defer func() { os.Stderr = stderr }() - logger, flush = logsjson.Factory{}.Create(options.Config) + logger, flush = logsjson.Factory{}.Create(*c) } klog.SetLogger(logger) defer klog.ClearLogger() @@ -210,16 +210,16 @@ func benchmarkOutputFormats(b *testing.B, config loadGeneratorConfig, discard bo b.Run("split-stream", func(b *testing.B) { if discard { - logger, flush = logsjson.NewJSONLogger(options.Config.Verbosity, logsjson.AddNopSync(&output), logsjson.AddNopSync(&output), nil) + logger, flush = logsjson.NewJSONLogger(c.Verbosity, logsjson.AddNopSync(&output), logsjson.AddNopSync(&output), nil) } else { stdout, stderr := os.Stdout, os.Stderr os.Stdout, os.Stderr = out1, out2 defer func() { os.Stdout, os.Stderr = stdout, stderr }() - options := logs.NewOptions() - options.Config.Options.JSON.SplitStream = true - logger, flush = logsjson.Factory{}.Create(options.Config) + c := logsapi.NewLoggingConfiguration() + c.Options.JSON.SplitStream = true + logger, flush = logsjson.Factory{}.Create(*c) } klog.SetLogger(logger) defer klog.ClearLogger() diff --git a/test/integration/logs/benchmark/common_test.go b/test/integration/logs/benchmark/common_test.go index 78602d1341e..0b6fae600f3 100644 --- a/test/integration/logs/benchmark/common_test.go +++ b/test/integration/logs/benchmark/common_test.go @@ -23,7 +23,7 @@ import ( "github.com/go-logr/logr" "go.uber.org/zap/zapcore" - "k8s.io/component-base/logs" + logsapi "k8s.io/component-base/logs/api/v1" logsjson "k8s.io/component-base/logs/json" "k8s.io/klog/v2" ) @@ -61,8 +61,8 @@ func newJSONLogger(out io.Writer) logr.Logger { encoderConfig := &zapcore.EncoderConfig{ MessageKey: "msg", } - options := logs.NewOptions() - logger, _ := logsjson.NewJSONLogger(options.Config.Verbosity, zapcore.AddSync(out), nil, encoderConfig) + c := logsapi.NewLoggingConfiguration() + logger, _ := logsjson.NewJSONLogger(c.Verbosity, zapcore.AddSync(out), nil, encoderConfig) return logger } diff --git a/test/integration/logs/functional/json/output_test.go b/test/integration/logs/functional/json/output_test.go index c1b4287dbc4..b24eda85f1f 100644 --- a/test/integration/logs/functional/json/output_test.go +++ b/test/integration/logs/functional/json/output_test.go @@ -25,7 +25,7 @@ import ( "github.com/go-logr/logr" "go.uber.org/zap/zapcore" - "k8s.io/component-base/config" + logsapi "k8s.io/component-base/logs/api/v1" logsjson "k8s.io/component-base/logs/json" "k8s.io/klog/v2/test" ) @@ -37,7 +37,7 @@ func init() { // TestJsonOutput tests the JSON logger, directly and as backend for klog. func TestJSONOutput(t *testing.T) { newLogger := func(out io.Writer, v int, vmodule string) logr.Logger { - logger, _ := logsjson.NewJSONLogger(config.VerbosityLevel(v), logsjson.AddNopSync(out), nil, + logger, _ := logsjson.NewJSONLogger(logsapi.VerbosityLevel(v), logsjson.AddNopSync(out), nil, &zapcore.EncoderConfig{ MessageKey: "msg", CallerKey: "caller", diff --git a/vendor/modules.txt b/vendor/modules.txt index 4313049d62e..0804a4f8b3d 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -2063,10 +2063,10 @@ k8s.io/component-base/configz k8s.io/component-base/featuregate k8s.io/component-base/featuregate/testing k8s.io/component-base/logs +k8s.io/component-base/logs/api/v1 k8s.io/component-base/logs/json k8s.io/component-base/logs/json/register k8s.io/component-base/logs/logreduction -k8s.io/component-base/logs/registry k8s.io/component-base/logs/testinit k8s.io/component-base/metrics k8s.io/component-base/metrics/legacyregistry