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 }