github.com/spotmaxtech/k8s-apimachinery-v0260@v0.0.1/pkg/api/apitesting/naming/naming.go (about)

     1  /*
     2  Copyright 2019 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 naming
    18  
    19  import (
    20  	"encoding/json"
    21  	"fmt"
    22  	"reflect"
    23  	"strings"
    24  
    25  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/runtime"
    26  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/runtime/schema"
    27  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/util/errors"
    28  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/util/sets"
    29  )
    30  
    31  var (
    32  	marshalerType   = reflect.TypeOf((*json.Marshaler)(nil)).Elem()
    33  	unmarshalerType = reflect.TypeOf((*json.Unmarshaler)(nil)).Elem()
    34  )
    35  
    36  // VerifyGroupNames ensures that all groups in the scheme ends with the k8s.io suffix.
    37  // Exceptions can be tolerated using the legacyUnsuffixedGroups parameter
    38  func VerifyGroupNames(scheme *runtime.Scheme, legacyUnsuffixedGroups sets.String) error {
    39  	errs := []error{}
    40  	for _, gv := range scheme.PrioritizedVersionsAllGroups() {
    41  		if !strings.HasSuffix(gv.Group, ".k8s.io") && !legacyUnsuffixedGroups.Has(gv.Group) {
    42  			errs = append(errs, fmt.Errorf("group %s does not have the standard kubernetes API group suffix of .k8s.io", gv.Group))
    43  		}
    44  	}
    45  	return errors.NewAggregate(errs)
    46  }
    47  
    48  // VerifyTagNaming ensures that all types in the scheme have JSON tags set on external types, and JSON tags not set on internal types.
    49  // Exceptions can be tolerated using the typesAllowedTags and allowedNonstandardJSONNames parameters
    50  func VerifyTagNaming(scheme *runtime.Scheme, typesAllowedTags map[reflect.Type]bool, allowedNonstandardJSONNames map[reflect.Type]string) error {
    51  	errs := []error{}
    52  	for gvk, knownType := range scheme.AllKnownTypes() {
    53  		var err error
    54  		if gvk.Version == runtime.APIVersionInternal {
    55  			err = errors.NewAggregate(ensureNoTags(gvk, knownType, nil, typesAllowedTags))
    56  		} else {
    57  			err = errors.NewAggregate(ensureTags(gvk, knownType, nil, allowedNonstandardJSONNames))
    58  		}
    59  		if err != nil {
    60  			errs = append(errs, err)
    61  		}
    62  	}
    63  	return errors.NewAggregate(errs)
    64  }
    65  
    66  func ensureNoTags(gvk schema.GroupVersionKind, tp reflect.Type, parents []reflect.Type, typesAllowedTags map[reflect.Type]bool) []error {
    67  	errs := []error{}
    68  	if _, ok := typesAllowedTags[tp]; ok {
    69  		return errs
    70  	}
    71  
    72  	// Don't look at the same type multiple times
    73  	if containsType(parents, tp) {
    74  		return nil
    75  	}
    76  	parents = append(parents, tp)
    77  
    78  	switch tp.Kind() {
    79  	case reflect.Map, reflect.Slice, reflect.Pointer:
    80  		errs = append(errs, ensureNoTags(gvk, tp.Elem(), parents, typesAllowedTags)...)
    81  
    82  	case reflect.String, reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int32, reflect.Int64, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.Interface:
    83  		// no-op
    84  
    85  	case reflect.Struct:
    86  		for i := 0; i < tp.NumField(); i++ {
    87  			f := tp.Field(i)
    88  			if f.PkgPath != "" {
    89  				continue // Ignore unexported fields
    90  			}
    91  			jsonTag := f.Tag.Get("json")
    92  			protoTag := f.Tag.Get("protobuf")
    93  			if len(jsonTag) > 0 || len(protoTag) > 0 {
    94  				errs = append(errs, fmt.Errorf("internal types should not have json or protobuf tags. %#v has tag on field %v: %v.\n%s", gvk, f.Name, f.Tag, fmtParentString(parents)))
    95  			}
    96  
    97  			errs = append(errs, ensureNoTags(gvk, f.Type, parents, typesAllowedTags)...)
    98  		}
    99  
   100  	default:
   101  		errs = append(errs, fmt.Errorf("unexpected type %v in %#v.\n%s", tp.Kind(), gvk, fmtParentString(parents)))
   102  	}
   103  	return errs
   104  }
   105  
   106  func ensureTags(gvk schema.GroupVersionKind, tp reflect.Type, parents []reflect.Type, allowedNonstandardJSONNames map[reflect.Type]string) []error {
   107  	errs := []error{}
   108  	// This type handles its own encoding/decoding and doesn't need json tags
   109  	if tp.Implements(marshalerType) && (tp.Implements(unmarshalerType) || reflect.PointerTo(tp).Implements(unmarshalerType)) {
   110  		return errs
   111  	}
   112  
   113  	// Don't look at the same type multiple times
   114  	if containsType(parents, tp) {
   115  		return nil
   116  	}
   117  	parents = append(parents, tp)
   118  
   119  	switch tp.Kind() {
   120  	case reflect.Map, reflect.Slice, reflect.Pointer:
   121  		errs = append(errs, ensureTags(gvk, tp.Elem(), parents, allowedNonstandardJSONNames)...)
   122  
   123  	case reflect.String, reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int32, reflect.Int64, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.Interface:
   124  		// no-op
   125  
   126  	case reflect.Struct:
   127  		for i := 0; i < tp.NumField(); i++ {
   128  			f := tp.Field(i)
   129  			jsonTag := f.Tag.Get("json")
   130  			if len(jsonTag) == 0 {
   131  				errs = append(errs, fmt.Errorf("external types should have json tags. %#v tags on field %v are: %s.\n%s", gvk, f.Name, f.Tag, fmtParentString(parents)))
   132  			}
   133  
   134  			jsonTagName := strings.Split(jsonTag, ",")[0]
   135  			if len(jsonTagName) > 0 && (jsonTagName[0] < 'a' || jsonTagName[0] > 'z') && jsonTagName != "-" && allowedNonstandardJSONNames[tp] != jsonTagName {
   136  				errs = append(errs, fmt.Errorf("external types should have json names starting with lowercase letter. %#v has json tag on field %v with name %s.\n%s", gvk, f.Name, jsonTagName, fmtParentString(parents)))
   137  			}
   138  
   139  			errs = append(errs, ensureTags(gvk, f.Type, parents, allowedNonstandardJSONNames)...)
   140  		}
   141  
   142  	default:
   143  		errs = append(errs, fmt.Errorf("unexpected type %v in %#v.\n%s", tp.Kind(), gvk, fmtParentString(parents)))
   144  	}
   145  	return errs
   146  }
   147  
   148  func fmtParentString(parents []reflect.Type) string {
   149  	str := "Type parents:\n"
   150  	for i, tp := range parents {
   151  		str += fmt.Sprintf("%s%v\n", strings.Repeat(" ", i), tp)
   152  	}
   153  	return str
   154  }
   155  
   156  // containsType returns true if s contains t, false otherwise
   157  func containsType(s []reflect.Type, t reflect.Type) bool {
   158  	for _, u := range s {
   159  		if t == u {
   160  			return true
   161  		}
   162  	}
   163  	return false
   164  }