Revert "apimachinery: contextual logging in network util code"

This commit is contained in:
Patrick Ohly 2026-01-27 17:21:02 +01:00 committed by GitHub
parent 4d212f7f47
commit 9d65b9be20
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
66 changed files with 438 additions and 2157 deletions

View file

@ -17,8 +17,6 @@ limitations under the License.
package meta
import (
"context"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
@ -111,7 +109,7 @@ type RESTMapping struct {
// to API groups. In other words, kinds and resources should not be assumed to be
// unique across groups.
//
// Deprecated: use RESTMapperWithContext instead.
// TODO: split into sub-interfaces
type RESTMapper interface {
// KindFor takes a partial resource and returns the single match. Returns an error if there are multiple matches
KindFor(resource schema.GroupVersionResource) (schema.GroupVersionKind, error)
@ -135,112 +133,11 @@ type RESTMapper interface {
ResourceSingularizer(resource string) (singular string, err error)
}
// RESTMapperWithContext allows clients to map resources to kind, and map kind and version
// to interfaces for manipulating those objects. It is primarily intended for
// consumers of Kubernetes compatible REST APIs as defined in docs/devel/api-conventions.md.
//
// The Kubernetes API provides versioned resources and object kinds which are scoped
// to API groups. In other words, kinds and resources should not be assumed to be
// unique across groups.
type RESTMapperWithContext interface {
// KindFor takes a partial resource and returns the single match. Returns an error if there are multiple matches
KindForWithContext(ctx context.Context, resource schema.GroupVersionResource) (schema.GroupVersionKind, error)
// KindsFor takes a partial resource and returns the list of potential kinds in priority order
KindsForWithContext(ctx context.Context, resource schema.GroupVersionResource) ([]schema.GroupVersionKind, error)
// ResourceFor takes a partial resource and returns the single match. Returns an error if there are multiple matches
ResourceForWithContext(ctx context.Context, input schema.GroupVersionResource) (schema.GroupVersionResource, error)
// ResourcesFor takes a partial resource and returns the list of potential resource in priority order
ResourcesForWithContext(ctx context.Context, input schema.GroupVersionResource) ([]schema.GroupVersionResource, error)
// RESTMapping identifies a preferred resource mapping for the provided group kind.
RESTMappingWithContext(ctx context.Context, gk schema.GroupKind, versions ...string) (*RESTMapping, error)
// RESTMappings returns all resource mappings for the provided group kind if no
// version search is provided. Otherwise identifies a preferred resource mapping for
// the provided version(s).
RESTMappingsWithContext(ctx context.Context, gk schema.GroupKind, versions ...string) ([]*RESTMapping, error)
ResourceSingularizerWithContext(ctx context.Context, resource string) (singular string, err error)
}
func ToRESTMapperWithContext(m RESTMapper) RESTMapperWithContext {
if m == nil {
return nil
}
if m, ok := m.(RESTMapperWithContext); ok {
return m
}
return &restMapperWrapper{
delegate: m,
}
}
type restMapperWrapper struct {
delegate RESTMapper
}
func (m *restMapperWrapper) KindForWithContext(ctx context.Context, resource schema.GroupVersionResource) (schema.GroupVersionKind, error) {
return m.delegate.KindFor(resource)
}
func (m *restMapperWrapper) KindsForWithContext(ctx context.Context, resource schema.GroupVersionResource) ([]schema.GroupVersionKind, error) {
return m.delegate.KindsFor(resource)
}
func (m *restMapperWrapper) ResourceForWithContext(ctx context.Context, input schema.GroupVersionResource) (schema.GroupVersionResource, error) {
return m.delegate.ResourceFor(input)
}
func (m *restMapperWrapper) ResourcesForWithContext(ctx context.Context, input schema.GroupVersionResource) ([]schema.GroupVersionResource, error) {
return m.delegate.ResourcesFor(input)
}
func (m *restMapperWrapper) RESTMappingWithContext(ctx context.Context, gk schema.GroupKind, versions ...string) (*RESTMapping, error) {
return m.delegate.RESTMapping(gk, versions...)
}
func (m *restMapperWrapper) RESTMappingsWithContext(ctx context.Context, gk schema.GroupKind, versions ...string) ([]*RESTMapping, error) {
return m.delegate.RESTMappings(gk, versions...)
}
func (m *restMapperWrapper) ResourceSingularizerWithContext(ctx context.Context, resource string) (singular string, err error) {
return m.delegate.ResourceSingularizer(resource)
}
// ResettableRESTMapper is a RESTMapper which is capable of resetting itself
// from discovery.
// All rest mappers that delegate to other rest mappers must implement this interface and dynamically
// check if the delegate mapper supports the Reset() operation.
//
// Deprecated: use ResettableRESTMapperWithContext instead.
type ResettableRESTMapper interface {
RESTMapper
Reset()
}
// ResettableRESTMapperWithContext is a RESTMapper which is capable of resetting itself
// from discovery.
// All rest mappers that delegate to other rest mappers must implement this interface and dynamically
// check if the delegate mapper supports the ResetWithContext() operation.
type ResettableRESTMapperWithContext interface {
RESTMapperWithContext
ResetWithContext(ctx context.Context)
}
func ToResettableRESTMapperWithContext(m ResettableRESTMapper) ResettableRESTMapperWithContext {
if m == nil {
return nil
}
if m, ok := m.(ResettableRESTMapperWithContext); ok {
return m
}
return &resettableRESTMapperWrapper{
RESTMapperWithContext: ToRESTMapperWithContext(m),
delegate: m,
}
}
type resettableRESTMapperWrapper struct {
RESTMapperWithContext
delegate ResettableRESTMapper
}
func (m *resettableRESTMapperWrapper) ResetWithContext(ctx context.Context) {
m.delegate.Reset()
}

View file

@ -600,7 +600,6 @@ func (a genericAccessor) GetOwnerReferences() []metav1.OwnerReference {
var ret []metav1.OwnerReference
s := a.ownerReferences
if s.Kind() != reflect.Pointer || s.Elem().Kind() != reflect.Slice {
//nolint:logcheck // Should not happen.
klog.Errorf("expect %v to be a pointer to slice", s)
return ret
}
@ -609,7 +608,6 @@ func (a genericAccessor) GetOwnerReferences() []metav1.OwnerReference {
ret = make([]metav1.OwnerReference, s.Len(), s.Len()+1)
for i := 0; i < s.Len(); i++ {
if err := extractFromOwnerReference(s.Index(i), &ret[i]); err != nil {
//nolint:logcheck // Should not happen.
klog.Errorf("extractFromOwnerReference failed: %v", err)
return ret
}
@ -620,14 +618,12 @@ func (a genericAccessor) GetOwnerReferences() []metav1.OwnerReference {
func (a genericAccessor) SetOwnerReferences(references []metav1.OwnerReference) {
s := a.ownerReferences
if s.Kind() != reflect.Pointer || s.Elem().Kind() != reflect.Slice {
//nolint:logcheck // Should not happen.
klog.Errorf("expect %v to be a pointer to slice", s)
}
s = s.Elem()
newReferences := reflect.MakeSlice(s.Type(), len(references), len(references))
for i := 0; i < len(references); i++ {
if err := setOwnerReference(newReferences.Index(i), &references[i]); err != nil {
//nolint:logcheck // Should not happen.
klog.Errorf("setOwnerReference failed: %v", err)
return
}

View file

@ -17,7 +17,6 @@ limitations under the License.
package meta
import (
"context"
"fmt"
"strings"
@ -26,29 +25,13 @@ import (
)
var (
_ ResettableRESTMapper = MultiRESTMapper{}
_ fmt.Stringer = MultiRESTMapper{}
_ ResettableRESTMapperWithContext = MultiRESTMapperWithContext{}
_ fmt.Stringer = MultiRESTMapperWithContext{}
_ ResettableRESTMapper = MultiRESTMapper{}
)
// MultiRESTMapper is a wrapper for multiple RESTMappers.
//
// Deprecated: use MultiRESTMapperWithContext instead.
type MultiRESTMapper []RESTMapper
// MultiRESTMapperWithContext is a wrapper for multiple RESTMapperWithContext instances.
type MultiRESTMapperWithContext []RESTMapperWithContext
func (m MultiRESTMapper) String() string {
return stringifyMapper("MultiRESTMapper", m)
}
func (m MultiRESTMapperWithContext) String() string {
return stringifyMapper("MultiRESTMapperWithContext", m)
}
func stringifyMapper[T any](typeName string, m []T) string {
nested := make([]string, 0, len(m))
for _, t := range m {
currString := fmt.Sprintf("%v", t)
@ -56,77 +39,14 @@ func stringifyMapper[T any](typeName string, m []T) string {
nested = append(nested, strings.Join(splitStrings, "\n\t"))
}
return fmt.Sprintf("%s{\n\t%s\n}", typeName, strings.Join(nested, "\n\t"))
}
func ToMultiRESTMapperWithContext(m MultiRESTMapper) MultiRESTMapperWithContext {
if m == nil {
return nil
}
mc := make(MultiRESTMapperWithContext, len(m))
for i, m := range m {
mc[i] = ToRESTMapperWithContext(m)
}
return mc
return fmt.Sprintf("MultiRESTMapper{\n\t%s\n}", strings.Join(nested, "\n\t"))
}
// ResourceSingularizer converts a REST resource name from plural to singular (e.g., from pods to pod)
// This implementation supports multiple REST schemas and return the first match.
//
// Deprecated: use MultiRESTMapperWithContext.ResourceSingularizerWithContext instead.
func (m MultiRESTMapper) ResourceSingularizer(resource string) (singular string, err error) {
return ToMultiRESTMapperWithContext(m).ResourceSingularizerWithContext(context.Background(), resource)
}
// Deprecated: use MultiRESTMapperWithContext.ResourcesForWithContext instead.
func (m MultiRESTMapper) ResourcesFor(resource schema.GroupVersionResource) ([]schema.GroupVersionResource, error) {
return ToMultiRESTMapperWithContext(m).ResourcesForWithContext(context.Background(), resource)
}
// Deprecated: use MultiRESTMapperWithContext.KindsForWithContext instead.
func (m MultiRESTMapper) KindsFor(resource schema.GroupVersionResource) (gvk []schema.GroupVersionKind, err error) {
return ToMultiRESTMapperWithContext(m).KindsForWithContext(context.Background(), resource)
}
// Deprecated: use MultiRESTMapperWithContext.ResourceForWithContext instead.
func (m MultiRESTMapper) ResourceFor(resource schema.GroupVersionResource) (schema.GroupVersionResource, error) {
return ToMultiRESTMapperWithContext(m).ResourceForWithContext(context.Background(), resource)
}
// Deprecated: use MultiRESTMapperWithContext.KindForWithContext instead.
func (m MultiRESTMapper) KindFor(resource schema.GroupVersionResource) (schema.GroupVersionKind, error) {
return ToMultiRESTMapperWithContext(m).KindForWithContext(context.Background(), resource)
}
// RESTMapping provides the REST mapping for the resource based on the
// kind and version. This implementation supports multiple REST schemas and
// return the first match.
//
// Deprecated: use MultiRESTMapperWithContext.RESTMappingWithContext instead.
func (m MultiRESTMapper) RESTMapping(gk schema.GroupKind, versions ...string) (*RESTMapping, error) {
return ToMultiRESTMapperWithContext(m).RESTMappingWithContext(context.Background(), gk, versions...)
}
// RESTMappings returns all possible RESTMappings for the provided group kind, or an error
// if the type is not recognized.
//
// Deprecated: use MultiRESTMapperWithContext.RESTMappingsWithContext instead.
func (m MultiRESTMapper) RESTMappings(gk schema.GroupKind, versions ...string) ([]*RESTMapping, error) {
return ToMultiRESTMapperWithContext(m).RESTMappingsWithContext(context.Background(), gk, versions...)
}
// Deprecated: use MultiRESTMapperWithContext.Reset instead.
func (m MultiRESTMapper) Reset() {
for _, t := range m {
MaybeResetRESTMapper(t)
}
}
// ResourceSingularizer converts a REST resource name from plural to singular (e.g., from pods to pod)
// This implementation supports multiple REST schemas and return the first match.
func (m MultiRESTMapperWithContext) ResourceSingularizerWithContext(ctx context.Context, resource string) (singular string, err error) {
for _, t := range m {
singular, err = t.ResourceSingularizerWithContext(ctx, resource)
singular, err = t.ResourceSingularizer(resource)
if err == nil {
return
}
@ -134,10 +54,10 @@ func (m MultiRESTMapperWithContext) ResourceSingularizerWithContext(ctx context.
return
}
func (m MultiRESTMapperWithContext) ResourcesForWithContext(ctx context.Context, resource schema.GroupVersionResource) ([]schema.GroupVersionResource, error) {
func (m MultiRESTMapper) ResourcesFor(resource schema.GroupVersionResource) ([]schema.GroupVersionResource, error) {
allGVRs := []schema.GroupVersionResource{}
for _, t := range m {
gvrs, err := t.ResourcesForWithContext(ctx, resource)
gvrs, err := t.ResourcesFor(resource)
// ignore "no match" errors, but any other error percolates back up
if IsNoMatchError(err) {
continue
@ -169,10 +89,10 @@ func (m MultiRESTMapperWithContext) ResourcesForWithContext(ctx context.Context,
return allGVRs, nil
}
func (m MultiRESTMapperWithContext) KindsForWithContext(ctx context.Context, resource schema.GroupVersionResource) (gvk []schema.GroupVersionKind, err error) {
func (m MultiRESTMapper) KindsFor(resource schema.GroupVersionResource) (gvk []schema.GroupVersionKind, err error) {
allGVKs := []schema.GroupVersionKind{}
for _, t := range m {
gvks, err := t.KindsForWithContext(ctx, resource)
gvks, err := t.KindsFor(resource)
// ignore "no match" errors, but any other error percolates back up
if IsNoMatchError(err) {
continue
@ -204,8 +124,8 @@ func (m MultiRESTMapperWithContext) KindsForWithContext(ctx context.Context, res
return allGVKs, nil
}
func (m MultiRESTMapperWithContext) ResourceForWithContext(ctx context.Context, resource schema.GroupVersionResource) (schema.GroupVersionResource, error) {
resources, err := m.ResourcesForWithContext(ctx, resource)
func (m MultiRESTMapper) ResourceFor(resource schema.GroupVersionResource) (schema.GroupVersionResource, error) {
resources, err := m.ResourcesFor(resource)
if err != nil {
return schema.GroupVersionResource{}, err
}
@ -216,8 +136,8 @@ func (m MultiRESTMapperWithContext) ResourceForWithContext(ctx context.Context,
return schema.GroupVersionResource{}, &AmbiguousResourceError{PartialResource: resource, MatchingResources: resources}
}
func (m MultiRESTMapperWithContext) KindForWithContext(ctx context.Context, resource schema.GroupVersionResource) (schema.GroupVersionKind, error) {
kinds, err := m.KindsForWithContext(ctx, resource)
func (m MultiRESTMapper) KindFor(resource schema.GroupVersionResource) (schema.GroupVersionKind, error) {
kinds, err := m.KindsFor(resource)
if err != nil {
return schema.GroupVersionKind{}, err
}
@ -231,12 +151,12 @@ func (m MultiRESTMapperWithContext) KindForWithContext(ctx context.Context, reso
// RESTMapping provides the REST mapping for the resource based on the
// kind and version. This implementation supports multiple REST schemas and
// return the first match.
func (m MultiRESTMapperWithContext) RESTMappingWithContext(ctx context.Context, gk schema.GroupKind, versions ...string) (*RESTMapping, error) {
func (m MultiRESTMapper) RESTMapping(gk schema.GroupKind, versions ...string) (*RESTMapping, error) {
allMappings := []*RESTMapping{}
errors := []error{}
for _, t := range m {
currMapping, err := t.RESTMappingWithContext(ctx, gk, versions...)
currMapping, err := t.RESTMapping(gk, versions...)
// ignore "no match" errors, but any other error percolates back up
if IsNoMatchError(err) {
continue
@ -268,12 +188,12 @@ func (m MultiRESTMapperWithContext) RESTMappingWithContext(ctx context.Context,
// RESTMappings returns all possible RESTMappings for the provided group kind, or an error
// if the type is not recognized.
func (m MultiRESTMapperWithContext) RESTMappingsWithContext(ctx context.Context, gk schema.GroupKind, versions ...string) ([]*RESTMapping, error) {
func (m MultiRESTMapper) RESTMappings(gk schema.GroupKind, versions ...string) ([]*RESTMapping, error) {
var allMappings []*RESTMapping
var errors []error
for _, t := range m {
currMappings, err := t.RESTMappingsWithContext(ctx, gk, versions...)
currMappings, err := t.RESTMappings(gk, versions...)
// ignore "no match" errors, but any other error percolates back up
if IsNoMatchError(err) {
continue
@ -293,8 +213,8 @@ func (m MultiRESTMapperWithContext) RESTMappingsWithContext(ctx context.Context,
return allMappings, nil
}
func (m MultiRESTMapperWithContext) ResetWithContext(ctx context.Context) {
func (m MultiRESTMapper) Reset() {
for _, t := range m {
MaybeResetRESTMapperWithContext(ctx, t)
MaybeResetRESTMapper(t)
}
}

View file

@ -17,7 +17,6 @@ limitations under the License.
package meta
import (
"context"
"fmt"
"k8s.io/apimachinery/pkg/runtime/schema"
@ -31,15 +30,11 @@ const (
)
var (
_ ResettableRESTMapper = PriorityRESTMapper{}
_ ResettableRESTMapperWithContext = PriorityRESTMapperWithContext{}
_ fmt.Stringer = PriorityRESTMapperWithContext{}
_ ResettableRESTMapper = PriorityRESTMapper{}
)
// PriorityRESTMapper is a wrapper for automatically choosing a particular Resource or Kind
// when multiple matches are possible
//
// Deprecated: use PriorityRESTMapperWithContext instead.
type PriorityRESTMapper struct {
// Delegate is the RESTMapper to use to locate all the Kind and Resource matches
Delegate RESTMapper
@ -61,84 +56,9 @@ func (m PriorityRESTMapper) String() string {
return fmt.Sprintf("PriorityRESTMapper{\n\t%v\n\t%v\n\t%v\n}", m.ResourcePriority, m.KindPriority, m.Delegate)
}
// PriorityRESTMapperWithContext is a wrapper for automatically choosing a particular Resource or Kind
// when multiple matches are possible
type PriorityRESTMapperWithContext struct {
// Delegate is the RESTMapperWithContext to use to locate all the Kind and Resource matches
Delegate RESTMapperWithContext
// ResourcePriority is a list of priority patterns to apply to matching resources.
// The list of all matching resources is narrowed based on the patterns until only one remains.
// A pattern with no matches is skipped. A pattern with more than one match uses its
// matches as the list to continue matching against.
ResourcePriority []schema.GroupVersionResource
// KindPriority is a list of priority patterns to apply to matching kinds.
// The list of all matching kinds is narrowed based on the patterns until only one remains.
// A pattern with no matches is skipped. A pattern with more than one match uses its
// matches as the list to continue matching against.
KindPriority []schema.GroupVersionKind
}
func (m PriorityRESTMapperWithContext) String() string {
return fmt.Sprintf("PriorityRESTMapperWithContext{\n\t%v\n\t%v\n\t%v\n}", m.ResourcePriority, m.KindPriority, m.Delegate)
}
func ToPriorityRESTMapperWithContext(m PriorityRESTMapper) PriorityRESTMapperWithContext {
return PriorityRESTMapperWithContext{
Delegate: ToRESTMapperWithContext(m.Delegate),
ResourcePriority: m.ResourcePriority,
KindPriority: m.KindPriority,
}
}
// ResourceFor finds all resources, then passes them through the ResourcePriority patterns to find a single matching hit.
//
// Deprecated: use PriorityRESTMapperWithContext.ResourceForWithContext instead.
func (m PriorityRESTMapper) ResourceFor(partiallySpecifiedResource schema.GroupVersionResource) (schema.GroupVersionResource, error) {
return ToPriorityRESTMapperWithContext(m).ResourceForWithContext(context.Background(), partiallySpecifiedResource)
}
// KindFor finds all kinds, then passes them through the KindPriority patterns to find a single matching hit.
//
// Deprecated: use PriorityRESTMapperWithContext.KindForWithContext instead.
func (m PriorityRESTMapper) KindFor(partiallySpecifiedResource schema.GroupVersionResource) (schema.GroupVersionKind, error) {
return ToPriorityRESTMapperWithContext(m).KindForWithContext(context.Background(), partiallySpecifiedResource)
}
// Deprecated: use PriorityRESTMapperWithContext.RESTMappingWithContext instead.
func (m PriorityRESTMapper) RESTMapping(gk schema.GroupKind, versions ...string) (mapping *RESTMapping, err error) {
return ToPriorityRESTMapperWithContext(m).RESTMappingWithContext(context.Background(), gk, versions...)
}
// Deprecated: use PriorityRESTMapperWithContext.RESTMappingsWithContext instead.
func (m PriorityRESTMapper) RESTMappings(gk schema.GroupKind, versions ...string) ([]*RESTMapping, error) {
return m.Delegate.RESTMappings(gk, versions...)
}
// Deprecated: use PriorityRESTMapperWithContext.ResourceSingularizerWithContext instead.
func (m PriorityRESTMapper) ResourceSingularizer(resource string) (singular string, err error) {
return m.Delegate.ResourceSingularizer(resource)
}
// Deprecated: use PriorityRESTMapperWithContext.ResourcesForWithContext instead.
func (m PriorityRESTMapper) ResourcesFor(partiallySpecifiedResource schema.GroupVersionResource) ([]schema.GroupVersionResource, error) {
return m.Delegate.ResourcesFor(partiallySpecifiedResource)
}
// Deprecated: use PriorityRESTMapperWithContext.KindsForWithContext instead.
func (m PriorityRESTMapper) KindsFor(partiallySpecifiedResource schema.GroupVersionResource) (gvk []schema.GroupVersionKind, err error) {
return m.Delegate.KindsFor(partiallySpecifiedResource)
}
// Deprecated: use PriorityRESTMapperWithContext.ResetWithContext instead.
func (m PriorityRESTMapper) Reset() {
MaybeResetRESTMapper(m.Delegate)
}
// ResourceFor finds all resources, then passes them through the ResourcePriority patterns to find a single matching hit.
func (m PriorityRESTMapperWithContext) ResourceForWithContext(ctx context.Context, partiallySpecifiedResource schema.GroupVersionResource) (schema.GroupVersionResource, error) {
originalGVRs, originalErr := m.Delegate.ResourcesForWithContext(ctx, partiallySpecifiedResource)
originalGVRs, originalErr := m.Delegate.ResourcesFor(partiallySpecifiedResource)
if originalErr != nil && len(originalGVRs) == 0 {
return schema.GroupVersionResource{}, originalErr
}
@ -173,8 +93,8 @@ func (m PriorityRESTMapperWithContext) ResourceForWithContext(ctx context.Contex
}
// KindFor finds all kinds, then passes them through the KindPriority patterns to find a single matching hit.
func (m PriorityRESTMapperWithContext) KindForWithContext(ctx context.Context, partiallySpecifiedResource schema.GroupVersionResource) (schema.GroupVersionKind, error) {
originalGVKs, originalErr := m.Delegate.KindsForWithContext(ctx, partiallySpecifiedResource)
func (m PriorityRESTMapper) KindFor(partiallySpecifiedResource schema.GroupVersionResource) (schema.GroupVersionKind, error) {
originalGVKs, originalErr := m.Delegate.KindsFor(partiallySpecifiedResource)
if originalErr != nil && len(originalGVKs) == 0 {
return schema.GroupVersionKind{}, originalErr
}
@ -236,8 +156,8 @@ func kindMatches(pattern schema.GroupVersionKind, kind schema.GroupVersionKind)
return true
}
func (m PriorityRESTMapperWithContext) RESTMappingWithContext(ctx context.Context, gk schema.GroupKind, versions ...string) (mapping *RESTMapping, err error) {
mappings, originalErr := m.Delegate.RESTMappingsWithContext(ctx, gk, versions...)
func (m PriorityRESTMapper) RESTMapping(gk schema.GroupKind, versions ...string) (mapping *RESTMapping, err error) {
mappings, originalErr := m.Delegate.RESTMappings(gk, versions...)
if originalErr != nil && len(mappings) == 0 {
return nil, originalErr
}
@ -289,22 +209,22 @@ func (m PriorityRESTMapperWithContext) RESTMappingWithContext(ctx context.Contex
return nil, &AmbiguousKindError{PartialKind: gk.WithVersion(""), MatchingKinds: kinds}
}
func (m PriorityRESTMapperWithContext) RESTMappingsWithContext(ctx context.Context, gk schema.GroupKind, versions ...string) ([]*RESTMapping, error) {
return m.Delegate.RESTMappingsWithContext(ctx, gk, versions...)
func (m PriorityRESTMapper) RESTMappings(gk schema.GroupKind, versions ...string) ([]*RESTMapping, error) {
return m.Delegate.RESTMappings(gk, versions...)
}
func (m PriorityRESTMapperWithContext) ResourceSingularizerWithContext(ctx context.Context, resource string) (singular string, err error) {
return m.Delegate.ResourceSingularizerWithContext(ctx, resource)
func (m PriorityRESTMapper) ResourceSingularizer(resource string) (singular string, err error) {
return m.Delegate.ResourceSingularizer(resource)
}
func (m PriorityRESTMapperWithContext) ResourcesForWithContext(ctx context.Context, partiallySpecifiedResource schema.GroupVersionResource) ([]schema.GroupVersionResource, error) {
return m.Delegate.ResourcesForWithContext(ctx, partiallySpecifiedResource)
func (m PriorityRESTMapper) ResourcesFor(partiallySpecifiedResource schema.GroupVersionResource) ([]schema.GroupVersionResource, error) {
return m.Delegate.ResourcesFor(partiallySpecifiedResource)
}
func (m PriorityRESTMapperWithContext) KindsForWithContext(ctx context.Context, partiallySpecifiedResource schema.GroupVersionResource) (gvk []schema.GroupVersionKind, err error) {
return m.Delegate.KindsForWithContext(ctx, partiallySpecifiedResource)
func (m PriorityRESTMapper) KindsFor(partiallySpecifiedResource schema.GroupVersionResource) (gvk []schema.GroupVersionKind, err error) {
return m.Delegate.KindsFor(partiallySpecifiedResource)
}
func (m PriorityRESTMapperWithContext) ResetWithContext(ctx context.Context) {
MaybeResetRESTMapperWithContext(ctx, m.Delegate)
func (m PriorityRESTMapper) Reset() {
MaybeResetRESTMapper(m.Delegate)
}

View file

@ -18,7 +18,6 @@ limitations under the License.
package meta
import (
"context"
"fmt"
"sort"
"strings"
@ -73,8 +72,6 @@ func (m *DefaultRESTMapper) String() string {
}
var _ RESTMapper = &DefaultRESTMapper{}
var _ RESTMapperWithContext = &DefaultRESTMapper{}
var _ fmt.Stringer = &DefaultRESTMapper{}
// NewDefaultRESTMapper initializes a mapping between Kind and APIVersion
// to a resource name and back based on the objects in a runtime.Scheme
@ -182,10 +179,6 @@ func (m *DefaultRESTMapper) ResourceSingularizer(resourceType string) (string, e
return singular.Resource, nil
}
func (m *DefaultRESTMapper) ResourceSingularizerWithContext(_ context.Context, resourceType string) (string, error) {
return m.ResourceSingularizer(resourceType)
}
// coerceResourceForMatching makes the resource lower case and converts internal versions to unspecified (legacy behavior)
func coerceResourceForMatching(resource schema.GroupVersionResource) schema.GroupVersionResource {
resource.Resource = strings.ToLower(resource.Resource)
@ -283,10 +276,6 @@ func (m *DefaultRESTMapper) ResourcesFor(input schema.GroupVersionResource) ([]s
return ret, nil
}
func (m *DefaultRESTMapper) ResourcesForWithContext(_ context.Context, input schema.GroupVersionResource) ([]schema.GroupVersionResource, error) {
return m.ResourcesFor(input)
}
func (m *DefaultRESTMapper) ResourceFor(resource schema.GroupVersionResource) (schema.GroupVersionResource, error) {
resources, err := m.ResourcesFor(resource)
if err != nil {
@ -299,10 +288,6 @@ func (m *DefaultRESTMapper) ResourceFor(resource schema.GroupVersionResource) (s
return schema.GroupVersionResource{}, &AmbiguousResourceError{PartialResource: resource, MatchingResources: resources}
}
func (m *DefaultRESTMapper) ResourceForWithContext(_ context.Context, resource schema.GroupVersionResource) (schema.GroupVersionResource, error) {
return m.ResourceFor(resource)
}
func (m *DefaultRESTMapper) KindsFor(input schema.GroupVersionResource) ([]schema.GroupVersionKind, error) {
resource := coerceResourceForMatching(input)
@ -370,10 +355,6 @@ func (m *DefaultRESTMapper) KindsFor(input schema.GroupVersionResource) ([]schem
return ret, nil
}
func (m *DefaultRESTMapper) KindsForWithContext(_ context.Context, input schema.GroupVersionResource) ([]schema.GroupVersionKind, error) {
return m.KindsFor(input)
}
func (m *DefaultRESTMapper) KindFor(resource schema.GroupVersionResource) (schema.GroupVersionKind, error) {
kinds, err := m.KindsFor(resource)
if err != nil {
@ -386,10 +367,6 @@ func (m *DefaultRESTMapper) KindFor(resource schema.GroupVersionResource) (schem
return schema.GroupVersionKind{}, &AmbiguousResourceError{PartialResource: resource, MatchingKinds: kinds}
}
func (m *DefaultRESTMapper) KindForWithContext(_ context.Context, resource schema.GroupVersionResource) (schema.GroupVersionKind, error) {
return m.KindFor(resource)
}
type kindByPreferredGroupVersion struct {
list []schema.GroupVersionKind
sortOrder []schema.GroupVersion
@ -484,10 +461,6 @@ func (m *DefaultRESTMapper) RESTMapping(gk schema.GroupKind, versions ...string)
return mappings[0], nil
}
func (m *DefaultRESTMapper) RESTMappingWithContext(_ context.Context, gk schema.GroupKind, versions ...string) (*RESTMapping, error) {
return m.RESTMapping(gk, versions...)
}
// RESTMappings returns the RESTMappings for the provided group kind. If a version search order
// is not provided, the search order provided to DefaultRESTMapper will be used.
func (m *DefaultRESTMapper) RESTMappings(gk schema.GroupKind, versions ...string) ([]*RESTMapping, error) {
@ -547,31 +520,10 @@ func (m *DefaultRESTMapper) RESTMappings(gk schema.GroupKind, versions ...string
return mappings, nil
}
func (m *DefaultRESTMapper) RESTMappingsWithContext(_ context.Context, gk schema.GroupKind, versions ...string) ([]*RESTMapping, error) {
return m.RESTMappings(gk, versions...)
}
// MaybeResetRESTMapper calls Reset() on the mapper if it is a ResettableRESTMapper or
// ResetWithContext() if it is a ResettableRESTMapperWithContext.
//
// Deprecated: use MaybeResetRESTMapperWithContext instead.
// MaybeResetRESTMapper calls Reset() on the mapper if it is a ResettableRESTMapper.
func MaybeResetRESTMapper(mapper RESTMapper) {
maybeReset(context.Background(), mapper)
}
// MaybeResetRESTMapperWithContext calls Reset() on the mapper if it is a ResettableRESTMapper or
// ResetWithContext() if it is a ResettableRESTMapperWithContext.
func MaybeResetRESTMapperWithContext(ctx context.Context, mapper RESTMapperWithContext) {
maybeReset(ctx, mapper)
}
func maybeReset(ctx context.Context, mapper any) {
if m, ok := mapper.(ResettableRESTMapperWithContext); ok {
m.ResetWithContext(ctx)
return
}
if m, ok := mapper.(ResettableRESTMapper); ok {
m, ok := mapper.(ResettableRESTMapper)
if ok {
m.Reset()
return
}
}

View file

@ -169,7 +169,6 @@ func (c *connection) newSpdyStream(stream *spdystream.Stream) {
err := c.newStreamHandler(stream, replySent)
rejectStream := (err != nil)
if rejectStream {
//nolint:logcheck // Hopefully this never gets triggered.
klog.Warningf("Stream rejected: %v", err)
stream.Reset()
return
@ -196,7 +195,6 @@ func (c *connection) sendPings(period time.Duration) {
case <-t.C:
}
if _, err := c.ping(); err != nil {
//nolint:logcheck // Hopefully this never gets triggered.
klog.V(3).Infof("SPDY Ping failed: %v", err)
// Continue, in case this is a transient failure.
// c.conn.CloseChan above will tell us when the connection is

View file

@ -105,14 +105,14 @@ func (u responseUpgrader) UpgradeResponse(w http.ResponseWriter, req *http.Reque
conn, bufrw, err := hijacker.Hijack()
if err != nil {
runtime.HandleErrorWithContext(req.Context(), err, "Unable to upgrade: error hijacking response")
runtime.HandleError(fmt.Errorf("unable to upgrade: error hijacking response: %v", err))
return nil
}
connWithBuf := &connWrapper{Conn: conn, bufReader: bufrw.Reader}
spdyConn, err := NewServerConnectionWithPings(connWithBuf, newStreamHandler, u.pingPeriod)
if err != nil {
runtime.HandleErrorWithContext(req.Context(), err, "Unable to upgrade: error creating SPDY server connection")
runtime.HandleError(fmt.Errorf("unable to upgrade: error creating SPDY server connection: %v", err))
return nil
}

View file

@ -126,16 +126,8 @@ func IsWebSocketRequestWithTunnelingProtocol(req *http.Request) bool {
// IgnoreReceives reads from a WebSocket until it is closed, then returns. If timeout is set, the
// read and write deadlines are pushed every time a new message is received.
//
// Contextual logging: IgnoreReceivesWithLogger should be used instead of IgnoreReceives in code which uses contextual logging.
func IgnoreReceives(ws *websocket.Conn, timeout time.Duration) {
IgnoreReceivesWithLogger(klog.Background(), ws, timeout)
}
// IgnoreReceivesWithLogger reads from a WebSocket until it is closed, then returns. If timeout is set, the
// read and write deadlines are pushed every time a new message is received.
func IgnoreReceivesWithLogger(logger klog.Logger, ws *websocket.Conn, timeout time.Duration) {
defer runtime.HandleCrashWithLogger(logger)
defer runtime.HandleCrash()
var data []byte
for {
resetTimeout(ws, timeout)
@ -244,7 +236,7 @@ func (conn *Conn) Open(w http.ResponseWriter, req *http.Request) (string, []io.R
// "conn.ready" and then blocks until serving is complete.
select {
case <-conn.ready:
klog.FromContext(req.Context()).V(8).Info("websocket server initialized--serving")
klog.V(8).Infof("websocket server initialized--serving")
case <-serveHTTPComplete:
// websocket server returned before completing initialization; cleanup and return error.
conn.closeNonThreadSafe() //nolint:errcheck
@ -338,19 +330,13 @@ func (conn *Conn) handle(ws *websocket.Conn) {
conn.initialize(ws)
defer conn.Close()
supportsStreamClose := protocolSupportsStreamClose(conn.selectedProtocol)
// conn.handle is typically used on the server-side and thus we have a request,
// but don't assume that and use klog.Background as fallback.
logger := klog.Background()
if req := ws.Request(); req != nil {
logger = klog.FromContext(req.Context())
}
for {
conn.resetTimeout()
var data []byte
if err := websocket.Message.Receive(ws, &data); err != nil {
if err != io.EOF {
logger.Error(err, "Error on socket receive")
klog.Errorf("Error on socket receive: %v", err)
}
break
}
@ -359,15 +345,15 @@ func (conn *Conn) handle(ws *websocket.Conn) {
}
if supportsStreamClose && data[0] == remotecommand.StreamClose {
if len(data) != 2 {
logger.Error(nil, "Single channel byte should follow stream close signal", "receivedLength", len(data)-1)
klog.Errorf("Single channel byte should follow stream close signal. Got %d bytes", len(data)-1)
break
} else {
channel := data[1]
if int(channel) >= len(conn.channels) {
logger.Error(nil, "Close is targeted for a channel that is not valid, possible protocol error", "channel", channel)
klog.Errorf("Close is targeted for a channel %d that is not valid, possible protocol error", channel)
break
}
logger.V(4).Info("Received half-close signal from client, close stream", "channel", channel)
klog.V(4).Infof("Received half-close signal from client; close %d stream", channel)
conn.channels[channel].Close() // After first Close, other closes are noop.
}
continue
@ -378,11 +364,11 @@ func (conn *Conn) handle(ws *websocket.Conn) {
}
data = data[1:]
if int(channel) >= len(conn.channels) {
logger.V(6).Info("Frame is targeted for a reader that is not valid, possible protocol error", "channel", channel)
klog.V(6).Infof("Frame is targeted for a reader %d that is not valid, possible protocol error", channel)
continue
}
if _, err := conn.channels[channel].DataFromSocket(data); err != nil {
logger.Error(err, "Unable to write frame", "sendLength", len(data), "channel", channel, "err", err)
klog.Errorf("Unable to write frame (%d bytes) to %d: %v", len(data), channel, err)
continue
}
}

View file

@ -17,7 +17,6 @@ limitations under the License.
package wsstream
import (
"context"
"encoding/base64"
"io"
"net/http"
@ -27,7 +26,6 @@ import (
"golang.org/x/net/websocket"
"k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/klog/v2"
)
// The WebSocket subprotocol "binary.k8s.io" will only send messages to the
@ -58,7 +56,6 @@ func NewDefaultReaderProtocols() map[string]ReaderProtocolConfig {
// Reader supports returning an arbitrary byte stream over a websocket channel.
type Reader struct {
logger klog.Logger
err chan error
r io.Reader
ping bool
@ -66,7 +63,7 @@ type Reader struct {
protocols map[string]ReaderProtocolConfig
selectedProtocol string
handleCrash func(ctx context.Context, additionalHandlers ...func(context.Context, interface{})) // overridable for testing
handleCrash func(additionalHandlers ...func(interface{})) // overridable for testing
}
// NewReader creates a WebSocket pipe that will copy the contents of r to a provided
@ -75,26 +72,13 @@ type Reader struct {
//
// The protocols parameter maps subprotocol names to StreamProtocols. The empty string
// subprotocol name is used if websocket.Config.Protocol is empty.
//
//logcheck:context // NewReaderWithLogger should be used instead of NewReader in code which supports contextual logging.
func NewReader(r io.Reader, ping bool, protocols map[string]ReaderProtocolConfig) *Reader {
return NewReaderWithLogger(klog.Background(), r, ping, protocols)
}
// NewReaderWithLogger creates a WebSocket pipe that will copy the contents of r to a provided
// WebSocket connection. If ping is true, a zero length message will be sent to the client
// before the stream begins reading.
//
// The protocols parameter maps subprotocol names to StreamProtocols. The empty string
// subprotocol name is used if websocket.Config.Protocol is empty.
func NewReaderWithLogger(logger klog.Logger, r io.Reader, ping bool, protocols map[string]ReaderProtocolConfig) *Reader {
return &Reader{
logger: logger,
r: r,
err: make(chan error),
ping: ping,
protocols: protocols,
handleCrash: runtime.HandleCrashWithContext,
handleCrash: runtime.HandleCrash,
}
}
@ -116,7 +100,7 @@ func (r *Reader) handshake(config *websocket.Config, req *http.Request) error {
// method completes.
func (r *Reader) Copy(w http.ResponseWriter, req *http.Request) error {
go func() {
defer r.handleCrash(req.Context())
defer r.handleCrash()
websocket.Server{Handshake: r.handshake, Handler: r.handle}.ServeHTTP(w, req)
}()
return <-r.err
@ -138,10 +122,10 @@ func (r *Reader) handle(ws *websocket.Conn) {
defer closeConn()
go func() {
defer runtime.HandleCrashWithLogger(r.logger)
defer runtime.HandleCrash()
// This blocks until the connection is closed.
// Client should not send anything.
IgnoreReceivesWithLogger(r.logger, ws, r.timeout)
IgnoreReceives(ws, r.timeout)
// Once the client closes, we should also close
closeConn()
}()

View file

@ -18,7 +18,6 @@ package wsstream
import (
"bytes"
"context"
"encoding/base64"
"fmt"
"io"
@ -33,7 +32,6 @@ import (
func TestStream(t *testing.T) {
input := "some random text"
//nolint:logcheck // Intentionally uses the old API.
r := NewReader(bytes.NewBuffer([]byte(input)), true, NewDefaultReaderProtocols())
r.SetIdleTimeout(time.Second)
data, err := readWebSocket(r, t, nil)
@ -47,7 +45,6 @@ func TestStream(t *testing.T) {
func TestStreamPing(t *testing.T) {
input := "some random text"
//nolint:logcheck // Intentionally uses the old API.
r := NewReader(bytes.NewBuffer([]byte(input)), true, NewDefaultReaderProtocols())
r.SetIdleTimeout(time.Second)
err := expectWebSocketFrames(r, t, nil, [][]byte{
@ -62,7 +59,6 @@ func TestStreamPing(t *testing.T) {
func TestStreamBase64(t *testing.T) {
input := "some random text"
encoded := base64.StdEncoding.EncodeToString([]byte(input))
//nolint:logcheck // Intentionally uses the old API.
r := NewReader(bytes.NewBuffer([]byte(input)), true, NewDefaultReaderProtocols())
data, err := readWebSocket(r, t, nil, "base64.binary.k8s.io")
if !reflect.DeepEqual(data, []byte(encoded)) {
@ -76,7 +72,6 @@ func TestStreamBase64(t *testing.T) {
func TestStreamVersionedBase64(t *testing.T) {
input := "some random text"
encoded := base64.StdEncoding.EncodeToString([]byte(input))
//nolint:logcheck // Intentionally uses the old API.
r := NewReader(bytes.NewBuffer([]byte(input)), true, map[string]ReaderProtocolConfig{
"": {Binary: true},
"binary.k8s.io": {Binary: true},
@ -105,7 +100,6 @@ func TestStreamVersionedCopy(t *testing.T) {
}
}
input := "some random text"
//nolint:logcheck // Intentionally uses the old API.
r := NewReader(bytes.NewBuffer([]byte(input)), true, supportedProtocols)
s, addr := newServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
err := r.Copy(w, req)
@ -151,7 +145,6 @@ func TestStreamError(t *testing.T) {
},
err: fmt.Errorf("bad read"),
}
//nolint:logcheck // Intentionally uses the old API.
r := NewReader(errs, false, NewDefaultReaderProtocols())
data, err := readWebSocket(r, t, nil)
@ -172,11 +165,10 @@ func TestStreamSurvivesPanic(t *testing.T) {
},
panicMessage: "bad read",
}
//nolint:logcheck // Intentionally uses the old API.
r := NewReader(errs, false, NewDefaultReaderProtocols())
// do not call runtime.HandleCrash() in handler. Otherwise, the tests are interrupted.
r.handleCrash = func(_ context.Context, additionalHandlers ...func(context.Context, interface{})) { recover() }
r.handleCrash = func(additionalHandlers ...func(interface{})) { recover() }
data, err := readWebSocket(r, t, nil)
if !reflect.DeepEqual(data, []byte(input)) {
@ -199,7 +191,6 @@ func TestStreamClosedDuringRead(t *testing.T) {
err: fmt.Errorf("stuff"),
pause: ch,
}
//nolint:logcheck // Intentionally uses the old API.
r := NewReader(errs, false, NewDefaultReaderProtocols())
data, err := readWebSocket(r, t, func(c *websocket.Conn) {

View file

@ -132,11 +132,9 @@ func SetTransportDefaults(t *http.Transport) *http.Transport {
t = SetOldTransportDefaults(t)
// Allow clients to disable http2 if needed.
if s := os.Getenv("DISABLE_HTTP2"); len(s) > 0 {
//nolint:logcheck // Should be rare, not worth converting.
klog.Info("HTTP2 has been explicitly disabled")
} else if allowsHTTP2(t) {
if err := configureHTTP2Transport(t); err != nil {
//nolint:logcheck // Should be rare, not worth converting.
klog.Warningf("Transport failed http2 configuration: %v", err)
}
}
@ -150,7 +148,6 @@ func readIdleTimeoutSeconds() int {
if s := os.Getenv("HTTP2_READ_IDLE_TIMEOUT_SECONDS"); len(s) > 0 {
i, err := strconv.Atoi(s)
if err != nil {
//nolint:logcheck // Should be rare, not worth converting.
klog.Warningf("Illegal HTTP2_READ_IDLE_TIMEOUT_SECONDS(%q): %v."+
" Default value %d is used", s, err, ret)
return ret
@ -165,7 +162,6 @@ func pingTimeoutSeconds() int {
if s := os.Getenv("HTTP2_PING_TIMEOUT_SECONDS"); len(s) > 0 {
i, err := strconv.Atoi(s)
if err != nil {
//nolint:logcheck // Should be rare, not worth converting.
klog.Warningf("Illegal HTTP2_PING_TIMEOUT_SECONDS(%q): %v."+
" Default value %d is used", s, err, ret)
return ret
@ -260,7 +256,6 @@ func CloseIdleConnectionsFor(transport http.RoundTripper) {
case RoundTripperWrapper:
CloseIdleConnectionsFor(transport.WrappedRoundTripper())
default:
//nolint:logcheck // Should be rare, not worth converting.
klog.Warningf("unknown transport type: %T", transport)
}
}

View file

@ -201,12 +201,12 @@ func parseIP(str string, family AddressFamily) (net.IP, error) {
return net.IP(bytes), nil
}
func isInterfaceUp(logger klog.Logger, intf *net.Interface) bool {
func isInterfaceUp(intf *net.Interface) bool {
if intf == nil {
return false
}
if intf.Flags&net.FlagUp != 0 {
logger.V(4).Info("Interface is up", "interface", intf.Name)
klog.V(4).Infof("Interface %v is up", intf.Name)
return true
}
return false
@ -218,23 +218,23 @@ func isLoopbackOrPointToPoint(intf *net.Interface) bool {
// getMatchingGlobalIP returns the first valid global unicast address of the given
// 'family' from the list of 'addrs'.
func getMatchingGlobalIP(logger klog.Logger, addrs []net.Addr, family AddressFamily) (net.IP, error) {
func getMatchingGlobalIP(addrs []net.Addr, family AddressFamily) (net.IP, error) {
if len(addrs) > 0 {
for i := range addrs {
logger.V(4).Info("Checking for matching global IP", "address", addrs[i])
klog.V(4).Infof("Checking addr %s.", addrs[i].String())
ip, _, err := netutils.ParseCIDRSloppy(addrs[i].String())
if err != nil {
return nil, err
}
if memberOf(ip, family) {
if ip.IsGlobalUnicast() {
logger.V(4).Info("IP found", "IP", ip)
klog.V(4).Infof("IP found %v", ip)
return ip, nil
} else {
logger.V(4).Info("Non-global unicast address found", "IP", ip)
klog.V(4).Infof("Non-global unicast address found %v", ip)
}
} else {
logger.V(4).Info("IP address has wrong version", "IP", ip, "IPVersion", int(family))
klog.V(4).Infof("%v is not an IPv%d address", ip, int(family))
}
}
@ -244,23 +244,23 @@ func getMatchingGlobalIP(logger klog.Logger, addrs []net.Addr, family AddressFam
// getIPFromInterface gets the IPs on an interface and returns a global unicast address, if any. The
// interface must be up, the IP must in the family requested, and the IP must be a global unicast address.
func getIPFromInterface(logger klog.Logger, intfName string, forFamily AddressFamily, nw networkInterfacer) (net.IP, error) {
func getIPFromInterface(intfName string, forFamily AddressFamily, nw networkInterfacer) (net.IP, error) {
intf, err := nw.InterfaceByName(intfName)
if err != nil {
return nil, err
}
if isInterfaceUp(logger, intf) {
if isInterfaceUp(intf) {
addrs, err := nw.Addrs(intf)
if err != nil {
return nil, err
}
logger.V(4).Info("Found addresses for interface", "interface", intfName, "numAddresses", len(addrs), "addresses", addrs)
matchingIP, err := getMatchingGlobalIP(logger, addrs, forFamily)
klog.V(4).Infof("Interface %q has %d addresses :%v.", intfName, len(addrs), addrs)
matchingIP, err := getMatchingGlobalIP(addrs, forFamily)
if err != nil {
return nil, err
}
if matchingIP != nil {
logger.V(4).Info("Found valid address", "IPVersion", int(forFamily), "IP", matchingIP, "interface", intfName)
klog.V(4).Infof("Found valid IPv%d address %v for interface %q.", int(forFamily), matchingIP, intfName)
return matchingIP, nil
}
}
@ -269,13 +269,13 @@ func getIPFromInterface(logger klog.Logger, intfName string, forFamily AddressFa
// getIPFromLoopbackInterface gets the IPs on a loopback interface and returns a global unicast address, if any.
// The loopback interface must be up, the IP must in the family requested, and the IP must be a global unicast address.
func getIPFromLoopbackInterface(logger klog.Logger, forFamily AddressFamily, nw networkInterfacer) (net.IP, error) {
func getIPFromLoopbackInterface(forFamily AddressFamily, nw networkInterfacer) (net.IP, error) {
intfs, err := nw.Interfaces()
if err != nil {
return nil, err
}
for _, intf := range intfs {
if !isInterfaceUp(logger, &intf) {
if !isInterfaceUp(&intf) {
continue
}
if intf.Flags&(net.FlagLoopback) != 0 {
@ -283,13 +283,13 @@ func getIPFromLoopbackInterface(logger klog.Logger, forFamily AddressFamily, nw
if err != nil {
return nil, err
}
logger.V(4).Info("Found addresses for interface", "interface", intf.Name, "numAddresses", len(addrs), "addresses", addrs)
matchingIP, err := getMatchingGlobalIP(logger, addrs, forFamily)
klog.V(4).Infof("Interface %q has %d addresses :%v.", intf.Name, len(addrs), addrs)
matchingIP, err := getMatchingGlobalIP(addrs, forFamily)
if err != nil {
return nil, err
}
if matchingIP != nil {
logger.V(4).Info("Found valid address", "IPVersion", int(forFamily), "IP", matchingIP, "interface", intf.Name)
klog.V(4).Infof("Found valid IPv%d address %v for interface %q.", int(forFamily), matchingIP, intf.Name)
return matchingIP, nil
}
}
@ -309,7 +309,7 @@ func memberOf(ip net.IP, family AddressFamily) bool {
// chooseIPFromHostInterfaces looks at all system interfaces, trying to find one that is up that
// has a global unicast address (non-loopback, non-link local, non-point2point), and returns the IP.
// addressFamilies determines whether it prefers IPv4 or IPv6
func chooseIPFromHostInterfaces(logger klog.Logger, nw networkInterfacer, addressFamilies AddressFamilyPreference) (net.IP, error) {
func chooseIPFromHostInterfaces(nw networkInterfacer, addressFamilies AddressFamilyPreference) (net.IP, error) {
intfs, err := nw.Interfaces()
if err != nil {
return nil, err
@ -318,14 +318,14 @@ func chooseIPFromHostInterfaces(logger klog.Logger, nw networkInterfacer, addres
return nil, fmt.Errorf("no interfaces found on host.")
}
for _, family := range addressFamilies {
logger.V(4).Info("Looking for system interface with a global address", "IPVersion", uint(family))
klog.V(4).Infof("Looking for system interface with a global IPv%d address", uint(family))
for _, intf := range intfs {
if !isInterfaceUp(logger, &intf) {
logger.V(4).Info("Skipping: interface is down", "interface", intf.Name)
if !isInterfaceUp(&intf) {
klog.V(4).Infof("Skipping: down interface %q", intf.Name)
continue
}
if isLoopbackOrPointToPoint(&intf) {
logger.V(4).Info("Skipping: is LB or P2P", "interface", intf.Name)
klog.V(4).Infof("Skipping: LB or P2P interface %q", intf.Name)
continue
}
addrs, err := nw.Addrs(&intf)
@ -333,7 +333,7 @@ func chooseIPFromHostInterfaces(logger klog.Logger, nw networkInterfacer, addres
return nil, err
}
if len(addrs) == 0 {
logger.V(4).Info("Skipping: no addresses", "interface", intf.Name)
klog.V(4).Infof("Skipping: no addresses on interface %q", intf.Name)
continue
}
for _, addr := range addrs {
@ -342,15 +342,15 @@ func chooseIPFromHostInterfaces(logger klog.Logger, nw networkInterfacer, addres
return nil, fmt.Errorf("unable to parse CIDR for interface %q: %s", intf.Name, err)
}
if !memberOf(ip, family) {
logger.V(4).Info("Skipping: no address family match", "IP", ip, "interface", intf.Name)
klog.V(4).Infof("Skipping: no address family match for %q on interface %q.", ip, intf.Name)
continue
}
// TODO: Decide if should open up to allow IPv6 LLAs in future.
if !ip.IsGlobalUnicast() {
logger.V(4).Info("Skipping: non-global address", "IP", ip, "interface", intf.Name)
klog.V(4).Infof("Skipping: non-global address %q on interface %q.", ip, intf.Name)
continue
}
logger.V(4).Info("Found global unicast address", "IP", ip, "interface", intf.Name)
klog.V(4).Infof("Found global unicast address %q on interface %q.", ip, intf.Name)
return ip, nil
}
}
@ -363,31 +363,20 @@ func chooseIPFromHostInterfaces(logger klog.Logger, nw networkInterfacer, addres
// interfaces. Otherwise, it will use IPv4 and IPv6 route information to return the
// IP of the interface with a gateway on it (with priority given to IPv4). For a node
// with no internet connection, it returns error.
//
//logcheck:context // [ChooseHostInterfaceWithLogger] should be used instead of ChooseHostInterface in code which supports contextual logging.
func ChooseHostInterface() (net.IP, error) {
return ChooseHostInterfaceWithLogger(klog.Background())
return chooseHostInterface(preferIPv4)
}
// ChooseHostInterfaceWithLogger is a method used fetch an IP for a daemon.
// If there is no routing info file, it will choose a global IP from the system
// interfaces. Otherwise, it will use IPv4 and IPv6 route information to return the
// IP of the interface with a gateway on it (with priority given to IPv4). For a node
// with no internet connection, it returns error.
func ChooseHostInterfaceWithLogger(logger klog.Logger) (net.IP, error) {
return chooseHostInterface(logger, preferIPv4)
}
func chooseHostInterface(logger klog.Logger, addressFamilies AddressFamilyPreference) (net.IP, error) {
func chooseHostInterface(addressFamilies AddressFamilyPreference) (net.IP, error) {
var nw networkInterfacer = networkInterface{}
if _, err := os.Stat(ipv4RouteFile); os.IsNotExist(err) {
return chooseIPFromHostInterfaces(logger, nw, addressFamilies)
return chooseIPFromHostInterfaces(nw, addressFamilies)
}
routes, err := getAllDefaultRoutes()
if err != nil {
return nil, err
}
return chooseHostInterfaceFromRoute(logger, routes, nw, addressFamilies)
return chooseHostInterfaceFromRoute(routes, nw, addressFamilies)
}
// networkInterfacer defines an interface for several net library functions. Production
@ -438,36 +427,36 @@ func getAllDefaultRoutes() ([]Route, error) {
// global IP address from the interface for the route. If there are routes but no global
// address is obtained from the interfaces, it checks if the loopback interface has a global address.
// addressFamilies determines whether it prefers IPv4 or IPv6
func chooseHostInterfaceFromRoute(logger klog.Logger, routes []Route, nw networkInterfacer, addressFamilies AddressFamilyPreference) (net.IP, error) {
func chooseHostInterfaceFromRoute(routes []Route, nw networkInterfacer, addressFamilies AddressFamilyPreference) (net.IP, error) {
for _, family := range addressFamilies {
logger.V(4).Info("Looking for default routes with IP addresses", "IPVersion", uint(family))
klog.V(4).Infof("Looking for default routes with IPv%d addresses", uint(family))
for _, route := range routes {
if route.Family != family {
continue
}
logger.V(4).Info("Default route transits interface", "interface", route.Interface)
finalIP, err := getIPFromInterface(logger, route.Interface, family, nw)
klog.V(4).Infof("Default route transits interface %q", route.Interface)
finalIP, err := getIPFromInterface(route.Interface, family, nw)
if err != nil {
return nil, err
}
if finalIP != nil {
logger.V(4).Info("Found active IP", "IP", finalIP)
klog.V(4).Infof("Found active IP %v ", finalIP)
return finalIP, nil
}
// In case of network setups where default routes are present, but network
// interfaces use only link-local addresses (e.g. as described in RFC5549).
// the global IP is assigned to the loopback interface, and we should use it
loopbackIP, err := getIPFromLoopbackInterface(logger, family, nw)
loopbackIP, err := getIPFromLoopbackInterface(family, nw)
if err != nil {
return nil, err
}
if loopbackIP != nil {
logger.V(4).Info("Found active IP on Loopback interface", "IP", loopbackIP)
klog.V(4).Infof("Found active IP %v on Loopback interface", loopbackIP)
return loopbackIP, nil
}
}
}
logger.V(4).Info("No active IP found by looking at default routes")
klog.V(4).Infof("No active IP found by looking at default routes")
return nil, fmt.Errorf("unable to select an IP from default routes.")
}
@ -476,25 +465,14 @@ func chooseHostInterfaceFromRoute(logger klog.Logger, routes []Route, nw network
// If bindAddress is unspecified or loopback, it returns the default IP of the same
// address family as bindAddress.
// Otherwise, it just returns bindAddress.
//
//logcheck:context // [ResolveBindAddressWithLogger] should be used instead of ResolveBindAddress in code which supports contextual logging.
func ResolveBindAddress(bindAddress net.IP) (net.IP, error) {
return ResolveBindAddressWithLogger(klog.Background(), bindAddress)
}
// ResolveBindAddressWithLogger returns the IP address of a daemon, based on the given bindAddress:
// If bindAddress is unset, it returns the host's default IP, as with ChooseHostInterface().
// If bindAddress is unspecified or loopback, it returns the default IP of the same
// address family as bindAddress.
// Otherwise, it just returns bindAddress.
func ResolveBindAddressWithLogger(logger klog.Logger, bindAddress net.IP) (net.IP, error) {
addressFamilies := preferIPv4
if bindAddress != nil && memberOf(bindAddress, familyIPv6) {
addressFamilies = preferIPv6
}
if bindAddress == nil || bindAddress.IsUnspecified() || bindAddress.IsLoopback() {
hostIP, err := chooseHostInterface(logger, addressFamilies)
hostIP, err := chooseHostInterface(addressFamilies)
if err != nil {
return nil, err
}
@ -507,20 +485,10 @@ func ResolveBindAddressWithLogger(logger klog.Logger, bindAddress net.IP) (net.I
// This is required in case of network setups where default routes are present, but network
// interfaces use only link-local addresses (e.g. as described in RFC5549).
// e.g when using BGP to announce a host IP over link-local ip addresses and this ip address is attached to the lo interface.
//
//logcheck:context // [ChooseBindAddressForInterfaceWithLogger] should be used instead of ChooseBindAddressForInterface in code which supports contextual logging.
func ChooseBindAddressForInterface(intfName string) (net.IP, error) {
return ChooseBindAddressForInterfaceWithLogger(klog.Background(), intfName)
}
// ChooseBindAddressForInterfaceWithLogger choose a global IP for a specific interface, with priority given to IPv4.
// This is required in case of network setups where default routes are present, but network
// interfaces use only link-local addresses (e.g. as described in RFC5549).
// e.g when using BGP to announce a host IP over link-local ip addresses and this ip address is attached to the lo interface.
func ChooseBindAddressForInterfaceWithLogger(logger klog.Logger, intfName string) (net.IP, error) {
var nw networkInterfacer = networkInterface{}
for _, family := range preferIPv4 {
ip, err := getIPFromInterface(logger, intfName, family, nw)
ip, err := getIPFromInterface(intfName, family, nw)
if err != nil {
return nil, err
}

View file

@ -23,7 +23,6 @@ import (
"strings"
"testing"
"k8s.io/klog/v2/ktesting"
netutils "k8s.io/utils/net"
)
@ -243,7 +242,6 @@ func TestParseIP(t *testing.T) {
}
func TestIsInterfaceUp(t *testing.T) {
logger, _ := ktesting.NewTestContext(t)
testCases := []struct {
tcase string
intf *net.Interface
@ -254,7 +252,7 @@ func TestIsInterfaceUp(t *testing.T) {
{"no interface", nil, false},
}
for _, tc := range testCases {
it := isInterfaceUp(logger, tc.intf)
it := isInterfaceUp(tc.intf)
if it != tc.expected {
t.Errorf("case[%v]: expected %v, got %v .", tc.tcase, tc.expected, it)
}
@ -271,7 +269,6 @@ func (a addrStruct) String() string {
}
func TestFinalIP(t *testing.T) {
logger, _ := ktesting.NewTestContext(t)
testCases := []struct {
tcase string
addr []net.Addr
@ -292,7 +289,7 @@ func TestFinalIP(t *testing.T) {
{"no addresses", []net.Addr{}, familyIPv4, nil},
}
for _, tc := range testCases {
ip, err := getMatchingGlobalIP(logger, tc.addr, tc.family)
ip, err := getMatchingGlobalIP(tc.addr, tc.family)
if !ip.Equal(tc.expected) {
t.Errorf("case[%v]: expected %v, got %v .err : %v", tc.tcase, tc.expected, ip, err)
}
@ -552,7 +549,6 @@ func (_ networkInterfaceWithInvalidAddr) Interfaces() ([]net.Interface, error) {
}
func TestGetIPFromInterface(t *testing.T) {
logger, _ := ktesting.NewTestContext(t)
testCases := []struct {
tcase string
nwname string
@ -571,7 +567,7 @@ func TestGetIPFromInterface(t *testing.T) {
{"bad addr", "eth3", familyIPv4, networkInterfaceWithInvalidAddr{}, nil, "invalid CIDR"},
}
for _, tc := range testCases {
ip, err := getIPFromInterface(logger, tc.nwname, tc.family, tc.nw)
ip, err := getIPFromInterface(tc.nwname, tc.family, tc.nw)
if err != nil {
if !strings.Contains(err.Error(), tc.errStrFrag) {
t.Errorf("case[%s]: Error string %q does not contain %q", tc.tcase, err, tc.errStrFrag)
@ -585,7 +581,6 @@ func TestGetIPFromInterface(t *testing.T) {
}
func TestGetIPFromLoopbackInterface(t *testing.T) {
logger, _ := ktesting.NewTestContext(t)
testCases := []struct {
tcase string
family AddressFamily
@ -599,7 +594,7 @@ func TestGetIPFromLoopbackInterface(t *testing.T) {
{"no global ipv6", familyIPv6, loopbackNetworkInterface{}, nil, ""},
}
for _, tc := range testCases {
ip, err := getIPFromLoopbackInterface(logger, tc.family, tc.nw)
ip, err := getIPFromLoopbackInterface(tc.family, tc.nw)
if err != nil {
if !strings.Contains(err.Error(), tc.errStrFrag) {
t.Errorf("case[%s]: Error string %q does not contain %q", tc.tcase, err, tc.errStrFrag)
@ -613,7 +608,6 @@ func TestGetIPFromLoopbackInterface(t *testing.T) {
}
func TestChooseHostInterfaceFromRoute(t *testing.T) {
logger, _ := ktesting.NewTestContext(t)
testCases := []struct {
tcase string
routes []Route
@ -642,7 +636,7 @@ func TestChooseHostInterfaceFromRoute(t *testing.T) {
{"fail get IP", routeV4, networkInterfaceFailGetAddrs{}, preferIPv4, nil},
}
for _, tc := range testCases {
ip, err := chooseHostInterfaceFromRoute(logger, tc.routes, tc.nw, tc.order)
ip, err := chooseHostInterfaceFromRoute(tc.routes, tc.nw, tc.order)
if !ip.Equal(tc.expected) {
t.Errorf("case[%v]: expected %v, got %+v .err : %v", tc.tcase, tc.expected, ip, err)
}
@ -669,7 +663,6 @@ func TestMemberOf(t *testing.T) {
}
func TestGetIPFromHostInterfaces(t *testing.T) {
logger, _ := ktesting.NewTestContext(t)
testCases := []struct {
tcase string
nw networkInterfacer
@ -695,7 +688,7 @@ func TestGetIPFromHostInterfaces(t *testing.T) {
}
for _, tc := range testCases {
ip, err := chooseIPFromHostInterfaces(logger, tc.nw, tc.order)
ip, err := chooseIPFromHostInterfaces(tc.nw, tc.order)
if !ip.Equal(tc.expected) {
t.Errorf("case[%s]: expected %+v, got %+v with err : %v", tc.tcase, tc.expected, ip, err)
}

View file

@ -39,7 +39,7 @@ func DialURL(ctx context.Context, url *url.URL, transport http.RoundTripper) (ne
dialer, err := utilnet.DialerFor(transport)
if err != nil {
klog.FromContext(ctx).V(5).Info("Unable to unwrap transport to get dialer", "type", fmt.Sprintf("%T", transport), "err", err)
klog.V(5).Infof("Unable to unwrap transport %T to get dialer: %v", transport, err)
}
switch url.Scheme {
@ -53,7 +53,7 @@ func DialURL(ctx context.Context, url *url.URL, transport http.RoundTripper) (ne
// Get the tls config from the transport if we recognize it
tlsConfig, err := utilnet.TLSClientConfig(transport)
if err != nil {
klog.FromContext(ctx).V(5).Info("Unable to unwrap transport to get at TLS config", "type", fmt.Sprintf("%T", transport), "err", err)
klog.V(5).Infof("Unable to unwrap transport %T to get at TLS config: %v", transport, err)
}
if dialer != nil {
@ -65,7 +65,7 @@ func DialURL(ctx context.Context, url *url.URL, transport http.RoundTripper) (ne
}
if tlsConfig == nil {
// tls.Client requires non-nil config
klog.FromContext(ctx).Info("Warning: using custom dialer with no TLSClientConfig, defaulting to InsecureSkipVerify")
klog.Warning("using custom dialer with no TLSClientConfig. Defaulting to InsecureSkipVerify")
// tls.Handshake() requires ServerName or InsecureSkipVerify
tlsConfig = &tls.Config{
InsecureSkipVerify: true,

View file

@ -249,7 +249,7 @@ func (t *Transport) rewriteResponse(req *http.Request, resp *http.Response) (*ht
// This is fine
default:
// Some encoding we don't understand-- don't try to parse this
klog.FromContext(req.Context()).Error(nil, "Proxy encountered unknown encoding for text/html, can't understand this so not fixing links", "encoding", encoding)
klog.Errorf("Proxy encountered encoding %v for text/html; can't understand this so not fixing links.", encoding)
return resp, nil
}
@ -258,7 +258,7 @@ func (t *Transport) rewriteResponse(req *http.Request, resp *http.Response) (*ht
}
err := rewriteHTML(reader, writer, urlRewriter)
if err != nil {
klog.FromContext(req.Context()).Error(err, "Failed to rewrite URLs")
klog.Errorf("Failed to rewrite URLs: %v", err)
return resp, err
}

View file

@ -307,9 +307,8 @@ func (noSuppressPanicError) Write(p []byte) (n int, err error) {
// tryUpgrade returns true if the request was handled.
func (h *UpgradeAwareHandler) tryUpgrade(w http.ResponseWriter, req *http.Request) bool {
logger := klog.FromContext(req.Context())
if !httpstream.IsUpgradeRequest(req) {
logger.V(6).Info("Request was not an upgrade")
klog.V(6).Infof("Request was not an upgrade")
return false
}
@ -333,15 +332,15 @@ func (h *UpgradeAwareHandler) tryUpgrade(w http.ResponseWriter, req *http.Reques
// Only append X-Forwarded-For in the upgrade path, since httputil.NewSingleHostReverseProxy
// handles this in the non-upgrade path.
utilnet.AppendForwardedForHeader(clone)
logger.V(6).Info("Connecting to backend proxy (direct dial)", "location", &location, "headers", clone.Header)
klog.V(6).Infof("Connecting to backend proxy (direct dial) %s\n Headers: %v", &location, clone.Header)
if h.UseLocationHost {
clone.Host = h.Location.Host
}
clone.URL = &location
logger.V(6).Info("UpgradeAwareProxy: dialing for SPDY upgrade with headers", "headers", clone.Header)
klog.V(6).Infof("UpgradeAwareProxy: dialing for SPDY upgrade with headers: %v", clone.Header)
backendConn, err = h.DialForUpgrade(clone)
if err != nil {
logger.V(6).Info("Proxy connection error", "err", err)
klog.V(6).Infof("Proxy connection error: %v", err)
h.Responder.Error(w, req, err)
return true
}
@ -350,7 +349,7 @@ func (h *UpgradeAwareHandler) tryUpgrade(w http.ResponseWriter, req *http.Reques
// determine the http response code from the backend by reading from rawResponse+backendConn
backendHTTPResponse, headerBytes, err := getResponse(io.MultiReader(bytes.NewReader(rawResponse), backendConn))
if err != nil {
logger.V(6).Info("Proxy connection error", "err", err)
klog.V(6).Infof("Proxy connection error: %v", err)
h.Responder.Error(w, req, err)
return true
}
@ -364,7 +363,7 @@ func (h *UpgradeAwareHandler) tryUpgrade(w http.ResponseWriter, req *http.Reques
// return a generic error here.
if backendHTTPResponse.StatusCode != http.StatusSwitchingProtocols && backendHTTPResponse.StatusCode < 400 {
err := fmt.Errorf("invalid upgrade response: status code %d", backendHTTPResponse.StatusCode)
logger.Error(err, "Proxy upgrade error")
klog.Errorf("Proxy upgrade error: %v", err)
h.Responder.Error(w, req, err)
return true
}
@ -373,13 +372,13 @@ func (h *UpgradeAwareHandler) tryUpgrade(w http.ResponseWriter, req *http.Reques
// hijacking should be the last step in the upgrade.
requestHijacker, ok := w.(http.Hijacker)
if !ok {
logger.Error(nil, "Unable to hijack response writer", "type", fmt.Sprintf("%T", w))
klog.Errorf("Unable to hijack response writer: %T", w)
h.Responder.Error(w, req, fmt.Errorf("request connection cannot be hijacked: %T", w))
return true
}
requestHijackedConn, _, err := requestHijacker.Hijack()
if err != nil {
logger.Error(err, "Unable to hijack response")
klog.Errorf("Unable to hijack response: %v", err)
h.Responder.Error(w, req, fmt.Errorf("error hijacking connection: %v", err))
return true
}
@ -387,7 +386,7 @@ func (h *UpgradeAwareHandler) tryUpgrade(w http.ResponseWriter, req *http.Reques
if backendHTTPResponse.StatusCode != http.StatusSwitchingProtocols {
// If the backend did not upgrade the request, echo the response from the backend to the client and return, closing the connection.
logger.V(6).Info("Proxy upgrade error", "statusCode", backendHTTPResponse.StatusCode)
klog.V(6).Infof("Proxy upgrade error, status code %d", backendHTTPResponse.StatusCode)
// set read/write deadlines
deadline := time.Now().Add(10 * time.Second)
backendConn.SetReadDeadline(deadline)
@ -395,7 +394,7 @@ func (h *UpgradeAwareHandler) tryUpgrade(w http.ResponseWriter, req *http.Reques
// write the response to the client
err := backendHTTPResponse.Write(requestHijackedConn)
if err != nil && !strings.Contains(err.Error(), "use of closed network connection") {
logger.Error(err, "Error proxying data from backend to client")
klog.Errorf("Error proxying data from backend to client: %v", err)
}
// Indicate we handled the request
return true
@ -403,9 +402,9 @@ func (h *UpgradeAwareHandler) tryUpgrade(w http.ResponseWriter, req *http.Reques
// Forward raw response bytes back to client.
if len(rawResponse) > 0 {
logger.V(6).Info("Writing to hijacked connection", "length", len(rawResponse))
klog.V(6).Infof("Writing %d bytes to hijacked connection", len(rawResponse))
if _, err = requestHijackedConn.Write(rawResponse); err != nil {
utilruntime.HandleErrorWithLogger(logger, err, "Error proxying response from backend to client")
utilruntime.HandleError(fmt.Errorf("Error proxying response from backend to client: %v", err))
}
}
@ -425,7 +424,7 @@ func (h *UpgradeAwareHandler) tryUpgrade(w http.ResponseWriter, req *http.Reques
}
_, err := io.Copy(writer, requestHijackedConn)
if err != nil && !strings.Contains(err.Error(), "use of closed network connection") {
logger.Error(err, "Error proxying data from client to backend")
klog.Errorf("Error proxying data from client to backend: %v", err)
}
close(writerComplete)
}()
@ -439,7 +438,7 @@ func (h *UpgradeAwareHandler) tryUpgrade(w http.ResponseWriter, req *http.Reques
}
_, err := io.Copy(requestHijackedConn, reader)
if err != nil && !strings.Contains(err.Error(), "use of closed network connection") {
logger.Error(err, "Error proxying data from backend to client")
klog.Errorf("Error proxying data from backend to client: %v", err)
}
close(readerComplete)
}()
@ -450,7 +449,7 @@ func (h *UpgradeAwareHandler) tryUpgrade(w http.ResponseWriter, req *http.Reques
case <-writerComplete:
case <-readerComplete:
}
logger.V(6).Info("Disconnecting from backend proxy", "location", &location, "headers", clone.Header)
klog.V(6).Infof("Disconnecting from backend proxy %s\n Headers: %v", &location, clone.Header)
return true
}

View file

@ -60,8 +60,7 @@ func NewTunnelingHandler(upgradeHandler http.Handler) *TunnelingHandler {
// case the upstream upgrade fails, we delegate communication to the passed
// in "w" ResponseWriter.
func (h *TunnelingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
logger := klog.FromContext(req.Context())
logger.V(4).Info("TunnelingHandler ServeHTTP")
klog.V(4).Infoln("TunnelingHandler ServeHTTP")
spdyProtocols := spdyProtocolsFromWebsocketProtocols(req)
if len(spdyProtocols) == 0 {
@ -76,12 +75,10 @@ func (h *TunnelingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// and the "conn" is hijacked and used in the subsequent upgradeHandler, or
// the upgrade failed, and "w" is the delegate used for the non-upgrade response.
writer := &tunnelingResponseWriter{
logger: logger,
// "w" is used in the non-upgrade error cases called in the upgradeHandler.
w: w,
// "conn" is returned in the successful upgrade case when hijacked in the upgradeHandler.
conn: &headerInterceptingConn{
logger: logger,
initializableConn: &tunnelingWebsocketUpgraderConn{
w: w,
req: req,
@ -89,7 +86,7 @@ func (h *TunnelingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
},
}
logger.V(4).Info("Tunnel spdy through websockets using the UpgradeAwareProxy")
klog.V(4).Infoln("Tunnel spdy through websockets using the UpgradeAwareProxy")
h.upgradeHandler.ServeHTTP(writer, spdyRequest)
}
@ -134,7 +131,6 @@ var _ http.Hijacker = &tunnelingResponseWriter{}
// Once Write or WriteHeader is called, Hijack returns an error.
// Once Hijack is called, Write, WriteHeader, and Hijack return errors.
type tunnelingResponseWriter struct {
logger klog.Logger
// w is used to delegate Header(), WriteHeader(), and Write() calls
w http.ResponseWriter
// conn is returned from Hijack()
@ -154,15 +150,15 @@ func (w *tunnelingResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error)
w.mu.Lock()
defer w.mu.Unlock()
if w.written {
w.logger.Error(nil, "Hijack called after write")
klog.Errorf("Hijack called after write")
return nil, nil, errors.New("connection has already been written to")
}
if w.hijacked {
w.logger.Error(nil, "Hijack called after hijack")
klog.Errorf("Hijack called after hijack")
return nil, nil, errors.New("connection has already been hijacked")
}
w.hijacked = true
w.logger.V(6).Info("Hijack returning websocket tunneling net.Conn")
klog.V(6).Infof("Hijack returning websocket tunneling net.Conn")
return w.conn, nil, nil
}
@ -176,7 +172,7 @@ func (w *tunnelingResponseWriter) Write(p []byte) (int, error) {
w.mu.Lock()
defer w.mu.Unlock()
if w.hijacked {
w.logger.Error(nil, "Write called after hijack")
klog.Errorf("Write called after hijack")
return 0, http.ErrHijacked
}
w.written = true
@ -188,18 +184,18 @@ func (w *tunnelingResponseWriter) WriteHeader(statusCode int) {
w.mu.Lock()
defer w.mu.Unlock()
if w.written {
w.logger.Error(nil, "WriteHeader called after write")
klog.Errorf("WriteHeader called after write")
return
}
if w.hijacked {
w.logger.Error(nil, "WriteHeader called after hijack")
klog.Errorf("WriteHeader called after hijack")
return
}
w.written = true
if statusCode == http.StatusSwitchingProtocols {
// 101 upgrade responses must come via the hijacked connection, not WriteHeader
w.logger.Error(nil, "WriteHeader called with 101 upgrade")
klog.Errorf("WriteHeader called with 101 upgrade")
http.Error(w.w, "unexpected upgrade", http.StatusInternalServerError)
return
}
@ -212,7 +208,6 @@ func (w *tunnelingResponseWriter) WriteHeader(statusCode int) {
// HTTP response status/headers from the upstream SPDY connection, then use
// that to decide how to initialize the delegate connection for writes.
type headerInterceptingConn struct {
logger klog.Logger
// initializableConn is delegated to for all net.Conn methods.
// initializableConn.Write() is not called until response headers have been read
// and initializableConn#InitializeWrite() has been called with the result.
@ -279,7 +274,7 @@ func (h *headerInterceptingConn) Write(b []byte) (int, error) {
}
resp, err := http.ReadResponse(bufio.NewReader(bytes.NewReader(headerBytes)), nil)
if err != nil {
h.logger.Error(err, "Invalid headers")
klog.Errorf("invalid headers: %v", err)
h.initializeErr = err
return len(b), err
}
@ -329,12 +324,11 @@ func (u *tunnelingWebsocketUpgraderConn) InitializeWrite(backendResponse *http.R
return u.err
}
logger := klog.FromContext(u.req.Context())
if backendResponse.StatusCode == http.StatusSwitchingProtocols {
connectionHeader := strings.ToLower(backendResponse.Header.Get(httpstream.HeaderConnection))
upgradeHeader := strings.ToLower(backendResponse.Header.Get(httpstream.HeaderUpgrade))
if !strings.Contains(connectionHeader, strings.ToLower(httpstream.HeaderUpgrade)) || !strings.Contains(upgradeHeader, strings.ToLower(spdy.HeaderSpdy31)) {
logger.Error(nil, "Unable to upgrade: missing upgrade headers in response", "headers", backendResponse.Header)
klog.Errorf("unable to upgrade: missing upgrade headers in response: %#v", backendResponse.Header)
u.err = fmt.Errorf("unable to upgrade: missing upgrade headers in response")
metrics.IncStreamTunnelRequest(context.Background(), strconv.Itoa(http.StatusInternalServerError))
http.Error(u.w, u.err.Error(), http.StatusInternalServerError)
@ -357,26 +351,26 @@ func (u *tunnelingWebsocketUpgraderConn) InitializeWrite(backendResponse *http.R
}
conn, err := upgrader.Upgrade(u.w, u.req, nil)
if err != nil {
logger.Error(err, "Error upgrading websocket connection")
klog.Errorf("error upgrading websocket connection: %v", err)
metrics.IncStreamTunnelRequest(context.Background(), strconv.Itoa(http.StatusInternalServerError))
u.err = err
return u.err
}
logger.V(4).Info("Websocket connection created", "protocol", conn.Subprotocol())
klog.V(4).Infof("websocket connection created: %s", conn.Subprotocol())
metrics.IncStreamTunnelRequest(context.Background(), strconv.Itoa(http.StatusSwitchingProtocols))
u.conn = portforward.NewTunnelingConnectionWithLogger(klog.LoggerWithName(logger, "server"), conn)
u.conn = portforward.NewTunnelingConnection("server", conn)
return nil
}
// anything other than an upgrade should pass through the backend response
logger.Error(nil, "SPDY upgrade failed", "status", backendResponse.Status)
klog.Errorf("SPDY upgrade failed: %s", backendResponse.Status)
metrics.IncStreamTunnelRequest(context.Background(), strconv.Itoa(backendResponse.StatusCode))
// try to hijack
conn, _, err = u.w.(http.Hijacker).Hijack()
if err != nil {
logger.Error(err, "Unable to hijack response")
klog.Errorf("Unable to hijack response: %v", err)
u.err = err
return u.err
}

View file

@ -43,7 +43,7 @@ func NewTranslatingHandler(delegate http.Handler, translator http.Handler, shoul
func (t *translatingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
if t.shouldTranslate(req) {
klog.FromContext(req.Context()).V(4).Info("Request handled by translator proxy")
klog.V(4).Infof("request handled by translator proxy")
t.translator.ServeHTTP(w, req)
return
}

View file

@ -17,7 +17,6 @@ limitations under the License.
package disk
import (
"context"
"errors"
"io"
"net/http"
@ -43,7 +42,7 @@ import (
// CachedDiscoveryClient implements the functions that discovery server-supported API groups,
// versions and resources.
type CachedDiscoveryClient struct {
delegate discovery.DiscoveryInterfaceWithContext
delegate discovery.DiscoveryInterface
// cacheDirectory is the directory where discovery docs are held. It must be unique per host:port combination to work well.
cacheDirectory string
@ -62,97 +61,72 @@ type CachedDiscoveryClient struct {
fresh bool
// caching openapi v3 client which wraps the delegate's client
openapiClient *cachedopenapi.Client
openapiClient openapi.Client
}
var (
_ discovery.CachedDiscoveryInterface = &CachedDiscoveryClient{}
_ discovery.CachedDiscoveryInterfaceWithContext = &CachedDiscoveryClient{}
)
var _ discovery.CachedDiscoveryInterface = &CachedDiscoveryClient{}
// ServerResourcesForGroupVersion returns the supported resources for a group and version.
//
// Deprecated: use ServerResourcesForGroupVersionWithContext instead.
func (d *CachedDiscoveryClient) ServerResourcesForGroupVersion(groupVersion string) (*metav1.APIResourceList, error) {
return d.ServerResourcesForGroupVersionWithContext(context.Background(), groupVersion)
}
// ServerResourcesForGroupVersionWithContext returns the supported resources for a group and version.
func (d *CachedDiscoveryClient) ServerResourcesForGroupVersionWithContext(ctx context.Context, groupVersion string) (*metav1.APIResourceList, error) {
filename := filepath.Join(d.cacheDirectory, groupVersion, "serverresources.json")
cachedBytes, err := d.getCachedFile(filename)
// don't fail on errors, we either don't have a file or won't be able to run the cached check. Either way we can fallback.
if err == nil {
cachedResources := &metav1.APIResourceList{}
if err := runtime.DecodeInto(scheme.Codecs.UniversalDecoder(), cachedBytes, cachedResources); err == nil {
klog.FromContext(ctx).V(10).Info("Returning cached discovery info", "fileName", filename)
klog.V(10).Infof("returning cached discovery info from %v", filename)
return cachedResources, nil
}
}
liveResources, err := d.delegate.ServerResourcesForGroupVersionWithContext(ctx, groupVersion)
liveResources, err := d.delegate.ServerResourcesForGroupVersion(groupVersion)
if err != nil {
klog.FromContext(ctx).V(3).Info("Skipped caching discovery info due to error", "err", err)
klog.V(3).Infof("skipped caching discovery info due to %v", err)
return liveResources, err
}
if liveResources == nil || len(liveResources.APIResources) == 0 {
klog.FromContext(ctx).V(3).Info("skipped caching discovery info, no resources found")
klog.V(3).Infof("skipped caching discovery info, no resources found")
return liveResources, err
}
if err := d.writeCachedFile(filename, liveResources); err != nil {
klog.FromContext(ctx).V(1).Info("Failed to write cache", "fileName", filename, "err", err)
klog.V(1).Infof("failed to write cache to %v due to %v", filename, err)
}
return liveResources, nil
}
// ServerGroupsAndResources returns the supported groups and resources for all groups and versions.
//
// Deprecated: use ServerGroupsAndResourcesWithContext instead.
func (d *CachedDiscoveryClient) ServerGroupsAndResources() ([]*metav1.APIGroup, []*metav1.APIResourceList, error) {
return d.ServerGroupsAndResourcesWithContext(context.Background())
}
// ServerGroupsAndResourcesWithContext returns the supported groups and resources for all groups and versions.
func (d *CachedDiscoveryClient) ServerGroupsAndResourcesWithContext(ctx context.Context) ([]*metav1.APIGroup, []*metav1.APIResourceList, error) {
return discovery.ServerGroupsAndResourcesWithContext(ctx, d)
return discovery.ServerGroupsAndResources(d)
}
// ServerGroups returns the supported groups, with information like supported versions and the
// preferred version.
//
// Deprecated: use ServerGroupsWithContext instead.
func (d *CachedDiscoveryClient) ServerGroups() (*metav1.APIGroupList, error) {
return d.ServerGroupsWithContext(context.Background())
}
// ServerGroupsWithContext returns the supported groups, with information like supported versions and the
// preferred version.
func (d *CachedDiscoveryClient) ServerGroupsWithContext(ctx context.Context) (*metav1.APIGroupList, error) {
filename := filepath.Join(d.cacheDirectory, "servergroups.json")
cachedBytes, err := d.getCachedFile(filename)
// don't fail on errors, we either don't have a file or won't be able to run the cached check. Either way we can fallback.
if err == nil {
cachedGroups := &metav1.APIGroupList{}
if err := runtime.DecodeInto(scheme.Codecs.UniversalDecoder(), cachedBytes, cachedGroups); err == nil {
klog.FromContext(ctx).V(10).Info("Returning cached discovery info", "fileName", filename)
klog.V(10).Infof("returning cached discovery info from %v", filename)
return cachedGroups, nil
}
}
liveGroups, err := d.delegate.ServerGroupsWithContext(ctx)
liveGroups, err := d.delegate.ServerGroups()
if err != nil {
klog.FromContext(ctx).V(3).Info("Skipped caching discovery info due to error", "err", err)
klog.V(3).Infof("skipped caching discovery info due to %v", err)
return liveGroups, err
}
if liveGroups == nil || len(liveGroups.Groups) == 0 {
klog.FromContext(ctx).V(3).Info("Skipped caching discovery info, no groups found")
klog.V(3).Infof("skipped caching discovery info, no groups found")
return liveGroups, err
}
if err := d.writeCachedFile(filename, liveGroups); err != nil {
klog.FromContext(ctx).V(1).Info("Failed to write cache", "fileName", filename, "err", err)
klog.V(1).Infof("failed to write cache to %v due to %v", filename, err)
}
return liveGroups, nil
@ -245,67 +219,28 @@ func (d *CachedDiscoveryClient) RESTClient() restclient.Interface {
// ServerPreferredResources returns the supported resources with the version preferred by the
// server.
//
// Deprecated: use ServerPreferredResourcesWithContext instead.
func (d *CachedDiscoveryClient) ServerPreferredResources() ([]*metav1.APIResourceList, error) {
return d.ServerPreferredResourcesWithContext(context.Background())
}
// ServerPreferredResourcesWithContext returns the supported resources with the version preferred by the
// server.
func (d *CachedDiscoveryClient) ServerPreferredResourcesWithContext(ctx context.Context) ([]*metav1.APIResourceList, error) {
return discovery.ServerPreferredResourcesWithContext(ctx, d)
return discovery.ServerPreferredResources(d)
}
// ServerPreferredNamespacedResources returns the supported namespaced resources with the
// version preferred by the server.
//
// Deprecated: use ServerPreferredNamespacedResourcesWithContext instead.
func (d *CachedDiscoveryClient) ServerPreferredNamespacedResources() ([]*metav1.APIResourceList, error) {
return d.ServerPreferredNamespacedResourcesWithContext(context.Background())
}
// ServerPreferredNamespacedResourcesWithContext returns the supported namespaced resources with the
// version preferred by the server.
func (d *CachedDiscoveryClient) ServerPreferredNamespacedResourcesWithContext(ctx context.Context) ([]*metav1.APIResourceList, error) {
return discovery.ServerPreferredNamespacedResourcesWithContext(ctx, d)
return discovery.ServerPreferredNamespacedResources(d)
}
// ServerVersion retrieves and parses the server's version (git version).
//
// Deprecated: use ServerVersionWithContext instead.
func (d *CachedDiscoveryClient) ServerVersion() (*version.Info, error) {
return d.ServerVersionWithContext(context.Background())
}
// ServerVersionWithContext retrieves and parses the server's version (git version).
func (d *CachedDiscoveryClient) ServerVersionWithContext(ctx context.Context) (*version.Info, error) {
return d.delegate.ServerVersionWithContext(ctx)
return d.delegate.ServerVersion()
}
// OpenAPISchema retrieves and parses the swagger API schema the server supports.
//
// Deprecated: use OpenAPISchemaWithContext instead.
func (d *CachedDiscoveryClient) OpenAPISchema() (*openapi_v2.Document, error) {
return d.OpenAPISchemaWithContext(context.Background())
}
// OpenAPISchemaWithContext retrieves and parses the swagger API schema the server supports.
func (d *CachedDiscoveryClient) OpenAPISchemaWithContext(ctx context.Context) (*openapi_v2.Document, error) {
return d.delegate.OpenAPISchemaWithContext(ctx)
return d.delegate.OpenAPISchema()
}
// OpenAPIV3 retrieves and parses the OpenAPIV3 specs exposed by the server
func (d *CachedDiscoveryClient) OpenAPIV3() openapi.Client {
return d.openAPIV3(context.Background())
}
// OpenAPIV3WithContext retrieves and parses the OpenAPIV3 specs exposed by the server
func (d *CachedDiscoveryClient) OpenAPIV3WithContext(ctx context.Context) openapi.ClientWithContext {
return d.openAPIV3(ctx)
}
func (d *CachedDiscoveryClient) openAPIV3(ctx context.Context) *cachedopenapi.Client {
// Must take lock since Invalidate call may modify openapiClient
d.mutex.Lock()
defer d.mutex.Unlock()
@ -313,7 +248,7 @@ func (d *CachedDiscoveryClient) openAPIV3(ctx context.Context) *cachedopenapi.Cl
if d.openapiClient == nil {
// Delegate is discovery client created with special HTTP client which
// respects E-Tag cache responses to serve cache from disk.
d.openapiClient = cachedopenapi.NewClientWithContext(d.delegate.OpenAPIV3WithContext(ctx))
d.openapiClient = cachedopenapi.NewClient(d.delegate.OpenAPIV3())
}
return d.openapiClient
@ -321,15 +256,7 @@ func (d *CachedDiscoveryClient) openAPIV3(ctx context.Context) *cachedopenapi.Cl
// Fresh is supposed to tell the caller whether or not to retry if the cache
// fails to find something (false = retry, true = no need to retry).
//
// Deprecated: use FreshWithContext instead.
func (d *CachedDiscoveryClient) Fresh() bool {
return d.FreshWithContext(context.Background())
}
// FreshWithContext is supposed to tell the caller whether or not to retry if the cache
// fails to find something (false = retry, true = no need to retry).
func (d *CachedDiscoveryClient) FreshWithContext(ctx context.Context) bool {
d.mutex.Lock()
defer d.mutex.Unlock()
@ -337,14 +264,7 @@ func (d *CachedDiscoveryClient) FreshWithContext(ctx context.Context) bool {
}
// Invalidate enforces that no cached data is used in the future that is older than the current time.
//
// Deprecated: use InvalidateWithContext instead.
func (d *CachedDiscoveryClient) Invalidate() {
d.InvalidateWithContext(context.Background())
}
// InvalidateWithContext enforces that no cached data is used in the future that is older than the current time.
func (d *CachedDiscoveryClient) InvalidateWithContext(ctx context.Context) {
d.mutex.Lock()
defer d.mutex.Unlock()
@ -352,15 +272,8 @@ func (d *CachedDiscoveryClient) InvalidateWithContext(ctx context.Context) {
d.fresh = true
d.invalidated = true
d.openapiClient = nil
ad, ok := d.delegate.(discovery.CachedDiscoveryInterfaceWithContext)
if !ok {
if ad2, ok2 := d.delegate.(discovery.CachedDiscoveryInterface); ok2 {
ad = discovery.ToCachedDiscoveryInterfaceWithContext(ad2)
ok = true
}
}
if ok {
ad.InvalidateWithContext(ctx)
if ad, ok := d.delegate.(discovery.CachedDiscoveryInterface); ok {
ad.Invalidate()
}
}
@ -370,12 +283,6 @@ func (d *CachedDiscoveryClient) WithLegacy() discovery.DiscoveryInterface {
return d
}
// WithLegacyWithContext returns current cached discovery client;
// current client does not support legacy-only discovery.
func (d *CachedDiscoveryClient) WithLegacyWithContext(ctx context.Context) discovery.DiscoveryInterfaceWithContext {
return d
}
// NewCachedDiscoveryClientForConfig creates a new DiscoveryClient for the given config, and wraps
// the created client in a CachedDiscoveryClient. The provided configuration is updated with a
// custom transport that understands cache responses.
@ -403,11 +310,11 @@ func NewCachedDiscoveryClientForConfig(config *restclient.Config, discoveryCache
// The delegate caches the discovery groups and resources (memcache). "ServerGroups",
// which usually only returns (and caches) the groups, can now store the resources as
// well if the server supports the newer aggregated discovery format.
return newCachedDiscoveryClient(memory.NewMemCacheClientWithContext(discoveryClient), discoveryCacheDir, ttl), nil
return newCachedDiscoveryClient(memory.NewMemCacheClient(discoveryClient), discoveryCacheDir, ttl), nil
}
// NewCachedDiscoveryClient creates a new DiscoveryClient. cacheDirectory is the directory where discovery docs are held. It must be unique per host:port combination to work well.
func newCachedDiscoveryClient(delegate discovery.DiscoveryInterfaceWithContext, cacheDirectory string, ttl time.Duration) *CachedDiscoveryClient {
func newCachedDiscoveryClient(delegate discovery.DiscoveryInterface, cacheDirectory string, ttl time.Duration) *CachedDiscoveryClient {
return &CachedDiscoveryClient{
delegate: delegate,
cacheDirectory: cacheDirectory,

View file

@ -52,7 +52,7 @@ func TestCachedDiscoveryClient_Fresh(t *testing.T) {
defer os.RemoveAll(d)
c := fakeDiscoveryClient{}
cdc := newCachedDiscoveryClient(discovery.ToDiscoveryInterfaceWithContext(&c), d, 60*time.Second)
cdc := newCachedDiscoveryClient(&c, d, 60*time.Second)
assert.True(cdc.Fresh(), "should be fresh after creation")
cdc.ServerGroups()
@ -71,7 +71,7 @@ func TestCachedDiscoveryClient_Fresh(t *testing.T) {
assert.True(cdc.Fresh(), "should be fresh after another resources call")
assert.Equal(1, c.resourceCalls)
cdc = newCachedDiscoveryClient(discovery.ToDiscoveryInterfaceWithContext(&c), d, 60*time.Second)
cdc = newCachedDiscoveryClient(&c, d, 60*time.Second)
cdc.ServerGroups()
assert.False(cdc.Fresh(), "should NOT be fresh after recreation with existing groups cache")
assert.Equal(1, c.groupCalls)
@ -96,7 +96,7 @@ func TestNewCachedDiscoveryClient_TTL(t *testing.T) {
defer os.RemoveAll(d)
c := fakeDiscoveryClient{}
cdc := newCachedDiscoveryClient(discovery.ToDiscoveryInterfaceWithContext(&c), d, 1*time.Nanosecond)
cdc := newCachedDiscoveryClient(&c, d, 1*time.Nanosecond)
cdc.ServerGroups()
assert.Equal(1, c.groupCalls)
@ -115,7 +115,7 @@ func TestNewCachedDiscoveryClient_PathPerm(t *testing.T) {
defer os.RemoveAll(d)
c := fakeDiscoveryClient{}
cdc := newCachedDiscoveryClient(discovery.ToDiscoveryInterfaceWithContext(&c), d, 1*time.Nanosecond)
cdc := newCachedDiscoveryClient(&c, d, 1*time.Nanosecond)
cdc.ServerGroups()
err = filepath.Walk(d, func(path string, info os.FileInfo, err error) error {

View file

@ -60,7 +60,7 @@ func (rt *cacheRoundTripper) CancelRequest(req *http.Request) {
if cr, ok := rt.rt.Transport.(canceler); ok {
cr.CancelRequest(req)
} else {
klog.FromContext(req.Context()).Error(nil, "CancelRequest not implemented by round-tripper", "type", fmt.Sprintf("%T", rt.rt.Transport))
klog.Errorf("CancelRequest not implemented by %T", rt.rt.Transport)
}
}

View file

@ -17,7 +17,6 @@ limitations under the License.
package memory
import (
"context"
"errors"
"fmt"
"sync"
@ -48,13 +47,13 @@ type cacheEntry struct {
// TODO: Switch to a watch interface. Right now it will poll after each
// Invalidate() call.
type memCacheClient struct {
delegate discovery.DiscoveryInterfaceWithContext
delegate discovery.DiscoveryInterface
lock sync.RWMutex
groupToServerResources map[string]*cacheEntry
groupList *metav1.APIGroupList
cacheValid bool
openapiClient *cachedopenapi.Client
openapiClient openapi.Client
receivedAggregatedDiscovery bool
}
@ -73,7 +72,6 @@ func (e *emptyResponseError) Error() string {
}
var _ discovery.CachedDiscoveryInterface = &memCacheClient{}
var _ discovery.CachedDiscoveryInterfaceWithContext = &memCacheClient{}
// isTransientConnectionError checks whether given error is "Connection refused" or
// "Connection reset" error which usually means that apiserver is temporarily
@ -100,15 +98,10 @@ func isTransientError(err error) bool {
// ServerResourcesForGroupVersion returns the supported resources for a group and version.
func (d *memCacheClient) ServerResourcesForGroupVersion(groupVersion string) (*metav1.APIResourceList, error) {
return d.ServerResourcesForGroupVersionWithContext(context.Background(), groupVersion)
}
// ServerResourcesForGroupVersion returns the supported resources for a group and version.
func (d *memCacheClient) ServerResourcesForGroupVersionWithContext(ctx context.Context, groupVersion string) (*metav1.APIResourceList, error) {
d.lock.Lock()
defer d.lock.Unlock()
if !d.cacheValid {
if err := d.refreshLocked(ctx); err != nil {
if err := d.refreshLocked(); err != nil {
return nil, err
}
}
@ -118,14 +111,14 @@ func (d *memCacheClient) ServerResourcesForGroupVersionWithContext(ctx context.C
}
if cachedVal.err != nil && isTransientError(cachedVal.err) {
r, err := d.serverResourcesForGroupVersion(ctx, groupVersion)
r, err := d.serverResourcesForGroupVersion(groupVersion)
if err != nil {
// Don't log "empty response" as an error; it is a common response for metrics.
if _, emptyErr := err.(*emptyResponseError); emptyErr {
// Log at same verbosity as disk cache.
klog.FromContext(ctx).V(3).Info(err.Error())
klog.V(3).Infof("%v", err)
} else {
utilruntime.HandleErrorWithContext(ctx, err, "Couldn't get resource list", "gv", groupVersion)
utilruntime.HandleError(fmt.Errorf("couldn't get resource list for %v: %v", groupVersion, err))
}
}
cachedVal = &cacheEntry{r, err}
@ -136,35 +129,19 @@ func (d *memCacheClient) ServerResourcesForGroupVersionWithContext(ctx context.C
}
// ServerGroupsAndResources returns the groups and supported resources for all groups and versions.
//
// Deprecated: use ServerGroupsAndResourcesWithContext instead.
func (d *memCacheClient) ServerGroupsAndResources() ([]*metav1.APIGroup, []*metav1.APIResourceList, error) {
return d.ServerGroupsAndResourcesWithContext(context.Background())
}
// ServerGroupsAndResources returns the groups and supported resources for all groups and versions.
func (d *memCacheClient) ServerGroupsAndResourcesWithContext(ctx context.Context) ([]*metav1.APIGroup, []*metav1.APIResourceList, error) {
return discovery.ServerGroupsAndResourcesWithContext(ctx, d)
return discovery.ServerGroupsAndResources(d)
}
// GroupsAndMaybeResources returns the list of APIGroups, and possibly the map of group/version
// to resources. The returned groups will never be nil, but the resources map can be nil
// if there are no cached resources.
//
// Deprecated: use GroupsAndMaybeResourcesWithContext instead.
func (d *memCacheClient) GroupsAndMaybeResources() (*metav1.APIGroupList, map[schema.GroupVersion]*metav1.APIResourceList, map[schema.GroupVersion]error, error) {
return d.GroupsAndMaybeResourcesWithContext(context.Background())
}
// GroupsAndMaybeResourcesWithContext returns the list of APIGroups, and possibly the map of group/version
// to resources. The returned groups will never be nil, but the resources map can be nil
// if there are no cached resources.
func (d *memCacheClient) GroupsAndMaybeResourcesWithContext(ctx context.Context) (*metav1.APIGroupList, map[schema.GroupVersion]*metav1.APIResourceList, map[schema.GroupVersion]error, error) {
d.lock.Lock()
defer d.lock.Unlock()
if !d.cacheValid {
if err := d.refreshLocked(ctx); err != nil {
if err := d.refreshLocked(); err != nil {
return nil, nil, nil, err
}
}
@ -189,13 +166,8 @@ func (d *memCacheClient) GroupsAndMaybeResourcesWithContext(ctx context.Context)
return d.groupList, resourcesMap, failedGVs, nil
}
// Deprecated: use ServerGroupsWithContext instead.
func (d *memCacheClient) ServerGroups() (*metav1.APIGroupList, error) {
return d.ServerGroupsWithContext(context.Background())
}
func (d *memCacheClient) ServerGroupsWithContext(ctx context.Context) (*metav1.APIGroupList, error) {
groups, _, _, err := d.GroupsAndMaybeResourcesWithContext(ctx)
groups, _, _, err := d.GroupsAndMaybeResources()
if err != nil {
return nil, err
}
@ -206,69 +178,35 @@ func (d *memCacheClient) RESTClient() restclient.Interface {
return d.delegate.RESTClient()
}
// Deprecated: use ServerPreferredResourcesWithContext instead.
func (d *memCacheClient) ServerPreferredResources() ([]*metav1.APIResourceList, error) {
return d.ServerPreferredResourcesWithContext(context.Background())
return discovery.ServerPreferredResources(d)
}
func (d *memCacheClient) ServerPreferredResourcesWithContext(ctx context.Context) ([]*metav1.APIResourceList, error) {
return discovery.ServerPreferredResourcesWithContext(ctx, d)
}
// Deprecated: use ServerPreferredNamespacedResourcesWithContext instead.
func (d *memCacheClient) ServerPreferredNamespacedResources() ([]*metav1.APIResourceList, error) {
return d.ServerPreferredNamespacedResourcesWithContext(context.Background())
return discovery.ServerPreferredNamespacedResources(d)
}
func (d *memCacheClient) ServerPreferredNamespacedResourcesWithContext(ctx context.Context) ([]*metav1.APIResourceList, error) {
return discovery.ServerPreferredNamespacedResourcesWithContext(ctx, d)
}
// Deprecated: use ServerVersionWithContext instead.
func (d *memCacheClient) ServerVersion() (*version.Info, error) {
return d.ServerVersionWithContext(context.Background())
return d.delegate.ServerVersion()
}
func (d *memCacheClient) ServerVersionWithContext(ctx context.Context) (*version.Info, error) {
return d.delegate.ServerVersionWithContext(ctx)
}
// Deprecated: use OpenAPISchemaWithContext instead.
func (d *memCacheClient) OpenAPISchema() (*openapi_v2.Document, error) {
return d.OpenAPISchemaWithContext(context.Background())
return d.delegate.OpenAPISchema()
}
func (d *memCacheClient) OpenAPISchemaWithContext(ctx context.Context) (*openapi_v2.Document, error) {
return d.delegate.OpenAPISchemaWithContext(ctx)
}
// Deprecated: use OpenAPIV3WithContext instead.
func (d *memCacheClient) OpenAPIV3() openapi.Client {
return d.openAPIV3(context.Background())
}
func (d *memCacheClient) OpenAPIV3WithContext(ctx context.Context) openapi.ClientWithContext {
return d.openAPIV3(ctx)
}
func (d *memCacheClient) openAPIV3(ctx context.Context) *cachedopenapi.Client {
// Must take lock since Invalidate call may modify openapiClient
d.lock.Lock()
defer d.lock.Unlock()
if d.openapiClient == nil {
d.openapiClient = cachedopenapi.NewClientWithContext(d.delegate.OpenAPIV3WithContext(ctx))
d.openapiClient = cachedopenapi.NewClient(d.delegate.OpenAPIV3())
}
return d.openapiClient
}
// Deprecated: use FreshWithContext instead.
func (d *memCacheClient) Fresh() bool {
return d.FreshWithContext(context.Background())
}
func (d *memCacheClient) FreshWithContext(ctx context.Context) bool {
d.lock.RLock()
defer d.lock.RUnlock()
// Return whether the cache is populated at all. It is still possible that
@ -279,15 +217,7 @@ func (d *memCacheClient) FreshWithContext(ctx context.Context) bool {
// Invalidate enforces that no cached data that is older than the current time
// is used.
//
// Deprecated: use InvalidateWithContext instead.
func (d *memCacheClient) Invalidate() {
d.InvalidateWithContext(context.Background())
}
// InvalidateWithContext enforces that no cached data that is older than the current time
// is used.
func (d *memCacheClient) InvalidateWithContext(ctx context.Context) {
d.lock.Lock()
defer d.lock.Unlock()
d.cacheValid = false
@ -295,39 +225,24 @@ func (d *memCacheClient) InvalidateWithContext(ctx context.Context) {
d.groupList = nil
d.openapiClient = nil
d.receivedAggregatedDiscovery = false
ad, ok := d.delegate.(discovery.CachedDiscoveryInterfaceWithContext)
if !ok {
ad2, ok2 := d.delegate.(discovery.CachedDiscoveryInterface)
if ok2 {
ad = discovery.ToCachedDiscoveryInterfaceWithContext(ad2)
ok = true
}
}
if ok {
ad.InvalidateWithContext(ctx)
if ad, ok := d.delegate.(discovery.CachedDiscoveryInterface); ok {
ad.Invalidate()
}
}
// refreshLocked refreshes the state of cache. The caller must hold d.lock for
// writing.
func (d *memCacheClient) refreshLocked(ctx context.Context) error {
func (d *memCacheClient) refreshLocked() error {
// TODO: Could this multiplicative set of calls be replaced by a single call
// to ServerResources? If it's possible for more than one resulting
// APIResourceList to have the same GroupVersion, the lists would need merged.
var gl *metav1.APIGroupList
var err error
ad, ok := d.delegate.(discovery.AggregatedDiscoveryInterfaceWithContext)
if !ok {
if ad2, ok2 := d.delegate.(discovery.AggregatedDiscoveryInterface); ok2 {
ad = discovery.ToAggregatedDiscoveryInterfaceWithContext(ad2)
ok = true
}
}
if ok {
if ad, ok := d.delegate.(discovery.AggregatedDiscoveryInterface); ok {
var resources map[schema.GroupVersion]*metav1.APIResourceList
var failedGVs map[schema.GroupVersion]error
gl, resources, failedGVs, err = ad.GroupsAndMaybeResourcesWithContext(ctx)
gl, resources, failedGVs, err = ad.GroupsAndMaybeResources()
if resources != nil && err == nil {
// Cache the resources.
d.groupToServerResources = map[string]*cacheEntry{}
@ -344,10 +259,10 @@ func (d *memCacheClient) refreshLocked(ctx context.Context) error {
return nil
}
} else {
gl, err = d.delegate.ServerGroupsWithContext(ctx)
gl, err = d.delegate.ServerGroups()
}
if err != nil || len(gl.Groups) == 0 {
utilruntime.HandleErrorWithContext(ctx, err, "Couldn't get current server API group list")
utilruntime.HandleError(fmt.Errorf("couldn't get current server API group list: %v", err))
return err
}
@ -360,16 +275,16 @@ func (d *memCacheClient) refreshLocked(ctx context.Context) error {
wg.Add(1)
go func() {
defer wg.Done()
defer utilruntime.HandleCrashWithContext(ctx)
defer utilruntime.HandleCrash()
r, err := d.serverResourcesForGroupVersion(ctx, gv)
r, err := d.serverResourcesForGroupVersion(gv)
if err != nil {
// Don't log "empty response" as an error; it is a common response for metrics.
if _, emptyErr := err.(*emptyResponseError); emptyErr {
// Log at same verbosity as disk cache.
klog.FromContext(ctx).V(3).Info(err.Error())
klog.V(3).Infof("%v", err)
} else {
utilruntime.HandleErrorWithContext(ctx, err, "Couldn't get resource list", "groupVersion", gv)
utilruntime.HandleError(fmt.Errorf("couldn't get resource list for %v: %v", gv, err))
}
}
@ -386,8 +301,8 @@ func (d *memCacheClient) refreshLocked(ctx context.Context) error {
return nil
}
func (d *memCacheClient) serverResourcesForGroupVersion(ctx context.Context, groupVersion string) (*metav1.APIResourceList, error) {
r, err := d.delegate.ServerResourcesForGroupVersionWithContext(ctx, groupVersion)
func (d *memCacheClient) serverResourcesForGroupVersion(groupVersion string) (*metav1.APIResourceList, error) {
r, err := d.delegate.ServerResourcesForGroupVersion(groupVersion)
if err != nil {
return r, err
}
@ -399,39 +314,16 @@ func (d *memCacheClient) serverResourcesForGroupVersion(ctx context.Context, gro
// WithLegacy returns current memory-cached discovery client;
// current client does not support legacy-only discovery.
//
// Deprecated: use WithLegacyWithContext instead.
func (d *memCacheClient) WithLegacy() discovery.DiscoveryInterface {
return d
}
// WithLegacyWithContext returns current memory-cached discovery client;
// current client does not support legacy-only discovery.
func (d *memCacheClient) WithLegacyWithContext(ctx context.Context) discovery.DiscoveryInterfaceWithContext {
return d
}
// NewMemCacheClient creates a new CachedDiscoveryInterface which caches
// discovery information in memory and will stay up-to-date if Invalidate is
// called with regularity.
//
// NOTE: The client will NOT resort to live lookups on cache misses.
//
// Deprecated: use NewMemCacheClientWithContext instead.
func NewMemCacheClient(delegate discovery.DiscoveryInterface) discovery.CachedDiscoveryInterface {
return newMemCacheClient(discovery.ToDiscoveryInterfaceWithContext(delegate))
}
// NewMemCacheClientWithContext creates a new CachedDiscoveryInterfaceWithContext which caches
// discovery information in memory and will stay up-to-date if Invalidate is
// called with regularity.
//
// NOTE: The client will NOT resort to live lookups on cache misses.
func NewMemCacheClientWithContext(delegate discovery.DiscoveryInterfaceWithContext) discovery.CachedDiscoveryInterfaceWithContext {
return newMemCacheClient(delegate)
}
func newMemCacheClient(delegate discovery.DiscoveryInterfaceWithContext) *memCacheClient {
return &memCacheClient{
delegate: delegate,
groupToServerResources: map[string]*cacheEntry{},

View file

@ -35,10 +35,10 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/client-go/discovery"
"k8s.io/client-go/discovery/fake"
"k8s.io/client-go/openapi"
"k8s.io/client-go/rest"
testutil "k8s.io/client-go/util/testing"
"k8s.io/klog/v2/ktesting"
)
type resourceMapEntry struct {
@ -47,10 +47,7 @@ type resourceMapEntry struct {
}
type fakeDiscovery struct {
// Intentionally limited to discovery.DiscoveryInterface because that is all that this
// code knows about and partly overrides. *fake.FakeDiscovery would inherit
// e.g. ServerGroupsWithContext which then would have to be overridden.
discovery.DiscoveryInterface
*fake.FakeDiscovery
lock sync.Mutex
groupList *metav1.APIGroupList
@ -438,8 +435,6 @@ func TestOpenAPIMemCache(t *testing.T) {
continue
}
// This is the original OpenAPI client. It is not affected
// by client.Invalidate() below.
pathsAgain, err := openapiClient.Paths()
if !assert.NoError(t, err) {
continue
@ -450,8 +445,7 @@ func TestOpenAPIMemCache(t *testing.T) {
continue
}
// The map itself might be different, but the content should not be.
assert.Equal(t, reflect.ValueOf(paths[k]).Pointer(), reflect.ValueOf(pathsAgain[k]).Pointer())
assert.Equal(t, reflect.ValueOf(paths).Pointer(), reflect.ValueOf(pathsAgain).Pointer())
assert.Equal(t, reflect.ValueOf(original).Pointer(), reflect.ValueOf(schemaAgain).Pointer())
// Invalidate and try again. This time pointers should not be equal
@ -467,75 +461,6 @@ func TestOpenAPIMemCache(t *testing.T) {
continue
}
assert.NotEqual(t, reflect.ValueOf(paths[k]).Pointer(), reflect.ValueOf(pathsAgain[k]).Pointer())
assert.NotEqual(t, reflect.ValueOf(original).Pointer(), reflect.ValueOf(schemaAgain).Pointer())
assert.Equal(t, original, schemaAgain)
}
})
}
}
// Tests that schema instances returned by openapi are cached and returned after
// successive calls.
func TestOpenAPIMemCacheWithContext(t *testing.T) {
_, ctx := ktesting.NewTestContext(t)
fakeServer, err := testutil.NewFakeOpenAPIV3Server("../../testdata")
require.NoError(t, err)
defer fakeServer.HttpServer.Close()
require.NotEmpty(t, fakeServer.ServedDocuments)
client := NewMemCacheClientWithContext(
discovery.NewDiscoveryClientForConfigOrDie(
&rest.Config{Host: fakeServer.HttpServer.URL},
),
)
openapiClient := client.OpenAPIV3WithContext(ctx)
paths, err := openapiClient.PathsWithContext(ctx)
require.NoError(t, err)
contentTypes := []string{
runtime.ContentTypeJSON, openapi.ContentTypeOpenAPIV3PB,
}
for _, contentType := range contentTypes {
t.Run(contentType, func(t *testing.T) {
for k, v := range paths {
original, err := v.SchemaWithContext(ctx, contentType)
if !assert.NoError(t, err) {
continue
}
// This is the original OpenAPI client. It is not affected
// by client.Invalidate() below.
pathsAgain, err := openapiClient.PathsWithContext(ctx)
if !assert.NoError(t, err) {
continue
}
schemaAgain, err := pathsAgain[k].SchemaWithContext(ctx, contentType)
if !assert.NoError(t, err) {
continue
}
// When using the *WithContext API, the map itself is cached.
assert.Equal(t, reflect.ValueOf(paths).Pointer(), reflect.ValueOf(pathsAgain).Pointer())
assert.Equal(t, reflect.ValueOf(original).Pointer(), reflect.ValueOf(schemaAgain).Pointer())
// Invalidate and try again. This time pointers should not be equal
client.InvalidateWithContext(ctx)
pathsAgain, err = client.OpenAPIV3WithContext(ctx).PathsWithContext(ctx)
if !assert.NoError(t, err) {
continue
}
schemaAgain, err = pathsAgain[k].SchemaWithContext(ctx, contentType)
if !assert.NoError(t, err) {
continue
}
assert.NotEqual(t, reflect.ValueOf(paths).Pointer(), reflect.ValueOf(pathsAgain).Pointer())
assert.NotEqual(t, reflect.ValueOf(original).Pointer(), reflect.ValueOf(schemaAgain).Pointer())
assert.Equal(t, original, schemaAgain)

View file

@ -76,8 +76,6 @@ var v2GVK = schema.GroupVersionKind{Group: "apidiscovery.k8s.io", Version: "v2",
// DiscoveryInterface holds the methods that discover server-supported API groups,
// versions and resources.
//
// Deprecated: use DiscoveryInterfaceWithContext instead.
type DiscoveryInterface interface {
RESTClient() restclient.Interface
ServerGroupsInterface
@ -88,109 +86,22 @@ type DiscoveryInterface interface {
// Returns copy of current discovery client that will only
// receive the legacy discovery format, or pointer to current
// discovery client if it does not support legacy-only discovery.
//
// Deprecated: use DiscoveryInterfaceWithContext.WithLegacyWithContext instead.
WithLegacy() DiscoveryInterface
}
// DiscoveryInterfaceWithContext holds the methods that discover server-supported API groups,
// versions and resources.
type DiscoveryInterfaceWithContext interface {
RESTClient() restclient.Interface
ServerGroupsInterfaceWithContext
ServerResourcesInterfaceWithContext
ServerVersionInterfaceWithContext
OpenAPISchemaInterfaceWithContext
OpenAPIV3SchemaInterfaceWithContext
// Returns copy of current discovery client that will only
// receive the legacy discovery format, or pointer to current
// discovery client if it does not support legacy-only discovery.
WithLegacyWithContext(ctx context.Context) DiscoveryInterfaceWithContext
}
func ToDiscoveryInterfaceWithContext(i DiscoveryInterface) DiscoveryInterfaceWithContext {
if i == nil {
return nil
}
if i, ok := i.(DiscoveryInterfaceWithContext); ok {
return i
}
return &discoveryInterfaceWrapper{
ServerGroupsInterfaceWithContext: ToServerGroupsInterfaceWithContext(i),
ServerResourcesInterfaceWithContext: ToServerResourcesInterfaceWithContext(i),
ServerVersionInterfaceWithContext: ToServerVersionInterfaceWithContext(i),
OpenAPISchemaInterfaceWithContext: ToOpenAPISchemaInterfaceWithContext(i),
OpenAPIV3SchemaInterfaceWithContext: OpenAPIV3ToSchemaInterfaceWithContext(i),
delegate: i,
}
}
type discoveryInterfaceWrapper struct {
ServerGroupsInterfaceWithContext
ServerResourcesInterfaceWithContext
ServerVersionInterfaceWithContext
OpenAPISchemaInterfaceWithContext
OpenAPIV3SchemaInterfaceWithContext
delegate DiscoveryInterface
}
func (i *discoveryInterfaceWrapper) RESTClient() restclient.Interface {
return i.delegate.RESTClient()
}
func (i *discoveryInterfaceWrapper) WithLegacyWithContext(ctx context.Context) DiscoveryInterfaceWithContext {
return ToDiscoveryInterfaceWithContext(i.delegate.WithLegacy())
}
// AggregatedDiscoveryInterface extends DiscoveryInterface to include a method to possibly
// return discovery resources along with the discovery groups, which is what the newer
// aggregated discovery format does (APIGroupDiscoveryList).
//
// Deprecated: use AggregatedDiscoveryInterfaceWithContext instead.
type AggregatedDiscoveryInterface interface {
DiscoveryInterface
// Deprecated: use AggregatedDiscoveryInterfaceWithContext.GroupsAndMaybeResourcesWithContext instead.
GroupsAndMaybeResources() (*metav1.APIGroupList, map[schema.GroupVersion]*metav1.APIResourceList, map[schema.GroupVersion]error, error)
}
// AggregatedDiscoveryInterfaceWithContext extends DiscoveryInterface to include a method to possibly
// return discovery resources along with the discovery groups, which is what the newer
// aggregated discovery format does (APIGroupDiscoveryList).
type AggregatedDiscoveryInterfaceWithContext interface {
DiscoveryInterfaceWithContext
GroupsAndMaybeResourcesWithContext(ctx context.Context) (*metav1.APIGroupList, map[schema.GroupVersion]*metav1.APIResourceList, map[schema.GroupVersion]error, error)
}
func ToAggregatedDiscoveryInterfaceWithContext(i AggregatedDiscoveryInterface) AggregatedDiscoveryInterfaceWithContext {
if i == nil {
return nil
}
if i, ok := i.(AggregatedDiscoveryInterfaceWithContext); ok {
return i
}
return &aggregatedDiscoveryInterfaceWrapper{
DiscoveryInterfaceWithContext: ToDiscoveryInterfaceWithContext(i),
delegate: i,
}
}
type aggregatedDiscoveryInterfaceWrapper struct {
DiscoveryInterfaceWithContext
delegate AggregatedDiscoveryInterface
}
func (i *aggregatedDiscoveryInterfaceWrapper) GroupsAndMaybeResourcesWithContext(ctx context.Context) (*metav1.APIGroupList, map[schema.GroupVersion]*metav1.APIResourceList, map[schema.GroupVersion]error, error) {
return i.delegate.GroupsAndMaybeResources()
}
// CachedDiscoveryInterface is a DiscoveryInterface with cache invalidation and freshness.
// Note that If the ServerResourcesForGroupVersion method returns a cache miss
// error, the user needs to explicitly call Invalidate to clear the cache,
// otherwise the same cache miss error will be returned next time.
//
// Deprecated: use CachedDiscoveryInterfaceWithContext instead.
type CachedDiscoveryInterface interface {
DiscoveryInterface
// Fresh is supposed to tell the caller whether or not to retry if the cache
@ -198,312 +109,58 @@ type CachedDiscoveryInterface interface {
//
// TODO: this needs to be revisited, this interface can't be locked properly
// and doesn't make a lot of sense.
//
// Deprecated: use CachedDiscoveryInterfaceWithContext.FreshWithContext instead.
Fresh() bool
// Invalidate enforces that no cached data that is older than the current time
// is used.
//
// Deprecated: use CachedDiscoveryInterfaceWithContext.InvalidateWithContext instead.
Invalidate()
}
// CachedDiscoveryInterfaceWithContext is a DiscoveryInterfaceWithContext with cache invalidation and freshness.
// Note that If the ServerResourcesForGroupVersion method returns a cache miss
// error, the user needs to explicitly call Invalidate to clear the cache,
// otherwise the same cache miss error will be returned next time.
type CachedDiscoveryInterfaceWithContext interface {
DiscoveryInterfaceWithContext
// Fresh is supposed to tell the caller whether or not to retry if the cache
// fails to find something (false = retry, true = no need to retry).
//
// TODO: this needs to be revisited, this interface can't be locked properly
// and doesn't make a lot of sense.
FreshWithContext(ctx context.Context) bool
// Invalidate enforces that no cached data that is older than the current time
// is used.
InvalidateWithContext(ctx context.Context)
}
func ToCachedDiscoveryInterfaceWithContext(i CachedDiscoveryInterface) CachedDiscoveryInterfaceWithContext {
if i == nil {
return nil
}
if i, ok := i.(CachedDiscoveryInterfaceWithContext); ok {
return i
}
return &cachedDiscoveryInterfaceWrapper{
DiscoveryInterfaceWithContext: ToDiscoveryInterfaceWithContext(i),
delegate: i,
}
}
type cachedDiscoveryInterfaceWrapper struct {
DiscoveryInterfaceWithContext
delegate CachedDiscoveryInterface
}
func (i *cachedDiscoveryInterfaceWrapper) FreshWithContext(ctx context.Context) bool {
return i.delegate.Fresh()
}
func (i *cachedDiscoveryInterfaceWrapper) InvalidateWithContext(ctx context.Context) {
i.delegate.Invalidate()
}
// ServerGroupsInterface has methods for obtaining supported groups on the API server
//
// Deprecated: use ServerGroupsInterfaceWithContext instead.
type ServerGroupsInterface interface {
// ServerGroups returns the supported groups, with information like supported versions and the
// preferred version.
//
// Deprecated: use ServerGroupsInterfaceWithContext.ServerGroups instead.
ServerGroups() (*metav1.APIGroupList, error)
}
// ServerGroupsInterfaceWithContext has methods for obtaining supported groups on the API server
type ServerGroupsInterfaceWithContext interface {
// ServerGroups returns the supported groups, with information like supported versions and the
// preferred version.
ServerGroupsWithContext(ctx context.Context) (*metav1.APIGroupList, error)
}
func ToServerGroupsInterfaceWithContext(i ServerGroupsInterface) ServerGroupsInterfaceWithContext {
if i == nil {
return nil
}
if i, ok := i.(ServerGroupsInterfaceWithContext); ok {
return i
}
return &serverGroupsInterfaceWrapper{
delegate: i,
}
}
type serverGroupsInterfaceWrapper struct {
delegate ServerGroupsInterface
}
func (i *serverGroupsInterfaceWrapper) ServerGroupsWithContext(ctx context.Context) (*metav1.APIGroupList, error) {
return i.delegate.ServerGroups()
}
// ServerResourcesInterface has methods for obtaining supported resources on the API server
//
// Deprecated: use ServerGroupsAndResourcesWithContext instead.
type ServerResourcesInterface interface {
// ServerResourcesForGroupVersion returns the supported resources for a group and version.
//
// Deprecated: use ServerGroupsAndResourcesWithContext.ServerResourcesForGroupVersionWithContext instead.
ServerResourcesForGroupVersion(groupVersion string) (*metav1.APIResourceList, error)
// ServerGroupsAndResources returns the supported groups and resources for all groups and versions.
//
// The returned group and resource lists might be non-nil with partial results even in the
// case of non-nil error.
//
// Deprecated: use ServerGroupsAndResourcesWithContext.ServerGroupsAndResourcesWithContext instead.
ServerGroupsAndResources() ([]*metav1.APIGroup, []*metav1.APIResourceList, error)
// ServerPreferredResources returns the supported resources with the version preferred by the
// server.
//
// The returned group and resource lists might be non-nil with partial results even in the
// case of non-nil error.
//
// Deprecated: use ServerResourcesInterfaceWithContext.ServerPreferredResourcesWithContext instead.
ServerPreferredResources() ([]*metav1.APIResourceList, error)
// ServerPreferredNamespacedResources returns the supported namespaced resources with the
// version preferred by the server.
//
// The returned resource list might be non-nil with partial results even in the case of
// non-nil error.
//
// Deprecated: use ServerResourcesInterfaceWithContext.ServerPreferredNamespacedResourcesWithContext instead.
ServerPreferredNamespacedResources() ([]*metav1.APIResourceList, error)
}
// ServerResourcesInterfaceWithContext has methods for obtaining supported resources on the API server
type ServerResourcesInterfaceWithContext interface {
// ServerResourcesForGroupVersionWithContext returns the supported resources for a group and version.
ServerResourcesForGroupVersionWithContext(ctx context.Context, groupVersion string) (*metav1.APIResourceList, error)
// ServerGroupsAndResourcesWithContext returns the supported groups and resources for all groups and versions.
//
// The returned group and resource lists might be non-nil with partial results even in the
// case of non-nil error.
ServerGroupsAndResourcesWithContext(ctx context.Context) ([]*metav1.APIGroup, []*metav1.APIResourceList, error)
// ServerPreferredResourcesWithContext returns the supported resources with the version preferred by the
// server.
//
// The returned group and resource lists might be non-nil with partial results even in the
// case of non-nil error.
ServerPreferredResourcesWithContext(ctx context.Context) ([]*metav1.APIResourceList, error)
// ServerPreferredNamespacedResourcesWithContext returns the supported namespaced resources with the
// version preferred by the server.
//
// The returned resource list might be non-nil with partial results even in the case of
// non-nil error.
ServerPreferredNamespacedResourcesWithContext(ctx context.Context) ([]*metav1.APIResourceList, error)
}
func ToServerResourcesInterfaceWithContext(i ServerResourcesInterface) ServerResourcesInterfaceWithContext {
if i == nil {
return nil
}
if i, ok := i.(ServerResourcesInterfaceWithContext); ok {
return i
}
return &serverResourcesInterfaceWrapper{
delegate: i,
}
}
type serverResourcesInterfaceWrapper struct {
delegate ServerResourcesInterface
}
func (i *serverResourcesInterfaceWrapper) ServerResourcesForGroupVersionWithContext(ctx context.Context, groupVersion string) (*metav1.APIResourceList, error) {
return i.delegate.ServerResourcesForGroupVersion(groupVersion)
}
func (i *serverResourcesInterfaceWrapper) ServerGroupsAndResourcesWithContext(ctx context.Context) ([]*metav1.APIGroup, []*metav1.APIResourceList, error) {
return i.delegate.ServerGroupsAndResources()
}
func (i *serverResourcesInterfaceWrapper) ServerPreferredResourcesWithContext(ctx context.Context) ([]*metav1.APIResourceList, error) {
return i.delegate.ServerPreferredResources()
}
func (i *serverResourcesInterfaceWrapper) ServerPreferredNamespacedResourcesWithContext(ctx context.Context) ([]*metav1.APIResourceList, error) {
return i.delegate.ServerPreferredNamespacedResources()
}
// ServerVersionInterface has a method for retrieving the server's version.
//
// Deprecated: use ServerVersionInterfaceWithContext instead.
type ServerVersionInterface interface {
// ServerVersion retrieves and parses the server's version (git version).
//
// Deprecated: use ServerVersionInterfaceWithContext.ServerVersionWithContext instead.
ServerVersion() (*version.Info, error)
}
// ServerVersionInterface has a method for retrieving the server's version.
type ServerVersionInterfaceWithContext interface {
// ServerVersion retrieves and parses the server's version (git version).
ServerVersionWithContext(ctx context.Context) (*version.Info, error)
}
func ToServerVersionInterfaceWithContext(i ServerVersionInterface) ServerVersionInterfaceWithContext {
if i == nil {
return nil
}
if i, ok := i.(ServerVersionInterfaceWithContext); ok {
return i
}
return &serverVersionInterfaceWrapper{
delegate: i,
}
}
type serverVersionInterfaceWrapper struct {
delegate ServerVersionInterface
}
func (i *serverVersionInterfaceWrapper) ServerVersionWithContext(ctx context.Context) (*version.Info, error) {
return i.delegate.ServerVersion()
}
// OpenAPISchemaInterface has a method to retrieve the open API schema.
//
// Deprecated: use OpenAPISchemaInterfaceWithContext instead.
type OpenAPISchemaInterface interface {
// OpenAPISchema retrieves and parses the swagger API schema the server supports.
//
// Deprecated: use OpenAPISchemaInterfaceWithContext.OpenAPISchemaWithContext instead.
OpenAPISchema() (*openapi_v2.Document, error)
}
// OpenAPISchemaInterfaceWithContext has a method to retrieve the open API schema.
type OpenAPISchemaInterfaceWithContext interface {
// OpenAPISchemaWithContext retrieves and parses the swagger API schema the server supports.
OpenAPISchemaWithContext(ctx context.Context) (*openapi_v2.Document, error)
}
func ToOpenAPISchemaInterfaceWithContext(i OpenAPISchemaInterface) OpenAPISchemaInterfaceWithContext {
if i == nil {
return nil
}
if i, ok := i.(OpenAPISchemaInterfaceWithContext); ok {
return i
}
return &openAPISchemaInterfaceWrapper{
delegate: i,
}
}
type openAPISchemaInterfaceWrapper struct {
delegate OpenAPISchemaInterface
}
func (i *openAPISchemaInterfaceWrapper) OpenAPISchemaWithContext(ctx context.Context) (*openapi_v2.Document, error) {
return i.delegate.OpenAPISchema()
}
// Deprecated: use OpenAPIV3SchemaInterfaceWithContext instead.
type OpenAPIV3SchemaInterface interface {
// Deprecated: use OpenAPIV3SchemaInterfaceWithContext.OpenAPIV3WithContext instead.
OpenAPIV3() openapi.Client
}
type OpenAPIV3SchemaInterfaceWithContext interface {
OpenAPIV3WithContext(ctx context.Context) openapi.ClientWithContext
}
func OpenAPIV3ToSchemaInterfaceWithContext(i OpenAPIV3SchemaInterface) OpenAPIV3SchemaInterfaceWithContext {
if i == nil {
return nil
}
if i, ok := i.(OpenAPIV3SchemaInterfaceWithContext); ok {
return i
}
return &openAPIV3SchemaInterfaceWrapper{
delegate: i,
}
}
type openAPIV3SchemaInterfaceWrapper struct {
delegate OpenAPIV3SchemaInterface
}
func (i *openAPIV3SchemaInterfaceWrapper) OpenAPIV3WithContext(ctx context.Context) openapi.ClientWithContext {
return ToOpenAPIClientWithContext(i.delegate.OpenAPIV3())
}
func ToOpenAPIClientWithContext(i openapi.Client) openapi.ClientWithContext {
if i == nil {
return nil
}
if i, ok := i.(openapi.ClientWithContext); ok {
return i
}
return &openAPIClientWrapper{
delegate: i,
}
}
type openAPIClientWrapper struct {
delegate openapi.Client
}
func (i *openAPIClientWrapper) PathsWithContext(ctx context.Context) (map[string]openapi.GroupVersionWithContext, error) {
resultWithoutContext, err := i.delegate.Paths()
result := make(map[string]openapi.GroupVersionWithContext, len(resultWithoutContext))
for key, entry := range resultWithoutContext {
result[key] = openapi.ToGroupVersionWithContext(entry)
}
return result, err
}
// DiscoveryClient implements the functions that discover server-supported API groups,
// versions and resources.
type DiscoveryClient struct {
@ -545,36 +202,19 @@ func apiVersionsToAPIGroup(apiVersions *metav1.APIVersions) (apiGroup metav1.API
// as aggregated discovery format or legacy format. For safety, resources will only
// be returned if both endpoints returned resources. Returned "failedGVs" can be
// empty, but will only be nil in the case an error is returned.
//
// Deprecated: use GroupsAndMaybeResourcesWithContext instead.
func (d *DiscoveryClient) GroupsAndMaybeResources() (
*metav1.APIGroupList,
map[schema.GroupVersion]*metav1.APIResourceList,
map[schema.GroupVersion]error,
error) {
return d.GroupsAndMaybeResourcesWithContext(context.Background())
}
// GroupsAndMaybeResources returns the discovery groups, and (if new aggregated
// discovery format) the resources keyed by group/version. Merges discovery groups
// and resources from /api and /apis (either aggregated or not). Legacy groups
// must be ordered first. The server will either return both endpoints (/api, /apis)
// as aggregated discovery format or legacy format. For safety, resources will only
// be returned if both endpoints returned resources. Returned "failedGVs" can be
// empty, but will only be nil in the case an error is returned.
func (d *DiscoveryClient) GroupsAndMaybeResourcesWithContext(ctx context.Context) (
*metav1.APIGroupList,
map[schema.GroupVersion]*metav1.APIResourceList,
map[schema.GroupVersion]error,
error) {
// Legacy group ordered first (there is only one -- core/v1 group). Returned groups must
// be non-nil, but it could be empty. Returned resources, apiResources map could be nil.
groups, resources, failedGVs, err := d.downloadLegacy(ctx)
groups, resources, failedGVs, err := d.downloadLegacy()
if err != nil {
return nil, nil, nil, err
}
// Discovery groups and (possibly) resources downloaded from /apis.
apiGroups, apiResources, failedApisGVs, aerr := d.downloadAPIs(ctx)
apiGroups, apiResources, failedApisGVs, aerr := d.downloadAPIs()
if aerr != nil {
return nil, nil, nil, aerr
}
@ -602,7 +242,7 @@ func (d *DiscoveryClient) GroupsAndMaybeResourcesWithContext(ctx context.Context
// possible for the resource map to be nil if the server returned
// the unaggregated discovery. Returned "failedGVs" can be empty, but
// will only be nil in the case of a returned error.
func (d *DiscoveryClient) downloadLegacy(ctx context.Context) (
func (d *DiscoveryClient) downloadLegacy() (
*metav1.APIGroupList,
map[schema.GroupVersion]*metav1.APIResourceList,
map[schema.GroupVersion]error,
@ -612,7 +252,7 @@ func (d *DiscoveryClient) downloadLegacy(ctx context.Context) (
body, err := d.restClient.Get().
AbsPath("/api").
SetHeader("Accept", accept).
Do(ctx).
Do(context.TODO()).
ContentType(&responseContentType).
Raw()
apiGroupList := &metav1.APIGroupList{}
@ -665,7 +305,7 @@ func (d *DiscoveryClient) downloadLegacy(ctx context.Context) (
// discovery resources. The returned groups will always exist, but the
// resources map may be nil. Returned "failedGVs" can be empty, but will
// only be nil in the case of a returned error.
func (d *DiscoveryClient) downloadAPIs(ctx context.Context) (
func (d *DiscoveryClient) downloadAPIs() (
*metav1.APIGroupList,
map[schema.GroupVersion]*metav1.APIResourceList,
map[schema.GroupVersion]error,
@ -675,7 +315,7 @@ func (d *DiscoveryClient) downloadAPIs(ctx context.Context) (
body, err := d.restClient.Get().
AbsPath("/apis").
SetHeader("Accept", accept).
Do(ctx).
Do(context.TODO()).
ContentType(&responseContentType).
Raw()
if err != nil {
@ -748,16 +388,8 @@ func ContentTypeIsGVK(contentType string, gvk schema.GroupVersionKind) (bool, er
// ServerGroups returns the supported groups, with information like supported versions and the
// preferred version.
//
// Deprecated: use ServerGroupsWithContext instead.
func (d *DiscoveryClient) ServerGroups() (*metav1.APIGroupList, error) {
return d.ServerGroupsWithContext(context.Background())
}
// ServerGroupsWithContext returns the supported groups, with information like supported versions and the
// preferred version.
func (d *DiscoveryClient) ServerGroupsWithContext(ctx context.Context) (*metav1.APIGroupList, error) {
groups, _, _, err := d.GroupsAndMaybeResourcesWithContext(ctx)
groups, _, _, err := d.GroupsAndMaybeResources()
if err != nil {
return nil, err
}
@ -765,13 +397,7 @@ func (d *DiscoveryClient) ServerGroupsWithContext(ctx context.Context) (*metav1.
}
// ServerResourcesForGroupVersion returns the supported resources for a group and version.
// Deprecated: use ServerResourcesForGroupVersionWithContext instead.
func (d *DiscoveryClient) ServerResourcesForGroupVersion(groupVersion string) (resources *metav1.APIResourceList, err error) {
return d.ServerResourcesForGroupVersionWithContext(context.Background(), groupVersion)
}
// ServerResourcesForGroupVersionWithContext returns the supported resources for a group and version.
func (d *DiscoveryClient) ServerResourcesForGroupVersionWithContext(ctx context.Context, groupVersion string) (resources *metav1.APIResourceList, err error) {
url := url.URL{}
if len(groupVersion) == 0 {
return nil, fmt.Errorf("groupVersion shouldn't be empty")
@ -784,7 +410,7 @@ func (d *DiscoveryClient) ServerResourcesForGroupVersionWithContext(ctx context.
resources = &metav1.APIResourceList{
GroupVersion: groupVersion,
}
err = d.restClient.Get().AbsPath(url.String()).Do(ctx).Into(resources)
err = d.restClient.Get().AbsPath(url.String()).Do(context.TODO()).Into(resources)
if err != nil {
// Tolerate core/v1 not found response by returning empty resource list;
// this probably should not happen. But we should verify all callers are
@ -799,13 +425,8 @@ func (d *DiscoveryClient) ServerResourcesForGroupVersionWithContext(ctx context.
// ServerGroupsAndResources returns the supported resources for all groups and versions.
func (d *DiscoveryClient) ServerGroupsAndResources() ([]*metav1.APIGroup, []*metav1.APIResourceList, error) {
return d.ServerGroupsAndResourcesWithContext(context.Background())
}
// ServerGroupsAndResourcesWithContext returns the supported resources for all groups and versions.
func (d *DiscoveryClient) ServerGroupsAndResourcesWithContext(ctx context.Context) ([]*metav1.APIGroup, []*metav1.APIResourceList, error) {
return withRetries(defaultRetries, func() ([]*metav1.APIGroup, []*metav1.APIResourceList, error) {
return ServerGroupsAndResourcesWithContext(ctx, d)
return ServerGroupsAndResources(d)
})
}
@ -848,12 +469,7 @@ func GroupDiscoveryFailedErrorGroups(err error) (map[schema.GroupVersion]error,
return nil, false
}
// Deprecated: use ServerGroupsAndResourcesWithContext instead.
func ServerGroupsAndResources(d DiscoveryInterface) ([]*metav1.APIGroup, []*metav1.APIResourceList, error) {
return ServerGroupsAndResourcesWithContext(context.Background(), ToDiscoveryInterfaceWithContext(d))
}
func ServerGroupsAndResourcesWithContext(ctx context.Context, d DiscoveryInterfaceWithContext) ([]*metav1.APIGroup, []*metav1.APIResourceList, error) {
var sgs *metav1.APIGroupList
var resources []*metav1.APIResourceList
var failedGVs map[schema.GroupVersion]error
@ -861,21 +477,14 @@ func ServerGroupsAndResourcesWithContext(ctx context.Context, d DiscoveryInterfa
// If the passed discovery object implements the wider AggregatedDiscoveryInterface,
// then attempt to retrieve aggregated discovery with both groups and the resources.
ad, ok := d.(AggregatedDiscoveryInterfaceWithContext)
if !ok {
if ad2, ok2 := d.(AggregatedDiscoveryInterface); ok2 {
ad = ToAggregatedDiscoveryInterfaceWithContext(ad2)
ok = true
}
}
if ok {
if ad, ok := d.(AggregatedDiscoveryInterface); ok {
var resourcesByGV map[schema.GroupVersion]*metav1.APIResourceList
sgs, resourcesByGV, failedGVs, err = ad.GroupsAndMaybeResourcesWithContext(ctx)
sgs, resourcesByGV, failedGVs, err = ad.GroupsAndMaybeResources()
for _, resourceList := range resourcesByGV {
resources = append(resources, resourceList)
}
} else {
sgs, err = d.ServerGroupsWithContext(ctx)
sgs, err = d.ServerGroups()
}
if sgs == nil {
@ -896,7 +505,7 @@ func ServerGroupsAndResourcesWithContext(ctx context.Context, d DiscoveryInterfa
return resultGroups, resources, ferr
}
groupVersionResources, failedGroups := fetchGroupVersionResources(ctx, d, sgs)
groupVersionResources, failedGroups := fetchGroupVersionResources(d, sgs)
// order results by group/version discovery order
result := []*metav1.APIResourceList{}
@ -917,14 +526,7 @@ func ServerGroupsAndResourcesWithContext(ctx context.Context, d DiscoveryInterfa
}
// ServerPreferredResources uses the provided discovery interface to look up preferred resources
//
// Deprecated: use ServerPreferredResourcesWithContext instead.
func ServerPreferredResources(d DiscoveryInterface) ([]*metav1.APIResourceList, error) {
return ServerPreferredResourcesWithContext(context.Background(), ToDiscoveryInterfaceWithContext(d))
}
// ServerPreferredResourcesWithContext uses the provided discovery interface to look up preferred resources
func ServerPreferredResourcesWithContext(ctx context.Context, d DiscoveryInterfaceWithContext) ([]*metav1.APIResourceList, error) {
var serverGroupList *metav1.APIGroupList
var failedGroups map[schema.GroupVersion]error
var groupVersionResources map[schema.GroupVersion]*metav1.APIResourceList
@ -933,24 +535,18 @@ func ServerPreferredResourcesWithContext(ctx context.Context, d DiscoveryInterfa
// If the passed discovery object implements the wider AggregatedDiscoveryInterface,
// then it is attempt to retrieve both the groups and the resources. "failedGroups"
// are Group/Versions returned as stale in AggregatedDiscovery format.
ad, ok := d.(AggregatedDiscoveryInterfaceWithContext)
if !ok {
if ad2, ok2 := d.(AggregatedDiscoveryInterface); ok2 {
ad = ToAggregatedDiscoveryInterfaceWithContext(ad2)
ok = true
}
}
ad, ok := d.(AggregatedDiscoveryInterface)
if ok {
serverGroupList, groupVersionResources, failedGroups, err = ad.GroupsAndMaybeResourcesWithContext(ctx)
serverGroupList, groupVersionResources, failedGroups, err = ad.GroupsAndMaybeResources()
} else {
serverGroupList, err = d.ServerGroupsWithContext(ctx)
serverGroupList, err = d.ServerGroups()
}
if err != nil {
return nil, err
}
// Non-aggregated discovery must fetch resources from Groups.
if groupVersionResources == nil {
groupVersionResources, failedGroups = fetchGroupVersionResources(ctx, d, serverGroupList)
groupVersionResources, failedGroups = fetchGroupVersionResources(d, serverGroupList)
}
result := []*metav1.APIResourceList{}
@ -1006,7 +602,7 @@ func ServerPreferredResourcesWithContext(ctx context.Context, d DiscoveryInterfa
}
// fetchServerResourcesForGroupVersions uses the discovery client to fetch the resources for the specified groups in parallel.
func fetchGroupVersionResources(ctx context.Context, d DiscoveryInterfaceWithContext, apiGroups *metav1.APIGroupList) (map[schema.GroupVersion]*metav1.APIResourceList, map[schema.GroupVersion]error) {
func fetchGroupVersionResources(d DiscoveryInterface, apiGroups *metav1.APIGroupList) (map[schema.GroupVersion]*metav1.APIResourceList, map[schema.GroupVersion]error) {
groupVersionResources := make(map[schema.GroupVersion]*metav1.APIResourceList)
failedGroups := make(map[schema.GroupVersion]error)
@ -1018,9 +614,9 @@ func fetchGroupVersionResources(ctx context.Context, d DiscoveryInterfaceWithCon
wg.Add(1)
go func() {
defer wg.Done()
defer utilruntime.HandleCrashWithContext(ctx)
defer utilruntime.HandleCrash()
apiResourceList, err := d.ServerResourcesForGroupVersionWithContext(ctx, groupVersion.String())
apiResourceList, err := d.ServerResourcesForGroupVersion(groupVersion.String())
// lock to record results
resultLock.Lock()
@ -1044,8 +640,6 @@ func fetchGroupVersionResources(ctx context.Context, d DiscoveryInterfaceWithCon
// ServerPreferredResources returns the supported resources with the version preferred by the
// server.
//
// Deprecated: use ServerPreferredResourcesWithContext instead.
func (d *DiscoveryClient) ServerPreferredResources() ([]*metav1.APIResourceList, error) {
_, rs, err := withRetries(defaultRetries, func() ([]*metav1.APIGroup, []*metav1.APIResourceList, error) {
rs, err := ServerPreferredResources(d)
@ -1054,40 +648,15 @@ func (d *DiscoveryClient) ServerPreferredResources() ([]*metav1.APIResourceList,
return rs, err
}
// ServerPreferredResourcesWithContext returns the supported resources with the version preferred by the
// server.
func (d *DiscoveryClient) ServerPreferredResourcesWithContext(ctx context.Context) ([]*metav1.APIResourceList, error) {
_, rs, err := withRetries(defaultRetries, func() ([]*metav1.APIGroup, []*metav1.APIResourceList, error) {
rs, err := ServerPreferredResourcesWithContext(ctx, d)
return nil, rs, err
})
return rs, err
}
// ServerPreferredNamespacedResources returns the supported namespaced resources with the
// version preferred by the server.
//
// Deprecated: use ServerPreferredNamespacedResourcesWithContext instead.
func (d *DiscoveryClient) ServerPreferredNamespacedResources() ([]*metav1.APIResourceList, error) {
return ServerPreferredNamespacedResources(d)
}
// ServerPreferredNamespacedResources returns the supported namespaced resources with the
// version preferred by the server.
func (d *DiscoveryClient) ServerPreferredNamespacedResourcesWithContext(ctx context.Context) ([]*metav1.APIResourceList, error) {
return ServerPreferredNamespacedResourcesWithContext(ctx, d)
}
// ServerPreferredNamespacedResources uses the provided discovery interface to look up preferred namespaced resources
//
// Deprecated: use ServerPreferredNamespacedResourcesWithContext instead.
func ServerPreferredNamespacedResources(d DiscoveryInterface) ([]*metav1.APIResourceList, error) {
return ServerPreferredNamespacedResourcesWithContext(context.Background(), ToDiscoveryInterfaceWithContext(d))
}
// ServerPreferredNamespacedResourcesWithContext uses the provided discovery interface to look up preferred namespaced resources
func ServerPreferredNamespacedResourcesWithContext(ctx context.Context, d DiscoveryInterfaceWithContext) ([]*metav1.APIResourceList, error) {
all, err := ServerPreferredResourcesWithContext(ctx, d)
all, err := ServerPreferredResources(d)
return FilteredBy(ResourcePredicateFunc(func(groupVersion string, r *metav1.APIResource) bool {
return r.Namespaced
}), all), err
@ -1095,12 +664,7 @@ func ServerPreferredNamespacedResourcesWithContext(ctx context.Context, d Discov
// ServerVersion retrieves and parses the server's version (git version).
func (d *DiscoveryClient) ServerVersion() (*version.Info, error) {
return d.ServerVersionWithContext(context.Background())
}
// ServerVersionWithContext retrieves and parses the server's version (git version).
func (d *DiscoveryClient) ServerVersionWithContext(ctx context.Context) (*version.Info, error) {
body, err := d.restClient.Get().AbsPath("/version").Do(ctx).Raw()
body, err := d.restClient.Get().AbsPath("/version").Do(context.TODO()).Raw()
if err != nil {
return nil, err
}
@ -1114,12 +678,7 @@ func (d *DiscoveryClient) ServerVersionWithContext(ctx context.Context) (*versio
// OpenAPISchema fetches the open api v2 schema using a rest client and parses the proto.
func (d *DiscoveryClient) OpenAPISchema() (*openapi_v2.Document, error) {
return d.OpenAPISchemaWithContext(context.Background())
}
// OpenAPISchema fetches the open api v2 schema using a rest client and parses the proto.
func (d *DiscoveryClient) OpenAPISchemaWithContext(ctx context.Context) (*openapi_v2.Document, error) {
data, err := d.restClient.Get().AbsPath("/openapi/v2").SetHeader("Accept", openAPIV2mimePb).Do(ctx).Raw()
data, err := d.restClient.Get().AbsPath("/openapi/v2").SetHeader("Accept", openAPIV2mimePb).Do(context.TODO()).Raw()
if err != nil {
return nil, err
}
@ -1131,33 +690,18 @@ func (d *DiscoveryClient) OpenAPISchemaWithContext(ctx context.Context) (*openap
return document, nil
}
// Deprecated: used OpenAVPIV3WithContext instead.
func (d *DiscoveryClient) OpenAPIV3() openapi.Client {
return openapi.NewClient(d.restClient)
}
func (d *DiscoveryClient) OpenAPIV3WithContext(ctx context.Context) openapi.ClientWithContext {
return openapi.NewClientWithContext(d.restClient)
}
// WithLegacy returns copy of current discovery client that will only
// receive the legacy discovery format.
//
// Deprecated: use WithLegacyWithContext instead.
func (d *DiscoveryClient) WithLegacy() DiscoveryInterface {
client := *d
client.UseLegacyDiscovery = true
return &client
}
// WithLegacyWithContext returns copy of current discovery client that will only
// receive the legacy discovery format.
func (d *DiscoveryClient) WithLegacyWithContext(ctx context.Context) DiscoveryInterfaceWithContext {
client := *d
client.UseLegacyDiscovery = true
return &client
}
// withRetries retries the given recovery function in case the groups supported by the server change after ServerGroup() returns.
func withRetries(maxRetries int, f func() ([]*metav1.APIGroup, []*metav1.APIResourceList, error)) ([]*metav1.APIGroup, []*metav1.APIResourceList, error) {
var result []*metav1.APIResourceList

View file

@ -17,7 +17,6 @@ limitations under the License.
package fake
import (
"context"
"fmt"
"net/http"
@ -34,30 +33,16 @@ import (
"k8s.io/client-go/testing"
)
// FakeDiscovery implements discovery.DiscoveryInterface and discovery.DiscoveryInterfaceWithContext.
// It sometimes calls testing.Fake.Invoke with an action,
// FakeDiscovery implements discovery.DiscoveryInterface and sometimes calls testing.Fake.Invoke with an action,
// but doesn't respect the return value if any. There is a way to fake static values like ServerVersion by using the Faked... fields on the struct.
type FakeDiscovery struct {
*testing.Fake
FakedServerVersion *version.Info
}
var (
_ discovery.DiscoveryInterface = &FakeDiscovery{}
_ discovery.DiscoveryInterfaceWithContext = &FakeDiscovery{}
)
// ServerResourcesForGroupVersion returns the supported resources for a group
// and version.
//
// Deprecated: use ServerResourcesForGroupVersionWithContext instead.
func (c *FakeDiscovery) ServerResourcesForGroupVersion(groupVersion string) (*metav1.APIResourceList, error) {
return c.ServerResourcesForGroupVersionWithContext(context.Background(), groupVersion)
}
// ServerResourcesForGroupVersionWithContext returns the supported resources for a group
// and version.
func (c *FakeDiscovery) ServerResourcesForGroupVersionWithContext(ctx context.Context, groupVersion string) (*metav1.APIResourceList, error) {
action := testing.ActionImpl{
Verb: "get",
Resource: schema.GroupVersionResource{Resource: "resource"},
@ -80,15 +65,8 @@ func (c *FakeDiscovery) ServerResourcesForGroupVersionWithContext(ctx context.Co
}
// ServerGroupsAndResources returns the supported groups and resources for all groups and versions.
//
// Deprecated: use ServerGroupsAndResourcesWithContext instead.
func (c *FakeDiscovery) ServerGroupsAndResources() ([]*metav1.APIGroup, []*metav1.APIResourceList, error) {
return c.ServerGroupsAndResourcesWithContext(context.Background())
}
// ServerGroupsAndResourcesWithContext returns the supported groups and resources for all groups and versions.
func (c *FakeDiscovery) ServerGroupsAndResourcesWithContext(ctx context.Context) ([]*metav1.APIGroup, []*metav1.APIResourceList, error) {
sgs, err := c.ServerGroupsWithContext(ctx)
sgs, err := c.ServerGroups()
if err != nil {
return nil, nil, err
}
@ -109,43 +87,19 @@ func (c *FakeDiscovery) ServerGroupsAndResourcesWithContext(ctx context.Context)
// ServerPreferredResources returns the supported resources with the version
// preferred by the server.
//
// Deprecated: use ServerPreferredResourcesWithContext instead.
func (c *FakeDiscovery) ServerPreferredResources() ([]*metav1.APIResourceList, error) {
return c.ServerPreferredResourcesWithContext(context.Background())
}
// ServerPreferredResourcesWithContext returns the supported resources with the version
// preferred by the server.
func (c *FakeDiscovery) ServerPreferredResourcesWithContext(ctx context.Context) ([]*metav1.APIResourceList, error) {
return nil, nil
}
// ServerPreferredNamespacedResources returns the supported namespaced resources
// with the version preferred by the server.
//
// Deprecated: use ServerPreferredNamespacedResourcesWithContext instead.
func (c *FakeDiscovery) ServerPreferredNamespacedResources() ([]*metav1.APIResourceList, error) {
return c.ServerPreferredNamespacedResourcesWithContext(context.Background())
}
// ServerPreferredNamespacedResourcesWithContext returns the supported namespaced resources
// with the version preferred by the server.
func (c *FakeDiscovery) ServerPreferredNamespacedResourcesWithContext(ctx context.Context) ([]*metav1.APIResourceList, error) {
return nil, nil
}
// ServerGroups returns the supported groups, with information like supported
// versions and the preferred version.
//
// Deprecated: use ServerGroupsWithContext instead.
func (c *FakeDiscovery) ServerGroups() (*metav1.APIGroupList, error) {
return c.ServerGroupsWithContext(context.Background())
}
// ServerGroupsWithContext returns the supported groups, with information like supported
// versions and the preferred version.
func (c *FakeDiscovery) ServerGroupsWithContext(ctx context.Context) (*metav1.APIGroupList, error) {
action := testing.ActionImpl{
Verb: "get",
Resource: schema.GroupVersionResource{Resource: "group"},
@ -189,14 +143,7 @@ func (c *FakeDiscovery) ServerGroupsWithContext(ctx context.Context) (*metav1.AP
}
// ServerVersion retrieves and parses the server's version.
//
// Deprecated: use ServerVersionWithContext instead.
func (c *FakeDiscovery) ServerVersion() (*version.Info, error) {
return c.ServerVersionWithContext(context.Background())
}
// ServerVersionWithContext retrieves and parses the server's version.
func (c *FakeDiscovery) ServerVersionWithContext(ctx context.Context) (*version.Info, error) {
action := testing.ActionImpl{}
action.Verb = "get"
action.Resource = schema.GroupVersionResource{Resource: "version"}
@ -214,37 +161,20 @@ func (c *FakeDiscovery) ServerVersionWithContext(ctx context.Context) (*version.
}
// OpenAPISchema retrieves and parses the swagger API schema the server supports.
//
// Deprecated: use OpenAPISchemaWithContext instead.
func (c *FakeDiscovery) OpenAPISchema() (*openapi_v2.Document, error) {
return c.OpenAPISchemaWithContext(context.Background())
}
// OpenAPISchemaWithContext retrieves and parses the swagger API schema the server supports.
func (c *FakeDiscovery) OpenAPISchemaWithContext(ctx context.Context) (*openapi_v2.Document, error) {
return &openapi_v2.Document{}, nil
}
// Deprecated: use OpenAPIV3WithContext instead.
func (c *FakeDiscovery) OpenAPIV3() openapi.Client {
panic("unimplemented")
}
func (c *FakeDiscovery) OpenAPIV3WithContext(ctx context.Context) openapi.ClientWithContext {
panic("unimplemented")
}
// RESTClient returns a RESTClient that is used to communicate with API server
// by this client implementation.
func (c *FakeDiscovery) RESTClient() restclient.Interface {
return nil
}
// Deprecated: use WithLegacyWithContext instead.
func (c *FakeDiscovery) WithLegacy() discovery.DiscoveryInterface {
panic("unimplemented")
}
func (c *FakeDiscovery) WithLegacyWithContext(ctx context.Context) discovery.DiscoveryInterfaceWithContext {
panic("unimplemented")
}

View file

@ -141,13 +141,6 @@ func (f *envVarFeatureGates) Set(featureName Feature, featureValue bool) error {
// read from the corresponding environmental variable.
func (f *envVarFeatureGates) getEnabledMapFromEnvVar() map[Feature]bool {
f.readEnvVarsOnce.Do(func() {
// This code does not really support contextual logging. Making it do so has huge
// implications for several call chains because the Enabled call then needs
// a `*WithLogger` variant. This does not matter in Kubernetes itself because
// all Kubernetes components replace the feature gate implementation used
// by client-go, but it might matter elsewhere.
logger := klog.Background()
featureGatesState := map[Feature]bool{}
for feature, featureSpec := range f.known {
featureState, featureStateSet := os.LookupEnv(fmt.Sprintf("KUBE_FEATURE_%s", feature))
@ -157,10 +150,10 @@ func (f *envVarFeatureGates) getEnabledMapFromEnvVar() map[Feature]bool {
boolVal, boolErr := strconv.ParseBool(featureState)
switch {
case boolErr != nil:
utilruntime.HandleErrorWithLogger(logger, boolErr, "Could not set feature gate", "feature", feature, "desiredState", featureState)
utilruntime.HandleError(fmt.Errorf("cannot set feature gate %q to %q, due to %v", feature, featureState, boolErr))
case featureSpec.LockToDefault:
if boolVal != featureSpec.Default {
utilruntime.HandleErrorWithLogger(logger, nil, "Could not set feature gate, feature is locked", "feature", feature, "desiredState", featureState, "lockedState", featureSpec.Default)
utilruntime.HandleError(fmt.Errorf("cannot set feature gate %q to %q, feature is locked to %v", feature, featureState, featureSpec.Default))
break
}
featureGatesState[feature] = featureSpec.Default
@ -173,10 +166,10 @@ func (f *envVarFeatureGates) getEnabledMapFromEnvVar() map[Feature]bool {
for feature, featureSpec := range f.known {
if featureState, ok := featureGatesState[feature]; ok {
logger.V(1).Info("Feature gate updated state", "feature", feature, "enabled", featureState)
klog.V(1).InfoS("Feature gate updated state", "feature", feature, "enabled", featureState)
continue
}
logger.V(1).Info("Feature gate default state", "feature", feature, "enabled", featureSpec.Default)
klog.V(1).InfoS("Feature gate default state", "feature", feature, "enabled", featureSpec.Default)
}
})
return f.enabledViaEnvVar.Load().(map[Feature]bool)

View file

@ -17,7 +17,7 @@ limitations under the License.
package features
import (
"context"
"errors"
"sync/atomic"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
@ -127,9 +127,7 @@ func AddVersionedFeaturesToExistingFeatureGates(registry VersionedRegistry) erro
// clientgofeaturegate.ReplaceFeatureGates(utilfeature.DefaultMutableFeatureGate)
func ReplaceFeatureGates(newFeatureGates Gates) {
if replaceFeatureGatesWithWarningIndicator(newFeatureGates) {
// TODO (?): A new API would be needed where callers pass in a context or logger.
// Probably not worth it.
utilruntime.HandleErrorWithContext(context.TODO(), nil, "The default feature gates implementation has already been used and now it's being overwritten. This might lead to unexpected behaviour. Check your initialization order.")
utilruntime.HandleError(errors.New("the default feature gates implementation has already been used and now it's being overwritten. This might lead to unexpected behaviour. Check your initialization order"))
}
}

View file

@ -17,62 +17,34 @@ limitations under the License.
package cached
import (
"context"
"sync"
"k8s.io/client-go/openapi"
)
type Client struct {
delegate openapi.ClientWithContext
type client struct {
delegate openapi.Client
once sync.Once
result map[string]openapi.GroupVersionWithContext
result map[string]openapi.GroupVersion
err error
}
var (
_ openapi.Client = &Client{}
_ openapi.ClientWithContext = &Client{}
)
// Deprecated: use NewClientWithContext instead.
func NewClient(other openapi.Client) openapi.Client {
return newClient(openapi.ToClientWithContext(other))
}
func NewClientWithContext(other openapi.ClientWithContext) *Client {
return newClient(other)
}
func newClient(other openapi.ClientWithContext) *Client {
return &Client{
return &client{
delegate: other,
}
}
// Deprecated: use PathsWithContext instead.
func (c *Client) Paths() (map[string]openapi.GroupVersion, error) {
// For efficiency reasons in the *WithContext case this is a map to
// openapi.GroupVersionWithContext. But we know that all entries
// also implement openapi.GroupVersion.
resultWithContext, err := c.PathsWithContext(context.Background())
result := make(map[string]openapi.GroupVersion, len(resultWithContext))
for key, entry := range resultWithContext {
result[key] = entry.(openapi.GroupVersion)
}
return result, err
}
func (c *Client) PathsWithContext(ctx context.Context) (map[string]openapi.GroupVersionWithContext, error) {
func (c *client) Paths() (map[string]openapi.GroupVersion, error) {
c.once.Do(func() {
uncached, err := c.delegate.PathsWithContext(ctx)
uncached, err := c.delegate.Paths()
if err != nil {
c.err = err
return
}
result := make(map[string]openapi.GroupVersionWithContext, len(uncached))
result := make(map[string]openapi.GroupVersion, len(uncached))
for k, v := range uncached {
result[k] = newGroupVersion(v)
}

View file

@ -17,41 +17,30 @@ limitations under the License.
package cached
import (
"context"
"sync"
"k8s.io/client-go/openapi"
)
type groupversion struct {
delegate openapi.GroupVersionWithContext
delegate openapi.GroupVersion
lock sync.Mutex
docs map[string]docInfo
}
var (
_ openapi.GroupVersion = &groupversion{}
_ openapi.GroupVersionWithContext = &groupversion{}
)
type docInfo struct {
data []byte
err error
}
func newGroupVersion(delegate openapi.GroupVersionWithContext) *groupversion {
func newGroupVersion(delegate openapi.GroupVersion) *groupversion {
return &groupversion{
delegate: delegate,
}
}
// Deprecated: use SchemaWithContext instead.
func (g *groupversion) Schema(contentType string) ([]byte, error) {
return g.SchemaWithContext(context.Background(), contentType)
}
func (g *groupversion) SchemaWithContext(ctx context.Context, contentType string) ([]byte, error) {
g.lock.Lock()
defer g.lock.Unlock()
@ -61,7 +50,7 @@ func (g *groupversion) SchemaWithContext(ctx context.Context, contentType string
g.docs = make(map[string]docInfo)
}
cachedInfo.data, cachedInfo.err = g.delegate.SchemaWithContext(ctx, contentType)
cachedInfo.data, cachedInfo.err = g.delegate.Schema(contentType)
g.docs[contentType] = cachedInfo
}

View file

@ -25,75 +25,25 @@ import (
"k8s.io/kube-openapi/pkg/handler3"
)
// Deprecated: use ClientWithContext instead.
type Client interface {
Paths() (map[string]GroupVersion, error)
}
type ClientWithContext interface {
PathsWithContext(ctx context.Context) (map[string]GroupVersionWithContext, error)
}
func ToClientWithContext(c Client) ClientWithContext {
if c == nil {
return nil
}
if c, ok := c.(ClientWithContext); ok {
return c
}
return &clientWrapper{
delegate: c,
}
}
type clientWrapper struct {
delegate Client
}
func (c *clientWrapper) PathsWithContext(ctx context.Context) (map[string]GroupVersionWithContext, error) {
resultWithoutContext, err := c.delegate.Paths()
result := make(map[string]GroupVersionWithContext, len(resultWithoutContext))
for key, entry := range resultWithoutContext {
result[key] = ToGroupVersionWithContext(entry)
}
return result, err
}
type client struct {
// URL includes the `hash` query param to take advantage of cache busting
restClient rest.Interface
}
// Deprecated: use NewClientWithContext instead.
func NewClient(restClient rest.Interface) Client {
return newClient(restClient)
}
func NewClientWithContext(restClient rest.Interface) ClientWithContext {
return newClient(restClient)
}
func newClient(restClient rest.Interface) *client {
return &client{
restClient: restClient,
}
}
// Deprecated: use PathsWithContext instead.
func (c *client) Paths() (map[string]GroupVersion, error) {
resultWithContext, err := c.PathsWithContext(context.Background())
result := make(map[string]GroupVersion, len(resultWithContext))
for key, entry := range resultWithContext {
// We know that this is a *groupversion which implements GroupVersion.
result[key] = entry.(GroupVersion)
}
return result, err
}
func (c *client) PathsWithContext(ctx context.Context) (map[string]GroupVersionWithContext, error) {
data, err := c.restClient.Get().
AbsPath("/openapi/v3").
Do(ctx).
Do(context.TODO()).
Raw()
if err != nil {
@ -109,7 +59,7 @@ func (c *client) PathsWithContext(ctx context.Context) (map[string]GroupVersionW
// Calculate the client-side prefix for a "root" request
rootPrefix := strings.TrimSuffix(c.restClient.Get().AbsPath("/").URL().Path, "/")
// Create GroupVersions for each element of the result
result := make(map[string]GroupVersionWithContext, len(discoMap.Paths))
result := map[string]GroupVersion{}
for k, v := range discoMap.Paths {
// Trim off the prefix that will always be added in client-side
v.ServerRelativeURL = strings.TrimPrefix(v.ServerRelativeURL, rootPrefix)

View file

@ -25,7 +25,6 @@ import (
const ContentTypeOpenAPIV3PB = "application/com.github.proto-openapi.spec.v3@v1.0+protobuf"
// Deprecated: use GroupVersionWithContext instead.
type GroupVersion interface {
Schema(contentType string) ([]byte, error)
@ -36,61 +35,22 @@ type GroupVersion interface {
ServerRelativeURL() string
}
type GroupVersionWithContext interface {
SchemaWithContext(ctx context.Context, contentType string) ([]byte, error)
// ServerRelativeURL. Returns the path and parameters used to fetch the schema.
// You should use the Schema method to fetch it, but this value can be used
// to key the current version of the schema in a cache since it contains a
// hash string which changes upon schema update.
ServerRelativeURL() string
}
func ToGroupVersionWithContext(gv GroupVersion) GroupVersionWithContext {
if gv == nil {
return nil
}
if gv, ok := gv.(GroupVersionWithContext); ok {
return gv
}
return &groupVersionWrapper{
GroupVersion: gv,
}
}
type groupVersionWrapper struct {
GroupVersion
}
func (gv *groupVersionWrapper) SchemaWithContext(ctx context.Context, contentType string) ([]byte, error) {
return gv.Schema(contentType)
}
type groupversion struct {
client *client
item handler3.OpenAPIV3DiscoveryGroupVersion
useClientPrefix bool
}
var (
_ GroupVersion = &groupversion{}
_ GroupVersionWithContext = &groupversion{}
)
func newGroupVersion(client *client, item handler3.OpenAPIV3DiscoveryGroupVersion, useClientPrefix bool) *groupversion {
return &groupversion{client: client, item: item, useClientPrefix: useClientPrefix}
}
func (g *groupversion) Schema(contentType string) ([]byte, error) {
return g.SchemaWithContext(context.Background(), contentType)
}
func (g *groupversion) SchemaWithContext(ctx context.Context, contentType string) ([]byte, error) {
if !g.useClientPrefix {
return g.client.restClient.Get().
RequestURI(g.item.ServerRelativeURL).
SetHeader("Accept", contentType).
Do(ctx).
Do(context.TODO()).
Raw()
}
@ -112,7 +72,7 @@ func (g *groupversion) SchemaWithContext(ctx context.Context, contentType string
}
}
return path.Do(ctx).Raw()
return path.Do(context.TODO()).Raw()
}
// URL used for fetching the schema. The URL includes a hash and can be used

View file

@ -25,7 +25,6 @@ import (
func init() {
if err := rest.RegisterAuthProviderPlugin("azure", newAzureAuthProvider); err != nil {
//nolint:logcheck // Should not happen.
klog.Fatalf("Failed to register azure auth plugin: %v", err)
}
}

View file

@ -363,7 +363,6 @@ func (r *roundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
return r.base.RoundTrip(req)
}
ctx := req.Context()
creds, err := r.a.getCreds()
if err != nil {
return nil, fmt.Errorf("getting credentials: %v", err)
@ -378,7 +377,7 @@ func (r *roundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
}
if res.StatusCode == http.StatusUnauthorized {
if err := r.a.maybeRefreshCreds(creds); err != nil {
klog.FromContext(ctx).Error(err, "Refreshing credentials failed")
klog.Errorf("refreshing credentials: %v", err)
}
}
return res, nil

View file

@ -112,11 +112,7 @@ func incrementCallsMetric(err error) {
metrics.ExecPluginCalls.Increment(failureExitCode, pluginNotFoundError)
default: // We don't know about this error type.
// TODO (?): this code gets called through
// https://github.com/kubernetes/kubernetes/blob/8a5cf7b66f4bf8c76c4a78e249ee7c21f7d7403e/staging/src/k8s.io/client-go/transport/config.go#L152-L154
// which would have to be changed to accept a context.
// Probably not worth it?
klog.TODO().V(2).Info("unexpected exec plugin return error type", "type", reflect.TypeOf(err).String(), "err", err)
klog.V(2).InfoS("unexpected exec plugin return error type", "type", reflect.TypeOf(err).String(), "err", err)
metrics.ExecPluginCalls.Increment(failureExitCode, clientInternalError)
}
}

View file

@ -25,7 +25,6 @@ import (
func init() {
if err := rest.RegisterAuthProviderPlugin("gcp", newGCPAuthProvider); err != nil {
//nolint:logcheck // Should not happen.
klog.Fatalf("Failed to register gcp auth plugin: %v", err)
}
}

View file

@ -49,7 +49,6 @@ const (
func init() {
if err := restclient.RegisterAuthProviderPlugin("oidc", newOIDCAuthProvider); err != nil {
//nolint:logcheck // Should not happen.
klog.Fatalf("Failed to register oidc auth plugin: %v", err)
}
}
@ -126,9 +125,8 @@ func newOIDCAuthProvider(clusterAddress string, cfg map[string]string, persister
}
if len(cfg[cfgExtraScopes]) > 0 {
// TODO (?): the auth provider factory API would have to be changed
// to support contextual logging here. Not worth it?
klog.TODO().V(2).Info("Ignoring deprecated extra scopes, refresh request don't send scopes", "ignoredConfigField", cfgExtraScopes)
klog.V(2).Infof("%s auth provider field depricated, refresh request don't send scopes",
cfgExtraScopes)
}
var certAuthData []byte

View file

@ -17,96 +17,38 @@ limitations under the License.
package restmapper
import (
"context"
"iter"
"slices"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/discovery"
)
// CategoryExpander maps category strings to GroupResources.
// Categories are classification or 'tag' of a group of resources.
//
// Deprecated: use CategoryExpanderWithContext instead.
type CategoryExpander interface {
Expand(category string) ([]schema.GroupResource, bool)
}
// CategoryExpanderWithContext maps category strings to GroupResources.
// Categories are classification or 'tag' of a group of resources.
type CategoryExpanderWithContext interface {
ExpandWithContext(ctx context.Context, category string) ([]schema.GroupResource, bool)
}
func ToCategoryExpanderWithContext(c CategoryExpander) CategoryExpanderWithContext {
if c == nil {
return nil
}
if c == nil {
return nil
}
if c, ok := c.(CategoryExpanderWithContext); ok {
return c
}
return &categoryExpanderWrapper{
delegate: c,
}
}
type categoryExpanderWrapper struct {
delegate CategoryExpander
}
func (c *categoryExpanderWrapper) ExpandWithContext(ctx context.Context, category string) ([]schema.GroupResource, bool) {
return c.delegate.Expand(category)
}
// SimpleCategoryExpander implements CategoryExpander and CategoryExpanderWithContext interface
// SimpleCategoryExpander implements CategoryExpander interface
// using a static mapping of categories to GroupResource mapping.
type SimpleCategoryExpander struct {
Expansions map[string][]schema.GroupResource
}
var (
_ CategoryExpander = SimpleCategoryExpander{}
_ CategoryExpanderWithContext = SimpleCategoryExpander{}
)
// Expand fulfills CategoryExpander
func (e SimpleCategoryExpander) Expand(category string) ([]schema.GroupResource, bool) {
ret, ok := e.Expansions[category]
return ret, ok
}
// Expand fulfills CategoryExpanderWithContext
func (e SimpleCategoryExpander) ExpandWithContext(ctx context.Context, category string) ([]schema.GroupResource, bool) {
return e.Expand(category)
}
// discoveryCategoryExpander struct lets a REST Client wrapper (discoveryClient) to retrieve list of APIResourceList,
// and then convert to fallbackExpander
type discoveryCategoryExpander struct {
discoveryClient discovery.DiscoveryInterfaceWithContext
discoveryClient discovery.DiscoveryInterface
}
// NewDiscoveryCategoryExpander returns a category expander that makes use of the "categories" fields from
// the API, found through the discovery client. In case of any error or no category found (which likely
// means we're at a cluster prior to categories support, fallback to the expander provided.
//
// Deprecated: use NewDiscoveryCategoryExpanderWithContext instead.
func NewDiscoveryCategoryExpander(client discovery.DiscoveryInterface) CategoryExpander {
return newDiscoveryCategoryExpander(discovery.ToDiscoveryInterfaceWithContext(client))
}
// NewDiscoveryCategoryExpanderWithContext returns a category expander that makes use of the "categories" fields from
// the API, found through the discovery client. In case of any error or no category found (which likely
// means we're at a cluster prior to categories support, fallback to the expander provided.
func NewDiscoveryCategoryExpanderWithContext(client discovery.DiscoveryInterfaceWithContext) CategoryExpanderWithContext {
return newDiscoveryCategoryExpander(client)
}
func newDiscoveryCategoryExpander(client discovery.DiscoveryInterfaceWithContext) discoveryCategoryExpander {
if client == nil {
panic("Please provide discovery client to shortcut expander")
}
@ -114,16 +56,9 @@ func newDiscoveryCategoryExpander(client discovery.DiscoveryInterfaceWithContext
}
// Expand fulfills CategoryExpander
//
// Deprecated: use ExpandWithContext instead.
func (e discoveryCategoryExpander) Expand(category string) ([]schema.GroupResource, bool) {
return e.ExpandWithContext(context.Background(), category)
}
// ExpandWithContext fulfills CategoryExpanderWithContext
func (e discoveryCategoryExpander) ExpandWithContext(ctx context.Context, category string) ([]schema.GroupResource, bool) {
// Get all supported resources for groups and versions from server, if no resource found, fallback anyway.
_, apiResourceLists, _ := e.discoveryClient.ServerGroupsAndResourcesWithContext(ctx)
_, apiResourceLists, _ := e.discoveryClient.ServerGroupsAndResources()
if len(apiResourceLists) == 0 {
return nil, false
}
@ -154,41 +89,16 @@ func (e discoveryCategoryExpander) ExpandWithContext(ctx context.Context, catego
// UnionCategoryExpander implements CategoryExpander interface.
// It maps given category string to union of expansions returned by all the CategoryExpanders in the list.
//
// Deprecated: use UnionCategoryWithContext instead.
type UnionCategoryExpander []CategoryExpander
var _ CategoryExpander = UnionCategoryExpander{}
// Expand fulfills CategoryExpander
func (u UnionCategoryExpander) Expand(category string) ([]schema.GroupResource, bool) {
return unionExpand(context.Background(), func(yield func(i int, expander CategoryExpanderWithContext) bool) {
for i, expander := range u {
if !yield(i, ToCategoryExpanderWithContext(expander)) {
break
}
}
}, category)
}
// UnionCategoryExpanderWithContext implements CategoryExpanderWithContext interface.
// It maps given category string to union of expansions returned by all the CategoryExpanders in the list.
type UnionCategoryExpanderWithContext []CategoryExpanderWithContext
var _ CategoryExpanderWithContext = UnionCategoryExpanderWithContext{}
// ExpandWithContext fulfills CategoryExpanderWithContext
func (u UnionCategoryExpanderWithContext) ExpandWithContext(ctx context.Context, category string) ([]schema.GroupResource, bool) {
return unionExpand(ctx, slices.All(u), category)
}
func unionExpand(ctx context.Context, expanders iter.Seq2[int, CategoryExpanderWithContext], category string) ([]schema.GroupResource, bool) {
ret := []schema.GroupResource{}
ok := false
// Expand the category for each CategoryExpander in the list and merge/combine the results.
for _, expansion := range expanders {
curr, currOk := expansion.ExpandWithContext(ctx, category)
for _, expansion := range u {
curr, currOk := expansion.Expand(category)
for _, currGR := range curr {
found := false

View file

@ -17,7 +17,6 @@ limitations under the License.
package restmapper
import (
"context"
"fmt"
"strings"
"sync"
@ -41,38 +40,8 @@ type APIGroupResources struct {
// NewDiscoveryRESTMapper returns a PriorityRESTMapper based on the discovered
// groups and resources passed in.
//
// Deprecated: use NewDiscoveryRESTMapperWithContext instead.
func NewDiscoveryRESTMapper(groupResources []*APIGroupResources) meta.RESTMapper {
mappers, resourcePriority, kindPriority := newDiscoveryRESTMapper(groupResources)
multiMapper := make(meta.MultiRESTMapper, len(mappers))
for i, m := range mappers {
multiMapper[i] = m
}
return &meta.PriorityRESTMapper{
Delegate: multiMapper,
ResourcePriority: resourcePriority,
KindPriority: kindPriority,
}
}
// NewDiscoveryRESTMapperWithContext returns a PriorityRESTMapper based on the discovered
// groups and resources passed in.
func NewDiscoveryRESTMapperWithContext(groupResources []*APIGroupResources) meta.RESTMapperWithContext {
mappers, resourcePriority, kindPriority := newDiscoveryRESTMapper(groupResources)
multiMapper := make(meta.MultiRESTMapperWithContext, len(mappers))
for i, m := range mappers {
multiMapper[i] = m
}
return &meta.PriorityRESTMapperWithContext{
Delegate: multiMapper,
ResourcePriority: resourcePriority,
KindPriority: kindPriority,
}
}
func newDiscoveryRESTMapper(groupResources []*APIGroupResources) ([]*meta.DefaultRESTMapper, []schema.GroupVersionResource, []schema.GroupVersionKind) {
var unionMapper []*meta.DefaultRESTMapper
unionMapper := meta.MultiRESTMapper{}
var groupPriority []string
// /v1 is special. It should always come first
@ -166,21 +135,17 @@ func newDiscoveryRESTMapper(groupResources []*APIGroupResources) ([]*meta.Defaul
})
}
return unionMapper, resourcePriority, kindPriority
return meta.PriorityRESTMapper{
Delegate: unionMapper,
ResourcePriority: resourcePriority,
KindPriority: kindPriority,
}
}
// GetAPIGroupResources uses the provided discovery client to gather
// discovery information and populate a slice of APIGroupResources.
//
// Deprecated: use GetAPIGroupResourcesWithContext instead.
func GetAPIGroupResources(cl discovery.DiscoveryInterface) ([]*APIGroupResources, error) {
return GetAPIGroupResourcesWithContext(context.Background(), discovery.ToDiscoveryInterfaceWithContext(cl))
}
// GetAPIGroupResourcesWithContext uses the provided discovery client to gather
// discovery information and populate a slice of APIGroupResources.
func GetAPIGroupResourcesWithContext(ctx context.Context, cl discovery.DiscoveryInterfaceWithContext) ([]*APIGroupResources, error) {
gs, rs, err := cl.ServerGroupsAndResourcesWithContext(ctx)
gs, rs, err := cl.ServerGroupsAndResources()
if rs == nil || gs == nil {
return nil, err
// TODO track the errors and update callers to handle partial errors.
@ -208,42 +173,25 @@ func GetAPIGroupResourcesWithContext(ctx context.Context, cl discovery.Discovery
return result, nil
}
// DeferredDiscoveryRESTMapper is a RESTMapper and RESTMapperContext that will defer
// DeferredDiscoveryRESTMapper is a RESTMapper that will defer
// initialization of the RESTMapper until the first mapping is
// requested.
type DeferredDiscoveryRESTMapper struct {
initMu sync.Mutex
delegate meta.RESTMapperWithContext
cl discovery.CachedDiscoveryInterfaceWithContext
delegate meta.RESTMapper
cl discovery.CachedDiscoveryInterface
}
var (
_ meta.ResettableRESTMapper = &DeferredDiscoveryRESTMapper{}
_ meta.ResettableRESTMapperWithContext = &DeferredDiscoveryRESTMapper{}
_ fmt.Stringer = &DeferredDiscoveryRESTMapper{}
)
// NewDeferredDiscoveryRESTMapper returns a
// DeferredDiscoveryRESTMapper that will lazily query the provided
// client for discovery information to do REST mappings.
//
// Deprecated: use NewDeferredDiscoveryRESTMapperWithContext instead. NewDeferredDiscoveryRESTMapper will try to convert cl to discovery.CachedDiscoveryInterfaceWithContext and use a wrapper if that is not possible, but NewDeferredDiscoveryRESTMapperWithContext ensures that no such conversion is necessary.
func NewDeferredDiscoveryRESTMapper(cl discovery.CachedDiscoveryInterface) *DeferredDiscoveryRESTMapper {
return &DeferredDiscoveryRESTMapper{
cl: discovery.ToCachedDiscoveryInterfaceWithContext(cl),
}
}
// NewDeferredDiscoveryRESTMapperWithContext returns a
// DeferredDiscoveryRESTMapper that will lazily query the provided
// client for discovery information to do REST mappings.
func NewDeferredDiscoveryRESTMapperWithContext(cl discovery.CachedDiscoveryInterfaceWithContext) *DeferredDiscoveryRESTMapper {
return &DeferredDiscoveryRESTMapper{
cl: cl,
}
}
func (d *DeferredDiscoveryRESTMapper) getDelegate(ctx context.Context) (meta.RESTMapperWithContext, error) {
func (d *DeferredDiscoveryRESTMapper) getDelegate() (meta.RESTMapper, error) {
d.initMu.Lock()
defer d.initMu.Unlock()
@ -251,144 +199,98 @@ func (d *DeferredDiscoveryRESTMapper) getDelegate(ctx context.Context) (meta.RES
return d.delegate, nil
}
groupResources, err := GetAPIGroupResourcesWithContext(ctx, d.cl)
groupResources, err := GetAPIGroupResources(d.cl)
if err != nil {
return nil, err
}
d.delegate = NewDiscoveryRESTMapperWithContext(groupResources)
d.delegate = NewDiscoveryRESTMapper(groupResources)
return d.delegate, nil
}
// Reset resets the internally cached Discovery information and will
// cause the next mapping request to re-discover.
func (d *DeferredDiscoveryRESTMapper) Reset() {
d.ResetWithContext(context.Background())
}
// ResetWithContext resets the internally cached Discovery information and will
// cause the next mapping request to re-discover.
func (d *DeferredDiscoveryRESTMapper) ResetWithContext(ctx context.Context) {
klog.FromContext(ctx).V(5).Info("Invalidating discovery information")
klog.V(5).Info("Invalidating discovery information")
d.initMu.Lock()
defer d.initMu.Unlock()
d.cl.InvalidateWithContext(ctx)
d.cl.Invalidate()
d.delegate = nil
}
// KindFor takes a partial resource and returns back the single match.
// It returns an error if there are multiple matches.
//
// Deprecated: use KindForWithContext instead.
func (d *DeferredDiscoveryRESTMapper) KindFor(resource schema.GroupVersionResource) (gvk schema.GroupVersionKind, err error) {
return d.KindForWithContext(context.Background(), resource)
}
// KindForWithContext takes a partial resource and returns back the single match.
// It returns an error if there are multiple matches.
func (d *DeferredDiscoveryRESTMapper) KindForWithContext(ctx context.Context, resource schema.GroupVersionResource) (gvk schema.GroupVersionKind, err error) {
del, err := d.getDelegate(ctx)
del, err := d.getDelegate()
if err != nil {
return schema.GroupVersionKind{}, err
}
gvk, err = del.KindForWithContext(ctx, resource)
if err != nil && !d.cl.FreshWithContext(ctx) {
d.ResetWithContext(ctx)
gvk, err = d.KindForWithContext(ctx, resource)
gvk, err = del.KindFor(resource)
if err != nil && !d.cl.Fresh() {
d.Reset()
gvk, err = d.KindFor(resource)
}
return
}
// KindsFor takes a partial resource and returns back the list of
// potential kinds in priority order.
//
// Deprecated: use KindsForWithContext instead.
func (d *DeferredDiscoveryRESTMapper) KindsFor(resource schema.GroupVersionResource) (gvks []schema.GroupVersionKind, err error) {
return d.KindsForWithContext(context.Background(), resource)
}
// KindsForWithContext takes a partial resource and returns back the list of
// potential kinds in priority order.
func (d *DeferredDiscoveryRESTMapper) KindsForWithContext(ctx context.Context, resource schema.GroupVersionResource) (gvks []schema.GroupVersionKind, err error) {
del, err := d.getDelegate(ctx)
del, err := d.getDelegate()
if err != nil {
return nil, err
}
gvks, err = del.KindsForWithContext(ctx, resource)
if len(gvks) == 0 && !d.cl.FreshWithContext(ctx) {
d.ResetWithContext(ctx)
gvks, err = d.KindsForWithContext(ctx, resource)
gvks, err = del.KindsFor(resource)
if len(gvks) == 0 && !d.cl.Fresh() {
d.Reset()
gvks, err = d.KindsFor(resource)
}
return
}
// ResourceFor takes a partial resource and returns back the single
// match. It returns an error if there are multiple matches.
//
// Deprecated: use ResourceForWithContext instead.
func (d *DeferredDiscoveryRESTMapper) ResourceFor(input schema.GroupVersionResource) (gvr schema.GroupVersionResource, err error) {
return d.ResourceForWithContext(context.Background(), input)
}
// ResourceForWithContext takes a partial resource and returns back the single
// match. It returns an error if there are multiple matches.
func (d *DeferredDiscoveryRESTMapper) ResourceForWithContext(ctx context.Context, input schema.GroupVersionResource) (gvr schema.GroupVersionResource, err error) {
del, err := d.getDelegate(ctx)
del, err := d.getDelegate()
if err != nil {
return schema.GroupVersionResource{}, err
}
gvr, err = del.ResourceForWithContext(ctx, input)
if err != nil && !d.cl.FreshWithContext(ctx) {
d.ResetWithContext(ctx)
gvr, err = d.ResourceForWithContext(ctx, input)
gvr, err = del.ResourceFor(input)
if err != nil && !d.cl.Fresh() {
d.Reset()
gvr, err = d.ResourceFor(input)
}
return
}
// ResourcesFor takes a partial resource and returns back the list of
// potential resource in priority order.
//
// Deprecated: use ResourcesForWithContext instead.
func (d *DeferredDiscoveryRESTMapper) ResourcesFor(input schema.GroupVersionResource) (gvrs []schema.GroupVersionResource, err error) {
return d.ResourcesForWithContext(context.Background(), input)
}
// ResourcesForWithContext takes a partial resource and returns back the list of
// potential resource in priority order.
func (d *DeferredDiscoveryRESTMapper) ResourcesForWithContext(ctx context.Context, input schema.GroupVersionResource) (gvrs []schema.GroupVersionResource, err error) {
del, err := d.getDelegate(ctx)
del, err := d.getDelegate()
if err != nil {
return nil, err
}
gvrs, err = del.ResourcesForWithContext(ctx, input)
if len(gvrs) == 0 && !d.cl.FreshWithContext(ctx) {
d.ResetWithContext(ctx)
gvrs, err = d.ResourcesForWithContext(ctx, input)
gvrs, err = del.ResourcesFor(input)
if len(gvrs) == 0 && !d.cl.Fresh() {
d.Reset()
gvrs, err = d.ResourcesFor(input)
}
return
}
// RESTMapping identifies a preferred resource mapping for the
// provided group kind.
//
// Deprecated: use RESTMappingWithContext instead.
func (d *DeferredDiscoveryRESTMapper) RESTMapping(gk schema.GroupKind, versions ...string) (m *meta.RESTMapping, err error) {
return d.RESTMappingWithContext(context.Background(), gk, versions...)
}
// RESTMappingWithContext identifies a preferred resource mapping for the
// provided group kind.
func (d *DeferredDiscoveryRESTMapper) RESTMappingWithContext(ctx context.Context, gk schema.GroupKind, versions ...string) (m *meta.RESTMapping, err error) {
del, err := d.getDelegate(ctx)
del, err := d.getDelegate()
if err != nil {
return nil, err
}
m, err = del.RESTMappingWithContext(ctx, gk, versions...)
if err != nil && !d.cl.FreshWithContext(ctx) {
d.ResetWithContext(ctx)
m, err = d.RESTMappingWithContext(ctx, gk, versions...)
m, err = del.RESTMapping(gk, versions...)
if err != nil && !d.cl.Fresh() {
d.Reset()
m, err = d.RESTMapping(gk, versions...)
}
return
}
@ -396,55 +298,41 @@ func (d *DeferredDiscoveryRESTMapper) RESTMappingWithContext(ctx context.Context
// RESTMappings returns the RESTMappings for the provided group kind
// in a rough internal preferred order. If no kind is found, it will
// return a NoResourceMatchError.
//
// Deprecated: use RESTMappingsWithContext instead.
func (d *DeferredDiscoveryRESTMapper) RESTMappings(gk schema.GroupKind, versions ...string) (ms []*meta.RESTMapping, err error) {
return d.RESTMappingsWithContext(context.Background(), gk, versions...)
}
// RESTMappingsWithContext returns the RESTMappings for the provided group kind
// in a rough internal preferred order. If no kind is found, it will
// return a NoResourceMatchError.
func (d *DeferredDiscoveryRESTMapper) RESTMappingsWithContext(ctx context.Context, gk schema.GroupKind, versions ...string) (ms []*meta.RESTMapping, err error) {
del, err := d.getDelegate(ctx)
del, err := d.getDelegate()
if err != nil {
return nil, err
}
ms, err = del.RESTMappingsWithContext(ctx, gk, versions...)
if len(ms) == 0 && !d.cl.FreshWithContext(ctx) {
d.ResetWithContext(ctx)
ms, err = d.RESTMappingsWithContext(ctx, gk, versions...)
ms, err = del.RESTMappings(gk, versions...)
if len(ms) == 0 && !d.cl.Fresh() {
d.Reset()
ms, err = d.RESTMappings(gk, versions...)
}
return
}
// ResourceSingularizer converts a resource name from plural to
// singular (e.g., from pods to pod).
//
// Deprecated: use ResourceSingularizerWithContext instead.
func (d *DeferredDiscoveryRESTMapper) ResourceSingularizer(resource string) (singular string, err error) {
return d.ResourceSingularizerWithContext(context.Background(), resource)
}
// ResourceSingularizerWithContext converts a resource name from plural to
// singular (e.g., from pods to pod).
func (d *DeferredDiscoveryRESTMapper) ResourceSingularizerWithContext(ctx context.Context, resource string) (singular string, err error) {
del, err := d.getDelegate(ctx)
del, err := d.getDelegate()
if err != nil {
return resource, err
}
singular, err = del.ResourceSingularizerWithContext(ctx, resource)
if err != nil && !d.cl.FreshWithContext(ctx) {
d.ResetWithContext(ctx)
singular, err = d.ResourceSingularizerWithContext(ctx, resource)
singular, err = del.ResourceSingularizer(resource)
if err != nil && !d.cl.Fresh() {
d.Reset()
singular, err = d.ResourceSingularizer(resource)
}
return
}
func (d *DeferredDiscoveryRESTMapper) String() string {
del, err := d.getDelegate(context.Background() /* hopefully we already have a delegate and don't need the context */)
del, err := d.getDelegate()
if err != nil {
return fmt.Sprintf("DeferredDiscoveryRESTMapper{%v}", err)
}
return fmt.Sprintf("DeferredDiscoveryRESTMapper{\n\t%v\n}", del)
}
// Make sure it satisfies the interface
var _ meta.ResettableRESTMapper = &DeferredDiscoveryRESTMapper{}

View file

@ -17,7 +17,6 @@ limitations under the License.
package restmapper
import (
"context"
"fmt"
"strings"
@ -31,39 +30,22 @@ import (
// shortcutExpander is a RESTMapper that can be used for Kubernetes resources. It expands the resource first, then invokes the wrapped
type shortcutExpander struct {
RESTMapper meta.RESTMapperWithContext
RESTMapper meta.RESTMapper
discoveryClient discovery.DiscoveryInterfaceWithContext
discoveryClient discovery.DiscoveryInterface
warningHandler func(string)
}
var _ meta.ResettableRESTMapper = shortcutExpander{}
var _ meta.ResettableRESTMapperWithContext = shortcutExpander{}
// NewShortcutExpander wraps a restmapper in a layer that expands shortcuts found via discovery
//
// Deprecated: use NewShortcutExpanderWithContext instead.
func NewShortcutExpander(delegate meta.RESTMapper, client discovery.DiscoveryInterface, warningHandler func(string)) meta.RESTMapper {
return newShortcutExpander(meta.ToRESTMapperWithContext(delegate), discovery.ToDiscoveryInterfaceWithContext(client), warningHandler)
}
// NewShortcutExpanderWithContext wraps a restmapper in a layer that expands shortcuts found via discovery
func NewShortcutExpanderWithContext(delegate meta.RESTMapperWithContext, client discovery.DiscoveryInterfaceWithContext, warningHandler func(string)) meta.RESTMapperWithContext {
return newShortcutExpander(delegate, client, warningHandler)
}
func newShortcutExpander(delegate meta.RESTMapperWithContext, client discovery.DiscoveryInterfaceWithContext, warningHandler func(string)) shortcutExpander {
return shortcutExpander{RESTMapper: delegate, discoveryClient: client, warningHandler: warningHandler}
}
// KindFor fulfills meta.RESTMapper
func (e shortcutExpander) KindFor(resource schema.GroupVersionResource) (schema.GroupVersionKind, error) {
return e.KindForWithContext(context.Background(), resource)
}
// KindForWithContext fulfills meta.RESTMapperWithContext
func (e shortcutExpander) KindForWithContext(ctx context.Context, resource schema.GroupVersionResource) (schema.GroupVersionKind, error) {
// expandResourceShortcut works with current API resources as read from discovery cache.
// In case of new CRDs this means we potentially don't have current state of discovery.
// In the current wiring in k8s.io/cli-runtime/pkg/genericclioptions/config_flags.go#toRESTMapper,
@ -71,101 +53,59 @@ func (e shortcutExpander) KindForWithContext(ctx context.Context, resource schem
// cache and fetch all data from a cluster (see k8s.io/client-go/restmapper/discovery.go#KindFor).
// Thus another call to expandResourceShortcut, after a NoMatchError should successfully
// read Kind to the user or an error.
gvk, err := e.RESTMapper.KindForWithContext(ctx, e.expandResourceShortcut(ctx, resource))
gvk, err := e.RESTMapper.KindFor(e.expandResourceShortcut(resource))
if meta.IsNoMatchError(err) {
return e.RESTMapper.KindForWithContext(ctx, e.expandResourceShortcut(ctx, resource))
return e.RESTMapper.KindFor(e.expandResourceShortcut(resource))
}
return gvk, err
}
// KindsFor fulfills meta.RESTMapper
//
// Deprecated: use KindsForWithContext instead.
func (e shortcutExpander) KindsFor(resource schema.GroupVersionResource) ([]schema.GroupVersionKind, error) {
return e.RESTMapper.KindsForWithContext(context.Background(), resource)
}
// KindsForWithContext fulfills meta.RESTMapperWithContext
func (e shortcutExpander) KindsForWithContext(ctx context.Context, resource schema.GroupVersionResource) ([]schema.GroupVersionKind, error) {
return e.RESTMapper.KindsForWithContext(ctx, e.expandResourceShortcut(ctx, resource))
return e.RESTMapper.KindsFor(e.expandResourceShortcut(resource))
}
// ResourcesFor fulfills meta.RESTMapper
//
// Deprecated: use ResourcesForWithContext instead.
func (e shortcutExpander) ResourcesFor(resource schema.GroupVersionResource) ([]schema.GroupVersionResource, error) {
return e.ResourcesForWithContext(context.Background(), resource)
}
// ResourcesForWithContext fulfills meta.RESTMapperWithContext
func (e shortcutExpander) ResourcesForWithContext(ctx context.Context, resource schema.GroupVersionResource) ([]schema.GroupVersionResource, error) {
return e.RESTMapper.ResourcesForWithContext(ctx, e.expandResourceShortcut(ctx, resource))
return e.RESTMapper.ResourcesFor(e.expandResourceShortcut(resource))
}
// ResourceFor fulfills meta.RESTMapper
//
// Deprecated: use ResourceForWithContext instead.
func (e shortcutExpander) ResourceFor(resource schema.GroupVersionResource) (schema.GroupVersionResource, error) {
return e.ResourceForWithContext(context.Background(), resource)
}
// ResourceForWithContext fulfills meta.RESTMapperWithContext
func (e shortcutExpander) ResourceForWithContext(ctx context.Context, resource schema.GroupVersionResource) (schema.GroupVersionResource, error) {
return e.RESTMapper.ResourceForWithContext(ctx, e.expandResourceShortcut(ctx, resource))
return e.RESTMapper.ResourceFor(e.expandResourceShortcut(resource))
}
// ResourceSingularizer fulfills meta.RESTMapper
//
// Deprecated: use ResourceSingularizerWithContext instead.
func (e shortcutExpander) ResourceSingularizer(resource string) (string, error) {
return e.ResourceSingularizerWithContext(context.Background(), resource)
}
// ResourceSingularizerWithContext fulfills meta.RESTMapperWithContext
func (e shortcutExpander) ResourceSingularizerWithContext(ctx context.Context, resource string) (string, error) {
return e.RESTMapper.ResourceSingularizerWithContext(ctx, e.expandResourceShortcut(ctx, schema.GroupVersionResource{Resource: resource}).Resource)
return e.RESTMapper.ResourceSingularizer(e.expandResourceShortcut(schema.GroupVersionResource{Resource: resource}).Resource)
}
// RESTMapping fulfills meta.RESTMapper
//
// Deprecated: use RESTMappingWithContext instead.
func (e shortcutExpander) RESTMapping(gk schema.GroupKind, versions ...string) (*meta.RESTMapping, error) {
return e.RESTMappingWithContext(context.Background(), gk, versions...)
}
// RESTMappingWithContext fulfills meta.RESTMapperWithContext
func (e shortcutExpander) RESTMappingWithContext(ctx context.Context, gk schema.GroupKind, versions ...string) (*meta.RESTMapping, error) {
return e.RESTMapper.RESTMappingWithContext(ctx, gk, versions...)
return e.RESTMapper.RESTMapping(gk, versions...)
}
// RESTMappings fulfills meta.RESTMapper
//
// Deprecated: use KindForWithContext instead.
func (e shortcutExpander) RESTMappings(gk schema.GroupKind, versions ...string) ([]*meta.RESTMapping, error) {
return e.RESTMappingsWithContext(context.Background(), gk, versions...)
}
// RESTMappingsWithContext fulfills meta.RESTMapperWithContext
func (e shortcutExpander) RESTMappingsWithContext(ctx context.Context, gk schema.GroupKind, versions ...string) ([]*meta.RESTMapping, error) {
return e.RESTMapper.RESTMappingsWithContext(ctx, gk, versions...)
return e.RESTMapper.RESTMappings(gk, versions...)
}
// getShortcutMappings returns a set of tuples which holds short names for resources.
// First the list of potential resources will be taken from the API server.
// Next we will append the hardcoded list of resources - to be backward compatible with old servers.
// NOTE that the list is ordered by group priority.
func (e shortcutExpander) getShortcutMappings(ctx context.Context) ([]*metav1.APIResourceList, []resourceShortcuts, error) {
func (e shortcutExpander) getShortcutMappings() ([]*metav1.APIResourceList, []resourceShortcuts, error) {
res := []resourceShortcuts{}
// get server resources
// This can return an error *and* the results it was able to find. We don't need to fail on the error.
_, apiResList, err := e.discoveryClient.ServerGroupsAndResourcesWithContext(ctx)
_, apiResList, err := e.discoveryClient.ServerGroupsAndResources()
if err != nil {
klog.FromContext(ctx).V(1).Info("Error loading discovery information", "err", err)
klog.V(1).Infof("Error loading discovery information: %v", err)
}
for _, apiResources := range apiResList {
gv, err := schema.ParseGroupVersion(apiResources.GroupVersion)
if err != nil {
klog.FromContext(ctx).V(1).Info("Unable to parse group/version", "groupVersion", apiResources.GroupVersion, "err", err.Error())
klog.V(1).Infof("Unable to parse groupversion = %s due to = %s", apiResources.GroupVersion, err.Error())
continue
}
for _, apiRes := range apiResources.APIResources {
@ -186,9 +126,9 @@ func (e shortcutExpander) getShortcutMappings(ctx context.Context) ([]*metav1.AP
// (something that a pkg/api/meta.RESTMapper can understand), if it is
// indeed a shortcut. If no match has been found, we will match on group prefixing.
// Lastly we will return resource unmodified.
func (e shortcutExpander) expandResourceShortcut(ctx context.Context, resource schema.GroupVersionResource) schema.GroupVersionResource {
func (e shortcutExpander) expandResourceShortcut(resource schema.GroupVersionResource) schema.GroupVersionResource {
// get the shortcut mappings and return on first match.
if allResources, shortcutResources, err := e.getShortcutMappings(ctx); err == nil {
if allResources, shortcutResources, err := e.getShortcutMappings(); err == nil {
// avoid expanding if there's an exact match to a full resource name
for _, apiResources := range allResources {
gv, err := schema.ParseGroupVersion(apiResources.GroupVersion)
@ -259,13 +199,8 @@ func (e shortcutExpander) expandResourceShortcut(ctx context.Context, resource s
return resource
}
// Deprecated: use ResetWithContext instead.
func (e shortcutExpander) Reset() {
e.ResetWithContext(context.Background())
}
func (e shortcutExpander) ResetWithContext(ctx context.Context) {
meta.MaybeResetRESTMapperWithContext(ctx, e.RESTMapper)
meta.MaybeResetRESTMapper(e.RESTMapper)
}
// ResourceShortcuts represents a structure that holds the information how to

View file

@ -31,12 +31,9 @@ import (
"k8s.io/client-go/openapi"
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/rest/fake"
"k8s.io/klog/v2/ktesting"
)
func TestReplaceAliases(t *testing.T) {
_, ctx := ktesting.NewTestContext(t)
tests := []struct {
name string
arg string
@ -138,7 +135,7 @@ func TestReplaceAliases(t *testing.T) {
}
mapper := NewShortcutExpander(&fakeRESTMapper{}, ds, nil).(shortcutExpander)
actual := mapper.expandResourceShortcut(ctx, schema.GroupVersionResource{Resource: test.arg})
actual := mapper.expandResourceShortcut(schema.GroupVersionResource{Resource: test.arg})
if actual != test.expected {
t.Errorf("%s: unexpected argument: expected %s, got %s", test.name, test.expected, actual)
}
@ -263,8 +260,6 @@ func TestKindForWithNewCRDs(t *testing.T) {
}
func TestWarnAmbigious(t *testing.T) {
_, ctx := ktesting.NewTestContext(t)
tests := []struct {
name string
arg string
@ -444,7 +439,7 @@ func TestWarnAmbigious(t *testing.T) {
actualWarnings = append(actualWarnings, a)
}).(shortcutExpander)
actual := mapper.expandResourceShortcut(ctx, schema.GroupVersionResource{Resource: test.arg})
actual := mapper.expandResourceShortcut(schema.GroupVersionResource{Resource: test.arg})
if actual != test.expected {
t.Errorf("%s: unexpected argument: expected %s, got %s", test.name, test.expected, actual)
}

View file

@ -90,7 +90,7 @@ func ToWatcherWithContext(w Watcher) WatcherWithContext {
if w, ok := w.(WatcherWithContext); ok {
return w
}
return &watcherWrapper{
return watcherWrapper{
parent: w,
}
}
@ -99,7 +99,7 @@ type watcherWrapper struct {
parent Watcher
}
func (l *watcherWrapper) WatchWithContext(ctx context.Context, options metav1.ListOptions) (watch.Interface, error) {
func (l watcherWrapper) WatchWithContext(ctx context.Context, options metav1.ListOptions) (watch.Interface, error) {
return l.parent.Watch(options)
}
@ -121,7 +121,7 @@ func ToListerWatcherWithContext(lw ListerWatcher) ListerWatcherWithContext {
if lw, ok := lw.(ListerWatcherWithContext); ok {
return lw
}
return &listerWatcherWrapper{
return listerWatcherWrapper{
ListerWithContext: ToListerWithContext(lw),
WatcherWithContext: ToWatcherWithContext(lw),
}

View file

@ -679,13 +679,11 @@ func (config *inClusterClientConfig) Possible() bool {
// to the default config.
func BuildConfigFromFlags(masterUrl, kubeconfigPath string) (*restclient.Config, error) {
if kubeconfigPath == "" && masterUrl == "" {
//nolint:logcheck // A helper function like this should not log. But this is probably part of the the established client-go API and not worth changing.
klog.Warning("Neither --kubeconfig nor --master was specified. Using the inClusterConfig. This might not work.")
kubeconfig, err := restclient.InClusterConfig()
if err == nil {
return kubeconfig, nil
}
//nolint:logcheck // A helper function like this should not log. But this is probably part of the the established client-go API and not worth changing.
klog.Warning("error creating inClusterConfig, falling back to default config: ", err)
}
return NewNonInteractiveDeferredLoadingClientConfig(

View file

@ -492,7 +492,6 @@ func getConfigFromFile(filename string) (*clientcmdapi.Config, error) {
func GetConfigFromFileOrDie(filename string) *clientcmdapi.Config {
config, err := getConfigFromFile(filename)
if err != nil {
//nolint:logcheck // A helper function like this should not log. But this is probably part of the the established client-go API and not worth changing.
klog.FatalDepth(1, err)
}

View file

@ -137,7 +137,6 @@ type WarningHandler func(error)
func (handler WarningHandler) Warn(err error) {
if handler == nil {
//nolint:logcheck // This is the fallback when logging is not initialized. With nothing provided, using the global logger is the only option.
klog.V(1).Info(err)
} else {
handler(err)
@ -403,7 +402,6 @@ func LoadFromFile(filename string) (*clientcmdapi.Config, error) {
if err != nil {
return nil, err
}
//nolint:logcheck // A helper function like this should not log. But this is probably part of the the established client-go API and not worth changing.
klog.V(6).Infoln("Config loaded from file: ", filename)
// set LocationOfOrigin on every Cluster, User, and Context

View file

@ -122,7 +122,6 @@ func TestNonExistentCommandLineFile(t *testing.T) {
}
}
//nolint:logcheck // Tests klog APIs.
func TestToleratingMissingFiles(t *testing.T) {
envVarValue := "bogus"
loadingRules := ClientConfigLoadingRules{
@ -147,7 +146,6 @@ func TestToleratingMissingFiles(t *testing.T) {
}
}
//nolint:logcheck // Tests klog APIs.
func TestWarningMissingFiles(t *testing.T) {
envVarValue := "bogus"
t.Setenv(RecommendedConfigPathEnvVar, envVarValue)
@ -173,7 +171,6 @@ func TestWarningMissingFiles(t *testing.T) {
}
}
//nolint:logcheck // Tests klog APIs.
func TestNoWarningMissingFiles(t *testing.T) {
envVarValue := "bogus"
t.Setenv(RecommendedConfigPathEnvVar, envVarValue)

View file

@ -118,7 +118,6 @@ func (config *DeferredLoadingClientConfig) ClientConfig() (*restclient.Config, e
// check for in-cluster configuration and use it
if config.icc.Possible() {
//nolint:logcheck // A helper function like this should not log. But this is probably part of the the established client-go API and not worth changing.
klog.V(4).Infof("Using in-cluster configuration")
return config.icc.ClientConfig()
}
@ -161,7 +160,6 @@ func (config *DeferredLoadingClientConfig) Namespace() (string, bool, error) {
}
}
//nolint:logcheck // A helper function like this should not log. But this is probably part of the the established client-go API and not worth changing.
klog.V(4).Infof("Using in-cluster namespace")
// allow the namespace from the service account token directory to be used.

View file

@ -50,7 +50,6 @@ func NewFallbackDialer(primary, secondary httpstream.Dialer, shouldFallback func
func (f *FallbackDialer) Dial(protocols ...string) (httpstream.Connection, string, error) {
conn, version, err := f.primary.Dial(protocols...)
if err != nil && f.shouldFallback(err) {
//nolint:logcheck // This code is only used by kubectl where contextual logging is not that useful.
klog.V(4).Infof("fallback to secondary dialer from primary dialer err: %v", err)
return f.secondary.Dial(protocols...)
}

View file

@ -40,7 +40,7 @@ func TestFallbackDialer(t *testing.T) {
assert.Equal(t, primaryProtocol, negotiated, "primary negotiated protocol returned")
require.NoError(t, err, "error from primary dialer should be nil")
// If primary dialer error is upgrade error, then fallback returning secondary dial response.
primary = &fakeDialer{dialed: false, negotiatedProtocol: primaryProtocol, err: &httpstream.UpgradeFailureError{Cause: fmt.Errorf("fake error")}}
primary = &fakeDialer{dialed: false, negotiatedProtocol: primaryProtocol, err: &httpstream.UpgradeFailureError{}}
secondary = &fakeDialer{dialed: false, negotiatedProtocol: secondaryProtocol}
fallbackDialer = NewFallbackDialer(primary, secondary, httpstream.IsUpgradeFailure)
_, negotiated, err = fallbackDialer.Dial(protocols...)

View file

@ -17,7 +17,6 @@ limitations under the License.
package portforward
import (
"context"
"errors"
"fmt"
"io"
@ -31,8 +30,6 @@ import (
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/httpstream"
"k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/klog/v2"
netutils "k8s.io/utils/net"
)
@ -55,7 +52,6 @@ type PortForwarder struct {
ports []ForwardedPort
stopChan <-chan struct{}
logger klog.Logger
dialer httpstream.Dialer
streamConn httpstream.Connection
listeners []io.Closer
@ -169,14 +165,7 @@ func New(dialer httpstream.Dialer, ports []string, stopChan <-chan struct{}, rea
}
// NewOnAddresses creates a new PortForwarder with custom listen addresses.
//
//logcheck:context // NewOnAddressesWithContext should be used instead of NewOnAddresses in code which supports contextual logging.
func NewOnAddresses(dialer httpstream.Dialer, addresses []string, ports []string, stopChan <-chan struct{}, readyChan chan struct{}, out, errOut io.Writer) (*PortForwarder, error) {
return NewOnAddressesWithContext(wait.ContextForChannel(stopChan), dialer, addresses, ports, readyChan, out, errOut)
}
// NewOnAddressesWithContext creates a new PortForwarder with custom listen addresses.
func NewOnAddressesWithContext(ctx context.Context, dialer httpstream.Dialer, addresses []string, ports []string, readyChan chan struct{}, out, errOut io.Writer) (*PortForwarder, error) {
if len(addresses) == 0 {
return nil, errors.New("you must specify at least 1 address")
}
@ -192,11 +181,10 @@ func NewOnAddressesWithContext(ctx context.Context, dialer httpstream.Dialer, ad
return nil, err
}
return &PortForwarder{
logger: klog.FromContext(ctx),
dialer: dialer,
addresses: parsedAddresses,
ports: parsedPorts,
stopChan: ctx.Done(),
stopChan: stopChan,
Ready: readyChan,
out: out,
errOut: errOut,
@ -331,7 +319,7 @@ func (pf *PortForwarder) waitForConnection(listener net.Listener, port Forwarded
if err != nil {
// TODO consider using something like https://github.com/hydrogen18/stoppableListener?
if !strings.Contains(strings.ToLower(err.Error()), networkClosedError) {
runtime.HandleErrorWithLogger(pf.logger, err, "Error accepting connection", "localPort", port.Local)
runtime.HandleError(fmt.Errorf("error accepting connection on port %d: %v", port.Local, err))
}
return
}
@ -366,23 +354,21 @@ func (pf *PortForwarder) handleConnection(conn net.Conn, port ForwardedPort) {
headers.Set(v1.PortForwardRequestIDHeader, strconv.Itoa(requestID))
errorStream, err := pf.streamConn.CreateStream(headers)
if err != nil {
runtime.HandleErrorWithLogger(pf.logger, err, "Error creating error stream", "localPort", port.Local, "remotePort", port.Remote)
runtime.HandleError(fmt.Errorf("error creating error stream for port %d -> %d: %v", port.Local, port.Remote, err))
return
}
// we're not writing to this stream
errorStream.Close()
defer pf.streamConn.RemoveStreams(errorStream)
type readAllResult struct {
message []byte
err error
}
errorChan := make(chan readAllResult)
errorChan := make(chan error)
go func() {
message, err := io.ReadAll(errorStream)
errorChan <- readAllResult{
message: message,
err: err,
switch {
case err != nil:
errorChan <- fmt.Errorf("error reading from error stream for port %d -> %d: %v", port.Local, port.Remote, err)
case len(message) > 0:
errorChan <- fmt.Errorf("an error occurred forwarding %d -> %d: %v", port.Local, port.Remote, string(message))
}
close(errorChan)
}()
@ -391,7 +377,7 @@ func (pf *PortForwarder) handleConnection(conn net.Conn, port ForwardedPort) {
headers.Set(v1.StreamType, v1.StreamTypeData)
dataStream, err := pf.streamConn.CreateStream(headers)
if err != nil {
runtime.HandleErrorWithLogger(pf.logger, err, "Error creating forwarding stream", "localPort", port.Local, "remotePort", port.Remote)
runtime.HandleError(fmt.Errorf("error creating forwarding stream for port %d -> %d: %v", port.Local, port.Remote, err))
return
}
defer pf.streamConn.RemoveStreams(dataStream)
@ -402,7 +388,7 @@ func (pf *PortForwarder) handleConnection(conn net.Conn, port ForwardedPort) {
go func() {
// Copy from the remote side to the local port.
if _, err := io.Copy(conn, dataStream); err != nil && !strings.Contains(strings.ToLower(err.Error()), networkClosedError) {
runtime.HandleErrorWithLogger(pf.logger, err, "Error copying from remote stream to local connection", "localPort", port.Local, "remotePort", port.Remote)
runtime.HandleError(fmt.Errorf("error copying from remote stream to local connection: %v", err))
}
// inform the select below that the remote copy is done
@ -415,7 +401,7 @@ func (pf *PortForwarder) handleConnection(conn net.Conn, port ForwardedPort) {
// Copy from the local port to the remote side.
if _, err := io.Copy(dataStream, conn); err != nil && !strings.Contains(strings.ToLower(err.Error()), networkClosedError) {
runtime.HandleErrorWithLogger(pf.logger, err, "Error copying from local connection to remote stream", "localPort", port.Local, "remotePort", port.Remote)
runtime.HandleError(fmt.Errorf("error copying from local connection to remote stream: %v", err))
// break out of the select below without waiting for the other copy to finish
close(localError)
}
@ -432,14 +418,10 @@ func (pf *PortForwarder) handleConnection(conn net.Conn, port ForwardedPort) {
// the blocking data will affect errorStream and cause <-errorChan to block indefinitely.
_ = dataStream.Reset()
// always expect something on errorChan (it may be empty)
errResult := <-errorChan
switch {
case errResult.err != nil:
runtime.HandleErrorWithLogger(pf.logger, errResult.err, "Error reading from error stream", "localPort", port.Local, "remotePort", port.Remote)
pf.streamConn.Close()
case len(errResult.message) > 0:
runtime.HandleErrorWithLogger(pf.logger, errors.New(string(errResult.message)), "An error occurred forwarding", "localPort", port.Local, "remotePort", port.Remote)
// always expect something on errorChan (it may be nil)
err = <-errorChan
if err != nil {
runtime.HandleError(err)
pf.streamConn.Close()
}
}
@ -449,7 +431,7 @@ func (pf *PortForwarder) Close() {
// stop all listeners
for _, l := range pf.listeners {
if err := l.Close(); err != nil {
runtime.HandleErrorWithLogger(pf.logger, err, "Error closing listener")
runtime.HandleError(fmt.Errorf("error closing listener: %v", err))
}
}
}

View file

@ -299,7 +299,6 @@ func TestParsePortsAndNew(t *testing.T) {
var pf *PortForwarder
if len(test.addresses) > 0 {
//nolint:logcheck // Testing the original function.
pf, err = NewOnAddresses(dialer, test.addresses, test.input, expectedStopChan, readyChan, os.Stdout, os.Stderr)
} else {
pf, err = New(dialer, test.input, expectedStopChan, readyChan, os.Stdout, os.Stderr)

View file

@ -34,7 +34,7 @@ var _ net.Conn = &TunnelingConnection{}
// TunnelingConnection implements the "httpstream.Connection" interface, wrapping
// a websocket connection that tunnels SPDY.
type TunnelingConnection struct {
logger klog.Logger
name string
conn *gwebsocket.Conn
inProgressMessage io.Reader
closeOnce sync.Once
@ -42,46 +42,29 @@ type TunnelingConnection struct {
// NewTunnelingConnection wraps the passed gorilla/websockets connection
// with the TunnelingConnection struct (implementing net.Conn).
// The name is added to all log entries with [klog.LoggerWithName].
//
//logcheck:context // NewTunnelingConnectionWithLogger should be used instead of NewTunnelingConnection in code which supports contextual logging.
func NewTunnelingConnection(name string, conn *gwebsocket.Conn) *TunnelingConnection {
logger := klog.LoggerWithName(klog.Background(), name)
return NewTunnelingConnectionWithLogger(logger, conn)
}
// NewTunnelingConnectionWithLogger is a variant of NewTunnelingConnection where
// the caller is in control of logging. For example, [klog.LoggerWithName] can be used
// to add a common name for all log entries to identify the connection.
func NewTunnelingConnectionWithLogger(logger klog.Logger, conn *gwebsocket.Conn) *TunnelingConnection {
return &TunnelingConnection{
logger: logger,
conn: conn,
name: name,
conn: conn,
}
}
// Read implements "io.Reader" interface, reading from the stored connection
// into the passed buffer "p". Returns the number of bytes read and an error.
// Can keep track of the "inProgress" messsage from the tunneled connection.
func (c *TunnelingConnection) Read(p []byte) (len int, err error) {
c.logger.V(7).Info("Tunneling connection read...")
defer func() {
if loggerV := c.logger.V(8); loggerV.Enabled() {
loggerV.Info("Tunneling connection read...complete", "length", len, "data", p[:len], "err", err)
} else {
c.logger.V(7).Info("Tunneling connection read...complete")
}
}()
func (c *TunnelingConnection) Read(p []byte) (int, error) {
klog.V(7).Infof("%s: tunneling connection read...", c.name)
defer klog.V(7).Infof("%s: tunneling connection read...complete", c.name)
for {
if c.inProgressMessage == nil {
c.logger.V(8).Info("Tunneling connection read before NextReader()...")
klog.V(8).Infof("%s: tunneling connection read before NextReader()...", c.name)
messageType, nextReader, err := c.conn.NextReader()
if err != nil {
closeError := &gwebsocket.CloseError{}
if errors.As(err, &closeError) && closeError.Code == gwebsocket.CloseNormalClosure {
return 0, io.EOF
}
c.logger.V(4).Info("Tunneling connection NextReader() failed", "err", err)
klog.V(4).Infof("%s:tunneling connection NextReader() error: %v", c.name, err)
return 0, err
}
if messageType != gwebsocket.BinaryMessage {
@ -89,11 +72,12 @@ func (c *TunnelingConnection) Read(p []byte) (len int, err error) {
}
c.inProgressMessage = nextReader
}
c.logger.V(8).Info("Tunneling connection read in progress...")
klog.V(8).Infof("%s: tunneling connection read in progress message...", c.name)
i, err := c.inProgressMessage.Read(p)
if i == 0 && err == io.EOF {
c.inProgressMessage = nil
} else {
klog.V(8).Infof("%s: read %d bytes, error=%v, bytes=% X", c.name, i, err, p[:i])
return i, err
}
}
@ -103,8 +87,8 @@ func (c *TunnelingConnection) Read(p []byte) (len int, err error) {
// byte array "p" into the stored tunneled connection. Returns the number
// of bytes written and an error.
func (c *TunnelingConnection) Write(p []byte) (n int, err error) {
c.logger.V(7).Info("Tunneling connection write", "length", len(p), "data", p)
defer c.logger.V(7).Info("Tunneling connection write...complete")
klog.V(7).Infof("%s: write: %d bytes, bytes=% X", c.name, len(p), p)
defer klog.V(7).Infof("%s: tunneling connection write...complete", c.name)
w, err := c.conn.NextWriter(gwebsocket.BinaryMessage)
if err != nil {
return 0, err
@ -127,7 +111,7 @@ func (c *TunnelingConnection) Write(p []byte) (n int, err error) {
func (c *TunnelingConnection) Close() error {
var err error
c.closeOnce.Do(func() {
c.logger.V(7).Info("Tunneling connection Close()...")
klog.V(7).Infof("%s: tunneling connection Close()...", c.name)
// Signal other endpoint that websocket connection is closing; ignore error.
normalCloseMsg := gwebsocket.FormatCloseMessage(gwebsocket.CloseNormalClosure, "")
writeControlErr := c.conn.WriteControl(gwebsocket.CloseMessage, normalCloseMsg, time.Now().Add(time.Second))

View file

@ -36,13 +36,8 @@ import (
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/rest"
"k8s.io/client-go/transport/websocket"
"k8s.io/klog/v2"
)
func init() {
klog.InitFlags(nil)
}
func TestTunnelingConnection_ReadWriteClose(t *testing.T) {
// Stream channel that will receive streams created on upstream SPDY server.
streamChan := make(chan httpstream.Stream)
@ -65,7 +60,7 @@ func TestTunnelingConnection_ReadWriteClose(t *testing.T) {
t.Errorf("Not acceptable agreement Subprotocol: %v", conn.Subprotocol())
return
}
tunnelingConn := NewTunnelingConnectionWithLogger(klog.LoggerWithName(klog.Background(), "server"), conn)
tunnelingConn := NewTunnelingConnection("server", conn)
spdyConn, err := spdy.NewServerConnection(tunnelingConn, justQueueStream(streamChan))
if err != nil {
t.Errorf("unexpected error %v", err)
@ -78,7 +73,6 @@ func TestTunnelingConnection_ReadWriteClose(t *testing.T) {
// Dial the client tunneling connection to the tunneling server.
url, err := url.Parse(tunnelingServer.URL)
require.NoError(t, err)
//nolint:logcheck // Intentionally uses the old API.
dialer, err := NewSPDYOverWebsocketDialer(url, &rest.Config{Host: url.Host})
require.NoError(t, err)
spdyClient, protocol, err := dialer.Dial(constants.PortForwardV1Name)
@ -211,7 +205,6 @@ func dialForTunnelingConnection(url *url.URL) (*TunnelingConnection, error) {
if err != nil {
return nil, err
}
//nolint:logcheck // Intentionally uses the old API.
return NewTunnelingConnection("client", conn), nil
}

View file

@ -17,7 +17,6 @@ limitations under the License.
package portforward
import (
"context"
"fmt"
"net/http"
"net/url"
@ -36,7 +35,6 @@ const PingPeriod = 10 * time.Second
// tunnelingDialer implements "httpstream.Dial" interface
type tunnelingDialer struct {
logger klog.Logger
url *url.URL
transport http.RoundTripper
holder websocket.ConnectionHolder
@ -45,22 +43,12 @@ type tunnelingDialer struct {
// NewTunnelingDialer creates and returns the tunnelingDialer structure which implemements the "httpstream.Dialer"
// interface. The dialer can upgrade a websocket request, creating a websocket connection. This function
// returns an error if one occurs.
//
//logcheck:context // NewSPDYOverWebsocketDialerWithLogger should be used instead of NewSPDYOverWebsocketDialer in code which supports contextual logging.
func NewSPDYOverWebsocketDialer(url *url.URL, config *restclient.Config) (httpstream.Dialer, error) {
return NewSPDYOverWebsocketDialerWithLogger(klog.Background(), url, config)
}
// NewTunnelingDialer creates and returns the tunnelingDialer structure which implemements the "httpstream.Dialer"
// interface. The dialer can upgrade a websocket request, creating a websocket connection. This function
// returns an error if one occurs.
func NewSPDYOverWebsocketDialerWithLogger(logger klog.Logger, url *url.URL, config *restclient.Config) (httpstream.Dialer, error) {
transport, holder, err := websocket.RoundTripperFor(config)
if err != nil {
return nil, err
}
return &tunnelingDialer{
logger: logger,
url: url,
transport: transport,
holder: holder,
@ -71,10 +59,9 @@ func NewSPDYOverWebsocketDialerWithLogger(logger klog.Logger, url *url.URL, conf
// containing a WebSockets connection (which implements "net.Conn"). Also
// returns the protocol negotiated, or an error.
func (d *tunnelingDialer) Dial(protocols ...string) (httpstream.Connection, string, error) {
// There is no passed context, so use the background context when creating request for now.
ctx := klog.NewContext(context.Background(), d.logger)
// There is no passed context, so skip the context when creating request for now.
// Websockets requires "GET" method: RFC 6455 Sec. 4.1 (page 17).
req, err := http.NewRequestWithContext(ctx, "GET", d.url.String(), nil)
req, err := http.NewRequest("GET", d.url.String(), nil)
if err != nil {
return nil, "", err
}
@ -85,7 +72,7 @@ func (d *tunnelingDialer) Dial(protocols ...string) (httpstream.Connection, stri
tunnelingProtocol := constants.WebsocketsSPDYTunnelingPrefix + protocol
tunnelingProtocols = append(tunnelingProtocols, tunnelingProtocol)
}
d.logger.V(4).Info("Before WebSocket Upgrade Connection...")
klog.V(4).Infoln("Before WebSocket Upgrade Connection...")
conn, err := websocket.Negotiate(d.transport, d.holder, req, tunnelingProtocols...)
if err != nil {
return nil, "", err
@ -95,10 +82,10 @@ func (d *tunnelingDialer) Dial(protocols ...string) (httpstream.Connection, stri
}
protocol := conn.Subprotocol()
protocol = strings.TrimPrefix(protocol, constants.WebsocketsSPDYTunnelingPrefix)
d.logger.V(4).Info("Negotiation complete", "protocol", protocol)
klog.V(4).Infof("negotiated protocol: %s", protocol)
// Wrap the websocket connection which implements "net.Conn".
tConn := NewTunnelingConnectionWithLogger(klog.LoggerWithName(d.logger, "client"), conn)
tConn := NewTunnelingConnection("client", conn)
// Create SPDY connection injecting the previously created tunneling connection.
spdyConn, err := spdy.NewClientConnectionWithPings(tConn, PingPeriod)

View file

@ -21,7 +21,6 @@ import (
"io"
"k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/klog/v2"
)
// errorStreamDecoder interprets the data on the error channel and creates a go error object from it.
@ -33,11 +32,11 @@ type errorStreamDecoder interface {
// decodes it with the given errorStreamDecoder, sends the decoded error (or nil if the remote
// command exited successfully) to the returned error channel, and closes it.
// This function returns immediately.
func watchErrorStream(logger klog.Logger, errorStream io.Reader, d errorStreamDecoder) chan error {
func watchErrorStream(errorStream io.Reader, d errorStreamDecoder) chan error {
errorChan := make(chan error)
go func() {
defer runtime.HandleCrashWithLogger(logger)
defer runtime.HandleCrash()
message, err := io.ReadAll(errorStream)
switch {

View file

@ -53,7 +53,7 @@ func (f *FallbackExecutor) Stream(options StreamOptions) error {
func (f *FallbackExecutor) StreamWithContext(ctx context.Context, options StreamOptions) error {
err := f.primary.StreamWithContext(ctx, options)
if err != nil && f.shouldFallback(err) {
klog.FromContext(ctx).V(4).Info("RemoteCommand fallback", "err", err)
klog.V(4).Infof("RemoteCommand fallback: %v", err)
return f.secondary.StreamWithContext(ctx, options)
}
return err

View file

@ -22,7 +22,6 @@ import (
"net/http"
"k8s.io/apimachinery/pkg/util/httpstream"
"k8s.io/klog/v2"
)
// StreamOptions holds information pertaining to the current streaming session:
@ -55,5 +54,5 @@ type streamCreator interface {
}
type streamProtocolHandler interface {
stream(logger klog.Logger, conn streamCreator, ready chan<- struct{}) error
stream(conn streamCreator, ready chan<- struct{}) error
}

View file

@ -121,7 +121,6 @@ func (e *spdyStreamExecutor) newConnectionAndStream(ctx context.Context, options
var streamer streamProtocolHandler
logger := klog.FromContext(ctx)
switch protocol {
case remotecommand.StreamProtocolV5Name:
streamer = newStreamProtocolV5(options)
@ -132,7 +131,7 @@ func (e *spdyStreamExecutor) newConnectionAndStream(ctx context.Context, options
case remotecommand.StreamProtocolV2Name:
streamer = newStreamProtocolV2(options)
case "":
logger.V(4).Info("The server did not negotiate a streaming protocol version, falling back", "protocol", remotecommand.StreamProtocolV1Name)
klog.V(4).Infof("The server did not negotiate a streaming protocol version. Falling back to %s", remotecommand.StreamProtocolV1Name)
fallthrough
case remotecommand.StreamProtocolV1Name:
streamer = newStreamProtocolV1(options)
@ -162,7 +161,7 @@ func (e *spdyStreamExecutor) StreamWithContext(ctx context.Context, options Stre
// The SPDY executor does not need to synchronize stream creation, so we pass a nil
// ready channel. The underlying spdystream library handles stream multiplexing
// without a race condition.
errorChan <- streamer.stream(klog.FromContext(ctx), conn, nil)
errorChan <- streamer.stream(conn, nil)
}()
select {

View file

@ -38,7 +38,6 @@ import (
remotecommandconsts "k8s.io/apimachinery/pkg/util/remotecommand"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/rest"
"k8s.io/klog/v2/ktesting"
)
type AttachFunc func(in io.Reader, out, err io.WriteCloser, tty bool, resize <-chan TerminalSize) error
@ -329,7 +328,6 @@ func (w *writeDetector) Write(p []byte) (n int, err error) {
// and expects the deferred close of the connection leads to the exit of the goroutine on cancellation.
// This test verifies that works.
func TestStreamExitsAfterConnectionIsClosed(t *testing.T) {
logger, _ := ktesting.NewTestContext(t)
writeDetector := newWriterDetector(&fakeEmptyDataPty{})
options := StreamOptions{
Stdin: &fakeEmptyDataPty{},
@ -354,7 +352,7 @@ func TestStreamExitsAfterConnectionIsClosed(t *testing.T) {
errorChan := make(chan error)
go func() {
errorChan <- streamer.stream(logger, conn, nil)
errorChan <- streamer.stream(conn, nil)
}()
// Wait until stream goroutine starts.

View file

@ -21,7 +21,7 @@ import (
"io"
"net/http"
v1 "k8s.io/api/core/v1"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/httpstream"
"k8s.io/klog/v2"
)
@ -31,7 +31,6 @@ import (
// non-interactive stdin data has ended. See https://issues.k8s.io/13394 and
// https://issues.k8s.io/13395 for more details.
type streamProtocolV1 struct {
logger klog.Logger
StreamOptions
errorStream httpstream.Stream
@ -48,15 +47,15 @@ func newStreamProtocolV1(options StreamOptions) streamProtocolHandler {
}
}
func (p *streamProtocolV1) stream(logger klog.Logger, conn streamCreator, ready chan<- struct{}) error {
func (p *streamProtocolV1) stream(conn streamCreator, ready chan<- struct{}) error {
doneChan := make(chan struct{}, 2)
errorChan := make(chan error)
cp := func(s string, dst io.Writer, src io.Reader) {
p.logger.V(6).Info("Copying", "data", s)
defer p.logger.V(6).Info("Done copying", "data", s)
klog.V(6).Infof("Copying %s", s)
defer klog.V(6).Infof("Done copying %s", s)
if _, err := io.Copy(dst, src); err != nil && err != io.EOF {
p.logger.Error(err, "Error copying", "data", s)
klog.Errorf("Error copying %s: %v", s, err)
}
if s == v1.StreamTypeStdout || s == v1.StreamTypeStderr {
doneChan <- struct{}{}

View file

@ -22,9 +22,8 @@ import (
"net/http"
"sync"
v1 "k8s.io/api/core/v1"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/klog/v2"
)
// streamProtocolV2 implements version 2 of the streaming protocol for attach
@ -88,13 +87,13 @@ func (p *streamProtocolV2) createStreams(conn streamCreator) error {
return nil
}
func (p *streamProtocolV2) copyStdin(logger klog.Logger) {
func (p *streamProtocolV2) copyStdin() {
if p.Stdin != nil {
var once sync.Once
// copy from client's stdin to container's stdin
go func() {
defer runtime.HandleCrashWithLogger(logger)
defer runtime.HandleCrash()
// if p.stdin is noninteractive, p.g. `echo abc | kubectl exec -i <pod> -- cat`, make sure
// we close remoteStdin as soon as the copy from p.stdin to remoteStdin finishes. Otherwise
@ -102,7 +101,7 @@ func (p *streamProtocolV2) copyStdin(logger klog.Logger) {
defer once.Do(func() { p.remoteStdin.Close() })
if _, err := io.Copy(p.remoteStdin, readerWrapper{p.Stdin}); err != nil {
runtime.HandleErrorWithLogger(logger, err, "Copying stdin failed")
runtime.HandleError(err)
}
}()
@ -121,26 +120,26 @@ func (p *streamProtocolV2) copyStdin(logger klog.Logger) {
// When that happens, we must Close() on our side of remoteStdin, to
// allow the copy in hijack to complete, and hijack to return.
go func() {
defer runtime.HandleCrashWithLogger(logger)
defer runtime.HandleCrash()
defer once.Do(func() { p.remoteStdin.Close() })
// this "copy" doesn't actually read anything - it's just here to wait for
// the server to close remoteStdin.
if _, err := io.Copy(io.Discard, p.remoteStdin); err != nil {
runtime.HandleErrorWithLogger(logger, err, "Waiting for server to close stdin failed")
runtime.HandleError(err)
}
}()
}
}
func (p *streamProtocolV2) copyStdout(logger klog.Logger, wg *sync.WaitGroup) {
func (p *streamProtocolV2) copyStdout(wg *sync.WaitGroup) {
if p.Stdout == nil {
return
}
wg.Add(1)
go func() {
defer runtime.HandleCrashWithLogger(logger)
defer runtime.HandleCrash()
defer wg.Done()
// make sure, packet in queue can be consumed.
// block in queue may lead to deadlock in conn.server
@ -148,29 +147,29 @@ func (p *streamProtocolV2) copyStdout(logger klog.Logger, wg *sync.WaitGroup) {
defer io.Copy(io.Discard, p.remoteStdout)
if _, err := io.Copy(p.Stdout, p.remoteStdout); err != nil {
runtime.HandleErrorWithLogger(logger, err, "Copying stdout failed")
runtime.HandleError(err)
}
}()
}
func (p *streamProtocolV2) copyStderr(logger klog.Logger, wg *sync.WaitGroup) {
func (p *streamProtocolV2) copyStderr(wg *sync.WaitGroup) {
if p.Stderr == nil || p.Tty {
return
}
wg.Add(1)
go func() {
defer runtime.HandleCrashWithLogger(logger)
defer runtime.HandleCrash()
defer wg.Done()
defer io.Copy(io.Discard, p.remoteStderr)
if _, err := io.Copy(p.Stderr, p.remoteStderr); err != nil {
runtime.HandleErrorWithLogger(logger, err, "Copying stderr failed")
runtime.HandleError(err)
}
}()
}
func (p *streamProtocolV2) stream(logger klog.Logger, conn streamCreator, ready chan<- struct{}) error {
func (p *streamProtocolV2) stream(conn streamCreator, ready chan<- struct{}) error {
if err := p.createStreams(conn); err != nil {
return err
}
@ -182,13 +181,13 @@ func (p *streamProtocolV2) stream(logger klog.Logger, conn streamCreator, ready
// now that all the streams have been created, proceed with reading & copying
errorChan := watchErrorStream(logger, p.errorStream, &errorDecoderV2{})
errorChan := watchErrorStream(p.errorStream, &errorDecoderV2{})
p.copyStdin(logger)
p.copyStdin()
var wg sync.WaitGroup
p.copyStdout(logger, &wg)
p.copyStderr(logger, &wg)
p.copyStdout(&wg)
p.copyStderr(&wg)
// we're waiting for stdout/stderr to finish copying
wg.Wait()

View file

@ -28,7 +28,6 @@ import (
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/httpstream"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/klog/v2/ktesting"
)
type fakeReader struct {
@ -180,7 +179,6 @@ func TestV2CreateStreams(t *testing.T) {
}
func TestV2ErrorStreamReading(t *testing.T) {
logger, _ := ktesting.NewTestContext(t)
tests := []struct {
name string
stream io.Reader
@ -219,7 +217,7 @@ func TestV2ErrorStreamReading(t *testing.T) {
h := newStreamProtocolV2(StreamOptions{}).(*streamProtocolV2)
h.errorStream = test.stream
ch := watchErrorStream(logger, h.errorStream, &errorDecoderV2{})
ch := watchErrorStream(h.errorStream, &errorDecoderV2{})
if ch == nil {
t.Fatalf("%s: unexpected nil channel", test.name)
}

View file

@ -22,9 +22,8 @@ import (
"net/http"
"sync"
v1 "k8s.io/api/core/v1"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/klog/v2"
)
// streamProtocolV3 implements version 3 of the streaming protocol for attach
@ -63,12 +62,12 @@ func (p *streamProtocolV3) createStreams(conn streamCreator) error {
return nil
}
func (p *streamProtocolV3) handleResizes(logger klog.Logger) {
func (p *streamProtocolV3) handleResizes() {
if p.resizeStream == nil || p.TerminalSizeQueue == nil {
return
}
go func() {
defer runtime.HandleCrashWithLogger(logger)
defer runtime.HandleCrash()
encoder := json.NewEncoder(p.resizeStream)
for {
@ -77,13 +76,13 @@ func (p *streamProtocolV3) handleResizes(logger klog.Logger) {
return
}
if err := encoder.Encode(&size); err != nil {
runtime.HandleErrorWithLogger(logger, err, "Encoding terminal size failed")
runtime.HandleError(err)
}
}
}()
}
func (p *streamProtocolV3) stream(logger klog.Logger, conn streamCreator, ready chan<- struct{}) error {
func (p *streamProtocolV3) stream(conn streamCreator, ready chan<- struct{}) error {
if err := p.createStreams(conn); err != nil {
return err
}
@ -95,15 +94,15 @@ func (p *streamProtocolV3) stream(logger klog.Logger, conn streamCreator, ready
// now that all the streams have been created, proceed with reading & copying
errorChan := watchErrorStream(logger, p.errorStream, &errorDecoderV3{})
errorChan := watchErrorStream(p.errorStream, &errorDecoderV3{})
p.handleResizes(logger)
p.handleResizes()
p.copyStdin(logger)
p.copyStdin()
var wg sync.WaitGroup
p.copyStdout(logger, &wg)
p.copyStderr(logger, &wg)
p.copyStdout(&wg)
p.copyStderr(&wg)
// we're waiting for stdout/stderr to finish copying
wg.Wait()

View file

@ -26,7 +26,6 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/remotecommand"
"k8s.io/client-go/util/exec"
"k8s.io/klog/v2"
)
// streamProtocolV4 implements version 4 of the streaming protocol for attach
@ -48,11 +47,11 @@ func (p *streamProtocolV4) createStreams(conn streamCreator) error {
return p.streamProtocolV3.createStreams(conn)
}
func (p *streamProtocolV4) handleResizes(logger klog.Logger) {
p.streamProtocolV3.handleResizes(logger)
func (p *streamProtocolV4) handleResizes() {
p.streamProtocolV3.handleResizes()
}
func (p *streamProtocolV4) stream(logger klog.Logger, conn streamCreator, ready chan<- struct{}) error {
func (p *streamProtocolV4) stream(conn streamCreator, ready chan<- struct{}) error {
if err := p.createStreams(conn); err != nil {
return err
}
@ -64,15 +63,15 @@ func (p *streamProtocolV4) stream(logger klog.Logger, conn streamCreator, ready
// now that all the streams have been created, proceed with reading & copying
errorChan := watchErrorStream(logger, p.errorStream, &errorDecoderV4{})
errorChan := watchErrorStream(p.errorStream, &errorDecoderV4{})
p.handleResizes(logger)
p.handleResizes()
p.copyStdin(logger)
p.copyStdin()
var wg sync.WaitGroup
p.copyStdout(logger, &wg)
p.copyStderr(logger, &wg)
p.copyStdout(&wg)
p.copyStderr(&wg)
// we're waiting for stdout/stderr to finish copying
wg.Wait()

View file

@ -16,8 +16,6 @@ limitations under the License.
package remotecommand
import "k8s.io/klog/v2"
// streamProtocolV5 add support for V5 of the remote command subprotocol.
// For the streamProtocolHandler, this version is the same as V4.
type streamProtocolV5 struct {
@ -32,6 +30,6 @@ func newStreamProtocolV5(options StreamOptions) streamProtocolHandler {
}
}
func (p *streamProtocolV5) stream(logger klog.Logger, conn streamCreator, ready chan<- struct{}) error {
return p.streamProtocolV4.stream(logger, conn, ready)
func (p *streamProtocolV5) stream(conn streamCreator, ready chan<- struct{}) error {
return p.streamProtocolV4.stream(conn, ready)
}

View file

@ -130,8 +130,7 @@ func (e *wsStreamExecutor) StreamWithContext(ctx context.Context, options Stream
}
defer conn.Close()
e.negotiated = conn.Subprotocol()
logger := klog.FromContext(ctx)
logger.V(4).Info("Subprotocol negotiated", "protocol", e.negotiated)
klog.V(4).Infof("The subprotocol is %s", e.negotiated)
var streamer streamProtocolHandler
switch e.negotiated {
@ -144,7 +143,7 @@ func (e *wsStreamExecutor) StreamWithContext(ctx context.Context, options Stream
case remotecommand.StreamProtocolV2Name:
streamer = newStreamProtocolV2(options)
case "":
logger.V(4).Info("The server did not negotiate a streaming protocol version, falling back", "protocol", remotecommand.StreamProtocolV1Name)
klog.V(4).Infof("The server did not negotiate a streaming protocol version. Falling back to %s", remotecommand.StreamProtocolV1Name)
fallthrough
case remotecommand.StreamProtocolV1Name:
streamer = newStreamProtocolV1(options)
@ -160,7 +159,7 @@ func (e *wsStreamExecutor) StreamWithContext(ctx context.Context, options Stream
}()
readyChan := make(chan struct{})
creator := newWSStreamCreator(logger, conn)
creator := newWSStreamCreator(conn)
go func() {
select {
// Wait until all streams have been created before starting the readDemuxLoop.
@ -178,7 +177,7 @@ func (e *wsStreamExecutor) StreamWithContext(ctx context.Context, options Stream
e.heartbeatDeadline,
)
}()
errorChan <- streamer.stream(logger, creator, readyChan)
errorChan <- streamer.stream(creator, readyChan)
}()
select {
@ -192,8 +191,7 @@ func (e *wsStreamExecutor) StreamWithContext(ctx context.Context, options Stream
}
type wsStreamCreator struct {
logger klog.Logger
conn *gwebsocket.Conn
conn *gwebsocket.Conn
// Protects writing to websocket connection; reading is lock-free
connWriteLock sync.Mutex
// map of stream id to stream; multiple streams read/write the connection
@ -204,9 +202,8 @@ type wsStreamCreator struct {
setStreamErr error
}
func newWSStreamCreator(logger klog.Logger, conn *gwebsocket.Conn) *wsStreamCreator {
func newWSStreamCreator(conn *gwebsocket.Conn) *wsStreamCreator {
return &wsStreamCreator{
logger: logger,
conn: conn,
streams: map[byte]*stream{},
}
@ -241,7 +238,6 @@ func (c *wsStreamCreator) CreateStream(headers http.Header) (httpstream.Stream,
}
reader, writer := io.Pipe()
s := &stream{
logger: klog.LoggerWithValues(c.logger, "id", id),
headers: headers,
readPipe: reader,
writePipe: writer,
@ -264,11 +260,11 @@ func (c *wsStreamCreator) CreateStream(headers http.Header) (httpstream.Stream,
// connection reader at a time (a read mutex would provide no benefit).
func (c *wsStreamCreator) readDemuxLoop(bufferSize int, period time.Duration, deadline time.Duration) {
// Initialize and start the ping/pong heartbeat.
h := newHeartbeat(c.logger, c.conn, period, deadline)
h := newHeartbeat(c.conn, period, deadline)
// Set initial timeout for websocket connection reading.
c.logger.V(5).Info("Websocket read starts", "deadline", deadline)
klog.V(5).Infof("Websocket initial read deadline: %s", deadline)
if err := c.conn.SetReadDeadline(time.Now().Add(deadline)); err != nil {
c.logger.Error(err, "Websocket initial setting read deadline failed")
klog.Errorf("Websocket initial setting read deadline failed %v", err)
return
}
go h.start()
@ -312,7 +308,7 @@ func (c *wsStreamCreator) readDemuxLoop(bufferSize int, period time.Duration, de
streamID := readBuffer[0]
s := c.getStream(streamID)
if s == nil {
c.logger.Error(nil, "Unknown stream, discarding message", "id", streamID)
klog.Errorf("Unknown stream id %d, discarding message", streamID)
continue
}
for {
@ -355,7 +351,6 @@ func (c *wsStreamCreator) closeAllStreamReaders(err error) {
}
type stream struct {
logger klog.Logger
headers http.Header
readPipe *io.PipeReader
writePipe *io.PipeWriter
@ -374,8 +369,8 @@ func (s *stream) Read(p []byte) (n int, err error) {
// Write writes directly to the underlying WebSocket connection.
func (s *stream) Write(p []byte) (n int, err error) {
s.logger.V(8).Info("Write() on stream")
defer s.logger.V(8).Info("Write() done on stream")
klog.V(8).Infof("Write() on stream %d", s.id)
defer klog.V(8).Infof("Write() done on stream %d", s.id)
s.connWriteLock.Lock()
defer s.connWriteLock.Unlock()
if s.conn == nil {
@ -383,7 +378,7 @@ func (s *stream) Write(p []byte) (n int, err error) {
}
err = s.conn.SetWriteDeadline(time.Now().Add(writeDeadline))
if err != nil {
s.logger.V(4).Info("Websocket setting write deadline failed", "err", err)
klog.V(4).Infof("Websocket setting write deadline failed %v", err)
return 0, err
}
// Message writer buffers the message data, so we don't need to do that ourselves.
@ -412,8 +407,8 @@ func (s *stream) Write(p []byte) (n int, err error) {
// Close half-closes the stream, indicating this side is finished with the stream.
func (s *stream) Close() error {
s.logger.V(6).Info("Close() on stream")
defer s.logger.V(6).Info("Close() done on stream")
klog.V(6).Infof("Close() on stream %d", s.id)
defer klog.V(6).Infof("Close() done on stream %d", s.id)
s.connWriteLock.Lock()
defer s.connWriteLock.Unlock()
if s.conn == nil {
@ -426,8 +421,8 @@ func (s *stream) Close() error {
}
func (s *stream) Reset() error {
s.logger.V(4).Info("Reset() on stream")
defer s.logger.V(4).Info("Reset() done on stream")
klog.V(4).Infof("Reset() on stream %d", s.id)
defer klog.V(4).Infof("Reset() done on stream %d", s.id)
s.Close()
return s.writePipe.Close()
}
@ -447,8 +442,7 @@ func (s *stream) Identifier() uint32 {
// inside the "readDemuxLoop" will return an i/o error prompting a connection close
// and cleanup.
type heartbeat struct {
logger klog.Logger
conn *gwebsocket.Conn
conn *gwebsocket.Conn
// period defines how often a "ping" heartbeat message is sent to the other endpoint
period time.Duration
// closing the "closer" channel will clean up the heartbeat timers
@ -462,9 +456,8 @@ type heartbeat struct {
// newHeartbeat creates heartbeat structure encapsulating fields necessary to
// run the websocket connection ping/pong mechanism and sets up handlers on
// the websocket connection.
func newHeartbeat(logger klog.Logger, conn *gwebsocket.Conn, period time.Duration, deadline time.Duration) *heartbeat {
func newHeartbeat(conn *gwebsocket.Conn, period time.Duration, deadline time.Duration) *heartbeat {
h := &heartbeat{
logger: logger,
conn: conn,
period: period,
closer: make(chan struct{}),
@ -474,10 +467,10 @@ func newHeartbeat(logger klog.Logger, conn *gwebsocket.Conn, period time.Duratio
// be empty.
h.conn.SetPongHandler(func(msg string) error {
// Push the read deadline into the future.
logger.V(6).Info("Pong message received -- resetting read deadline", "message", msg)
klog.V(6).Infof("Pong message received (%s)--resetting read deadline", msg)
err := h.conn.SetReadDeadline(time.Now().Add(deadline))
if err != nil {
logger.Error(err, "Websocket setting read deadline failed")
klog.Errorf("Websocket setting read deadline failed %v", err)
return err
}
if len(msg) > 0 {
@ -509,16 +502,16 @@ func (h *heartbeat) start() {
for {
select {
case <-h.closer:
h.logger.V(5).Info("Closed channel -- returning")
klog.V(5).Infof("closed channel--returning")
return
case <-t.C:
// "WriteControl" does not need to be protected by a mutex. According to
// gorilla/websockets library docs: "The Close and WriteControl methods can
// be called concurrently with all other methods."
if err := h.conn.WriteControl(gwebsocket.PingMessage, h.message, time.Now().Add(pingReadDeadline)); err == nil {
h.logger.V(6).Info("Websocket Ping succeeeded")
klog.V(6).Infof("Websocket Ping succeeeded")
} else {
h.logger.Error(err, "Websocket Ping failed")
klog.Errorf("Websocket Ping failed: %v", err)
if errors.Is(err, gwebsocket.ErrCloseSent) {
// we continue because c.conn.CloseChan will manage closing the connection already
continue

View file

@ -48,7 +48,6 @@ import (
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/rest"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
"k8s.io/klog/v2/ktesting"
)
// TestWebSocketClient_LoopbackStdinToStdout returns random data sent on the STDIN channel
@ -1050,7 +1049,6 @@ func TestWebSocketClient_ExecutorErrors(t *testing.T) {
}
func TestWebSocketClient_HeartbeatSucceeds(t *testing.T) {
logger, _ := ktesting.NewTestContext(t)
var upgrader = gwebsocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return true // Accepting all requests
@ -1083,7 +1081,7 @@ func TestWebSocketClient_HeartbeatSucceeds(t *testing.T) {
var expectedMsg = "test heartbeat message"
var period = 100 * time.Millisecond
var deadline = 200 * time.Millisecond
heartbeat := newHeartbeat(logger, client, period, deadline)
heartbeat := newHeartbeat(client, period, deadline)
heartbeat.setMessage(expectedMsg)
// Add a channel to the handler to retrieve the "pong" message.
pongMsgCh := make(chan string)
@ -1123,8 +1121,7 @@ func TestWebSocketClient_HeartbeatSucceeds(t *testing.T) {
}
func TestLateStreamCreation(t *testing.T) {
logger, _ := ktesting.NewTestContext(t)
c := newWSStreamCreator(logger, nil)
c := newWSStreamCreator(nil)
c.closeAllStreamReaders(nil)
if err := c.setStream(0, nil); err == nil {
t.Fatal("expected error adding stream after closeAllStreamReaders")
@ -1132,10 +1129,8 @@ func TestLateStreamCreation(t *testing.T) {
}
func TestWebSocketClient_StreamsAndExpectedErrors(t *testing.T) {
logger, _ := ktesting.NewTestContext(t)
// Validate Stream functions.
c := newWSStreamCreator(logger, nil)
c := newWSStreamCreator(nil)
headers := http.Header{}
headers.Set(v1.StreamType, v1.StreamTypeStdin)
s, err := c.CreateStream(headers)