fuzzer.go 17 KB

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