...

Source file src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/fuzzer/fuzzer.go

Documentation: k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/fuzzer

     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  	"reflect"
    21  	"strings"
    22  
    23  	fuzz "github.com/google/gofuzz"
    24  
    25  	"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
    26  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    27  	runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer"
    28  	"k8s.io/utils/pointer"
    29  )
    30  
    31  var swaggerMetadataDescriptions = metav1.ObjectMeta{}.SwaggerDoc()
    32  
    33  // Funcs returns the fuzzer functions for the apiextensions apis.
    34  func Funcs(codecs runtimeserializer.CodecFactory) []interface{} {
    35  	return []interface{}{
    36  		func(obj *apiextensions.CustomResourceDefinitionSpec, c fuzz.Continue) {
    37  			c.FuzzNoCustom(obj)
    38  
    39  			// match our defaulter
    40  			if len(obj.Scope) == 0 {
    41  				obj.Scope = apiextensions.NamespaceScoped
    42  			}
    43  			if len(obj.Names.Singular) == 0 {
    44  				obj.Names.Singular = strings.ToLower(obj.Names.Kind)
    45  			}
    46  			if len(obj.Names.ListKind) == 0 && len(obj.Names.Kind) > 0 {
    47  				obj.Names.ListKind = obj.Names.Kind + "List"
    48  			}
    49  			if len(obj.Versions) == 0 && len(obj.Version) == 0 {
    50  				// internal object must have a version to roundtrip all fields
    51  				obj.Version = "v1"
    52  			}
    53  			if len(obj.Versions) == 0 && len(obj.Version) != 0 {
    54  				obj.Versions = []apiextensions.CustomResourceDefinitionVersion{
    55  					{
    56  						Name:    obj.Version,
    57  						Served:  true,
    58  						Storage: true,
    59  					},
    60  				}
    61  			} else if len(obj.Versions) != 0 {
    62  				obj.Version = obj.Versions[0].Name
    63  			}
    64  			if len(obj.AdditionalPrinterColumns) == 0 {
    65  				obj.AdditionalPrinterColumns = []apiextensions.CustomResourceColumnDefinition{
    66  					{Name: "Age", Type: "date", Description: swaggerMetadataDescriptions["creationTimestamp"], JSONPath: ".metadata.creationTimestamp"},
    67  				}
    68  			}
    69  			c.Fuzz(&obj.SelectableFields)
    70  			if obj.Conversion == nil {
    71  				obj.Conversion = &apiextensions.CustomResourceConversion{
    72  					Strategy: apiextensions.NoneConverter,
    73  				}
    74  			}
    75  			if obj.Conversion.Strategy == apiextensions.WebhookConverter && len(obj.Conversion.ConversionReviewVersions) == 0 {
    76  				obj.Conversion.ConversionReviewVersions = []string{"v1beta1"}
    77  			}
    78  			if obj.PreserveUnknownFields == nil {
    79  				obj.PreserveUnknownFields = pointer.BoolPtr(true)
    80  			}
    81  
    82  			// Move per-version schema, subresources, additionalPrinterColumns, selectableFields to the top-level.
    83  			// This is required by validation in v1beta1, and by round-tripping in v1.
    84  			if len(obj.Versions) == 1 {
    85  				if obj.Versions[0].Schema != nil {
    86  					obj.Validation = obj.Versions[0].Schema
    87  					obj.Versions[0].Schema = nil
    88  				}
    89  				if obj.Versions[0].AdditionalPrinterColumns != nil {
    90  					obj.AdditionalPrinterColumns = obj.Versions[0].AdditionalPrinterColumns
    91  					obj.Versions[0].AdditionalPrinterColumns = nil
    92  				}
    93  				if obj.Versions[0].SelectableFields != nil {
    94  					obj.SelectableFields = obj.Versions[0].SelectableFields
    95  					obj.Versions[0].SelectableFields = nil
    96  				}
    97  				if obj.Versions[0].Subresources != nil {
    98  					obj.Subresources = obj.Versions[0].Subresources
    99  					obj.Versions[0].Subresources = nil
   100  				}
   101  			}
   102  		},
   103  		func(obj *apiextensions.CustomResourceDefinition, c fuzz.Continue) {
   104  			c.FuzzNoCustom(obj)
   105  
   106  			if len(obj.Status.StoredVersions) == 0 {
   107  				for _, v := range obj.Spec.Versions {
   108  					if v.Storage && !apiextensions.IsStoredVersion(obj, v.Name) {
   109  						obj.Status.StoredVersions = append(obj.Status.StoredVersions, v.Name)
   110  					}
   111  				}
   112  			}
   113  		},
   114  		func(obj *apiextensions.JSONSchemaProps, c fuzz.Continue) {
   115  			// we cannot use c.FuzzNoCustom because of the interface{} fields. So let's loop with reflection.
   116  			vobj := reflect.ValueOf(obj).Elem()
   117  			tobj := reflect.TypeOf(obj).Elem()
   118  			for i := 0; i < tobj.NumField(); i++ {
   119  				field := tobj.Field(i)
   120  				switch field.Name {
   121  				case "Default", "Enum", "Example", "Ref":
   122  					continue
   123  				default:
   124  					isValue := true
   125  					switch field.Type.Kind() {
   126  					case reflect.Interface, reflect.Map, reflect.Slice, reflect.Pointer:
   127  						isValue = false
   128  					}
   129  					if isValue || c.Intn(10) == 0 {
   130  						c.Fuzz(vobj.Field(i).Addr().Interface())
   131  					}
   132  				}
   133  			}
   134  			if c.RandBool() {
   135  				validJSON := apiextensions.JSON(`{"some": {"json": "test"}, "string": 42}`)
   136  				obj.Default = &validJSON
   137  			}
   138  			if c.RandBool() {
   139  				obj.Enum = []apiextensions.JSON{c.Float64(), c.RandString(), c.RandBool()}
   140  			}
   141  			if c.RandBool() {
   142  				validJSON := apiextensions.JSON(`"foobarbaz"`)
   143  				obj.Example = &validJSON
   144  			}
   145  			if c.RandBool() {
   146  				validRef := "validRef"
   147  				obj.Ref = &validRef
   148  			}
   149  			if len(obj.Type) == 0 {
   150  				obj.Nullable = false // because this does not roundtrip through go-openapi
   151  			}
   152  			if obj.XIntOrString {
   153  				obj.Type = ""
   154  			}
   155  		},
   156  		func(obj *apiextensions.JSONSchemaPropsOrBool, c fuzz.Continue) {
   157  			if c.RandBool() {
   158  				obj.Allows = true
   159  				obj.Schema = &apiextensions.JSONSchemaProps{}
   160  				c.Fuzz(obj.Schema)
   161  			} else {
   162  				obj.Allows = c.RandBool()
   163  			}
   164  		},
   165  		func(obj *apiextensions.JSONSchemaPropsOrArray, c fuzz.Continue) {
   166  			// disallow both Schema and JSONSchemas to be nil.
   167  			if c.RandBool() {
   168  				obj.Schema = &apiextensions.JSONSchemaProps{}
   169  				c.Fuzz(obj.Schema)
   170  			} else {
   171  				obj.JSONSchemas = make([]apiextensions.JSONSchemaProps, c.Intn(3)+1)
   172  				for i := range obj.JSONSchemas {
   173  					c.Fuzz(&obj.JSONSchemas[i])
   174  				}
   175  			}
   176  		},
   177  		func(obj *apiextensions.JSONSchemaPropsOrStringArray, c fuzz.Continue) {
   178  			if c.RandBool() {
   179  				obj.Schema = &apiextensions.JSONSchemaProps{}
   180  				c.Fuzz(obj.Schema)
   181  			} else {
   182  				c.Fuzz(&obj.Property)
   183  			}
   184  		},
   185  		func(obj *int64, c fuzz.Continue) {
   186  			// JSON only supports 53 bits because everything is a float
   187  			*obj = int64(c.Uint64()) & ((int64(1) << 53) - 1)
   188  		},
   189  		func(obj *apiextensions.ValidationRule, c fuzz.Continue) {
   190  			c.FuzzNoCustom(obj)
   191  			if obj.Reason != nil && *(obj.Reason) == "" {
   192  				obj.Reason = nil
   193  			}
   194  		},
   195  	}
   196  }
   197  

View as plain text