k8s.io/kubernetes@v1.29.3/test/utils/format/format.go (about)

     1  /*
     2  Copyright 2022 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 format is an extension of Gomega's format package which
    18  // improves printing of objects that can be serialized well as YAML,
    19  // like the structs in the Kubernetes API.
    20  //
    21  // Just importing it is enough to activate this special YAML support
    22  // in Gomega.
    23  package format
    24  
    25  import (
    26  	"reflect"
    27  	"strings"
    28  
    29  	"github.com/onsi/gomega/format"
    30  
    31  	"sigs.k8s.io/yaml"
    32  )
    33  
    34  func init() {
    35  	format.RegisterCustomFormatter(handleYAML)
    36  }
    37  
    38  // Object makes Gomega's [format.Object] available without having to import that
    39  // package.
    40  func Object(object interface{}, indentation uint) string {
    41  	return format.Object(object, indentation)
    42  }
    43  
    44  // handleYAML formats all values as YAML where the result
    45  // is likely to look better as YAML:
    46  //   - pointer to struct or struct where all fields
    47  //     have `json` tags
    48  //   - slices containing such a value
    49  //   - maps where the key or value are such a value
    50  func handleYAML(object interface{}) (string, bool) {
    51  	value := reflect.ValueOf(object)
    52  	if !useYAML(value.Type()) {
    53  		return "", false
    54  	}
    55  	y, err := yaml.Marshal(object)
    56  	if err != nil {
    57  		return "", false
    58  	}
    59  	return "\n" + strings.TrimSpace(string(y)), true
    60  }
    61  
    62  func useYAML(t reflect.Type) bool {
    63  	switch t.Kind() {
    64  	case reflect.Pointer, reflect.Slice, reflect.Array:
    65  		return useYAML(t.Elem())
    66  	case reflect.Map:
    67  		return useYAML(t.Key()) || useYAML(t.Elem())
    68  	case reflect.Struct:
    69  		// All fields must have a `json` tag.
    70  		for i := 0; i < t.NumField(); i++ {
    71  			field := t.Field(i)
    72  			if _, ok := field.Tag.Lookup("json"); !ok {
    73  				return false
    74  			}
    75  		}
    76  		return true
    77  	default:
    78  		return false
    79  	}
    80  }