github.com/spotmaxtech/k8s-apimachinery-v0260@v0.0.1/pkg/apis/meta/fuzzer/fuzzer.go (about) 1 /* 2 Copyright 2017 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 fuzzer 18 19 import ( 20 "fmt" 21 "math/rand" 22 "sort" 23 "strconv" 24 "strings" 25 26 fuzz "github.com/google/gofuzz" 27 28 apitesting "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/api/apitesting" 29 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/api/apitesting/fuzzer" 30 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/api/resource" 31 metav1 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/apis/meta/v1" 32 metav1beta1 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/apis/meta/v1beta1" 33 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/runtime" 34 runtimeserializer "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/runtime/serializer" 35 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/types" 36 ) 37 38 func genericFuzzerFuncs(codecs runtimeserializer.CodecFactory) []interface{} { 39 return []interface{}{ 40 func(q *resource.Quantity, c fuzz.Continue) { 41 *q = *resource.NewQuantity(c.Int63n(1000), resource.DecimalExponent) 42 }, 43 func(j *int, c fuzz.Continue) { 44 *j = int(c.Int31()) 45 }, 46 func(j **int, c fuzz.Continue) { 47 if c.RandBool() { 48 i := int(c.Int31()) 49 *j = &i 50 } else { 51 *j = nil 52 } 53 }, 54 func(j *runtime.TypeMeta, c fuzz.Continue) { 55 // We have to customize the randomization of TypeMetas because their 56 // APIVersion and Kind must remain blank in memory. 57 j.APIVersion = "" 58 j.Kind = "" 59 }, 60 func(j *runtime.Object, c fuzz.Continue) { 61 // TODO: uncomment when round trip starts from a versioned object 62 if true { //c.RandBool() { 63 *j = &runtime.Unknown{ 64 // We do not set TypeMeta here because it is not carried through a round trip 65 Raw: []byte(`{"apiVersion":"unknown.group/unknown","kind":"Something","someKey":"someValue"}`), 66 ContentType: runtime.ContentTypeJSON, 67 } 68 } else { 69 types := []runtime.Object{&metav1.Status{}, &metav1.APIGroup{}} 70 t := types[c.Rand.Intn(len(types))] 71 c.Fuzz(t) 72 *j = t 73 } 74 }, 75 func(r *runtime.RawExtension, c fuzz.Continue) { 76 // Pick an arbitrary type and fuzz it 77 types := []runtime.Object{&metav1.Status{}, &metav1.APIGroup{}} 78 obj := types[c.Rand.Intn(len(types))] 79 c.Fuzz(obj) 80 81 // Find a codec for converting the object to raw bytes. This is necessary for the 82 // api version and kind to be correctly set be serialization. 83 var codec = apitesting.TestCodec(codecs, metav1.SchemeGroupVersion) 84 85 // Convert the object to raw bytes 86 bytes, err := runtime.Encode(codec, obj) 87 if err != nil { 88 panic(fmt.Sprintf("Failed to encode object: %v", err)) 89 } 90 91 // strip trailing newlines which do not survive roundtrips 92 for len(bytes) >= 1 && bytes[len(bytes)-1] == 10 { 93 bytes = bytes[:len(bytes)-1] 94 } 95 96 // Set the bytes field on the RawExtension 97 r.Raw = bytes 98 }, 99 } 100 } 101 102 // taken from gofuzz internals for RandString 103 type charRange struct { 104 first, last rune 105 } 106 107 func (c *charRange) choose(r *rand.Rand) rune { 108 count := int64(c.last - c.first + 1) 109 ch := c.first + rune(r.Int63n(count)) 110 111 return ch 112 } 113 114 // randomLabelPart produces a valid random label value or name-part 115 // of a label key. 116 func randomLabelPart(c fuzz.Continue, canBeEmpty bool) string { 117 validStartEnd := []charRange{{'0', '9'}, {'a', 'z'}, {'A', 'Z'}} 118 validMiddle := []charRange{{'0', '9'}, {'a', 'z'}, {'A', 'Z'}, 119 {'.', '.'}, {'-', '-'}, {'_', '_'}} 120 121 partLen := c.Rand.Intn(64) // len is [0, 63] 122 if !canBeEmpty { 123 partLen = c.Rand.Intn(63) + 1 // len is [1, 63] 124 } 125 126 runes := make([]rune, partLen) 127 if partLen == 0 { 128 return string(runes) 129 } 130 131 runes[0] = validStartEnd[c.Rand.Intn(len(validStartEnd))].choose(c.Rand) 132 for i := range runes[1:] { 133 runes[i+1] = validMiddle[c.Rand.Intn(len(validMiddle))].choose(c.Rand) 134 } 135 runes[len(runes)-1] = validStartEnd[c.Rand.Intn(len(validStartEnd))].choose(c.Rand) 136 137 return string(runes) 138 } 139 140 func randomDNSLabel(c fuzz.Continue) string { 141 validStartEnd := []charRange{{'0', '9'}, {'a', 'z'}} 142 validMiddle := []charRange{{'0', '9'}, {'a', 'z'}, {'-', '-'}} 143 144 partLen := c.Rand.Intn(63) + 1 // len is [1, 63] 145 runes := make([]rune, partLen) 146 147 runes[0] = validStartEnd[c.Rand.Intn(len(validStartEnd))].choose(c.Rand) 148 for i := range runes[1:] { 149 runes[i+1] = validMiddle[c.Rand.Intn(len(validMiddle))].choose(c.Rand) 150 } 151 runes[len(runes)-1] = validStartEnd[c.Rand.Intn(len(validStartEnd))].choose(c.Rand) 152 153 return string(runes) 154 } 155 156 func randomLabelKey(c fuzz.Continue) string { 157 namePart := randomLabelPart(c, false) 158 prefixPart := "" 159 160 usePrefix := c.RandBool() 161 if usePrefix { 162 // we can fit, with dots, at most 3 labels in the 253 allotted characters 163 prefixPartsLen := c.Rand.Intn(2) + 1 164 prefixParts := make([]string, prefixPartsLen) 165 for i := range prefixParts { 166 prefixParts[i] = randomDNSLabel(c) 167 } 168 prefixPart = strings.Join(prefixParts, ".") + "/" 169 } 170 171 return prefixPart + namePart 172 } 173 174 func v1FuzzerFuncs(codecs runtimeserializer.CodecFactory) []interface{} { 175 176 return []interface{}{ 177 func(j *metav1.TypeMeta, c fuzz.Continue) { 178 // We have to customize the randomization of TypeMetas because their 179 // APIVersion and Kind must remain blank in memory. 180 j.APIVersion = "" 181 j.Kind = "" 182 }, 183 func(j *metav1.ObjectMeta, c fuzz.Continue) { 184 c.FuzzNoCustom(j) 185 186 j.ResourceVersion = strconv.FormatUint(c.RandUint64(), 10) 187 j.UID = types.UID(c.RandString()) 188 189 // Fuzzing sec and nsec in a smaller range (uint32 instead of int64), 190 // so that the result Unix time is a valid date and can be parsed into RFC3339 format. 191 var sec, nsec uint32 192 c.Fuzz(&sec) 193 c.Fuzz(&nsec) 194 j.CreationTimestamp = metav1.Unix(int64(sec), int64(nsec)).Rfc3339Copy() 195 196 if j.DeletionTimestamp != nil { 197 c.Fuzz(&sec) 198 c.Fuzz(&nsec) 199 t := metav1.Unix(int64(sec), int64(nsec)).Rfc3339Copy() 200 j.DeletionTimestamp = &t 201 } 202 203 if len(j.Labels) == 0 { 204 j.Labels = nil 205 } else { 206 delete(j.Labels, "") 207 } 208 if len(j.Annotations) == 0 { 209 j.Annotations = nil 210 } else { 211 delete(j.Annotations, "") 212 } 213 if len(j.OwnerReferences) == 0 { 214 j.OwnerReferences = nil 215 } 216 if len(j.Finalizers) == 0 { 217 j.Finalizers = nil 218 } 219 }, 220 func(j *metav1.ResourceVersionMatch, c fuzz.Continue) { 221 matches := []metav1.ResourceVersionMatch{"", metav1.ResourceVersionMatchExact, metav1.ResourceVersionMatchNotOlderThan} 222 *j = matches[c.Rand.Intn(len(matches))] 223 }, 224 func(j *metav1.ListMeta, c fuzz.Continue) { 225 j.ResourceVersion = strconv.FormatUint(c.RandUint64(), 10) 226 j.SelfLink = c.RandString() 227 }, 228 func(j *metav1.LabelSelector, c fuzz.Continue) { 229 c.FuzzNoCustom(j) 230 // we can't have an entirely empty selector, so force 231 // use of MatchExpression if necessary 232 if len(j.MatchLabels) == 0 && len(j.MatchExpressions) == 0 { 233 j.MatchExpressions = make([]metav1.LabelSelectorRequirement, c.Rand.Intn(2)+1) 234 } 235 236 if j.MatchLabels != nil { 237 fuzzedMatchLabels := make(map[string]string, len(j.MatchLabels)) 238 for i := 0; i < len(j.MatchLabels); i++ { 239 fuzzedMatchLabels[randomLabelKey(c)] = randomLabelPart(c, true) 240 } 241 j.MatchLabels = fuzzedMatchLabels 242 } 243 244 validOperators := []metav1.LabelSelectorOperator{ 245 metav1.LabelSelectorOpIn, 246 metav1.LabelSelectorOpNotIn, 247 metav1.LabelSelectorOpExists, 248 metav1.LabelSelectorOpDoesNotExist, 249 } 250 251 if j.MatchExpressions != nil { 252 // NB: the label selector parser code sorts match expressions by key, and sorts the values, 253 // so we need to make sure ours are sorted as well here to preserve round-trip comparison. 254 // In practice, not sorting doesn't hurt anything... 255 256 for i := range j.MatchExpressions { 257 req := metav1.LabelSelectorRequirement{} 258 c.Fuzz(&req) 259 req.Key = randomLabelKey(c) 260 req.Operator = validOperators[c.Rand.Intn(len(validOperators))] 261 if req.Operator == metav1.LabelSelectorOpIn || req.Operator == metav1.LabelSelectorOpNotIn { 262 if len(req.Values) == 0 { 263 // we must have some values here, so randomly choose a short length 264 req.Values = make([]string, c.Rand.Intn(2)+1) 265 } 266 for i := range req.Values { 267 req.Values[i] = randomLabelPart(c, true) 268 } 269 sort.Strings(req.Values) 270 } else { 271 req.Values = nil 272 } 273 j.MatchExpressions[i] = req 274 } 275 276 sort.Slice(j.MatchExpressions, func(a, b int) bool { return j.MatchExpressions[a].Key < j.MatchExpressions[b].Key }) 277 } 278 }, 279 func(j *metav1.ManagedFieldsEntry, c fuzz.Continue) { 280 c.FuzzNoCustom(j) 281 j.FieldsV1 = nil 282 }, 283 } 284 } 285 286 func v1beta1FuzzerFuncs(codecs runtimeserializer.CodecFactory) []interface{} { 287 return []interface{}{ 288 func(r *metav1beta1.TableOptions, c fuzz.Continue) { 289 c.FuzzNoCustom(r) 290 // NoHeaders is not serialized to the wire but is allowed within the versioned 291 // type because we don't use meta internal types in the client and API server. 292 r.NoHeaders = false 293 }, 294 func(r *metav1beta1.TableRow, c fuzz.Continue) { 295 c.Fuzz(&r.Object) 296 c.Fuzz(&r.Conditions) 297 if len(r.Conditions) == 0 { 298 r.Conditions = nil 299 } 300 n := c.Intn(10) 301 if n > 0 { 302 r.Cells = make([]interface{}, n) 303 } 304 for i := range r.Cells { 305 t := c.Intn(6) 306 switch t { 307 case 0: 308 r.Cells[i] = c.RandString() 309 case 1: 310 r.Cells[i] = c.Int63() 311 case 2: 312 r.Cells[i] = c.RandBool() 313 case 3: 314 x := map[string]interface{}{} 315 for j := c.Intn(10) + 1; j >= 0; j-- { 316 x[c.RandString()] = c.RandString() 317 } 318 r.Cells[i] = x 319 case 4: 320 x := make([]interface{}, c.Intn(10)) 321 for i := range x { 322 x[i] = c.Int63() 323 } 324 r.Cells[i] = x 325 default: 326 r.Cells[i] = nil 327 } 328 } 329 }, 330 } 331 } 332 333 var Funcs = fuzzer.MergeFuzzerFuncs( 334 genericFuzzerFuncs, 335 v1FuzzerFuncs, 336 v1beta1FuzzerFuncs, 337 )