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  }