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  )