...

Source file src/k8s.io/component-base/logs/api/v1/options_test.go

Documentation: k8s.io/component-base/logs/api/v1

     1  /*
     2  Copyright 2021 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package v1
    18  
    19  import (
    20  	"bytes"
    21  	"context"
    22  	"flag"
    23  	"testing"
    24  
    25  	"github.com/go-logr/logr"
    26  	"github.com/spf13/pflag"
    27  	"github.com/stretchr/testify/assert"
    28  	"github.com/stretchr/testify/require"
    29  
    30  	"k8s.io/apimachinery/pkg/util/validation/field"
    31  	"k8s.io/component-base/featuregate"
    32  	"k8s.io/klog/v2"
    33  )
    34  
    35  func TestReapply(t *testing.T) {
    36  	oldReapplyHandling := ReapplyHandling
    37  	defer func() {
    38  		ReapplyHandling = oldReapplyHandling
    39  		if err := ResetForTest(nil /* feature gates */); err != nil {
    40  			t.Errorf("Unexpected error resetting the logging configuration: %v", err)
    41  		}
    42  	}()
    43  
    44  	newOptions := NewLoggingConfiguration()
    45  	if err := ValidateAndApply(newOptions, nil); err != nil {
    46  		t.Errorf("unexpected error for first ValidateAndApply: %v", err)
    47  	}
    48  	ReapplyHandling = ReapplyHandlingError
    49  	if err := ValidateAndApply(newOptions, nil); err == nil {
    50  		t.Error("did not get expected error for second ValidateAndApply")
    51  	}
    52  	ReapplyHandling = ReapplyHandlingIgnoreUnchanged
    53  	if err := ValidateAndApply(newOptions, nil); err != nil {
    54  		t.Errorf("unexpected error for third ValidateAndApply: %v", err)
    55  	}
    56  	modifiedOptions := newOptions.DeepCopy()
    57  	modifiedOptions.Verbosity = 100
    58  	if err := ValidateAndApply(modifiedOptions, nil); err == nil {
    59  		t.Errorf("unexpected success for forth ValidateAndApply, should have complained about modified config")
    60  	}
    61  }
    62  
    63  func TestOptions(t *testing.T) {
    64  	newOptions := NewLoggingConfiguration()
    65  	testcases := []struct {
    66  		name string
    67  		args []string
    68  		want *LoggingConfiguration
    69  		errs field.ErrorList
    70  	}{
    71  		{
    72  			name: "Default log format",
    73  			want: newOptions.DeepCopy(),
    74  		},
    75  		{
    76  			name: "Text log format",
    77  			args: []string{"--logging-format=text"},
    78  			want: newOptions.DeepCopy(),
    79  		},
    80  		{
    81  			name: "Unsupported log format",
    82  			args: []string{"--logging-format=test"},
    83  			want: func() *LoggingConfiguration {
    84  				c := newOptions.DeepCopy()
    85  				c.Format = "test"
    86  				return c
    87  			}(),
    88  			errs: field.ErrorList{&field.Error{
    89  				Type:     "FieldValueInvalid",
    90  				Field:    "format",
    91  				BadValue: "test",
    92  				Detail:   "Unsupported log format",
    93  			}},
    94  		},
    95  	}
    96  
    97  	for _, tc := range testcases {
    98  		t.Run(tc.name, func(t *testing.T) {
    99  			c := NewLoggingConfiguration()
   100  			fs := pflag.NewFlagSet("addflagstest", pflag.ContinueOnError)
   101  			AddFlags(c, fs)
   102  			fs.Parse(tc.args)
   103  			if !assert.Equal(t, tc.want, c) {
   104  				t.Errorf("Wrong Validate() result for %q. expect %v, got %v", tc.name, tc.want, c)
   105  			}
   106  			defer func() {
   107  				if err := ResetForTest(nil /* feature gates */); err != nil {
   108  					t.Errorf("Unexpected error resetting the logging configuration: %v", err)
   109  				}
   110  			}()
   111  			errs := ValidateAndApply(c, nil /* We don't care about feature gates here. */)
   112  			defer klog.StopFlushDaemon()
   113  			if !assert.ElementsMatch(t, tc.errs, errs) {
   114  				t.Errorf("Wrong Validate() result for %q.\n expect:\t%+v\n got:\t%+v", tc.name, tc.errs, errs)
   115  			}
   116  		})
   117  	}
   118  }
   119  
   120  func TestFlagSet(t *testing.T) {
   121  	t.Run("pflag", func(t *testing.T) {
   122  		newOptions := NewLoggingConfiguration()
   123  		var fs pflag.FlagSet
   124  		AddFlags(newOptions, &fs)
   125  		var buffer bytes.Buffer
   126  		fs.SetOutput(&buffer)
   127  		fs.PrintDefaults()
   128  		// Expected (Go 1.19, pflag v1.0.5):
   129  		//     --logging-format string          Sets the log format. Permitted formats: "text". (default "text")
   130  		//     --log-flush-frequency duration   Maximum number of seconds between log flushes (default 5s)
   131  		// -v, --v Level                        number for the log level verbosity
   132  		//     --vmodule pattern=N,...          comma-separated list of pattern=N settings for file-filtered logging (only works for text log format)
   133  		//     --log-text-split-stream                [Alpha] In text format, write error messages to stderr and info messages to stdout. The default is to write a single stream to stdout. Enable the LoggingAlphaOptions feature gate to use this.
   134  		//     --log-text-info-buffer-size quantity   [Alpha] In text 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). Enable the LoggingAlphaOptions feature gate to use this.
   135  		assert.Regexp(t, `^.*--logging-format.*default.*text.*
   136  .*--log-flush-frequency.*default 5s.*
   137  .*-v.*--v.*
   138  .*--vmodule.*pattern=N.*
   139  .*--log-text-split-stream.*
   140  .*--log-text-info-buffer-size quantity.*
   141  $`, buffer.String())
   142  	})
   143  
   144  	t.Run("flag", func(t *testing.T) {
   145  		newOptions := NewLoggingConfiguration()
   146  		var pfs pflag.FlagSet
   147  		AddFlags(newOptions, &pfs)
   148  		var fs flag.FlagSet
   149  		pfs.VisitAll(func(f *pflag.Flag) {
   150  			fs.Var(f.Value, f.Name, f.Usage)
   151  		})
   152  		var buffer bytes.Buffer
   153  		fs.SetOutput(&buffer)
   154  		fs.PrintDefaults()
   155  		// Expected (Go 1.19):
   156  		// -log-flush-frequency value
   157  		//   	Maximum number of seconds between log flushes (default 5s)
   158  		// -log-text-info-buffer-size value
   159  		//      [Alpha] In text 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). Enable the LoggingAlphaOptions feature gate to use this.
   160  		// -log-text-split-stream
   161  		//      [Alpha] In text format, write error messages to stderr and info messages to stdout. The default is to write a single stream to stdout. Enable the LoggingAlphaOptions feature gate to use this.
   162  		// -logging-format value
   163  		//   	Sets the log format. Permitted formats: "text". (default text)
   164  		// -v value
   165  		//   	number for the log level verbosity
   166  		// -vmodule value
   167  		//   	comma-separated list of pattern=N settings for file-filtered logging (only works for text log format)
   168  		assert.Regexp(t, `^.*-log-flush-frequency.*
   169  .*default 5s.*
   170  .*-log-text-info-buffer-size.*
   171  .*
   172  .*-log-text-split-stream.*
   173  .*
   174  .*-logging-format.*
   175  .*default.*text.*
   176  .*-v.*
   177  .*
   178  .*-vmodule.*
   179  .*
   180  $`, buffer.String())
   181  	})
   182  
   183  	t.Run("AddGoFlags", func(t *testing.T) {
   184  		newOptions := NewLoggingConfiguration()
   185  		var fs flag.FlagSet
   186  		var buffer bytes.Buffer
   187  		AddGoFlags(newOptions, &fs)
   188  		fs.SetOutput(&buffer)
   189  		fs.PrintDefaults()
   190  		// In contrast to copying through VisitAll, the type of some options is now
   191  		// known:
   192  		// -log-flush-frequency duration
   193  		//   	Maximum number of seconds between log flushes (default 5s)
   194  		// -log-text-info-buffer-size value
   195  		//      [Alpha] In text 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). Enable the LoggingAlphaOptions feature gate to use this.
   196  		// -log-text-split-stream
   197  		//      [Alpha] In text format, write error messages to stderr and info messages to stdout. The default is to write a single stream to stdout. Enable the LoggingAlphaOptions feature gate to use this.
   198  		// -logging-format string
   199  		//   	Sets the log format. Permitted formats: "text". (default "text")
   200  		// -v value
   201  		//   	number for the log level verbosity
   202  		// -vmodule value
   203  		//   	comma-separated list of pattern=N settings for file-filtered logging (only works for text log format)
   204  		assert.Regexp(t, `^.*-log-flush-frequency.*duration.*
   205  .*default 5s.*
   206  .*-log-text-info-buffer-size.*
   207  .*
   208  .*-log-text-split-stream.*
   209  .*
   210  .*-logging-format.*string.*
   211  .*default.*text.*
   212  .*-v.*
   213  .*
   214  .*-vmodule.*
   215  .*
   216  $`, buffer.String())
   217  	})
   218  }
   219  
   220  func TestContextualLogging(t *testing.T) {
   221  	t.Run("enabled", func(t *testing.T) {
   222  		testContextualLogging(t, true)
   223  	})
   224  
   225  	t.Run("disabled", func(t *testing.T) {
   226  		testContextualLogging(t, false)
   227  	})
   228  }
   229  
   230  func testContextualLogging(t *testing.T, enabled bool) {
   231  	var err error
   232  
   233  	c := NewLoggingConfiguration()
   234  	featureGate := featuregate.NewFeatureGate()
   235  	AddFeatureGates(featureGate)
   236  	err = featureGate.SetFromMap(map[string]bool{string(ContextualLogging): enabled})
   237  	require.NoError(t, err)
   238  	defer func() {
   239  		if err := ResetForTest(nil /* feature gates */); err != nil {
   240  			t.Errorf("Unexpected error resetting the logging configuration: %v", err)
   241  		}
   242  	}()
   243  	err = ValidateAndApply(c, featureGate)
   244  	require.NoError(t, err)
   245  	defer klog.StopFlushDaemon()
   246  	defer klog.EnableContextualLogging(true)
   247  
   248  	ctx := context.Background()
   249  	// nolint:logcheck // This intentionally adds a name independently of the feature gate.
   250  	logger := klog.NewKlogr().WithName("contextual")
   251  	// nolint:logcheck // This intentionally creates a new context independently of the feature gate.
   252  	ctx = logr.NewContext(ctx, logger)
   253  	if enabled {
   254  		assert.Equal(t, logger, klog.FromContext(ctx), "FromContext")
   255  		assert.NotEqual(t, ctx, klog.NewContext(ctx, logger), "NewContext")
   256  		assert.NotEqual(t, logger, klog.LoggerWithName(logger, "foo"), "LoggerWithName")
   257  		assert.NotEqual(t, logger, klog.LoggerWithValues(logger, "x", "y"), "LoggerWithValues")
   258  	} else {
   259  		assert.NotEqual(t, logger, klog.FromContext(ctx), "FromContext")
   260  		assert.Equal(t, ctx, klog.NewContext(ctx, logger), "NewContext")
   261  		assert.Equal(t, logger, klog.LoggerWithName(logger, "foo"), "LoggerWithName")
   262  		assert.Equal(t, logger, klog.LoggerWithValues(logger, "x", "y"), "LoggerWithValues")
   263  	}
   264  }
   265  

View as plain text