sigs.k8s.io/cluster-api@v1.7.1/internal/apis/core/v1alpha4/conversion_test.go (about) 1 /* 2 Copyright 2021 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 v1alpha4 18 19 import ( 20 "strconv" 21 "testing" 22 23 fuzz "github.com/google/gofuzz" 24 apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" 25 "k8s.io/apimachinery/pkg/api/apitesting/fuzzer" 26 runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer" 27 "k8s.io/utils/ptr" 28 29 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" 30 utilconversion "sigs.k8s.io/cluster-api/util/conversion" 31 ) 32 33 func TestFuzzyConversion(t *testing.T) { 34 t.Run("for Cluster", utilconversion.FuzzTestFunc(utilconversion.FuzzTestFuncInput{ 35 Hub: &clusterv1.Cluster{}, 36 Spoke: &Cluster{}, 37 FuzzerFuncs: []fuzzer.FuzzerFuncs{ClusterJSONFuzzFuncs}, 38 })) 39 t.Run("for ClusterClass", utilconversion.FuzzTestFunc(utilconversion.FuzzTestFuncInput{ 40 Hub: &clusterv1.ClusterClass{}, 41 Spoke: &ClusterClass{}, 42 FuzzerFuncs: []fuzzer.FuzzerFuncs{ClusterClassJSONFuzzFuncs}, 43 })) 44 45 t.Run("for Machine", utilconversion.FuzzTestFunc(utilconversion.FuzzTestFuncInput{ 46 Hub: &clusterv1.Machine{}, 47 Spoke: &Machine{}, 48 FuzzerFuncs: []fuzzer.FuzzerFuncs{MachineStatusFuzzFunc}, 49 })) 50 51 t.Run("for MachineSet", utilconversion.FuzzTestFunc(utilconversion.FuzzTestFuncInput{ 52 Hub: &clusterv1.MachineSet{}, 53 Spoke: &MachineSet{}, 54 })) 55 56 t.Run("for MachineDeployment", utilconversion.FuzzTestFunc(utilconversion.FuzzTestFuncInput{ 57 Hub: &clusterv1.MachineDeployment{}, 58 Spoke: &MachineDeployment{}, 59 })) 60 61 t.Run("for MachineHealthCheck", utilconversion.FuzzTestFunc(utilconversion.FuzzTestFuncInput{ 62 Hub: &clusterv1.MachineHealthCheck{}, 63 Spoke: &MachineHealthCheck{}, 64 })) 65 } 66 67 func MachineStatusFuzzFunc(_ runtimeserializer.CodecFactory) []interface{} { 68 return []interface{}{ 69 MachineStatusFuzzer, 70 } 71 } 72 73 func MachineStatusFuzzer(in *MachineStatus, c fuzz.Continue) { 74 c.FuzzNoCustom(in) 75 76 // These fields have been removed in v1beta1 77 // data is going to be lost, so we're forcing zero values to avoid round trip errors. 78 in.Version = nil 79 } 80 81 func ClusterJSONFuzzFuncs(_ runtimeserializer.CodecFactory) []interface{} { 82 return []interface{}{ 83 ClusterVariableFuzzer, 84 } 85 } 86 87 func ClusterVariableFuzzer(in *clusterv1.ClusterVariable, c fuzz.Continue) { 88 c.FuzzNoCustom(in) 89 90 // Not every random byte array is valid JSON, e.g. a string without `""`,so we're setting a valid value. 91 in.Value = apiextensionsv1.JSON{Raw: []byte("\"test-string\"")} 92 } 93 94 func ClusterClassJSONFuzzFuncs(_ runtimeserializer.CodecFactory) []interface{} { 95 return []interface{}{ 96 JSONPatchFuzzer, 97 JSONSchemaPropsFuzzer, 98 } 99 } 100 101 func JSONPatchFuzzer(in *clusterv1.JSONPatch, c fuzz.Continue) { 102 c.FuzzNoCustom(in) 103 104 // Not every random byte array is valid JSON, e.g. a string without `""`,so we're setting a valid value. 105 in.Value = &apiextensionsv1.JSON{Raw: []byte("5")} 106 } 107 108 func JSONSchemaPropsFuzzer(in *clusterv1.JSONSchemaProps, c fuzz.Continue) { 109 // NOTE: We have to fuzz the individual fields manually, 110 // because we cannot call `FuzzNoCustom` as it would lead 111 // to an infinite recursion. 112 in.Type = c.RandString() 113 for i := 0; i < c.Intn(10); i++ { 114 in.Required = append(in.Required, c.RandString()) 115 } 116 in.MaxItems = ptr.To(c.Int63()) 117 in.MinItems = ptr.To(c.Int63()) 118 in.UniqueItems = c.RandBool() 119 in.Format = c.RandString() 120 in.MaxLength = ptr.To(c.Int63()) 121 in.MinLength = ptr.To(c.Int63()) 122 in.Pattern = c.RandString() 123 in.Maximum = ptr.To(c.Int63()) 124 in.Maximum = ptr.To(c.Int63()) 125 in.ExclusiveMaximum = c.RandBool() 126 in.Minimum = ptr.To(c.Int63()) 127 in.ExclusiveMinimum = c.RandBool() 128 129 // Not every random byte array is valid JSON, e.g. a string without `""`,so we're setting valid values. 130 in.Enum = []apiextensionsv1.JSON{ 131 {Raw: []byte("\"a\"")}, 132 {Raw: []byte("\"b\"")}, 133 {Raw: []byte("\"c\"")}, 134 } 135 in.Default = &apiextensionsv1.JSON{Raw: []byte(strconv.FormatBool(c.RandBool()))} 136 137 // We're using a copy of the current JSONSchemaProps, 138 // because we cannot recursively fuzz new schemas. 139 in.AdditionalProperties = in.DeepCopy() 140 141 // We're using a copy of the current JSONSchemaProps, 142 // because we cannot recursively fuzz new schemas. 143 in.Properties = map[string]clusterv1.JSONSchemaProps{} 144 for i := 0; i < c.Intn(10); i++ { 145 in.Properties[c.RandString()] = *in.DeepCopy() 146 } 147 in.Items = in.DeepCopy() 148 }