k8s.io/kubernetes@v1.29.3/pkg/apis/core/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 "reflect" 21 "strconv" 22 "time" 23 24 fuzz "github.com/google/gofuzz" 25 26 v1 "k8s.io/api/core/v1" 27 "k8s.io/apimachinery/pkg/api/resource" 28 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 29 "k8s.io/apimachinery/pkg/runtime" 30 "k8s.io/apimachinery/pkg/runtime/schema" 31 runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer" 32 "k8s.io/apimachinery/pkg/util/intstr" 33 "k8s.io/kubernetes/pkg/apis/core" 34 utilpointer "k8s.io/utils/pointer" 35 ) 36 37 // Funcs returns the fuzzer functions for the core group. 38 var Funcs = func(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 *core.ObjectReference, c fuzz.Continue) { 44 // We have to customize the randomization of TypeMetas because their 45 // APIVersion and Kind must remain blank in memory. 46 j.APIVersion = c.RandString() 47 j.Kind = c.RandString() 48 j.Namespace = c.RandString() 49 j.Name = c.RandString() 50 j.ResourceVersion = strconv.FormatUint(c.RandUint64(), 10) 51 j.FieldPath = c.RandString() 52 }, 53 func(j *core.PodExecOptions, c fuzz.Continue) { 54 j.Stdout = true 55 j.Stderr = true 56 }, 57 func(j *core.PodAttachOptions, c fuzz.Continue) { 58 j.Stdout = true 59 j.Stderr = true 60 }, 61 func(j *core.PodPortForwardOptions, c fuzz.Continue) { 62 if c.RandBool() { 63 j.Ports = make([]int32, c.Intn(10)) 64 for i := range j.Ports { 65 j.Ports[i] = c.Int31n(65535) 66 } 67 } 68 }, 69 func(s *core.PodSpec, c fuzz.Continue) { 70 c.FuzzNoCustom(s) 71 // has a default value 72 ttl := int64(30) 73 if c.RandBool() { 74 ttl = int64(c.Uint32()) 75 } 76 s.TerminationGracePeriodSeconds = &ttl 77 78 c.Fuzz(s.SecurityContext) 79 80 if s.SecurityContext == nil { 81 s.SecurityContext = new(core.PodSecurityContext) 82 } 83 if s.Affinity == nil { 84 s.Affinity = new(core.Affinity) 85 } 86 if s.SchedulerName == "" { 87 s.SchedulerName = v1.DefaultSchedulerName 88 } 89 if s.EnableServiceLinks == nil { 90 enableServiceLinks := v1.DefaultEnableServiceLinks 91 s.EnableServiceLinks = &enableServiceLinks 92 } 93 }, 94 func(s *core.PodStatus, c fuzz.Continue) { 95 c.Fuzz(&s) 96 s.HostIPs = []core.HostIP{{IP: s.HostIP}} 97 }, 98 func(j *core.PodPhase, c fuzz.Continue) { 99 statuses := []core.PodPhase{core.PodPending, core.PodRunning, core.PodFailed, core.PodUnknown} 100 *j = statuses[c.Rand.Intn(len(statuses))] 101 }, 102 func(j *core.Binding, c fuzz.Continue) { 103 c.Fuzz(&j.ObjectMeta) 104 j.Target.Name = c.RandString() 105 }, 106 func(j *core.ReplicationController, c fuzz.Continue) { 107 c.FuzzNoCustom(j) 108 109 // match defaulting 110 if j.Spec.Template != nil { 111 if len(j.Labels) == 0 { 112 j.Labels = j.Spec.Template.Labels 113 } 114 if len(j.Spec.Selector) == 0 { 115 j.Spec.Selector = j.Spec.Template.Labels 116 } 117 } 118 }, 119 func(j *core.ReplicationControllerSpec, c fuzz.Continue) { 120 c.FuzzNoCustom(j) // fuzz self without calling this function again 121 //j.TemplateRef = nil // this is required for round trip 122 }, 123 func(j *core.List, c fuzz.Continue) { 124 c.FuzzNoCustom(j) // fuzz self without calling this function again 125 // TODO: uncomment when round trip starts from a versioned object 126 if false { //j.Items == nil { 127 j.Items = []runtime.Object{} 128 } 129 }, 130 func(q *core.ResourceRequirements, c fuzz.Continue) { 131 randomQuantity := func() resource.Quantity { 132 var q resource.Quantity 133 c.Fuzz(&q) 134 // precalc the string for benchmarking purposes 135 _ = q.String() 136 return q 137 } 138 q.Limits = make(core.ResourceList) 139 q.Requests = make(core.ResourceList) 140 cpuLimit := randomQuantity() 141 q.Limits[core.ResourceCPU] = cpuLimit.DeepCopy() 142 q.Requests[core.ResourceCPU] = cpuLimit.DeepCopy() 143 memoryLimit := randomQuantity() 144 q.Limits[core.ResourceMemory] = memoryLimit.DeepCopy() 145 q.Requests[core.ResourceMemory] = memoryLimit.DeepCopy() 146 storageLimit := randomQuantity() 147 q.Limits[core.ResourceStorage] = storageLimit.DeepCopy() 148 q.Requests[core.ResourceStorage] = storageLimit.DeepCopy() 149 }, 150 func(q *core.LimitRangeItem, c fuzz.Continue) { 151 var cpuLimit resource.Quantity 152 c.Fuzz(&cpuLimit) 153 154 q.Type = core.LimitTypeContainer 155 q.Default = make(core.ResourceList) 156 q.Default[core.ResourceCPU] = cpuLimit.DeepCopy() 157 158 q.DefaultRequest = make(core.ResourceList) 159 q.DefaultRequest[core.ResourceCPU] = cpuLimit.DeepCopy() 160 161 q.Max = make(core.ResourceList) 162 q.Max[core.ResourceCPU] = cpuLimit.DeepCopy() 163 164 q.Min = make(core.ResourceList) 165 q.Min[core.ResourceCPU] = cpuLimit.DeepCopy() 166 167 q.MaxLimitRequestRatio = make(core.ResourceList) 168 q.MaxLimitRequestRatio[core.ResourceCPU] = resource.MustParse("10") 169 }, 170 func(p *core.PullPolicy, c fuzz.Continue) { 171 policies := []core.PullPolicy{core.PullAlways, core.PullNever, core.PullIfNotPresent} 172 *p = policies[c.Rand.Intn(len(policies))] 173 }, 174 func(rp *core.RestartPolicy, c fuzz.Continue) { 175 policies := []core.RestartPolicy{core.RestartPolicyAlways, core.RestartPolicyNever, core.RestartPolicyOnFailure} 176 *rp = policies[c.Rand.Intn(len(policies))] 177 }, 178 // core.DownwardAPIVolumeFile needs to have a specific func since FieldRef has to be 179 // defaulted to a version otherwise roundtrip will fail 180 func(m *core.DownwardAPIVolumeFile, c fuzz.Continue) { 181 m.Path = c.RandString() 182 versions := []string{"v1"} 183 m.FieldRef = &core.ObjectFieldSelector{} 184 m.FieldRef.APIVersion = versions[c.Rand.Intn(len(versions))] 185 m.FieldRef.FieldPath = c.RandString() 186 c.Fuzz(m.Mode) 187 if m.Mode != nil { 188 *m.Mode &= 0777 189 } 190 }, 191 func(s *core.SecretVolumeSource, c fuzz.Continue) { 192 c.FuzzNoCustom(s) // fuzz self without calling this function again 193 194 if c.RandBool() { 195 opt := c.RandBool() 196 s.Optional = &opt 197 } 198 // DefaultMode should always be set, it has a default 199 // value and it is expected to be between 0 and 0777 200 var mode int32 201 c.Fuzz(&mode) 202 mode &= 0777 203 s.DefaultMode = &mode 204 }, 205 func(cm *core.ConfigMapVolumeSource, c fuzz.Continue) { 206 c.FuzzNoCustom(cm) // fuzz self without calling this function again 207 208 if c.RandBool() { 209 opt := c.RandBool() 210 cm.Optional = &opt 211 } 212 // DefaultMode should always be set, it has a default 213 // value and it is expected to be between 0 and 0777 214 var mode int32 215 c.Fuzz(&mode) 216 mode &= 0777 217 cm.DefaultMode = &mode 218 }, 219 func(d *core.DownwardAPIVolumeSource, c fuzz.Continue) { 220 c.FuzzNoCustom(d) // fuzz self without calling this function again 221 222 // DefaultMode should always be set, it has a default 223 // value and it is expected to be between 0 and 0777 224 var mode int32 225 c.Fuzz(&mode) 226 mode &= 0777 227 d.DefaultMode = &mode 228 }, 229 func(s *core.ProjectedVolumeSource, c fuzz.Continue) { 230 c.FuzzNoCustom(s) // fuzz self without calling this function again 231 232 // DefaultMode should always be set, it has a default 233 // value and it is expected to be between 0 and 0777 234 var mode int32 235 c.Fuzz(&mode) 236 mode &= 0777 237 s.DefaultMode = &mode 238 }, 239 func(k *core.KeyToPath, c fuzz.Continue) { 240 c.FuzzNoCustom(k) // fuzz self without calling this function again 241 k.Key = c.RandString() 242 k.Path = c.RandString() 243 244 // Mode is not mandatory, but if it is set, it should be 245 // a value between 0 and 0777 246 if k.Mode != nil { 247 *k.Mode &= 0777 248 } 249 }, 250 func(vs *core.VolumeSource, c fuzz.Continue) { 251 // Exactly one of the fields must be set. 252 v := reflect.ValueOf(vs).Elem() 253 i := int(c.RandUint64() % uint64(v.NumField())) 254 t := v.Field(i).Addr() 255 for v.Field(i).IsNil() { 256 c.Fuzz(t.Interface()) 257 } 258 }, 259 func(i *core.ISCSIVolumeSource, c fuzz.Continue) { 260 i.ISCSIInterface = c.RandString() 261 if i.ISCSIInterface == "" { 262 i.ISCSIInterface = "default" 263 } 264 }, 265 func(i *core.ISCSIPersistentVolumeSource, c fuzz.Continue) { 266 i.ISCSIInterface = c.RandString() 267 if i.ISCSIInterface == "" { 268 i.ISCSIInterface = "default" 269 } 270 }, 271 func(i *core.PersistentVolumeClaimSpec, c fuzz.Continue) { 272 // Match defaulting in pkg/apis/core/v1/defaults.go. 273 volumeMode := core.PersistentVolumeMode(c.RandString()) 274 if volumeMode == "" { 275 volumeMode = core.PersistentVolumeFilesystem 276 } 277 i.VolumeMode = &volumeMode 278 }, 279 func(d *core.DNSPolicy, c fuzz.Continue) { 280 policies := []core.DNSPolicy{core.DNSClusterFirst, core.DNSDefault} 281 *d = policies[c.Rand.Intn(len(policies))] 282 }, 283 func(p *core.Protocol, c fuzz.Continue) { 284 protocols := []core.Protocol{core.ProtocolTCP, core.ProtocolUDP, core.ProtocolSCTP} 285 *p = protocols[c.Rand.Intn(len(protocols))] 286 }, 287 func(p *core.ServiceAffinity, c fuzz.Continue) { 288 types := []core.ServiceAffinity{core.ServiceAffinityClientIP, core.ServiceAffinityNone} 289 *p = types[c.Rand.Intn(len(types))] 290 }, 291 func(p *core.ServiceType, c fuzz.Continue) { 292 types := []core.ServiceType{core.ServiceTypeClusterIP, core.ServiceTypeNodePort, core.ServiceTypeLoadBalancer} 293 *p = types[c.Rand.Intn(len(types))] 294 }, 295 func(p *core.IPFamily, c fuzz.Continue) { 296 types := []core.IPFamily{core.IPv4Protocol, core.IPv6Protocol} 297 selected := types[c.Rand.Intn(len(types))] 298 *p = selected 299 }, 300 func(p *core.ServiceExternalTrafficPolicy, c fuzz.Continue) { 301 types := []core.ServiceExternalTrafficPolicy{core.ServiceExternalTrafficPolicyCluster, core.ServiceExternalTrafficPolicyLocal} 302 *p = types[c.Rand.Intn(len(types))] 303 }, 304 func(p *core.ServiceInternalTrafficPolicy, c fuzz.Continue) { 305 types := []core.ServiceInternalTrafficPolicy{core.ServiceInternalTrafficPolicyCluster, core.ServiceInternalTrafficPolicyLocal} 306 *p = types[c.Rand.Intn(len(types))] 307 }, 308 func(ct *core.Container, c fuzz.Continue) { 309 c.FuzzNoCustom(ct) // fuzz self without calling this function again 310 ct.TerminationMessagePath = "/" + ct.TerminationMessagePath // Must be non-empty 311 ct.TerminationMessagePolicy = "File" 312 }, 313 func(ep *core.EphemeralContainer, c fuzz.Continue) { 314 c.FuzzNoCustom(ep) // fuzz self without calling this function again 315 ep.EphemeralContainerCommon.TerminationMessagePath = "/" + ep.TerminationMessagePath // Must be non-empty 316 ep.EphemeralContainerCommon.TerminationMessagePolicy = "File" 317 }, 318 func(p *core.Probe, c fuzz.Continue) { 319 c.FuzzNoCustom(p) 320 // These fields have default values. 321 intFieldsWithDefaults := [...]string{"TimeoutSeconds", "PeriodSeconds", "SuccessThreshold", "FailureThreshold"} 322 v := reflect.ValueOf(p).Elem() 323 for _, field := range intFieldsWithDefaults { 324 f := v.FieldByName(field) 325 if f.Int() == 0 { 326 f.SetInt(1) 327 } 328 } 329 }, 330 func(ev *core.EnvVar, c fuzz.Continue) { 331 ev.Name = c.RandString() 332 if c.RandBool() { 333 ev.Value = c.RandString() 334 } else { 335 ev.ValueFrom = &core.EnvVarSource{} 336 ev.ValueFrom.FieldRef = &core.ObjectFieldSelector{} 337 338 versions := []schema.GroupVersion{ 339 {Group: "admission.k8s.io", Version: "v1alpha1"}, 340 {Group: "apps", Version: "v1beta1"}, 341 {Group: "apps", Version: "v1beta2"}, 342 {Group: "foo", Version: "v42"}, 343 } 344 345 ev.ValueFrom.FieldRef.APIVersion = versions[c.Rand.Intn(len(versions))].String() 346 ev.ValueFrom.FieldRef.FieldPath = c.RandString() 347 } 348 }, 349 func(ev *core.EnvFromSource, c fuzz.Continue) { 350 if c.RandBool() { 351 ev.Prefix = "p_" 352 } 353 if c.RandBool() { 354 c.Fuzz(&ev.ConfigMapRef) 355 } else { 356 c.Fuzz(&ev.SecretRef) 357 } 358 }, 359 func(cm *core.ConfigMapEnvSource, c fuzz.Continue) { 360 c.FuzzNoCustom(cm) // fuzz self without calling this function again 361 if c.RandBool() { 362 opt := c.RandBool() 363 cm.Optional = &opt 364 } 365 }, 366 func(s *core.SecretEnvSource, c fuzz.Continue) { 367 c.FuzzNoCustom(s) // fuzz self without calling this function again 368 }, 369 func(sc *core.SecurityContext, c fuzz.Continue) { 370 c.FuzzNoCustom(sc) // fuzz self without calling this function again 371 if c.RandBool() { 372 priv := c.RandBool() 373 sc.Privileged = &priv 374 } 375 376 if c.RandBool() { 377 sc.Capabilities = &core.Capabilities{ 378 Add: make([]core.Capability, 0), 379 Drop: make([]core.Capability, 0), 380 } 381 c.Fuzz(&sc.Capabilities.Add) 382 c.Fuzz(&sc.Capabilities.Drop) 383 } 384 }, 385 func(s *core.Secret, c fuzz.Continue) { 386 c.FuzzNoCustom(s) // fuzz self without calling this function again 387 s.Type = core.SecretTypeOpaque 388 }, 389 func(r *core.RBDVolumeSource, c fuzz.Continue) { 390 r.RBDPool = c.RandString() 391 if r.RBDPool == "" { 392 r.RBDPool = "rbd" 393 } 394 r.RadosUser = c.RandString() 395 if r.RadosUser == "" { 396 r.RadosUser = "admin" 397 } 398 r.Keyring = c.RandString() 399 if r.Keyring == "" { 400 r.Keyring = "/etc/ceph/keyring" 401 } 402 }, 403 func(r *core.RBDPersistentVolumeSource, c fuzz.Continue) { 404 r.RBDPool = c.RandString() 405 if r.RBDPool == "" { 406 r.RBDPool = "rbd" 407 } 408 r.RadosUser = c.RandString() 409 if r.RadosUser == "" { 410 r.RadosUser = "admin" 411 } 412 r.Keyring = c.RandString() 413 if r.Keyring == "" { 414 r.Keyring = "/etc/ceph/keyring" 415 } 416 }, 417 func(obj *core.HostPathVolumeSource, c fuzz.Continue) { 418 c.FuzzNoCustom(obj) 419 types := []core.HostPathType{core.HostPathUnset, core.HostPathDirectoryOrCreate, core.HostPathDirectory, 420 core.HostPathFileOrCreate, core.HostPathFile, core.HostPathSocket, core.HostPathCharDev, core.HostPathBlockDev} 421 typeVol := types[c.Rand.Intn(len(types))] 422 if obj.Type == nil { 423 obj.Type = &typeVol 424 } 425 }, 426 func(pv *core.PersistentVolume, c fuzz.Continue) { 427 c.FuzzNoCustom(pv) // fuzz self without calling this function again 428 types := []core.PersistentVolumePhase{core.VolumeAvailable, core.VolumePending, core.VolumeBound, core.VolumeReleased, core.VolumeFailed} 429 pv.Status.Phase = types[c.Rand.Intn(len(types))] 430 pv.Status.Message = c.RandString() 431 reclamationPolicies := []core.PersistentVolumeReclaimPolicy{core.PersistentVolumeReclaimRecycle, core.PersistentVolumeReclaimRetain} 432 pv.Spec.PersistentVolumeReclaimPolicy = reclamationPolicies[c.Rand.Intn(len(reclamationPolicies))] 433 volumeModes := []core.PersistentVolumeMode{core.PersistentVolumeFilesystem, core.PersistentVolumeBlock} 434 pv.Spec.VolumeMode = &volumeModes[c.Rand.Intn(len(volumeModes))] 435 }, 436 func(pvc *core.PersistentVolumeClaim, c fuzz.Continue) { 437 c.FuzzNoCustom(pvc) // fuzz self without calling this function again 438 types := []core.PersistentVolumeClaimPhase{core.ClaimBound, core.ClaimPending, core.ClaimLost} 439 pvc.Status.Phase = types[c.Rand.Intn(len(types))] 440 volumeModes := []core.PersistentVolumeMode{core.PersistentVolumeFilesystem, core.PersistentVolumeBlock} 441 pvc.Spec.VolumeMode = &volumeModes[c.Rand.Intn(len(volumeModes))] 442 }, 443 func(obj *core.AzureDiskVolumeSource, c fuzz.Continue) { 444 if obj.CachingMode == nil { 445 obj.CachingMode = new(core.AzureDataDiskCachingMode) 446 *obj.CachingMode = core.AzureDataDiskCachingReadWrite 447 } 448 if obj.Kind == nil { 449 obj.Kind = new(core.AzureDataDiskKind) 450 *obj.Kind = core.AzureSharedBlobDisk 451 } 452 if obj.FSType == nil { 453 obj.FSType = new(string) 454 *obj.FSType = "ext4" 455 } 456 if obj.ReadOnly == nil { 457 obj.ReadOnly = new(bool) 458 *obj.ReadOnly = false 459 } 460 }, 461 func(sio *core.ScaleIOVolumeSource, c fuzz.Continue) { 462 sio.StorageMode = c.RandString() 463 if sio.StorageMode == "" { 464 sio.StorageMode = "ThinProvisioned" 465 } 466 sio.FSType = c.RandString() 467 if sio.FSType == "" { 468 sio.FSType = "xfs" 469 } 470 }, 471 func(sio *core.ScaleIOPersistentVolumeSource, c fuzz.Continue) { 472 sio.StorageMode = c.RandString() 473 if sio.StorageMode == "" { 474 sio.StorageMode = "ThinProvisioned" 475 } 476 sio.FSType = c.RandString() 477 if sio.FSType == "" { 478 sio.FSType = "xfs" 479 } 480 }, 481 func(s *core.NamespaceSpec, c fuzz.Continue) { 482 s.Finalizers = []core.FinalizerName{core.FinalizerKubernetes} 483 }, 484 func(s *core.Namespace, c fuzz.Continue) { 485 c.FuzzNoCustom(s) // fuzz self without calling this function again 486 // Match name --> label defaulting 487 if len(s.Name) > 0 { 488 if s.Labels == nil { 489 s.Labels = map[string]string{} 490 } 491 s.Labels["kubernetes.io/metadata.name"] = s.Name 492 } 493 }, 494 func(s *core.NamespaceStatus, c fuzz.Continue) { 495 s.Phase = core.NamespaceActive 496 }, 497 func(http *core.HTTPGetAction, c fuzz.Continue) { 498 c.FuzzNoCustom(http) // fuzz self without calling this function again 499 http.Path = "/" + http.Path // can't be blank 500 http.Scheme = "x" + http.Scheme // can't be blank 501 }, 502 func(ss *core.ServiceSpec, c fuzz.Continue) { 503 c.FuzzNoCustom(ss) // fuzz self without calling this function again 504 if len(ss.Ports) == 0 { 505 // There must be at least 1 port. 506 ss.Ports = append(ss.Ports, core.ServicePort{}) 507 c.Fuzz(&ss.Ports[0]) 508 } 509 for i := range ss.Ports { 510 switch ss.Ports[i].TargetPort.Type { 511 case intstr.Int: 512 ss.Ports[i].TargetPort.IntVal = 1 + ss.Ports[i].TargetPort.IntVal%65535 // non-zero 513 case intstr.String: 514 ss.Ports[i].TargetPort.StrVal = "x" + ss.Ports[i].TargetPort.StrVal // non-empty 515 } 516 } 517 types := []core.ServiceAffinity{core.ServiceAffinityNone, core.ServiceAffinityClientIP} 518 ss.SessionAffinity = types[c.Rand.Intn(len(types))] 519 switch ss.SessionAffinity { 520 case core.ServiceAffinityClientIP: 521 timeoutSeconds := int32(c.Rand.Intn(int(core.MaxClientIPServiceAffinitySeconds))) 522 ss.SessionAffinityConfig = &core.SessionAffinityConfig{ 523 ClientIP: &core.ClientIPConfig{ 524 TimeoutSeconds: &timeoutSeconds, 525 }, 526 } 527 case core.ServiceAffinityNone: 528 ss.SessionAffinityConfig = nil 529 } 530 if ss.AllocateLoadBalancerNodePorts == nil { 531 ss.AllocateLoadBalancerNodePorts = utilpointer.Bool(true) 532 } 533 }, 534 func(s *core.NodeStatus, c fuzz.Continue) { 535 c.FuzzNoCustom(s) 536 s.Allocatable = s.Capacity 537 }, 538 func(e *core.Event, c fuzz.Continue) { 539 c.FuzzNoCustom(e) 540 e.EventTime = metav1.MicroTime{Time: time.Unix(1, 1000)} 541 if e.Series != nil { 542 e.Series.LastObservedTime = metav1.MicroTime{Time: time.Unix(3, 3000)} 543 } 544 }, 545 func(j *core.GRPCAction, c fuzz.Continue) { 546 empty := "" 547 if j.Service == nil { 548 j.Service = &empty 549 } 550 }, 551 } 552 }