1
16
17 package naming
18
19 import (
20 "encoding/json"
21 "fmt"
22 "reflect"
23 "strings"
24
25 "k8s.io/apimachinery/pkg/runtime"
26 "k8s.io/apimachinery/pkg/runtime/schema"
27 "k8s.io/apimachinery/pkg/util/errors"
28 "k8s.io/apimachinery/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
37
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
49
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
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
84
85 case reflect.Struct:
86 for i := 0; i < tp.NumField(); i++ {
87 f := tp.Field(i)
88 if f.PkgPath != "" {
89 continue
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
109 if tp.Implements(marshalerType) && (tp.Implements(unmarshalerType) || reflect.PointerTo(tp).Implements(unmarshalerType)) {
110 return errs
111 }
112
113
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
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
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 }
165
View as plain text