...

Source file src/k8s.io/klog/v2/k8s_references.go

Documentation: k8s.io/klog/v2

     1  /*
     2  Copyright 2021 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package klog
    18  
    19  import (
    20  	"bytes"
    21  	"fmt"
    22  	"reflect"
    23  	"strings"
    24  
    25  	"github.com/go-logr/logr"
    26  )
    27  
    28  // ObjectRef references a kubernetes object
    29  type ObjectRef struct {
    30  	Name      string `json:"name"`
    31  	Namespace string `json:"namespace,omitempty"`
    32  }
    33  
    34  func (ref ObjectRef) String() string {
    35  	if ref.Namespace != "" {
    36  		var builder strings.Builder
    37  		builder.Grow(len(ref.Namespace) + len(ref.Name) + 1)
    38  		builder.WriteString(ref.Namespace)
    39  		builder.WriteRune('/')
    40  		builder.WriteString(ref.Name)
    41  		return builder.String()
    42  	}
    43  	return ref.Name
    44  }
    45  
    46  func (ref ObjectRef) WriteText(out *bytes.Buffer) {
    47  	out.WriteRune('"')
    48  	ref.writeUnquoted(out)
    49  	out.WriteRune('"')
    50  }
    51  
    52  func (ref ObjectRef) writeUnquoted(out *bytes.Buffer) {
    53  	if ref.Namespace != "" {
    54  		out.WriteString(ref.Namespace)
    55  		out.WriteRune('/')
    56  	}
    57  	out.WriteString(ref.Name)
    58  }
    59  
    60  // MarshalLog ensures that loggers with support for structured output will log
    61  // as a struct by removing the String method via a custom type.
    62  func (ref ObjectRef) MarshalLog() interface{} {
    63  	type or ObjectRef
    64  	return or(ref)
    65  }
    66  
    67  var _ logr.Marshaler = ObjectRef{}
    68  
    69  // KMetadata is a subset of the kubernetes k8s.io/apimachinery/pkg/apis/meta/v1.Object interface
    70  // this interface may expand in the future, but will always be a subset of the
    71  // kubernetes k8s.io/apimachinery/pkg/apis/meta/v1.Object interface
    72  type KMetadata interface {
    73  	GetName() string
    74  	GetNamespace() string
    75  }
    76  
    77  // KObj returns ObjectRef from ObjectMeta
    78  func KObj(obj KMetadata) ObjectRef {
    79  	if obj == nil {
    80  		return ObjectRef{}
    81  	}
    82  	if val := reflect.ValueOf(obj); val.Kind() == reflect.Ptr && val.IsNil() {
    83  		return ObjectRef{}
    84  	}
    85  
    86  	return ObjectRef{
    87  		Name:      obj.GetName(),
    88  		Namespace: obj.GetNamespace(),
    89  	}
    90  }
    91  
    92  // KRef returns ObjectRef from name and namespace
    93  func KRef(namespace, name string) ObjectRef {
    94  	return ObjectRef{
    95  		Name:      name,
    96  		Namespace: namespace,
    97  	}
    98  }
    99  
   100  // KObjs returns slice of ObjectRef from an slice of ObjectMeta
   101  //
   102  // DEPRECATED: Use KObjSlice instead, it has better performance.
   103  func KObjs(arg interface{}) []ObjectRef {
   104  	s := reflect.ValueOf(arg)
   105  	if s.Kind() != reflect.Slice {
   106  		return nil
   107  	}
   108  	objectRefs := make([]ObjectRef, 0, s.Len())
   109  	for i := 0; i < s.Len(); i++ {
   110  		if v, ok := s.Index(i).Interface().(KMetadata); ok {
   111  			objectRefs = append(objectRefs, KObj(v))
   112  		} else {
   113  			return nil
   114  		}
   115  	}
   116  	return objectRefs
   117  }
   118  
   119  // KObjSlice takes a slice of objects that implement the KMetadata interface
   120  // and returns an object that gets logged as a slice of ObjectRef values or a
   121  // string containing those values, depending on whether the logger prefers text
   122  // output or structured output.
   123  //
   124  // An error string is logged when KObjSlice is not passed a suitable slice.
   125  //
   126  // Processing of the argument is delayed until the value actually gets logged,
   127  // in contrast to KObjs where that overhead is incurred regardless of whether
   128  // the result is needed.
   129  func KObjSlice(arg interface{}) interface{} {
   130  	return kobjSlice{arg: arg}
   131  }
   132  
   133  type kobjSlice struct {
   134  	arg interface{}
   135  }
   136  
   137  var _ fmt.Stringer = kobjSlice{}
   138  var _ logr.Marshaler = kobjSlice{}
   139  
   140  func (ks kobjSlice) String() string {
   141  	objectRefs, errStr := ks.process()
   142  	if errStr != "" {
   143  		return errStr
   144  	}
   145  	return fmt.Sprintf("%v", objectRefs)
   146  }
   147  
   148  func (ks kobjSlice) MarshalLog() interface{} {
   149  	objectRefs, errStr := ks.process()
   150  	if errStr != "" {
   151  		return errStr
   152  	}
   153  	return objectRefs
   154  }
   155  
   156  func (ks kobjSlice) process() (objs []interface{}, err string) {
   157  	s := reflect.ValueOf(ks.arg)
   158  	switch s.Kind() {
   159  	case reflect.Invalid:
   160  		// nil parameter, print as nil.
   161  		return nil, ""
   162  	case reflect.Slice:
   163  		// Okay, handle below.
   164  	default:
   165  		return nil, fmt.Sprintf("<KObjSlice needs a slice, got type %T>", ks.arg)
   166  	}
   167  	objectRefs := make([]interface{}, 0, s.Len())
   168  	for i := 0; i < s.Len(); i++ {
   169  		item := s.Index(i).Interface()
   170  		if item == nil {
   171  			objectRefs = append(objectRefs, nil)
   172  		} else if v, ok := item.(KMetadata); ok {
   173  			objectRefs = append(objectRefs, KObj(v))
   174  		} else {
   175  			return nil, fmt.Sprintf("<KObjSlice needs a slice of values implementing KMetadata, got type %T>", item)
   176  		}
   177  	}
   178  	return objectRefs, ""
   179  }
   180  
   181  var nilToken = []byte("null")
   182  
   183  func (ks kobjSlice) WriteText(out *bytes.Buffer) {
   184  	s := reflect.ValueOf(ks.arg)
   185  	switch s.Kind() {
   186  	case reflect.Invalid:
   187  		// nil parameter, print as null.
   188  		out.Write(nilToken)
   189  		return
   190  	case reflect.Slice:
   191  		// Okay, handle below.
   192  	default:
   193  		fmt.Fprintf(out, `"<KObjSlice needs a slice, got type %T>"`, ks.arg)
   194  		return
   195  	}
   196  	out.Write([]byte{'['})
   197  	defer out.Write([]byte{']'})
   198  	for i := 0; i < s.Len(); i++ {
   199  		if i > 0 {
   200  			out.Write([]byte{','})
   201  		}
   202  		item := s.Index(i).Interface()
   203  		if item == nil {
   204  			out.Write(nilToken)
   205  		} else if v, ok := item.(KMetadata); ok {
   206  			KObj(v).WriteText(out)
   207  		} else {
   208  			fmt.Fprintf(out, `"<KObjSlice needs a slice of values implementing KMetadata, got type %T>"`, item)
   209  			return
   210  		}
   211  	}
   212  }
   213  

View as plain text