build: move e2e dependencies into e2e/go.mod

Several packages are only used while running the e2e suite. These
packages are less important to update, as the they can not influence the
final executable that is part of the Ceph-CSI container-image.

By moving these dependencies out of the main Ceph-CSI go.mod, it is
easier to identify if a reported CVE affects Ceph-CSI, or only the
testing (like most of the Kubernetes CVEs).

Signed-off-by: Niels de Vos <ndevos@ibm.com>
This commit is contained in:
Niels de Vos
2025-03-04 08:57:28 +01:00
committed by mergify[bot]
parent 15da101b1b
commit bec6090996
8047 changed files with 1407827 additions and 3453 deletions

17
e2e/vendor/k8s.io/klog/v2/.gitignore generated vendored Normal file
View File

@ -0,0 +1,17 @@
# OSX leaves these everywhere on SMB shares
._*
# OSX trash
.DS_Store
# Eclipse files
.classpath
.project
.settings/**
# Files generated by JetBrains IDEs, e.g. IntelliJ IDEA
.idea/
*.iml
# Vscode files
.vscode

6
e2e/vendor/k8s.io/klog/v2/.golangci.yaml generated vendored Normal file
View File

@ -0,0 +1,6 @@
linters:
disable-all: true
enable: # sorted alphabetical
- gofmt
- misspell
- revive

22
e2e/vendor/k8s.io/klog/v2/CONTRIBUTING.md generated vendored Normal file
View File

@ -0,0 +1,22 @@
# Contributing Guidelines
Welcome to Kubernetes. We are excited about the prospect of you joining our [community](https://github.com/kubernetes/community)! The Kubernetes community abides by the CNCF [code of conduct](code-of-conduct.md). Here is an excerpt:
_As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities._
## Getting Started
We have full documentation on how to get started contributing here:
- [Contributor License Agreement](https://git.k8s.io/community/CLA.md) Kubernetes projects require that you sign a Contributor License Agreement (CLA) before we can accept your pull requests
- [Kubernetes Contributor Guide](http://git.k8s.io/community/contributors/guide) - Main contributor documentation, or you can just jump directly to the [contributing section](http://git.k8s.io/community/contributors/guide#contributing)
- [Contributor Cheat Sheet](https://git.k8s.io/community/contributors/guide/contributor-cheatsheet) - Common resources for existing developers
## Mentorship
- [Mentoring Initiatives](https://git.k8s.io/community/mentoring) - We have a diverse set of mentorship programs available that are always looking for volunteers!
## Contact Information
- [Slack](https://kubernetes.slack.com/messages/sig-architecture)
- [Mailing List](https://groups.google.com/forum/#!forum/kubernetes-sig-architecture)

191
e2e/vendor/k8s.io/klog/v2/LICENSE generated vendored Normal file
View File

@ -0,0 +1,191 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction, and
distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by the copyright
owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all other entities
that control, are controlled by, or are under common control with that entity.
For the purposes of this definition, "control" means (i) the power, direct or
indirect, to cause the direction or management of such entity, whether by
contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity exercising
permissions granted by this License.
"Source" form shall mean the preferred form for making modifications, including
but not limited to software source code, documentation source, and configuration
files.
"Object" form shall mean any form resulting from mechanical transformation or
translation of a Source form, including but not limited to compiled object code,
generated documentation, and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or Object form, made
available under the License, as indicated by a copyright notice that is included
in or attached to the work (an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object form, that
is based on (or derived from) the Work and for which the editorial revisions,
annotations, elaborations, or other modifications represent, as a whole, an
original work of authorship. For the purposes of this License, Derivative Works
shall not include works that remain separable from, or merely link (or bind by
name) to the interfaces of, the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including the original version
of the Work and any modifications or additions to that Work or Derivative Works
thereof, that is intentionally submitted to Licensor for inclusion in the Work
by the copyright owner or by an individual or Legal Entity authorized to submit
on behalf of the copyright owner. For the purposes of this definition,
"submitted" means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems, and
issue tracking systems that are managed by, or on behalf of, the Licensor for
the purpose of discussing and improving the Work, but excluding communication
that is conspicuously marked or otherwise designated in writing by the copyright
owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
of whom a Contribution has been received by Licensor and subsequently
incorporated within the Work.
2. Grant of Copyright License.
Subject to the terms and conditions of this License, each Contributor hereby
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the Work and such
Derivative Works in Source or Object form.
3. Grant of Patent License.
Subject to the terms and conditions of this License, each Contributor hereby
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable (except as stated in this section) patent license to make, have
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
such license applies only to those patent claims licensable by such Contributor
that are necessarily infringed by their Contribution(s) alone or by combination
of their Contribution(s) with the Work to which such Contribution(s) was
submitted. If You institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
Contribution incorporated within the Work constitutes direct or contributory
patent infringement, then any patent licenses granted to You under this License
for that Work shall terminate as of the date such litigation is filed.
4. Redistribution.
You may reproduce and distribute copies of the Work or Derivative Works thereof
in any medium, with or without modifications, and in Source or Object form,
provided that You meet the following conditions:
You must give any other recipients of the Work or Derivative Works a copy of
this License; and
You must cause any modified files to carry prominent notices stating that You
changed the files; and
You must retain, in the Source form of any Derivative Works that You distribute,
all copyright, patent, trademark, and attribution notices from the Source form
of the Work, excluding those notices that do not pertain to any part of the
Derivative Works; and
If the Work includes a "NOTICE" text file as part of its distribution, then any
Derivative Works that You distribute must include a readable copy of the
attribution notices contained within such NOTICE file, excluding those notices
that do not pertain to any part of the Derivative Works, in at least one of the
following places: within a NOTICE text file distributed as part of the
Derivative Works; within the Source form or documentation, if provided along
with the Derivative Works; or, within a display generated by the Derivative
Works, if and wherever such third-party notices normally appear. The contents of
the NOTICE file are for informational purposes only and do not modify the
License. You may add Your own attribution notices within Derivative Works that
You distribute, alongside or as an addendum to the NOTICE text from the Work,
provided that such additional attribution notices cannot be construed as
modifying the License.
You may add Your own copyright statement to Your modifications and may provide
additional or different license terms and conditions for use, reproduction, or
distribution of Your modifications, or for any such Derivative Works as a whole,
provided Your use, reproduction, and distribution of the Work otherwise complies
with the conditions stated in this License.
5. Submission of Contributions.
Unless You explicitly state otherwise, any Contribution intentionally submitted
for inclusion in the Work by You to the Licensor shall be under the terms and
conditions of this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify the terms of
any separate license agreement you may have executed with Licensor regarding
such Contributions.
6. Trademarks.
This License does not grant permission to use the trade names, trademarks,
service marks, or product names of the Licensor, except as required for
reasonable and customary use in describing the origin of the Work and
reproducing the content of the NOTICE file.
7. Disclaimer of Warranty.
Unless required by applicable law or agreed to in writing, Licensor provides the
Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
including, without limitation, any warranties or conditions of TITLE,
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
solely responsible for determining the appropriateness of using or
redistributing the Work and assume any risks associated with Your exercise of
permissions under this License.
8. Limitation of Liability.
In no event and under no legal theory, whether in tort (including negligence),
contract, or otherwise, unless required by applicable law (such as deliberate
and grossly negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special, incidental,
or consequential damages of any character arising as a result of this License or
out of the use or inability to use the Work (including but not limited to
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
any and all other commercial damages or losses), even if such Contributor has
been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability.
While redistributing the Work or Derivative Works thereof, You may choose to
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
other liability obligations and/or rights consistent with this License. However,
in accepting such obligations, You may act only on Your own behalf and on Your
sole responsibility, not on behalf of any other Contributor, and only if You
agree to indemnify, defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason of your
accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work
To apply the Apache License to your work, attach the following boilerplate
notice, with the fields enclosed by brackets "[]" replaced with your own
identifying information. (Don't include the brackets!) The text should be
enclosed in the appropriate comment syntax for the file format. We also
recommend that a file or class name and description of purpose be included on
the same "printed page" as the copyright notice for easier identification within
third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

16
e2e/vendor/k8s.io/klog/v2/OWNERS generated vendored Normal file
View File

@ -0,0 +1,16 @@
# See the OWNERS docs at https://go.k8s.io/owners
reviewers:
- harshanarayana
- mengjiao-liu
- pohly
approvers:
- dims
- pohly
- thockin
emeritus_approvers:
- brancz
- justinsb
- lavalamp
- piosz
- serathius
- tallclair

118
e2e/vendor/k8s.io/klog/v2/README.md generated vendored Normal file
View File

@ -0,0 +1,118 @@
klog
====
klog is a permanent fork of https://github.com/golang/glog.
## Why was klog created?
The decision to create klog was one that wasn't made lightly, but it was necessary due to some
drawbacks that are present in [glog](https://github.com/golang/glog). Ultimately, the fork was created due to glog not being under active development; this can be seen in the glog README:
> The code in this repo [...] is not itself under development
This makes us unable to solve many use cases without a fork. The factors that contributed to needing feature development are listed below:
* `glog` [presents a lot "gotchas"](https://github.com/kubernetes/kubernetes/issues/61006) and introduces challenges in containerized environments, all of which aren't well documented.
* `glog` doesn't provide an easy way to test logs, which detracts from the stability of software using it
* A long term goal is to implement a logging interface that allows us to add context, change output format, etc.
Historical context is available here:
* https://github.com/kubernetes/kubernetes/issues/61006
* https://github.com/kubernetes/kubernetes/issues/70264
* https://groups.google.com/forum/#!msg/kubernetes-sig-architecture/wCWiWf3Juzs/hXRVBH90CgAJ
* https://groups.google.com/forum/#!msg/kubernetes-dev/7vnijOMhLS0/1oRiNtigBgAJ
## Release versioning
Semantic versioning is used in this repository. It contains several Go modules
with different levels of stability:
- `k8s.io/klog/v2` - stable API, `vX.Y.Z` tags
- `examples` - no stable API, no tags, no intention to ever stabilize
Exempt from the API stability guarantee are items (packages, functions, etc.)
which are marked explicitly as `EXPERIMENTAL` in their docs comment. Those
may still change in incompatible ways or get removed entirely. This can only
be used for code that is used in tests to avoid situations where non-test
code from two different Kubernetes dependencies depends on incompatible
releases of klog because an experimental API was changed.
----
How to use klog
===============
- Replace imports for `"github.com/golang/glog"` with `"k8s.io/klog/v2"`
- Use `klog.InitFlags(nil)` explicitly for initializing global flags as we no longer use `init()` method to register the flags
- You can now use `log_file` instead of `log_dir` for logging to a single file (See `examples/log_file/usage_log_file.go`)
- If you want to redirect everything logged using klog somewhere else (say syslog!), you can use `klog.SetOutput()` method and supply a `io.Writer`. (See `examples/set_output/usage_set_output.go`)
- For more logging conventions (See [Logging Conventions](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-instrumentation/logging.md))
- See our documentation on [pkg.go.dev/k8s.io](https://pkg.go.dev/k8s.io/klog).
**NOTE**: please use the newer go versions that support semantic import versioning in modules, ideally go 1.11.4 or greater.
### Coexisting with klog/v2
See [this example](examples/coexist_klog_v1_and_v2/) to see how to coexist with both klog/v1 and klog/v2.
### Coexisting with glog
This package can be used side by side with glog. [This example](examples/coexist_glog/coexist_glog.go) shows how to initialize and synchronize flags from the global `flag.CommandLine` FlagSet. In addition, the example makes use of stderr as combined output by setting `alsologtostderr` (or `logtostderr`) to `true`.
## Community, discussion, contribution, and support
Learn how to engage with the Kubernetes community on the [community page](http://kubernetes.io/community/).
You can reach the maintainers of this project at:
- [Slack](https://kubernetes.slack.com/messages/klog)
- [Mailing List](https://groups.google.com/forum/#!forum/kubernetes-sig-architecture)
### Code of conduct
Participation in the Kubernetes community is governed by the [Kubernetes Code of Conduct](code-of-conduct.md).
----
glog
====
Leveled execution logs for Go.
This is an efficient pure Go implementation of leveled logs in the
manner of the open source C++ package
https://github.com/google/glog
By binding methods to booleans it is possible to use the log package
without paying the expense of evaluating the arguments to the log.
Through the -vmodule flag, the package also provides fine-grained
control over logging at the file level.
The comment from glog.go introduces the ideas:
Package glog implements logging analogous to the Google-internal
C++ INFO/ERROR/V setup. It provides functions Info, Warning,
Error, Fatal, plus formatting variants such as Infof. It
also provides V-style logging controlled by the -v and
-vmodule=file=2 flags.
Basic examples:
glog.Info("Prepare to repel boarders")
glog.Fatalf("Initialization failed: %s", err)
See the documentation of the V function for an explanation
of these examples:
if glog.V(2) {
glog.Info("Starting transaction...")
}
glog.V(2).Infoln("Processed", nItems, "elements")
The repository contains an open source version of the log package
used inside Google. The master copy of the source lives inside
Google, not here. The code in this repo is for export only and is not itself
under development. Feature requests will be ignored.
Send bug reports to golang-nuts@googlegroups.com.

9
e2e/vendor/k8s.io/klog/v2/RELEASE.md generated vendored Normal file
View File

@ -0,0 +1,9 @@
# Release Process
The `klog` is released on an as-needed basis. The process is as follows:
1. An issue is proposing a new release with a changelog since the last release
1. All [OWNERS](OWNERS) must LGTM this release
1. An OWNER runs `git tag -s $VERSION` and inserts the changelog and pushes the tag with `git push $VERSION`
1. The release issue is closed
1. An announcement email is sent to `kubernetes-dev@googlegroups.com` with the subject `[ANNOUNCE] kubernetes-template-project $VERSION is released`

22
e2e/vendor/k8s.io/klog/v2/SECURITY.md generated vendored Normal file
View File

@ -0,0 +1,22 @@
# Security Policy
## Security Announcements
Join the [kubernetes-security-announce] group for security and vulnerability announcements.
You can also subscribe to an RSS feed of the above using [this link][kubernetes-security-announce-rss].
## Reporting a Vulnerability
Instructions for reporting a vulnerability can be found on the
[Kubernetes Security and Disclosure Information] page.
## Supported Versions
Information about supported Kubernetes versions can be found on the
[Kubernetes version and version skew support policy] page on the Kubernetes website.
[kubernetes-security-announce]: https://groups.google.com/forum/#!forum/kubernetes-security-announce
[kubernetes-security-announce-rss]: https://groups.google.com/forum/feed/kubernetes-security-announce/msgs/rss_v2_0.xml?num=50
[Kubernetes version and version skew support policy]: https://kubernetes.io/docs/setup/release/version-skew-policy/#supported-versions
[Kubernetes Security and Disclosure Information]: https://kubernetes.io/docs/reference/issues-security/security/#report-a-vulnerability

20
e2e/vendor/k8s.io/klog/v2/SECURITY_CONTACTS generated vendored Normal file
View File

@ -0,0 +1,20 @@
# Defined below are the security contacts for this repo.
#
# They are the contact point for the Product Security Committee to reach out
# to for triaging and handling of incoming issues.
#
# The below names agree to abide by the
# [Embargo Policy](https://git.k8s.io/security/private-distributors-list.md#embargo-policy)
# and will be removed and replaced if they violate that agreement.
#
# DO NOT REPORT SECURITY VULNERABILITIES DIRECTLY TO THESE NAMES, FOLLOW THE
# INSTRUCTIONS AT https://kubernetes.io/security/
dims
thockin
justinsb
tallclair
piosz
brancz
DirectXMan12
lavalamp

3
e2e/vendor/k8s.io/klog/v2/code-of-conduct.md generated vendored Normal file
View File

@ -0,0 +1,3 @@
# Kubernetes Community Code of Conduct
Please refer to our [Kubernetes Community Code of Conduct](https://git.k8s.io/community/code-of-conduct.md)

212
e2e/vendor/k8s.io/klog/v2/contextual.go generated vendored Normal file
View File

@ -0,0 +1,212 @@
/*
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 klog
import (
"context"
"github.com/go-logr/logr"
)
// This file provides the implementation of
// https://github.com/kubernetes/enhancements/tree/master/keps/sig-instrumentation/1602-structured-logging
//
// SetLogger and ClearLogger were originally added to klog.go and got moved
// here. Contextual logging adds a way to retrieve a Logger for direct logging
// without the logging calls in klog.go.
//
// The global variables are expected to be modified only during sequential
// parts of a program (init, serial tests) and therefore are not protected by
// mutex locking.
var (
// klogLogger is used as fallback for logging through the normal klog code
// when no Logger is set.
klogLogger logr.Logger = logr.New(&klogger{})
)
// SetLogger sets a Logger implementation that will be used as backing
// implementation of the traditional klog log calls. klog will do its own
// verbosity checks before calling logger.V().Info. logger.Error is always
// called, regardless of the klog verbosity settings.
//
// If set, all log lines will be suppressed from the regular output, and
// redirected to the logr implementation.
// Use as:
//
// ...
// klog.SetLogger(zapr.NewLogger(zapLog))
//
// To remove a backing logr implemention, use ClearLogger. Setting an
// empty logger with SetLogger(logr.Logger{}) does not work.
//
// Modifying the logger is not thread-safe and should be done while no other
// goroutines invoke log calls, usually during program initialization.
func SetLogger(logger logr.Logger) {
SetLoggerWithOptions(logger)
}
// SetLoggerWithOptions is a more flexible version of SetLogger. Without
// additional options, it behaves exactly like SetLogger. By passing
// ContextualLogger(true) as option, it can be used to set a logger that then
// will also get called directly by applications which retrieve it via
// FromContext, Background, or TODO.
//
// Supporting direct calls is recommended because it avoids the overhead of
// routing log entries through klogr into klog and then into the actual Logger
// backend.
func SetLoggerWithOptions(logger logr.Logger, opts ...LoggerOption) {
logging.loggerOptions = loggerOptions{}
for _, opt := range opts {
opt(&logging.loggerOptions)
}
logging.logger = &logWriter{
Logger: logger,
writeKlogBuffer: logging.loggerOptions.writeKlogBuffer,
}
}
// ContextualLogger determines whether the logger passed to
// SetLoggerWithOptions may also get called directly. Such a logger cannot rely
// on verbosity checking in klog.
func ContextualLogger(enabled bool) LoggerOption {
return func(o *loggerOptions) {
o.contextualLogger = enabled
}
}
// FlushLogger provides a callback for flushing data buffered by the logger.
func FlushLogger(flush func()) LoggerOption {
return func(o *loggerOptions) {
o.flush = flush
}
}
// WriteKlogBuffer sets a callback that will be invoked by klog to write output
// produced by non-structured log calls like Infof.
//
// The buffer will contain exactly the same data that klog normally would write
// into its own output stream(s). In particular this includes the header, if
// klog is configured to write one. The callback then can divert that data into
// its own output streams. The buffer may or may not end in a line break.
//
// Without such a callback, klog will call the logger's Info or Error method
// with just the message string (i.e. no header).
func WriteKlogBuffer(write func([]byte)) LoggerOption {
return func(o *loggerOptions) {
o.writeKlogBuffer = write
}
}
// LoggerOption implements the functional parameter paradigm for
// SetLoggerWithOptions.
type LoggerOption func(o *loggerOptions)
type loggerOptions struct {
contextualLogger bool
flush func()
writeKlogBuffer func([]byte)
}
// logWriter combines a logger (always set) with a write callback (optional).
type logWriter struct {
Logger
writeKlogBuffer func([]byte)
}
// ClearLogger removes a backing Logger implementation if one was set earlier
// with SetLogger.
//
// Modifying the logger is not thread-safe and should be done while no other
// goroutines invoke log calls, usually during program initialization.
func ClearLogger() {
logging.logger = nil
logging.loggerOptions = loggerOptions{}
}
// EnableContextualLogging controls whether contextual logging is enabled.
// By default it is enabled. When disabled, FromContext avoids looking up
// the logger in the context and always returns the global logger.
// LoggerWithValues, LoggerWithName, and NewContext become no-ops
// and return their input logger respectively context. This may be useful
// to avoid the additional overhead for contextual logging.
//
// This must be called during initialization before goroutines are started.
func EnableContextualLogging(enabled bool) {
logging.contextualLoggingEnabled = enabled
}
// FromContext retrieves a logger set by the caller or, if not set,
// falls back to the program's global logger (a Logger instance or klog
// itself).
func FromContext(ctx context.Context) Logger {
if logging.contextualLoggingEnabled {
if logger, err := logr.FromContext(ctx); err == nil {
return logger
}
}
return Background()
}
// TODO can be used as a last resort by code that has no means of
// receiving a logger from its caller. FromContext or an explicit logger
// parameter should be used instead.
func TODO() Logger {
return Background()
}
// Background retrieves the fallback logger. It should not be called before
// that logger was initialized by the program and not by code that should
// better receive a logger via its parameters. TODO can be used as a temporary
// solution for such code.
func Background() Logger {
if logging.loggerOptions.contextualLogger {
// Is non-nil because logging.loggerOptions.contextualLogger is
// only true if a logger was set.
return logging.logger.Logger
}
return klogLogger
}
// LoggerWithValues returns logger.WithValues(...kv) when
// contextual logging is enabled, otherwise the logger.
func LoggerWithValues(logger Logger, kv ...interface{}) Logger {
if logging.contextualLoggingEnabled {
return logger.WithValues(kv...)
}
return logger
}
// LoggerWithName returns logger.WithName(name) when contextual logging is
// enabled, otherwise the logger.
func LoggerWithName(logger Logger, name string) Logger {
if logging.contextualLoggingEnabled {
return logger.WithName(name)
}
return logger
}
// NewContext returns logr.NewContext(ctx, logger) when
// contextual logging is enabled, otherwise ctx.
func NewContext(ctx context.Context, logger Logger) context.Context {
if logging.contextualLoggingEnabled {
return logr.NewContext(ctx, logger)
}
return ctx
}

31
e2e/vendor/k8s.io/klog/v2/contextual_slog.go generated vendored Normal file
View File

@ -0,0 +1,31 @@
//go:build go1.21
// +build go1.21
/*
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 klog
import (
"log/slog"
"github.com/go-logr/logr"
)
// SetSlogLogger reconfigures klog to log through the slog logger. The logger must not be nil.
func SetSlogLogger(logger *slog.Logger) {
SetLoggerWithOptions(logr.FromSlogHandler(logger.Handler()), ContextualLogger(true))
}

69
e2e/vendor/k8s.io/klog/v2/exit.go generated vendored Normal file
View File

@ -0,0 +1,69 @@
// Go support for leveled logs, analogous to https://code.google.com/p/google-glog/
//
// Copyright 2013 Google Inc. All Rights Reserved.
// 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 klog
import (
"fmt"
"os"
"time"
)
var (
// ExitFlushTimeout is the timeout that klog has traditionally used during
// calls like Fatal or Exit when flushing log data right before exiting.
// Applications that replace those calls and do not have some specific
// requirements like "exit immediately" can use this value as parameter
// for FlushAndExit.
//
// Can be set for testing purpose or to change the application's
// default.
ExitFlushTimeout = 10 * time.Second
// OsExit is the function called by FlushAndExit to terminate the program.
//
// Can be set for testing purpose or to change the application's
// default behavior. Note that the function should not simply return
// because callers of functions like Fatal will not expect that.
OsExit = os.Exit
)
// FlushAndExit flushes log data for a certain amount of time and then calls
// os.Exit. Combined with some logging call it provides a replacement for
// traditional calls like Fatal or Exit.
func FlushAndExit(flushTimeout time.Duration, exitCode int) {
timeoutFlush(flushTimeout)
OsExit(exitCode)
}
// timeoutFlush calls Flush and returns when it completes or after timeout
// elapses, whichever happens first. This is needed because the hooks invoked
// by Flush may deadlock when klog.Fatal is called from a hook that holds
// a lock. Flushing also might take too long.
func timeoutFlush(timeout time.Duration) {
done := make(chan bool, 1)
go func() {
Flush() // calls logging.lockAndFlushAll()
done <- true
}()
select {
case <-done:
case <-time.After(timeout):
fmt.Fprintln(os.Stderr, "klog: Flush took longer than", timeout)
}
}

65
e2e/vendor/k8s.io/klog/v2/format.go generated vendored Normal file
View File

@ -0,0 +1,65 @@
/*
Copyright 2023 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 klog
import (
"encoding/json"
"fmt"
"strings"
"github.com/go-logr/logr"
)
// Format wraps a value of an arbitrary type and implement fmt.Stringer and
// logr.Marshaler for them. Stringer returns pretty-printed JSON. MarshalLog
// returns the original value with a type that has no special methods, in
// particular no MarshalLog or MarshalJSON.
//
// Wrapping values like that is useful when the value has a broken
// implementation of these special functions (for example, a type which
// inherits String from TypeMeta, but then doesn't re-implement String) or the
// implementation produces output that is less readable or unstructured (for
// example, the generated String functions for Kubernetes API types).
func Format(obj interface{}) interface{} {
return formatAny{Object: obj}
}
type formatAny struct {
Object interface{}
}
func (f formatAny) String() string {
var buffer strings.Builder
encoder := json.NewEncoder(&buffer)
encoder.SetIndent("", " ")
if err := encoder.Encode(&f.Object); err != nil {
return fmt.Sprintf("error marshaling %T to JSON: %v", f, err)
}
return buffer.String()
}
func (f formatAny) MarshalLog() interface{} {
// Returning a pointer to a pointer ensures that zapr doesn't find a
// fmt.Stringer or logr.Marshaler when it checks the type of the
// value. It then falls back to reflection, which dumps the value being
// pointed to (JSON doesn't have pointers).
ptr := &f.Object
return &ptr
}
var _ fmt.Stringer = formatAny{}
var _ logr.Marshaler = formatAny{}

38
e2e/vendor/k8s.io/klog/v2/imports.go generated vendored Normal file
View File

@ -0,0 +1,38 @@
/*
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 klog
import (
"github.com/go-logr/logr"
)
// The reason for providing these aliases is to allow code to work with logr
// without directly importing it.
// Logger in this package is exactly the same as logr.Logger.
type Logger = logr.Logger
// LogSink in this package is exactly the same as logr.LogSink.
type LogSink = logr.LogSink
// Runtimeinfo in this package is exactly the same as logr.RuntimeInfo.
type RuntimeInfo = logr.RuntimeInfo
var (
// New is an alias for logr.New.
New = logr.New
)

184
e2e/vendor/k8s.io/klog/v2/internal/buffer/buffer.go generated vendored Normal file
View File

@ -0,0 +1,184 @@
// Copyright 2013 Google Inc. All Rights Reserved.
// 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 buffer provides a cache for byte.Buffer instances that can be reused
// to avoid frequent allocation and deallocation. It also has utility code
// for log header formatting that use these buffers.
package buffer
import (
"bytes"
"os"
"sync"
"time"
"k8s.io/klog/v2/internal/severity"
)
var (
// Pid is inserted into log headers. Can be overridden for tests.
Pid = os.Getpid()
// Time, if set, will be used instead of the actual current time.
Time *time.Time
)
// Buffer holds a single byte.Buffer for reuse. The zero value is ready for
// use. It also provides some helper methods for output formatting.
type Buffer struct {
bytes.Buffer
Tmp [64]byte // temporary byte array for creating headers.
}
var buffers = sync.Pool{
New: func() interface{} {
return new(Buffer)
},
}
// GetBuffer returns a new, ready-to-use buffer.
func GetBuffer() *Buffer {
b := buffers.Get().(*Buffer)
b.Reset()
return b
}
// PutBuffer returns a buffer to the free list.
func PutBuffer(b *Buffer) {
if b.Len() >= 256 {
// Let big buffers die a natural death, without relying on
// sync.Pool behavior. The documentation implies that items may
// get deallocated while stored there ("If the Pool holds the
// only reference when this [= be removed automatically]
// happens, the item might be deallocated."), but
// https://github.com/golang/go/issues/23199 leans more towards
// having such a size limit.
return
}
buffers.Put(b)
}
// Some custom tiny helper functions to print the log header efficiently.
const digits = "0123456789"
// twoDigits formats a zero-prefixed two-digit integer at buf.Tmp[i].
func (buf *Buffer) twoDigits(i, d int) {
buf.Tmp[i+1] = digits[d%10]
d /= 10
buf.Tmp[i] = digits[d%10]
}
// nDigits formats an n-digit integer at buf.Tmp[i],
// padding with pad on the left.
// It assumes d >= 0.
func (buf *Buffer) nDigits(n, i, d int, pad byte) {
j := n - 1
for ; j >= 0 && d > 0; j-- {
buf.Tmp[i+j] = digits[d%10]
d /= 10
}
for ; j >= 0; j-- {
buf.Tmp[i+j] = pad
}
}
// someDigits formats a zero-prefixed variable-width integer at buf.Tmp[i].
func (buf *Buffer) someDigits(i, d int) int {
// Print into the top, then copy down. We know there's space for at least
// a 10-digit number.
j := len(buf.Tmp)
for {
j--
buf.Tmp[j] = digits[d%10]
d /= 10
if d == 0 {
break
}
}
return copy(buf.Tmp[i:], buf.Tmp[j:])
}
// FormatHeader formats a log header using the provided file name and line number
// and writes it into the buffer.
func (buf *Buffer) FormatHeader(s severity.Severity, file string, line int, now time.Time) {
if line < 0 {
line = 0 // not a real line number, but acceptable to someDigits
}
if s > severity.FatalLog {
s = severity.InfoLog // for safety.
}
// Avoid Fprintf, for speed. The format is so simple that we can do it quickly by hand.
// It's worth about 3X. Fprintf is hard.
if Time != nil {
now = *Time
}
_, month, day := now.Date()
hour, minute, second := now.Clock()
// Lmmdd hh:mm:ss.uuuuuu threadid file:line]
buf.Tmp[0] = severity.Char[s]
buf.twoDigits(1, int(month))
buf.twoDigits(3, day)
buf.Tmp[5] = ' '
buf.twoDigits(6, hour)
buf.Tmp[8] = ':'
buf.twoDigits(9, minute)
buf.Tmp[11] = ':'
buf.twoDigits(12, second)
buf.Tmp[14] = '.'
buf.nDigits(6, 15, now.Nanosecond()/1000, '0')
buf.Tmp[21] = ' '
buf.nDigits(7, 22, Pid, ' ') // TODO: should be TID
buf.Tmp[29] = ' '
buf.Write(buf.Tmp[:30])
buf.WriteString(file)
buf.Tmp[0] = ':'
n := buf.someDigits(1, line)
buf.Tmp[n+1] = ']'
buf.Tmp[n+2] = ' '
buf.Write(buf.Tmp[:n+3])
}
// SprintHeader formats a log header and returns a string. This is a simpler
// version of FormatHeader for use in ktesting.
func (buf *Buffer) SprintHeader(s severity.Severity, now time.Time) string {
if s > severity.FatalLog {
s = severity.InfoLog // for safety.
}
// Avoid Fprintf, for speed. The format is so simple that we can do it quickly by hand.
// It's worth about 3X. Fprintf is hard.
if Time != nil {
now = *Time
}
_, month, day := now.Date()
hour, minute, second := now.Clock()
// Lmmdd hh:mm:ss.uuuuuu threadid file:line]
buf.Tmp[0] = severity.Char[s]
buf.twoDigits(1, int(month))
buf.twoDigits(3, day)
buf.Tmp[5] = ' '
buf.twoDigits(6, hour)
buf.Tmp[8] = ':'
buf.twoDigits(9, minute)
buf.Tmp[11] = ':'
buf.twoDigits(12, second)
buf.Tmp[14] = '.'
buf.nDigits(6, 15, now.Nanosecond()/1000, '0')
buf.Tmp[21] = ']'
return string(buf.Tmp[:22])
}

7
e2e/vendor/k8s.io/klog/v2/internal/clock/README.md generated vendored Normal file
View File

@ -0,0 +1,7 @@
# Clock
This package provides an interface for time-based operations. It allows
mocking time for testing.
This is a copy of k8s.io/utils/clock. We have to copy it to avoid a circular
dependency (k8s.io/klog -> k8s.io/utils -> k8s.io/klog).

161
e2e/vendor/k8s.io/klog/v2/internal/clock/clock.go generated vendored Normal file
View File

@ -0,0 +1,161 @@
/*
Copyright 2014 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 clock
import "time"
// PassiveClock allows for injecting fake or real clocks into code
// that needs to read the current time but does not support scheduling
// activity in the future.
type PassiveClock interface {
Now() time.Time
Since(time.Time) time.Duration
}
// Clock allows for injecting fake or real clocks into code that
// needs to do arbitrary things based on time.
type Clock interface {
PassiveClock
// After returns the channel of a new Timer.
// This method does not allow to free/GC the backing timer before it fires. Use
// NewTimer instead.
After(d time.Duration) <-chan time.Time
// NewTimer returns a new Timer.
NewTimer(d time.Duration) Timer
// Sleep sleeps for the provided duration d.
// Consider making the sleep interruptible by using 'select' on a context channel and a timer channel.
Sleep(d time.Duration)
// NewTicker returns a new Ticker.
NewTicker(time.Duration) Ticker
}
// WithDelayedExecution allows for injecting fake or real clocks into
// code that needs to make use of AfterFunc functionality.
type WithDelayedExecution interface {
Clock
// AfterFunc executes f in its own goroutine after waiting
// for d duration and returns a Timer whose channel can be
// closed by calling Stop() on the Timer.
AfterFunc(d time.Duration, f func()) Timer
}
// WithTickerAndDelayedExecution allows for injecting fake or real clocks
// into code that needs Ticker and AfterFunc functionality
type WithTickerAndDelayedExecution interface {
Clock
// AfterFunc executes f in its own goroutine after waiting
// for d duration and returns a Timer whose channel can be
// closed by calling Stop() on the Timer.
AfterFunc(d time.Duration, f func()) Timer
}
// Ticker defines the Ticker interface.
type Ticker interface {
C() <-chan time.Time
Stop()
}
var _ Clock = RealClock{}
// RealClock really calls time.Now()
type RealClock struct{}
// Now returns the current time.
func (RealClock) Now() time.Time {
return time.Now()
}
// Since returns time since the specified timestamp.
func (RealClock) Since(ts time.Time) time.Duration {
return time.Since(ts)
}
// After is the same as time.After(d).
// This method does not allow to free/GC the backing timer before it fires. Use
// NewTimer instead.
func (RealClock) After(d time.Duration) <-chan time.Time {
return time.After(d)
}
// NewTimer is the same as time.NewTimer(d)
func (RealClock) NewTimer(d time.Duration) Timer {
return &realTimer{
timer: time.NewTimer(d),
}
}
// AfterFunc is the same as time.AfterFunc(d, f).
func (RealClock) AfterFunc(d time.Duration, f func()) Timer {
return &realTimer{
timer: time.AfterFunc(d, f),
}
}
// NewTicker returns a new Ticker.
func (RealClock) NewTicker(d time.Duration) Ticker {
return &realTicker{
ticker: time.NewTicker(d),
}
}
// Sleep is the same as time.Sleep(d)
// Consider making the sleep interruptible by using 'select' on a context channel and a timer channel.
func (RealClock) Sleep(d time.Duration) {
time.Sleep(d)
}
// Timer allows for injecting fake or real timers into code that
// needs to do arbitrary things based on time.
type Timer interface {
C() <-chan time.Time
Stop() bool
Reset(d time.Duration) bool
}
var _ = Timer(&realTimer{})
// realTimer is backed by an actual time.Timer.
type realTimer struct {
timer *time.Timer
}
// C returns the underlying timer's channel.
func (r *realTimer) C() <-chan time.Time {
return r.timer.C
}
// Stop calls Stop() on the underlying timer.
func (r *realTimer) Stop() bool {
return r.timer.Stop()
}
// Reset calls Reset() on the underlying timer.
func (r *realTimer) Reset(d time.Duration) bool {
return r.timer.Reset(d)
}
type realTicker struct {
ticker *time.Ticker
}
func (r *realTicker) C() <-chan time.Time {
return r.ticker.C
}
func (r *realTicker) Stop() {
r.ticker.Stop()
}

42
e2e/vendor/k8s.io/klog/v2/internal/dbg/dbg.go generated vendored Normal file
View File

@ -0,0 +1,42 @@
// Go support for leveled logs, analogous to https://code.google.com/p/google-glog/
//
// Copyright 2013 Google Inc. All Rights Reserved.
//
// 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 dbg provides some helper code for call traces.
package dbg
import (
"runtime"
)
// Stacks is a wrapper for runtime.Stack that attempts to recover the data for
// all goroutines or the calling one.
func Stacks(all bool) []byte {
// We don't know how big the traces are, so grow a few times if they don't fit. Start large, though.
n := 10000
if all {
n = 100000
}
var trace []byte
for i := 0; i < 5; i++ {
trace = make([]byte, n)
nbytes := runtime.Stack(trace, all)
if nbytes < len(trace) {
return trace[:nbytes]
}
n *= 2
}
return trace
}

View File

@ -0,0 +1,292 @@
/*
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 serialize
import (
"bytes"
"encoding/json"
"fmt"
"strconv"
"github.com/go-logr/logr"
)
type textWriter interface {
WriteText(*bytes.Buffer)
}
// WithValues implements LogSink.WithValues. The old key/value pairs are
// assumed to be well-formed, the new ones are checked and padded if
// necessary. It returns a new slice.
func WithValues(oldKV, newKV []interface{}) []interface{} {
if len(newKV) == 0 {
return oldKV
}
newLen := len(oldKV) + len(newKV)
hasMissingValue := newLen%2 != 0
if hasMissingValue {
newLen++
}
// The new LogSink must have its own slice.
kv := make([]interface{}, 0, newLen)
kv = append(kv, oldKV...)
kv = append(kv, newKV...)
if hasMissingValue {
kv = append(kv, missingValue)
}
return kv
}
// MergeKVs deduplicates elements provided in two key/value slices.
//
// Keys in each slice are expected to be unique, so duplicates can only occur
// when the first and second slice contain the same key. When that happens, the
// key/value pair from the second slice is used. The first slice must be well-formed
// (= even key/value pairs). The second one may have a missing value, in which
// case the special "missing value" is added to the result.
func MergeKVs(first, second []interface{}) []interface{} {
maxLength := len(first) + (len(second)+1)/2*2
if maxLength == 0 {
// Nothing to do at all.
return nil
}
if len(first) == 0 && len(second)%2 == 0 {
// Nothing to be overridden, second slice is well-formed
// and can be used directly.
return second
}
// Determine which keys are in the second slice so that we can skip
// them when iterating over the first one. The code intentionally
// favors performance over completeness: we assume that keys are string
// constants and thus compare equal when the string values are equal. A
// string constant being overridden by, for example, a fmt.Stringer is
// not handled.
overrides := map[interface{}]bool{}
for i := 0; i < len(second); i += 2 {
overrides[second[i]] = true
}
merged := make([]interface{}, 0, maxLength)
for i := 0; i+1 < len(first); i += 2 {
key := first[i]
if overrides[key] {
continue
}
merged = append(merged, key, first[i+1])
}
merged = append(merged, second...)
if len(merged)%2 != 0 {
merged = append(merged, missingValue)
}
return merged
}
type Formatter struct {
AnyToStringHook AnyToStringFunc
}
type AnyToStringFunc func(v interface{}) string
// MergeKVsInto is a variant of MergeKVs which directly formats the key/value
// pairs into a buffer.
func (f Formatter) MergeAndFormatKVs(b *bytes.Buffer, first, second []interface{}) {
if len(first) == 0 && len(second) == 0 {
// Nothing to do at all.
return
}
if len(first) == 0 && len(second)%2 == 0 {
// Nothing to be overridden, second slice is well-formed
// and can be used directly.
for i := 0; i < len(second); i += 2 {
f.KVFormat(b, second[i], second[i+1])
}
return
}
// Determine which keys are in the second slice so that we can skip
// them when iterating over the first one. The code intentionally
// favors performance over completeness: we assume that keys are string
// constants and thus compare equal when the string values are equal. A
// string constant being overridden by, for example, a fmt.Stringer is
// not handled.
overrides := map[interface{}]bool{}
for i := 0; i < len(second); i += 2 {
overrides[second[i]] = true
}
for i := 0; i < len(first); i += 2 {
key := first[i]
if overrides[key] {
continue
}
f.KVFormat(b, key, first[i+1])
}
// Round down.
l := len(second)
l = l / 2 * 2
for i := 1; i < l; i += 2 {
f.KVFormat(b, second[i-1], second[i])
}
if len(second)%2 == 1 {
f.KVFormat(b, second[len(second)-1], missingValue)
}
}
func MergeAndFormatKVs(b *bytes.Buffer, first, second []interface{}) {
Formatter{}.MergeAndFormatKVs(b, first, second)
}
const missingValue = "(MISSING)"
// KVListFormat serializes all key/value pairs into the provided buffer.
// A space gets inserted before the first pair and between each pair.
func (f Formatter) KVListFormat(b *bytes.Buffer, keysAndValues ...interface{}) {
for i := 0; i < len(keysAndValues); i += 2 {
var v interface{}
k := keysAndValues[i]
if i+1 < len(keysAndValues) {
v = keysAndValues[i+1]
} else {
v = missingValue
}
f.KVFormat(b, k, v)
}
}
func KVListFormat(b *bytes.Buffer, keysAndValues ...interface{}) {
Formatter{}.KVListFormat(b, keysAndValues...)
}
func KVFormat(b *bytes.Buffer, k, v interface{}) {
Formatter{}.KVFormat(b, k, v)
}
// formatAny is the fallback formatter for a value. It supports a hook (for
// example, for YAML encoding) and itself uses JSON encoding.
func (f Formatter) formatAny(b *bytes.Buffer, v interface{}) {
b.WriteRune('=')
if f.AnyToStringHook != nil {
b.WriteString(f.AnyToStringHook(v))
return
}
formatAsJSON(b, v)
}
func formatAsJSON(b *bytes.Buffer, v interface{}) {
encoder := json.NewEncoder(b)
l := b.Len()
if err := encoder.Encode(v); err != nil {
// This shouldn't happen. We discard whatever the encoder
// wrote and instead dump an error string.
b.Truncate(l)
b.WriteString(fmt.Sprintf(`"<internal error: %v>"`, err))
return
}
// Remove trailing newline.
b.Truncate(b.Len() - 1)
}
// StringerToString converts a Stringer to a string,
// handling panics if they occur.
func StringerToString(s fmt.Stringer) (ret string) {
defer func() {
if err := recover(); err != nil {
ret = fmt.Sprintf("<panic: %s>", err)
}
}()
ret = s.String()
return
}
// MarshalerToValue invokes a marshaler and catches
// panics.
func MarshalerToValue(m logr.Marshaler) (ret interface{}) {
defer func() {
if err := recover(); err != nil {
ret = fmt.Sprintf("<panic: %s>", err)
}
}()
ret = m.MarshalLog()
return
}
// ErrorToString converts an error to a string,
// handling panics if they occur.
func ErrorToString(err error) (ret string) {
defer func() {
if err := recover(); err != nil {
ret = fmt.Sprintf("<panic: %s>", err)
}
}()
ret = err.Error()
return
}
func writeTextWriterValue(b *bytes.Buffer, v textWriter) {
b.WriteByte('=')
defer func() {
if err := recover(); err != nil {
fmt.Fprintf(b, `"<panic: %s>"`, err)
}
}()
v.WriteText(b)
}
func writeStringValue(b *bytes.Buffer, v string) {
data := []byte(v)
index := bytes.IndexByte(data, '\n')
if index == -1 {
b.WriteByte('=')
// Simple string, quote quotation marks and non-printable characters.
b.WriteString(strconv.Quote(v))
return
}
// Complex multi-line string, show as-is with indention like this:
// I... "hello world" key=<
// <tab>line 1
// <tab>line 2
// >
//
// Tabs indent the lines of the value while the end of string delimiter
// is indented with a space. That has two purposes:
// - visual difference between the two for a human reader because indention
// will be different
// - no ambiguity when some value line starts with the end delimiter
//
// One downside is that the output cannot distinguish between strings that
// end with a line break and those that don't because the end delimiter
// will always be on the next line.
b.WriteString("=<\n")
for index != -1 {
b.WriteByte('\t')
b.Write(data[0 : index+1])
data = data[index+1:]
index = bytes.IndexByte(data, '\n')
}
if len(data) == 0 {
// String ended with line break, don't add another.
b.WriteString(" >")
} else {
// No line break at end of last line, write rest of string and
// add one.
b.WriteByte('\t')
b.Write(data)
b.WriteString("\n >")
}
}

View File

@ -0,0 +1,97 @@
//go:build !go1.21
// +build !go1.21
/*
Copyright 2023 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 serialize
import (
"bytes"
"fmt"
"github.com/go-logr/logr"
)
// KVFormat serializes one key/value pair into the provided buffer.
// A space gets inserted before the pair.
func (f Formatter) KVFormat(b *bytes.Buffer, k, v interface{}) {
// This is the version without slog support. Must be kept in sync with
// the version in keyvalues_slog.go.
b.WriteByte(' ')
// Keys are assumed to be well-formed according to
// https://github.com/kubernetes/community/blob/master/contributors/devel/sig-instrumentation/migration-to-structured-logging.md#name-arguments
// for the sake of performance. Keys with spaces,
// special characters, etc. will break parsing.
if sK, ok := k.(string); ok {
// Avoid one allocation when the key is a string, which
// normally it should be.
b.WriteString(sK)
} else {
b.WriteString(fmt.Sprintf("%s", k))
}
// The type checks are sorted so that more frequently used ones
// come first because that is then faster in the common
// cases. In Kubernetes, ObjectRef (a Stringer) is more common
// than plain strings
// (https://github.com/kubernetes/kubernetes/pull/106594#issuecomment-975526235).
switch v := v.(type) {
case textWriter:
writeTextWriterValue(b, v)
case fmt.Stringer:
writeStringValue(b, StringerToString(v))
case string:
writeStringValue(b, v)
case error:
writeStringValue(b, ErrorToString(v))
case logr.Marshaler:
value := MarshalerToValue(v)
// A marshaler that returns a string is useful for
// delayed formatting of complex values. We treat this
// case like a normal string. This is useful for
// multi-line support.
//
// We could do this by recursively formatting a value,
// but that comes with the risk of infinite recursion
// if a marshaler returns itself. Instead we call it
// only once and rely on it returning the intended
// value directly.
switch value := value.(type) {
case string:
writeStringValue(b, value)
default:
f.formatAny(b, value)
}
case []byte:
// In https://github.com/kubernetes/klog/pull/237 it was decided
// to format byte slices with "%+q". The advantages of that are:
// - readable output if the bytes happen to be printable
// - non-printable bytes get represented as unicode escape
// sequences (\uxxxx)
//
// The downsides are that we cannot use the faster
// strconv.Quote here and that multi-line output is not
// supported. If developers know that a byte array is
// printable and they want multi-line output, they can
// convert the value to string before logging it.
b.WriteByte('=')
b.WriteString(fmt.Sprintf("%+q", v))
default:
f.formatAny(b, v)
}
}

View File

@ -0,0 +1,155 @@
//go:build go1.21
// +build go1.21
/*
Copyright 2023 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 serialize
import (
"bytes"
"fmt"
"log/slog"
"strconv"
"github.com/go-logr/logr"
)
// KVFormat serializes one key/value pair into the provided buffer.
// A space gets inserted before the pair.
func (f Formatter) KVFormat(b *bytes.Buffer, k, v interface{}) {
// This is the version without slog support. Must be kept in sync with
// the version in keyvalues_slog.go.
b.WriteByte(' ')
// Keys are assumed to be well-formed according to
// https://github.com/kubernetes/community/blob/master/contributors/devel/sig-instrumentation/migration-to-structured-logging.md#name-arguments
// for the sake of performance. Keys with spaces,
// special characters, etc. will break parsing.
if sK, ok := k.(string); ok {
// Avoid one allocation when the key is a string, which
// normally it should be.
b.WriteString(sK)
} else {
b.WriteString(fmt.Sprintf("%s", k))
}
// The type checks are sorted so that more frequently used ones
// come first because that is then faster in the common
// cases. In Kubernetes, ObjectRef (a Stringer) is more common
// than plain strings
// (https://github.com/kubernetes/kubernetes/pull/106594#issuecomment-975526235).
//
// slog.LogValuer does not need to be handled here because the handler will
// already have resolved such special values to the final value for logging.
switch v := v.(type) {
case textWriter:
writeTextWriterValue(b, v)
case slog.Value:
// This must come before fmt.Stringer because slog.Value implements
// fmt.Stringer, but does not produce the output that we want.
b.WriteByte('=')
generateJSON(b, v)
case fmt.Stringer:
writeStringValue(b, StringerToString(v))
case string:
writeStringValue(b, v)
case error:
writeStringValue(b, ErrorToString(v))
case logr.Marshaler:
value := MarshalerToValue(v)
// A marshaler that returns a string is useful for
// delayed formatting of complex values. We treat this
// case like a normal string. This is useful for
// multi-line support.
//
// We could do this by recursively formatting a value,
// but that comes with the risk of infinite recursion
// if a marshaler returns itself. Instead we call it
// only once and rely on it returning the intended
// value directly.
switch value := value.(type) {
case string:
writeStringValue(b, value)
default:
f.formatAny(b, value)
}
case slog.LogValuer:
value := slog.AnyValue(v).Resolve()
if value.Kind() == slog.KindString {
writeStringValue(b, value.String())
} else {
b.WriteByte('=')
generateJSON(b, value)
}
case []byte:
// In https://github.com/kubernetes/klog/pull/237 it was decided
// to format byte slices with "%+q". The advantages of that are:
// - readable output if the bytes happen to be printable
// - non-printable bytes get represented as unicode escape
// sequences (\uxxxx)
//
// The downsides are that we cannot use the faster
// strconv.Quote here and that multi-line output is not
// supported. If developers know that a byte array is
// printable and they want multi-line output, they can
// convert the value to string before logging it.
b.WriteByte('=')
b.WriteString(fmt.Sprintf("%+q", v))
default:
f.formatAny(b, v)
}
}
// generateJSON has the same preference for plain strings as KVFormat.
// In contrast to KVFormat it always produces valid JSON with no line breaks.
func generateJSON(b *bytes.Buffer, v interface{}) {
switch v := v.(type) {
case slog.Value:
switch v.Kind() {
case slog.KindGroup:
// Format as a JSON group. We must not involve f.AnyToStringHook (if there is any),
// because there is no guarantee that it produces valid JSON.
b.WriteByte('{')
for i, attr := range v.Group() {
if i > 0 {
b.WriteByte(',')
}
b.WriteString(strconv.Quote(attr.Key))
b.WriteByte(':')
generateJSON(b, attr.Value)
}
b.WriteByte('}')
case slog.KindLogValuer:
generateJSON(b, v.Resolve())
default:
// Peel off the slog.Value wrapper and format the actual value.
generateJSON(b, v.Any())
}
case fmt.Stringer:
b.WriteString(strconv.Quote(StringerToString(v)))
case logr.Marshaler:
generateJSON(b, MarshalerToValue(v))
case slog.LogValuer:
generateJSON(b, slog.AnyValue(v).Resolve().Any())
case string:
b.WriteString(strconv.Quote(v))
case error:
b.WriteString(strconv.Quote(v.Error()))
default:
formatAsJSON(b, v)
}
}

View File

@ -0,0 +1,58 @@
// Copyright 2013 Google Inc. All Rights Reserved.
// 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 severity provides definitions for klog severity (info, warning, ...)
package severity
import (
"strings"
)
// severity identifies the sort of log: info, warning etc. The binding to flag.Value
// is handled in klog.go
type Severity int32 // sync/atomic int32
// These constants identify the log levels in order of increasing severity.
// A message written to a high-severity log file is also written to each
// lower-severity log file.
const (
InfoLog Severity = iota
WarningLog
ErrorLog
FatalLog
NumSeverity = 4
)
// Char contains one shortcut letter per severity level.
const Char = "IWEF"
// Name contains one name per severity level.
var Name = []string{
InfoLog: "INFO",
WarningLog: "WARNING",
ErrorLog: "ERROR",
FatalLog: "FATAL",
}
// ByName looks up a severity level by name.
func ByName(s string) (Severity, bool) {
s = strings.ToUpper(s)
for i, name := range Name {
if name == s {
return Severity(i), true
}
}
return 0, false
}

View File

@ -0,0 +1,96 @@
//go:build go1.21
// +build go1.21
/*
Copyright 2023 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 sloghandler
import (
"context"
"log/slog"
"runtime"
"strings"
"time"
"k8s.io/klog/v2/internal/severity"
)
func Handle(_ context.Context, record slog.Record, groups string, printWithInfos func(file string, line int, now time.Time, err error, s severity.Severity, msg string, kvList []interface{})) error {
now := record.Time
if now.IsZero() {
// This format doesn't support printing entries without a time.
now = time.Now()
}
// slog has numeric severity levels, with 0 as default "info", negative for debugging, and
// positive with some pre-defined levels for more important. Those ranges get mapped to
// the corresponding klog levels where possible, with "info" the default that is used
// also for negative debug levels.
level := record.Level
s := severity.InfoLog
switch {
case level >= slog.LevelError:
s = severity.ErrorLog
case level >= slog.LevelWarn:
s = severity.WarningLog
}
var file string
var line int
if record.PC != 0 {
// Same as https://cs.opensource.google/go/x/exp/+/642cacee:slog/record.go;drc=642cacee5cc05231f45555a333d07f1005ffc287;l=70
fs := runtime.CallersFrames([]uintptr{record.PC})
f, _ := fs.Next()
if f.File != "" {
file = f.File
if slash := strings.LastIndex(file, "/"); slash >= 0 {
file = file[slash+1:]
}
line = f.Line
}
} else {
file = "???"
line = 1
}
kvList := make([]interface{}, 0, 2*record.NumAttrs())
record.Attrs(func(attr slog.Attr) bool {
kvList = appendAttr(groups, kvList, attr)
return true
})
printWithInfos(file, line, now, nil, s, record.Message, kvList)
return nil
}
func Attrs2KVList(groups string, attrs []slog.Attr) []interface{} {
kvList := make([]interface{}, 0, 2*len(attrs))
for _, attr := range attrs {
kvList = appendAttr(groups, kvList, attr)
}
return kvList
}
func appendAttr(groups string, kvList []interface{}, attr slog.Attr) []interface{} {
var key string
if groups != "" {
key = groups + "." + attr.Key
} else {
key = attr.Key
}
return append(kvList, key, attr.Value)
}

View File

@ -0,0 +1,303 @@
/*
Copyright 2013 Google Inc. All Rights Reserved.
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 verbosity
import (
"bytes"
"errors"
"flag"
"fmt"
"path/filepath"
"runtime"
"strconv"
"strings"
"sync"
"sync/atomic"
)
// New returns a struct that implements -v and -vmodule support. Changing and
// checking these settings is thread-safe, with all concurrency issues handled
// internally.
func New() *VState {
vs := new(VState)
// The two fields must have a pointer to the overal struct for their
// implementation of Set.
vs.vmodule.vs = vs
vs.verbosity.vs = vs
return vs
}
// Value is an extension that makes it possible to use the values in pflag.
type Value interface {
flag.Value
Type() string
}
func (vs *VState) V() Value {
return &vs.verbosity
}
func (vs *VState) VModule() Value {
return &vs.vmodule
}
// VState contains settings and state. Some of its fields can be accessed
// through atomic read/writes, in other cases a mutex must be held.
type VState struct {
mu sync.Mutex
// These flags are modified only under lock, although verbosity may be fetched
// safely using atomic.LoadInt32.
vmodule moduleSpec // The state of the -vmodule flag.
verbosity levelSpec // V logging level, the value of the -v flag/
// pcs is used in V to avoid an allocation when computing the caller's PC.
pcs [1]uintptr
// vmap is a cache of the V Level for each V() call site, identified by PC.
// It is wiped whenever the vmodule flag changes state.
vmap map[uintptr]Level
// filterLength stores the length of the vmodule filter chain. If greater
// than zero, it means vmodule is enabled. It may be read safely
// using sync.LoadInt32, but is only modified under mu.
filterLength int32
}
// Level must be an int32 to support atomic read/writes.
type Level int32
type levelSpec struct {
vs *VState
l Level
}
// get returns the value of the level.
func (l *levelSpec) get() Level {
return Level(atomic.LoadInt32((*int32)(&l.l)))
}
// set sets the value of the level.
func (l *levelSpec) set(val Level) {
atomic.StoreInt32((*int32)(&l.l), int32(val))
}
// String is part of the flag.Value interface.
func (l *levelSpec) String() string {
return strconv.FormatInt(int64(l.l), 10)
}
// Get is part of the flag.Getter interface. It returns the
// verbosity level as int32.
func (l *levelSpec) Get() interface{} {
return int32(l.l)
}
// Type is part of pflag.Value.
func (l *levelSpec) Type() string {
return "Level"
}
// Set is part of the flag.Value interface.
func (l *levelSpec) Set(value string) error {
v, err := strconv.ParseInt(value, 10, 32)
if err != nil {
return err
}
l.vs.mu.Lock()
defer l.vs.mu.Unlock()
l.vs.set(Level(v), l.vs.vmodule.filter, false)
return nil
}
// moduleSpec represents the setting of the -vmodule flag.
type moduleSpec struct {
vs *VState
filter []modulePat
}
// modulePat contains a filter for the -vmodule flag.
// It holds a verbosity level and a file pattern to match.
type modulePat struct {
pattern string
literal bool // The pattern is a literal string
level Level
}
// match reports whether the file matches the pattern. It uses a string
// comparison if the pattern contains no metacharacters.
func (m *modulePat) match(file string) bool {
if m.literal {
return file == m.pattern
}
match, _ := filepath.Match(m.pattern, file)
return match
}
func (m *moduleSpec) String() string {
// Lock because the type is not atomic. TODO: clean this up.
// Empty instances don't have and don't need a lock (can
// happen when flag uses introspection).
if m.vs != nil {
m.vs.mu.Lock()
defer m.vs.mu.Unlock()
}
var b bytes.Buffer
for i, f := range m.filter {
if i > 0 {
b.WriteRune(',')
}
fmt.Fprintf(&b, "%s=%d", f.pattern, f.level)
}
return b.String()
}
// Get is part of the (Go 1.2) flag.Getter interface. It always returns nil for this flag type since the
// struct is not exported.
func (m *moduleSpec) Get() interface{} {
return nil
}
// Type is part of pflag.Value
func (m *moduleSpec) Type() string {
return "pattern=N,..."
}
var errVmoduleSyntax = errors.New("syntax error: expect comma-separated list of filename=N")
// Set will sets module value
// Syntax: -vmodule=recordio=2,file=1,gfs*=3
func (m *moduleSpec) Set(value string) error {
var filter []modulePat
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 errVmoduleSyntax
}
pattern := patLev[0]
v, err := strconv.ParseInt(patLev[1], 10, 32)
if err != nil {
return errors.New("syntax error: expect comma-separated list of filename=N")
}
if v < 0 {
return errors.New("negative value for vmodule level")
}
if v == 0 {
continue // Ignore. It's harmless but no point in paying the overhead.
}
// TODO: check syntax of filter?
filter = append(filter, modulePat{pattern, isLiteral(pattern), Level(v)})
}
m.vs.mu.Lock()
defer m.vs.mu.Unlock()
m.vs.set(m.vs.verbosity.l, filter, true)
return nil
}
// isLiteral reports whether the pattern is a literal string, that is, has no metacharacters
// that require filepath.Match to be called to match the pattern.
func isLiteral(pattern string) bool {
return !strings.ContainsAny(pattern, `\*?[]`)
}
// set sets a consistent state for V logging.
// The mutex must be held.
func (vs *VState) set(l Level, filter []modulePat, setFilter bool) {
// Turn verbosity off so V will not fire while we are in transition.
vs.verbosity.set(0)
// Ditto for filter length.
atomic.StoreInt32(&vs.filterLength, 0)
// Set the new filters and wipe the pc->Level map if the filter has changed.
if setFilter {
vs.vmodule.filter = filter
vs.vmap = make(map[uintptr]Level)
}
// Things are consistent now, so enable filtering and verbosity.
// They are enabled in order opposite to that in V.
atomic.StoreInt32(&vs.filterLength, int32(len(filter)))
vs.verbosity.set(l)
}
// Enabled checks whether logging is enabled at the given level. This must be
// called with depth=0 when the caller of enabled will do the logging and
// higher values when more stack levels need to be skipped.
//
// The mutex will be locked only if needed.
func (vs *VState) Enabled(level Level, depth int) bool {
// This function tries hard to be cheap unless there's work to do.
// The fast path is two atomic loads and compares.
// Here is a cheap but safe test to see if V logging is enabled globally.
if vs.verbosity.get() >= level {
return true
}
// It's off globally but vmodule may still be set.
// Here is another cheap but safe test to see if vmodule is enabled.
if atomic.LoadInt32(&vs.filterLength) > 0 {
// Now we need a proper lock to use the logging structure. The pcs field
// is shared so we must lock before accessing it. This is fairly expensive,
// but if V logging is enabled we're slow anyway.
vs.mu.Lock()
defer vs.mu.Unlock()
if runtime.Callers(depth+2, vs.pcs[:]) == 0 {
return false
}
// runtime.Callers returns "return PCs", but we want
// to look up the symbolic information for the call,
// so subtract 1 from the PC. runtime.CallersFrames
// would be cleaner, but allocates.
pc := vs.pcs[0] - 1
v, ok := vs.vmap[pc]
if !ok {
v = vs.setV(pc)
}
return v >= level
}
return false
}
// setV computes and remembers the V level for a given PC
// when vmodule is enabled.
// File pattern matching takes the basename of the file, stripped
// of its .go suffix, and uses filepath.Match, which is a little more
// general than the *? matching used in C++.
// Mutex is held.
func (vs *VState) setV(pc uintptr) Level {
fn := runtime.FuncForPC(pc)
file, _ := fn.FileLine(pc)
// The file is something like /a/b/c/d.go. We want just the d.
file = strings.TrimSuffix(file, ".go")
if slash := strings.LastIndex(file, "/"); slash >= 0 {
file = file[slash+1:]
}
for _, filter := range vs.vmodule.filter {
if filter.match(file) {
vs.vmap[pc] = filter.level
return filter.level
}
}
vs.vmap[pc] = 0
return 0
}

212
e2e/vendor/k8s.io/klog/v2/k8s_references.go generated vendored Normal file
View File

@ -0,0 +1,212 @@
/*
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 klog
import (
"bytes"
"fmt"
"reflect"
"strings"
"github.com/go-logr/logr"
)
// ObjectRef references a kubernetes object
type ObjectRef struct {
Name string `json:"name"`
Namespace string `json:"namespace,omitempty"`
}
func (ref ObjectRef) String() string {
if ref.Namespace != "" {
var builder strings.Builder
builder.Grow(len(ref.Namespace) + len(ref.Name) + 1)
builder.WriteString(ref.Namespace)
builder.WriteRune('/')
builder.WriteString(ref.Name)
return builder.String()
}
return ref.Name
}
func (ref ObjectRef) WriteText(out *bytes.Buffer) {
out.WriteRune('"')
ref.writeUnquoted(out)
out.WriteRune('"')
}
func (ref ObjectRef) writeUnquoted(out *bytes.Buffer) {
if ref.Namespace != "" {
out.WriteString(ref.Namespace)
out.WriteRune('/')
}
out.WriteString(ref.Name)
}
// MarshalLog ensures that loggers with support for structured output will log
// as a struct by removing the String method via a custom type.
func (ref ObjectRef) MarshalLog() interface{} {
type or ObjectRef
return or(ref)
}
var _ logr.Marshaler = ObjectRef{}
// KMetadata is a subset of the kubernetes k8s.io/apimachinery/pkg/apis/meta/v1.Object interface
// this interface may expand in the future, but will always be a subset of the
// kubernetes k8s.io/apimachinery/pkg/apis/meta/v1.Object interface
type KMetadata interface {
GetName() string
GetNamespace() string
}
// KObj returns ObjectRef from ObjectMeta
func KObj(obj KMetadata) ObjectRef {
if obj == nil {
return ObjectRef{}
}
if val := reflect.ValueOf(obj); val.Kind() == reflect.Ptr && val.IsNil() {
return ObjectRef{}
}
return ObjectRef{
Name: obj.GetName(),
Namespace: obj.GetNamespace(),
}
}
// KRef returns ObjectRef from name and namespace
func KRef(namespace, name string) ObjectRef {
return ObjectRef{
Name: name,
Namespace: namespace,
}
}
// KObjs returns slice of ObjectRef from an slice of ObjectMeta
//
// DEPRECATED: Use KObjSlice instead, it has better performance.
func KObjs(arg interface{}) []ObjectRef {
s := reflect.ValueOf(arg)
if s.Kind() != reflect.Slice {
return nil
}
objectRefs := make([]ObjectRef, 0, s.Len())
for i := 0; i < s.Len(); i++ {
if v, ok := s.Index(i).Interface().(KMetadata); ok {
objectRefs = append(objectRefs, KObj(v))
} else {
return nil
}
}
return objectRefs
}
// KObjSlice takes a slice of objects that implement the KMetadata interface
// and returns an object that gets logged as a slice of ObjectRef values or a
// string containing those values, depending on whether the logger prefers text
// output or structured output.
//
// An error string is logged when KObjSlice is not passed a suitable slice.
//
// Processing of the argument is delayed until the value actually gets logged,
// in contrast to KObjs where that overhead is incurred regardless of whether
// the result is needed.
func KObjSlice(arg interface{}) interface{} {
return kobjSlice{arg: arg}
}
type kobjSlice struct {
arg interface{}
}
var _ fmt.Stringer = kobjSlice{}
var _ logr.Marshaler = kobjSlice{}
func (ks kobjSlice) String() string {
objectRefs, errStr := ks.process()
if errStr != "" {
return errStr
}
return fmt.Sprintf("%v", objectRefs)
}
func (ks kobjSlice) MarshalLog() interface{} {
objectRefs, errStr := ks.process()
if errStr != "" {
return errStr
}
return objectRefs
}
func (ks kobjSlice) process() (objs []interface{}, err string) {
s := reflect.ValueOf(ks.arg)
switch s.Kind() {
case reflect.Invalid:
// nil parameter, print as nil.
return nil, ""
case reflect.Slice:
// Okay, handle below.
default:
return nil, fmt.Sprintf("<KObjSlice needs a slice, got type %T>", ks.arg)
}
objectRefs := make([]interface{}, 0, s.Len())
for i := 0; i < s.Len(); i++ {
item := s.Index(i).Interface()
if item == nil {
objectRefs = append(objectRefs, nil)
} else if v, ok := item.(KMetadata); ok {
objectRefs = append(objectRefs, KObj(v))
} else {
return nil, fmt.Sprintf("<KObjSlice needs a slice of values implementing KMetadata, got type %T>", item)
}
}
return objectRefs, ""
}
var nilToken = []byte("null")
func (ks kobjSlice) WriteText(out *bytes.Buffer) {
s := reflect.ValueOf(ks.arg)
switch s.Kind() {
case reflect.Invalid:
// nil parameter, print as null.
out.Write(nilToken)
return
case reflect.Slice:
// Okay, handle below.
default:
fmt.Fprintf(out, `"<KObjSlice needs a slice, got type %T>"`, ks.arg)
return
}
out.Write([]byte{'['})
defer out.Write([]byte{']'})
for i := 0; i < s.Len(); i++ {
if i > 0 {
out.Write([]byte{','})
}
item := s.Index(i).Interface()
if item == nil {
out.Write(nilToken)
} else if v, ok := item.(KMetadata); ok {
KObj(v).WriteText(out)
} else {
fmt.Fprintf(out, `"<KObjSlice needs a slice of values implementing KMetadata, got type %T>"`, item)
return
}
}
}

39
e2e/vendor/k8s.io/klog/v2/k8s_references_slog.go generated vendored Normal file
View File

@ -0,0 +1,39 @@
//go:build go1.21
// +build go1.21
/*
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 klog
import (
"log/slog"
)
func (ref ObjectRef) LogValue() slog.Value {
if ref.Namespace != "" {
return slog.GroupValue(slog.String("name", ref.Name), slog.String("namespace", ref.Namespace))
}
return slog.GroupValue(slog.String("name", ref.Name))
}
var _ slog.LogValuer = ObjectRef{}
func (ks kobjSlice) LogValue() slog.Value {
return slog.AnyValue(ks.MarshalLog())
}
var _ slog.LogValuer = kobjSlice{}

1734
e2e/vendor/k8s.io/klog/v2/klog.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

130
e2e/vendor/k8s.io/klog/v2/klog_file.go generated vendored Normal file
View File

@ -0,0 +1,130 @@
// Go support for leveled logs, analogous to https://code.google.com/p/google-glog/
//
// Copyright 2013 Google Inc. All Rights Reserved.
//
// 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.
// File I/O for logs.
package klog
import (
"errors"
"fmt"
"os"
"path/filepath"
"strings"
"sync"
"time"
)
// MaxSize is the maximum size of a log file in bytes.
var MaxSize uint64 = 1024 * 1024 * 1800
// logDirs lists the candidate directories for new log files.
var logDirs []string
func createLogDirs() {
if logging.logDir != "" {
logDirs = append(logDirs, logging.logDir)
}
logDirs = append(logDirs, os.TempDir())
}
var (
pid = os.Getpid()
program = filepath.Base(os.Args[0])
host = "unknownhost"
userName = "unknownuser"
userNameOnce sync.Once
)
func init() {
if h, err := os.Hostname(); err == nil {
host = shortHostname(h)
}
}
// shortHostname returns its argument, truncating at the first period.
// For instance, given "www.google.com" it returns "www".
func shortHostname(hostname string) string {
if i := strings.Index(hostname, "."); i >= 0 {
return hostname[:i]
}
return hostname
}
// logName returns a new log file name containing tag, with start time t, and
// the name for the symlink for tag.
func logName(tag string, t time.Time) (name, link string) {
name = fmt.Sprintf("%s.%s.%s.log.%s.%04d%02d%02d-%02d%02d%02d.%d",
program,
host,
getUserName(),
tag,
t.Year(),
t.Month(),
t.Day(),
t.Hour(),
t.Minute(),
t.Second(),
pid)
return name, program + "." + tag
}
var onceLogDirs sync.Once
// create creates a new log file and returns the file and its filename, which
// contains tag ("INFO", "FATAL", etc.) and t. If the file is created
// successfully, create also attempts to update the symlink for that tag, ignoring
// errors.
// The startup argument indicates whether this is the initial startup of klog.
// If startup is true, existing files are opened for appending instead of truncated.
func create(tag string, t time.Time, startup bool) (f *os.File, filename string, err error) {
if logging.logFile != "" {
f, err := openOrCreate(logging.logFile, startup)
if err == nil {
return f, logging.logFile, nil
}
return nil, "", fmt.Errorf("log: unable to create log: %v", err)
}
onceLogDirs.Do(createLogDirs)
if len(logDirs) == 0 {
return nil, "", errors.New("log: no log dirs")
}
name, link := logName(tag, t)
var lastErr error
for _, dir := range logDirs {
fname := filepath.Join(dir, name)
f, err := openOrCreate(fname, startup)
if err == nil {
symlink := filepath.Join(dir, link)
_ = os.Remove(symlink) // ignore err
_ = os.Symlink(name, symlink) // ignore err
return f, fname, nil
}
lastErr = err
}
return nil, "", fmt.Errorf("log: cannot create log: %v", lastErr)
}
// The startup argument indicates whether this is the initial startup of klog.
// If startup is true, existing files are opened for appending instead of truncated.
func openOrCreate(name string, startup bool) (*os.File, error) {
if startup {
f, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
return f, err
}
f, err := os.Create(name)
return f, err
}

19
e2e/vendor/k8s.io/klog/v2/klog_file_others.go generated vendored Normal file
View File

@ -0,0 +1,19 @@
//go:build !windows
// +build !windows
package klog
import (
"os/user"
)
func getUserName() string {
userNameOnce.Do(func() {
current, err := user.Current()
if err == nil {
userName = current.Username
}
})
return userName
}

34
e2e/vendor/k8s.io/klog/v2/klog_file_windows.go generated vendored Normal file
View File

@ -0,0 +1,34 @@
//go:build windows
// +build windows
package klog
import (
"os"
"strings"
)
func getUserName() string {
userNameOnce.Do(func() {
// On Windows, the Go 'user' package requires netapi32.dll.
// This affects Windows Nano Server:
// https://github.com/golang/go/issues/21867
// Fallback to using environment variables.
u := os.Getenv("USERNAME")
if len(u) == 0 {
return
}
// Sanitize the USERNAME since it may contain filepath separators.
u = strings.Replace(u, `\`, "_", -1)
// user.Current().Username normally produces something like 'USERDOMAIN\USERNAME'
d := os.Getenv("USERDOMAIN")
if len(d) != 0 {
userName = d + "_" + u
} else {
userName = u
}
})
return userName
}

105
e2e/vendor/k8s.io/klog/v2/klogr.go generated vendored Normal file
View File

@ -0,0 +1,105 @@
/*
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 klog
import (
"github.com/go-logr/logr"
"k8s.io/klog/v2/internal/serialize"
)
const (
// nameKey is used to log the `WithName` values as an additional attribute.
nameKey = "logger"
)
// NewKlogr returns a logger that is functionally identical to
// klogr.NewWithOptions(klogr.FormatKlog), i.e. it passes through to klog. The
// difference is that it uses a simpler implementation.
func NewKlogr() Logger {
return New(&klogger{})
}
// klogger is a subset of klogr/klogr.go. It had to be copied to break an
// import cycle (klogr wants to use klog, and klog wants to use klogr).
type klogger struct {
callDepth int
// hasPrefix is true if the first entry in values is the special
// nameKey key/value. Such an entry gets added and later updated in
// WithName.
hasPrefix bool
values []interface{}
groups string
}
func (l *klogger) Init(info logr.RuntimeInfo) {
l.callDepth += info.CallDepth
}
func (l *klogger) Info(level int, msg string, kvList ...interface{}) {
merged := serialize.MergeKVs(l.values, kvList)
// Skip this function.
VDepth(l.callDepth+1, Level(level)).InfoSDepth(l.callDepth+1, msg, merged...)
}
func (l *klogger) Enabled(level int) bool {
return VDepth(l.callDepth+1, Level(level)).Enabled()
}
func (l *klogger) Error(err error, msg string, kvList ...interface{}) {
merged := serialize.MergeKVs(l.values, kvList)
ErrorSDepth(l.callDepth+1, err, msg, merged...)
}
// WithName returns a new logr.Logger with the specified name appended. klogr
// uses '.' characters to separate name elements. Callers should not pass '.'
// in the provided name string, but this library does not actually enforce that.
func (l klogger) WithName(name string) logr.LogSink {
if l.hasPrefix {
// Copy slice and modify value. No length checks and type
// assertions are needed because hasPrefix is only true if the
// first two elements exist and are key/value strings.
v := make([]interface{}, 0, len(l.values))
v = append(v, l.values...)
prefix, _ := v[1].(string)
v[1] = prefix + "." + name
l.values = v
} else {
// Preprend new key/value pair.
v := make([]interface{}, 0, 2+len(l.values))
v = append(v, nameKey, name)
v = append(v, l.values...)
l.values = v
l.hasPrefix = true
}
return &l
}
func (l klogger) WithValues(kvList ...interface{}) logr.LogSink {
l.values = serialize.WithValues(l.values, kvList)
return &l
}
func (l klogger) WithCallDepth(depth int) logr.LogSink {
l.callDepth += depth
return &l
}
var _ logr.LogSink = &klogger{}
var _ logr.CallDepthLogSink = &klogger{}

96
e2e/vendor/k8s.io/klog/v2/klogr_slog.go generated vendored Normal file
View File

@ -0,0 +1,96 @@
//go:build go1.21
// +build go1.21
/*
Copyright 2023 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 klog
import (
"context"
"log/slog"
"strconv"
"time"
"github.com/go-logr/logr"
"k8s.io/klog/v2/internal/buffer"
"k8s.io/klog/v2/internal/serialize"
"k8s.io/klog/v2/internal/severity"
"k8s.io/klog/v2/internal/sloghandler"
)
func (l *klogger) Handle(ctx context.Context, record slog.Record) error {
if logging.logger != nil {
if slogSink, ok := logging.logger.GetSink().(logr.SlogSink); ok {
// Let that logger do the work.
return slogSink.Handle(ctx, record)
}
}
return sloghandler.Handle(ctx, record, l.groups, slogOutput)
}
// slogOutput corresponds to several different functions in klog.go.
// It goes through some of the same checks and formatting steps before
// it ultimately converges by calling logging.printWithInfos.
func slogOutput(file string, line int, now time.Time, err error, s severity.Severity, msg string, kvList []interface{}) {
// See infoS.
if logging.logger != nil {
// Taking this path happens when klog has a logger installed
// as backend which doesn't support slog. Not good, we have to
// guess about the call depth and drop the actual location.
logger := logging.logger.WithCallDepth(2)
if s > severity.ErrorLog {
logger.Error(err, msg, kvList...)
} else {
logger.Info(msg, kvList...)
}
return
}
// See printS.
b := buffer.GetBuffer()
b.WriteString(strconv.Quote(msg))
if err != nil {
serialize.KVListFormat(&b.Buffer, "err", err)
}
serialize.KVListFormat(&b.Buffer, kvList...)
// See print + header.
buf := logging.formatHeader(s, file, line, now)
logging.printWithInfos(buf, file, line, s, nil, nil, 0, &b.Buffer)
buffer.PutBuffer(b)
}
func (l *klogger) WithAttrs(attrs []slog.Attr) logr.SlogSink {
clone := *l
clone.values = serialize.WithValues(l.values, sloghandler.Attrs2KVList(l.groups, attrs))
return &clone
}
func (l *klogger) WithGroup(name string) logr.SlogSink {
clone := *l
if clone.groups != "" {
clone.groups += "." + name
} else {
clone.groups = name
}
return &clone
}
var _ logr.SlogSink = &klogger{}

34
e2e/vendor/k8s.io/klog/v2/safeptr.go generated vendored Normal file
View File

@ -0,0 +1,34 @@
//go:build go1.18
// +build go1.18
/*
Copyright 2023 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 klog
// SafePtr is a function that takes a pointer of any type (T) as an argument.
// If the provided pointer is not nil, it returns the same pointer. If it is nil, it returns nil instead.
//
// This function is particularly useful to prevent nil pointer dereferencing when:
//
// - The type implements interfaces that are called by the logger, such as `fmt.Stringer`.
// - And these interface implementations do not perform nil checks themselves.
func SafePtr[T any](p *T) any {
if p == nil {
return nil
}
return p
}

154
e2e/vendor/k8s.io/klog/v2/textlogger/options.go generated vendored Normal file
View File

@ -0,0 +1,154 @@
/*
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 textlogger
import (
"flag"
"io"
"os"
"strconv"
"time"
"k8s.io/klog/v2/internal/verbosity"
)
// Config influences logging in a text logger. To make this configurable via
// command line flags, instantiate this once per program and use AddFlags to
// bind command line flags to the instance before passing it to NewTestContext.
//
// Must be constructed with NewConfig.
type Config struct {
vstate *verbosity.VState
co configOptions
}
// Verbosity returns a value instance that can be used to query (via String) or
// modify (via Set) the verbosity threshold. This is thread-safe and can be
// done at runtime.
func (c *Config) Verbosity() flag.Value {
return c.vstate.V()
}
// VModule returns a value instance that can be used to query (via String) or
// modify (via Set) the vmodule settings. This is thread-safe and can be done
// at runtime.
func (c *Config) VModule() flag.Value {
return c.vstate.VModule()
}
// ConfigOption implements functional parameters for NewConfig.
type ConfigOption func(co *configOptions)
type configOptions struct {
verbosityFlagName string
vmoduleFlagName string
verbosityDefault int
fixedTime *time.Time
unwind func(int) (string, int)
output io.Writer
}
// VerbosityFlagName overrides the default -v for the verbosity level.
func VerbosityFlagName(name string) ConfigOption {
return func(co *configOptions) {
co.verbosityFlagName = name
}
}
// VModulFlagName overrides the default -vmodule for the per-module
// verbosity levels.
func VModuleFlagName(name string) ConfigOption {
return func(co *configOptions) {
co.vmoduleFlagName = name
}
}
// Verbosity overrides the default verbosity level of 0.
// See https://github.com/kubernetes/community/blob/9406b4352fe2d5810cb21cc3cb059ce5886de157/contributors/devel/sig-instrumentation/logging.md#logging-conventions
// for log level conventions in Kubernetes.
func Verbosity(level int) ConfigOption {
return func(co *configOptions) {
co.verbosityDefault = level
}
}
// Output overrides stderr as the output stream.
func Output(output io.Writer) ConfigOption {
return func(co *configOptions) {
co.output = output
}
}
// FixedTime overrides the actual time with a fixed time. Useful only for testing.
//
// # Experimental
//
// Notice: This function is EXPERIMENTAL and may be changed or removed in a
// later release.
func FixedTime(ts time.Time) ConfigOption {
return func(co *configOptions) {
co.fixedTime = &ts
}
}
// Backtrace overrides the default mechanism for determining the call site.
// The callback is invoked with the number of function calls between itself
// and the call site. It must return the file name and line number. An empty
// file name indicates that the information is unknown.
//
// # Experimental
//
// Notice: This function is EXPERIMENTAL and may be changed or removed in a
// later release.
func Backtrace(unwind func(skip int) (filename string, line int)) ConfigOption {
return func(co *configOptions) {
co.unwind = unwind
}
}
// NewConfig returns a configuration with recommended defaults and optional
// modifications. Command line flags are not bound to any FlagSet yet.
func NewConfig(opts ...ConfigOption) *Config {
c := &Config{
vstate: verbosity.New(),
co: configOptions{
verbosityFlagName: "v",
vmoduleFlagName: "vmodule",
verbosityDefault: 0,
unwind: runtimeBacktrace,
output: os.Stderr,
},
}
for _, opt := range opts {
opt(&c.co)
}
// Cannot fail for this input.
_ = c.Verbosity().Set(strconv.FormatInt(int64(c.co.verbosityDefault), 10))
return c
}
// AddFlags registers the command line flags that control the configuration.
//
// The default flag names are the same as in klog, so unless those defaults
// are changed, either klog.InitFlags or Config.AddFlags can be used for the
// same flag set, but not both.
func (c *Config) AddFlags(fs *flag.FlagSet) {
fs.Var(c.Verbosity(), c.co.verbosityFlagName, "number for the log level verbosity of the testing logger")
fs.Var(c.VModule(), c.co.vmoduleFlagName, "comma-separated list of pattern=N log level settings for files matching the patterns")
}

187
e2e/vendor/k8s.io/klog/v2/textlogger/textlogger.go generated vendored Normal file
View File

@ -0,0 +1,187 @@
/*
Copyright 2019 The Kubernetes Authors.
Copyright 2020 Intel Corporation.
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 textlogger contains an implementation of the logr interface which is
// producing the exact same output as klog. It does not route output through
// klog (i.e. ignores [k8s.io/klog/v2.InitFlags]). Instead, all settings must be
// configured through its own [NewConfig] and [Config.AddFlags].
package textlogger
import (
"runtime"
"strconv"
"strings"
"time"
"github.com/go-logr/logr"
"k8s.io/klog/v2/internal/buffer"
"k8s.io/klog/v2/internal/serialize"
"k8s.io/klog/v2/internal/severity"
"k8s.io/klog/v2/internal/verbosity"
)
var (
// TimeNow is used to retrieve the current time. May be changed for testing.
TimeNow = time.Now
)
const (
// nameKey is used to log the `WithName` values as an additional attribute.
nameKey = "logger"
)
// NewLogger constructs a new logger.
//
// Verbosity can be modified at any time through the Config.V and
// Config.VModule API.
func NewLogger(c *Config) logr.Logger {
return logr.New(&tlogger{
values: nil,
config: c,
})
}
type tlogger struct {
callDepth int
// hasPrefix is true if the first entry in values is the special
// nameKey key/value. Such an entry gets added and later updated in
// WithName.
hasPrefix bool
values []interface{}
groups string
config *Config
}
func (l *tlogger) Init(info logr.RuntimeInfo) {
l.callDepth = info.CallDepth
}
func (l *tlogger) WithCallDepth(depth int) logr.LogSink {
newLogger := *l
newLogger.callDepth += depth
return &newLogger
}
func (l *tlogger) Enabled(level int) bool {
return l.config.vstate.Enabled(verbosity.Level(level), 1+l.callDepth)
}
func (l *tlogger) Info(_ int, msg string, kvList ...interface{}) {
l.print(nil, severity.InfoLog, msg, kvList)
}
func (l *tlogger) Error(err error, msg string, kvList ...interface{}) {
l.print(err, severity.ErrorLog, msg, kvList)
}
func (l *tlogger) print(err error, s severity.Severity, msg string, kvList []interface{}) {
// Determine caller.
// +1 for this frame, +1 for Info/Error.
skip := l.callDepth + 2
file, line := l.config.co.unwind(skip)
if file == "" {
file = "???"
line = 1
} else if slash := strings.LastIndex(file, "/"); slash >= 0 {
file = file[slash+1:]
}
l.printWithInfos(file, line, time.Now(), err, s, msg, kvList)
}
func runtimeBacktrace(skip int) (string, int) {
_, file, line, ok := runtime.Caller(skip + 1)
if !ok {
return "", 0
}
return file, line
}
func (l *tlogger) printWithInfos(file string, line int, now time.Time, err error, s severity.Severity, msg string, kvList []interface{}) {
// Only create a new buffer if we don't have one cached.
b := buffer.GetBuffer()
defer buffer.PutBuffer(b)
// Format header.
if l.config.co.fixedTime != nil {
now = *l.config.co.fixedTime
}
b.FormatHeader(s, file, line, now)
// The message is always quoted, even if it contains line breaks.
// If developers want multi-line output, they should use a small, fixed
// message and put the multi-line output into a value.
b.WriteString(strconv.Quote(msg))
if err != nil {
serialize.KVFormat(&b.Buffer, "err", err)
}
serialize.MergeAndFormatKVs(&b.Buffer, l.values, kvList)
if b.Len() == 0 || b.Bytes()[b.Len()-1] != '\n' {
b.WriteByte('\n')
}
_, _ = l.config.co.output.Write(b.Bytes())
}
func (l *tlogger) WriteKlogBuffer(data []byte) {
_, _ = l.config.co.output.Write(data)
}
// WithName returns a new logr.Logger with the specified name appended. klogr
// uses '/' characters to separate name elements. Callers should not pass '/'
// in the provided name string, but this library does not actually enforce that.
func (l *tlogger) WithName(name string) logr.LogSink {
clone := *l
if l.hasPrefix {
// Copy slice and modify value. No length checks and type
// assertions are needed because hasPrefix is only true if the
// first two elements exist and are key/value strings.
v := make([]interface{}, 0, len(l.values))
v = append(v, l.values...)
prefix, _ := v[1].(string)
v[1] = prefix + "." + name
clone.values = v
} else {
// Preprend new key/value pair.
v := make([]interface{}, 0, 2+len(l.values))
v = append(v, nameKey, name)
v = append(v, l.values...)
clone.values = v
clone.hasPrefix = true
}
return &clone
}
func (l *tlogger) WithValues(kvList ...interface{}) logr.LogSink {
clone := *l
clone.values = serialize.WithValues(l.values, kvList)
return &clone
}
// KlogBufferWriter is implemented by the textlogger LogSink.
type KlogBufferWriter interface {
// WriteKlogBuffer takes a pre-formatted buffer prepared by klog and
// writes it unchanged to the output stream. Can be used with
// klog.WriteKlogBuffer when setting a logger through
// klog.SetLoggerWithOptions.
WriteKlogBuffer([]byte)
}
var _ logr.LogSink = &tlogger{}
var _ logr.CallDepthLogSink = &tlogger{}
var _ KlogBufferWriter = &tlogger{}

View File

@ -0,0 +1,52 @@
//go:build go1.21
// +build go1.21
/*
Copyright 2023 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 textlogger
import (
"context"
"log/slog"
"github.com/go-logr/logr"
"k8s.io/klog/v2/internal/serialize"
"k8s.io/klog/v2/internal/sloghandler"
)
func (l *tlogger) Handle(ctx context.Context, record slog.Record) error {
return sloghandler.Handle(ctx, record, l.groups, l.printWithInfos)
}
func (l *tlogger) WithAttrs(attrs []slog.Attr) logr.SlogSink {
clone := *l
clone.values = serialize.WithValues(l.values, sloghandler.Attrs2KVList(l.groups, attrs))
return &clone
}
func (l *tlogger) WithGroup(name string) logr.SlogSink {
clone := *l
if clone.groups != "" {
clone.groups += "." + name
} else {
clone.groups = name
}
return &clone
}
var _ logr.SlogSink = &tlogger{}