sigs.k8s.io/cluster-api@v1.7.1/internal/topology/variables/cluster_variable_defaulting_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 variables
    18  
    19  import (
    20  	"testing"
    21  
    22  	. "github.com/onsi/gomega"
    23  	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
    24  	"k8s.io/apimachinery/pkg/util/validation/field"
    25  
    26  	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
    27  )
    28  
    29  func Test_DefaultClusterVariables(t *testing.T) {
    30  	tests := []struct {
    31  		name            string
    32  		definitions     []clusterv1.ClusterClassStatusVariable
    33  		values          []clusterv1.ClusterVariable
    34  		createVariables bool
    35  		want            []clusterv1.ClusterVariable
    36  		wantErr         bool
    37  	}{
    38  		{
    39  			name:        "Return error if variable is not defined in ClusterClass",
    40  			definitions: []clusterv1.ClusterClassStatusVariable{},
    41  			values: []clusterv1.ClusterVariable{
    42  				{
    43  					Name: "cpu",
    44  					Value: apiextensionsv1.JSON{
    45  						Raw: []byte(`1`),
    46  					},
    47  				},
    48  			},
    49  			createVariables: true,
    50  			wantErr:         true,
    51  		},
    52  		{
    53  			name: "Default one variable of each valid type",
    54  			definitions: []clusterv1.ClusterClassStatusVariable{
    55  				{
    56  					Name: "cpu",
    57  					Definitions: []clusterv1.ClusterClassStatusVariableDefinition{
    58  						{
    59  
    60  							Required: true,
    61  							From:     clusterv1.VariableDefinitionFromInline,
    62  							Schema: clusterv1.VariableSchema{
    63  								OpenAPIV3Schema: clusterv1.JSONSchemaProps{
    64  									Type:    "integer",
    65  									Default: &apiextensionsv1.JSON{Raw: []byte(`1`)},
    66  								},
    67  							},
    68  						},
    69  					},
    70  				},
    71  				{
    72  					Name: "location",
    73  					Definitions: []clusterv1.ClusterClassStatusVariableDefinition{
    74  						{
    75  
    76  							Required: true,
    77  							From:     clusterv1.VariableDefinitionFromInline,
    78  							Schema: clusterv1.VariableSchema{
    79  								OpenAPIV3Schema: clusterv1.JSONSchemaProps{
    80  									Type:    "string",
    81  									Default: &apiextensionsv1.JSON{Raw: []byte(`"us-east"`)},
    82  								},
    83  							},
    84  						},
    85  					},
    86  				},
    87  
    88  				{
    89  					Name: "count",
    90  					Definitions: []clusterv1.ClusterClassStatusVariableDefinition{
    91  						{
    92  
    93  							Required: true,
    94  							From:     clusterv1.VariableDefinitionFromInline,
    95  							Schema: clusterv1.VariableSchema{
    96  								OpenAPIV3Schema: clusterv1.JSONSchemaProps{
    97  									Type:    "number",
    98  									Default: &apiextensionsv1.JSON{Raw: []byte(`0.1`)},
    99  								},
   100  							},
   101  						},
   102  					},
   103  				},
   104  				{
   105  					Name: "correct",
   106  					Definitions: []clusterv1.ClusterClassStatusVariableDefinition{
   107  						{
   108  
   109  							Required: true,
   110  							From:     clusterv1.VariableDefinitionFromInline,
   111  							Schema: clusterv1.VariableSchema{
   112  								OpenAPIV3Schema: clusterv1.JSONSchemaProps{
   113  									Type:    "boolean",
   114  									Default: &apiextensionsv1.JSON{Raw: []byte(`true`)},
   115  								},
   116  							},
   117  						},
   118  					},
   119  				},
   120  			},
   121  			values:          []clusterv1.ClusterVariable{},
   122  			createVariables: true,
   123  			want: []clusterv1.ClusterVariable{
   124  				{
   125  					Name: "cpu",
   126  					Value: apiextensionsv1.JSON{
   127  						Raw: []byte(`1`),
   128  					},
   129  				},
   130  				{
   131  					Name: "location",
   132  					Value: apiextensionsv1.JSON{
   133  						Raw: []byte(`"us-east"`),
   134  					},
   135  				},
   136  				{
   137  					Name: "count",
   138  					Value: apiextensionsv1.JSON{
   139  						Raw: []byte(`0.1`),
   140  					},
   141  				},
   142  				{
   143  					Name: "correct",
   144  					Value: apiextensionsv1.JSON{
   145  						Raw: []byte(`true`),
   146  					},
   147  				},
   148  			},
   149  		},
   150  		{
   151  			name: "Don't default variable if variable creation is disabled",
   152  			definitions: []clusterv1.ClusterClassStatusVariable{
   153  				{
   154  					Name: "cpu",
   155  					Definitions: []clusterv1.ClusterClassStatusVariableDefinition{
   156  						{
   157  
   158  							Required: true,
   159  							From:     clusterv1.VariableDefinitionFromInline,
   160  							Schema: clusterv1.VariableSchema{
   161  								OpenAPIV3Schema: clusterv1.JSONSchemaProps{
   162  									Type:    "integer",
   163  									Default: &apiextensionsv1.JSON{Raw: []byte(`1`)},
   164  								},
   165  							},
   166  						},
   167  					},
   168  				},
   169  			},
   170  			values:          []clusterv1.ClusterVariable{},
   171  			createVariables: false,
   172  			want:            []clusterv1.ClusterVariable{},
   173  		},
   174  		{
   175  			name: "Don't default variables that are set",
   176  			definitions: []clusterv1.ClusterClassStatusVariable{
   177  				{
   178  					Name: "cpu",
   179  					Definitions: []clusterv1.ClusterClassStatusVariableDefinition{
   180  						{
   181  
   182  							Required: true,
   183  							From:     clusterv1.VariableDefinitionFromInline,
   184  							Schema: clusterv1.VariableSchema{
   185  								OpenAPIV3Schema: clusterv1.JSONSchemaProps{
   186  									Type:    "integer",
   187  									Default: &apiextensionsv1.JSON{Raw: []byte(`1`)},
   188  								},
   189  							},
   190  						},
   191  					},
   192  				},
   193  				{
   194  					Name: "correct",
   195  					Definitions: []clusterv1.ClusterClassStatusVariableDefinition{
   196  						{
   197  
   198  							Required: true,
   199  							From:     clusterv1.VariableDefinitionFromInline,
   200  							Schema: clusterv1.VariableSchema{
   201  								OpenAPIV3Schema: clusterv1.JSONSchemaProps{
   202  									Type:    "boolean",
   203  									Default: &apiextensionsv1.JSON{Raw: []byte(`true`)},
   204  								},
   205  							},
   206  						},
   207  					},
   208  				},
   209  			},
   210  			values: []clusterv1.ClusterVariable{
   211  				{
   212  					Name: "correct",
   213  					// Value is set here and shouldn't be defaulted.
   214  					Value: apiextensionsv1.JSON{
   215  						Raw: []byte(`false`),
   216  					},
   217  				},
   218  			},
   219  			createVariables: true,
   220  			want: []clusterv1.ClusterVariable{
   221  				{
   222  					Name: "correct",
   223  					Value: apiextensionsv1.JSON{
   224  						Raw: []byte(`false`),
   225  					},
   226  				},
   227  				{
   228  					Name: "cpu",
   229  					Value: apiextensionsv1.JSON{
   230  						Raw: []byte(`1`),
   231  					},
   232  				},
   233  			},
   234  		},
   235  		{
   236  			name: "Don't add variables that have no default schema",
   237  			definitions: []clusterv1.ClusterClassStatusVariable{
   238  				{
   239  					Name: "cpu",
   240  					Definitions: []clusterv1.ClusterClassStatusVariableDefinition{
   241  						{
   242  
   243  							Required: true,
   244  							From:     clusterv1.VariableDefinitionFromInline,
   245  							Schema: clusterv1.VariableSchema{
   246  								OpenAPIV3Schema: clusterv1.JSONSchemaProps{
   247  									Type:    "integer",
   248  									Default: &apiextensionsv1.JSON{Raw: []byte(`1`)},
   249  								},
   250  							},
   251  						},
   252  					},
   253  				},
   254  				{
   255  					Name: "correct",
   256  					Definitions: []clusterv1.ClusterClassStatusVariableDefinition{
   257  						{
   258  							Required: true,
   259  							From:     clusterv1.VariableDefinitionFromInline,
   260  							Schema: clusterv1.VariableSchema{
   261  								OpenAPIV3Schema: clusterv1.JSONSchemaProps{
   262  									Type: "boolean",
   263  								},
   264  							},
   265  						},
   266  					},
   267  				},
   268  			},
   269  			values:          []clusterv1.ClusterVariable{},
   270  			createVariables: true,
   271  			want: []clusterv1.ClusterVariable{
   272  				{
   273  					Name: "cpu",
   274  					Value: apiextensionsv1.JSON{
   275  						Raw: []byte(`1`),
   276  					},
   277  				},
   278  			},
   279  		},
   280  		// Variables with multiple non-conflicting definitions.
   281  		{
   282  			name: "Don't default if a value is set with empty definitionFrom",
   283  			definitions: []clusterv1.ClusterClassStatusVariable{
   284  				{
   285  					Name: "cpu",
   286  					Definitions: []clusterv1.ClusterClassStatusVariableDefinition{
   287  						{
   288  							Required: true,
   289  							From:     clusterv1.VariableDefinitionFromInline,
   290  							Schema: clusterv1.VariableSchema{
   291  								OpenAPIV3Schema: clusterv1.JSONSchemaProps{
   292  									Type:    "integer",
   293  									Default: &apiextensionsv1.JSON{Raw: []byte(`1`)},
   294  								},
   295  							},
   296  						},
   297  						{
   298  							Required: true,
   299  							From:     "somepatch",
   300  							Schema: clusterv1.VariableSchema{
   301  								OpenAPIV3Schema: clusterv1.JSONSchemaProps{
   302  									Type:    "integer",
   303  									Default: &apiextensionsv1.JSON{Raw: []byte(`1`)},
   304  								},
   305  							},
   306  						},
   307  					},
   308  				},
   309  			},
   310  			values: []clusterv1.ClusterVariable{
   311  				{
   312  					Name: "cpu",
   313  					Value: apiextensionsv1.JSON{
   314  						Raw: []byte(`2`),
   315  					},
   316  					// This variable does not have definitionConflicts. An unset definitionFrom is valid.
   317  				},
   318  			},
   319  			createVariables: true,
   320  			want: []clusterv1.ClusterVariable{
   321  				{
   322  					Name: "cpu",
   323  					Value: apiextensionsv1.JSON{
   324  						// Expect value set in Cluster.
   325  						Raw: []byte(`2`),
   326  					},
   327  				},
   328  			},
   329  		},
   330  		{
   331  			name: "Default many non-conflicting variables to one value in Cluster",
   332  			definitions: []clusterv1.ClusterClassStatusVariable{
   333  				{
   334  					Name: "cpu",
   335  					Definitions: []clusterv1.ClusterClassStatusVariableDefinition{
   336  						{
   337  
   338  							Required: true,
   339  							From:     clusterv1.VariableDefinitionFromInline,
   340  							Schema: clusterv1.VariableSchema{
   341  								OpenAPIV3Schema: clusterv1.JSONSchemaProps{
   342  									Type:    "integer",
   343  									Default: &apiextensionsv1.JSON{Raw: []byte(`1`)},
   344  								},
   345  							},
   346  						},
   347  						{
   348  
   349  							Required: true,
   350  							From:     "somepatch",
   351  							Schema: clusterv1.VariableSchema{
   352  								OpenAPIV3Schema: clusterv1.JSONSchemaProps{
   353  									Type:    "integer",
   354  									Default: &apiextensionsv1.JSON{Raw: []byte(`1`)},
   355  								},
   356  							},
   357  						},
   358  						{
   359  
   360  							Required: true,
   361  							From:     "otherpatch",
   362  							Schema: clusterv1.VariableSchema{
   363  								OpenAPIV3Schema: clusterv1.JSONSchemaProps{
   364  									Type:    "integer",
   365  									Default: &apiextensionsv1.JSON{Raw: []byte(`1`)},
   366  								},
   367  							},
   368  						},
   369  					},
   370  				},
   371  			},
   372  			values:          []clusterv1.ClusterVariable{},
   373  			createVariables: true,
   374  			want: []clusterv1.ClusterVariable{
   375  				{
   376  					// As this variable is non-conflicting it can be set only once in the Cluster through defaulting.
   377  					Name: "cpu",
   378  					Value: apiextensionsv1.JSON{
   379  						Raw: []byte(`1`),
   380  					},
   381  				},
   382  			},
   383  		},
   384  		{
   385  			name: "Default many non-conflicting definitions to many values in Cluster when some values are set with definitionFrom",
   386  			definitions: []clusterv1.ClusterClassStatusVariable{
   387  				{
   388  					Name: "cpu",
   389  					Definitions: []clusterv1.ClusterClassStatusVariableDefinition{
   390  						{
   391  							Required: true,
   392  							From:     clusterv1.VariableDefinitionFromInline,
   393  							Schema: clusterv1.VariableSchema{
   394  								OpenAPIV3Schema: clusterv1.JSONSchemaProps{
   395  									Type:    "integer",
   396  									Default: &apiextensionsv1.JSON{Raw: []byte(`1`)},
   397  								},
   398  							},
   399  						},
   400  						{
   401  							Required: true,
   402  							From:     "somepatch",
   403  							Schema: clusterv1.VariableSchema{
   404  								OpenAPIV3Schema: clusterv1.JSONSchemaProps{
   405  									Type:    "integer",
   406  									Default: &apiextensionsv1.JSON{Raw: []byte(`1`)},
   407  								},
   408  							},
   409  						},
   410  					},
   411  				},
   412  			},
   413  			values: []clusterv1.ClusterVariable{
   414  				// The variable is set with definitionFrom for one of two definitions.
   415  				{
   416  					Name: "cpu",
   417  					Value: apiextensionsv1.JSON{
   418  						Raw: []byte(`2`),
   419  					},
   420  					DefinitionFrom: clusterv1.VariableDefinitionFromInline,
   421  				},
   422  			},
   423  			createVariables: true,
   424  			want: []clusterv1.ClusterVariable{
   425  				// Expect the value in the Cluster to be retained.
   426  				{
   427  					Name: "cpu",
   428  					Value: apiextensionsv1.JSON{
   429  						Raw: []byte(`2`),
   430  					},
   431  					DefinitionFrom: clusterv1.VariableDefinitionFromInline,
   432  				},
   433  				// Expect defaulting for the definition with no value in the Cluster.
   434  				{
   435  					Name: "cpu",
   436  					Value: apiextensionsv1.JSON{
   437  						Raw: []byte(`1`),
   438  					},
   439  					DefinitionFrom: "somepatch",
   440  				},
   441  			},
   442  		},
   443  		// Variables with conflicting definitions.
   444  		{
   445  			name: "Default many conflicting definitions to many values in Cluster",
   446  			definitions: []clusterv1.ClusterClassStatusVariable{
   447  				{
   448  					Name:                "cpu",
   449  					DefinitionsConflict: true,
   450  					Definitions: []clusterv1.ClusterClassStatusVariableDefinition{
   451  						{
   452  
   453  							Required: true,
   454  							From:     clusterv1.VariableDefinitionFromInline,
   455  							Schema: clusterv1.VariableSchema{
   456  								OpenAPIV3Schema: clusterv1.JSONSchemaProps{
   457  									Type:    "integer",
   458  									Default: &apiextensionsv1.JSON{Raw: []byte(`1`)},
   459  								},
   460  							},
   461  						},
   462  						{
   463  							// Variable isn't required, but should still be defaulted according to its schema.
   464  							Required: false,
   465  							From:     "somepatch",
   466  							Schema: clusterv1.VariableSchema{
   467  								OpenAPIV3Schema: clusterv1.JSONSchemaProps{
   468  									Type:    "integer",
   469  									Default: &apiextensionsv1.JSON{Raw: []byte(`2`)},
   470  								},
   471  							},
   472  						},
   473  					},
   474  				},
   475  			},
   476  			values:          []clusterv1.ClusterVariable{},
   477  			createVariables: true,
   478  			want: []clusterv1.ClusterVariable{
   479  				// As this variable has conflicting definitions expect one value in the Cluster
   480  				// for each "DefinitionFrom".
   481  				{
   482  					Name: "cpu",
   483  					Value: apiextensionsv1.JSON{
   484  						Raw: []byte(`1`),
   485  					},
   486  					DefinitionFrom: clusterv1.VariableDefinitionFromInline,
   487  				},
   488  				{
   489  					Name: "cpu",
   490  					Value: apiextensionsv1.JSON{
   491  						Raw: []byte(`2`),
   492  					},
   493  					DefinitionFrom: "somepatch",
   494  				},
   495  			},
   496  		},
   497  		{
   498  			name: "Default many conflicting definitions to many values in Cluster when some values are set with definitionFrom",
   499  			definitions: []clusterv1.ClusterClassStatusVariable{
   500  				{
   501  					Name:                "cpu",
   502  					DefinitionsConflict: true,
   503  					Definitions: []clusterv1.ClusterClassStatusVariableDefinition{
   504  						{
   505  
   506  							Required: true,
   507  							From:     clusterv1.VariableDefinitionFromInline,
   508  							Schema: clusterv1.VariableSchema{
   509  								OpenAPIV3Schema: clusterv1.JSONSchemaProps{
   510  									Type:    "integer",
   511  									Default: &apiextensionsv1.JSON{Raw: []byte(`1`)},
   512  								},
   513  							},
   514  						},
   515  						{
   516  							// Variable isn't required, but should still be defaulted according to its schema.
   517  							Required: false,
   518  							From:     "somepatch",
   519  							Schema: clusterv1.VariableSchema{
   520  								OpenAPIV3Schema: clusterv1.JSONSchemaProps{
   521  									Type:    "integer",
   522  									Default: &apiextensionsv1.JSON{Raw: []byte(`2`)},
   523  								},
   524  							},
   525  						},
   526  					},
   527  				},
   528  			},
   529  			values: []clusterv1.ClusterVariable{
   530  				{
   531  					Name: "cpu",
   532  					Value: apiextensionsv1.JSON{
   533  						Raw: []byte(`3`),
   534  					},
   535  					DefinitionFrom: clusterv1.VariableDefinitionFromInline,
   536  				},
   537  			},
   538  			createVariables: true,
   539  			want: []clusterv1.ClusterVariable{
   540  				// As this variable has conflicting definitions expect one value in the Cluster
   541  				// for each "DefinitionFrom" and retain values set in Cluster.
   542  				{
   543  					Name: "cpu",
   544  					Value: apiextensionsv1.JSON{
   545  						Raw: []byte(`3`),
   546  					},
   547  					DefinitionFrom: clusterv1.VariableDefinitionFromInline,
   548  				},
   549  				{
   550  					Name: "cpu",
   551  					Value: apiextensionsv1.JSON{
   552  						Raw: []byte(`2`),
   553  					},
   554  					DefinitionFrom: "somepatch",
   555  				},
   556  			},
   557  		},
   558  		{
   559  			name:    "Error if a value is set with empty and non-empty definitionFrom.",
   560  			wantErr: true,
   561  			definitions: []clusterv1.ClusterClassStatusVariable{
   562  				{
   563  					Name: "cpu",
   564  					Definitions: []clusterv1.ClusterClassStatusVariableDefinition{
   565  						{
   566  							Schema: clusterv1.VariableSchema{
   567  								OpenAPIV3Schema: clusterv1.JSONSchemaProps{
   568  									Type: "integer",
   569  								},
   570  							},
   571  							From: "somepatch",
   572  						},
   573  						{
   574  							Schema: clusterv1.VariableSchema{
   575  								OpenAPIV3Schema: clusterv1.JSONSchemaProps{
   576  									Type: "integer",
   577  								},
   578  							},
   579  							From: clusterv1.VariableDefinitionFromInline,
   580  						},
   581  					},
   582  				},
   583  			},
   584  			values: []clusterv1.ClusterVariable{
   585  				{
   586  					Name: "cpu",
   587  					Value: apiextensionsv1.JSON{
   588  						Raw: []byte(`1`),
   589  					},
   590  					// Mix of empty and non-empty definitionFrom is not valid.
   591  					DefinitionFrom: "",
   592  				},
   593  				{
   594  					Name: "cpu",
   595  					Value: apiextensionsv1.JSON{
   596  						Raw: []byte(`2`),
   597  					},
   598  					DefinitionFrom: "somepatch",
   599  				},
   600  			},
   601  		},
   602  		{
   603  			name:    "Error if a value is set twice with the same definitionFrom.",
   604  			wantErr: true,
   605  			definitions: []clusterv1.ClusterClassStatusVariable{
   606  				{
   607  					Name: "cpu",
   608  					Definitions: []clusterv1.ClusterClassStatusVariableDefinition{
   609  						{
   610  							Schema: clusterv1.VariableSchema{
   611  								OpenAPIV3Schema: clusterv1.JSONSchemaProps{
   612  									Type: "integer",
   613  								},
   614  							},
   615  							From: "somepatch",
   616  						},
   617  						{
   618  							Schema: clusterv1.VariableSchema{
   619  								OpenAPIV3Schema: clusterv1.JSONSchemaProps{
   620  									Type: "integer",
   621  								},
   622  							},
   623  							From: clusterv1.VariableDefinitionFromInline,
   624  						},
   625  					},
   626  				},
   627  			},
   628  			values: []clusterv1.ClusterVariable{
   629  				// Identical definitionFrom and name for two different values.
   630  				{
   631  					Name: "cpu",
   632  					Value: apiextensionsv1.JSON{
   633  						Raw: []byte(`1`),
   634  					},
   635  					DefinitionFrom: "",
   636  				},
   637  				{
   638  					Name: "cpu",
   639  					Value: apiextensionsv1.JSON{
   640  						Raw: []byte(`2`),
   641  					},
   642  					DefinitionFrom: "",
   643  				},
   644  			},
   645  		},
   646  	}
   647  	for _, tt := range tests {
   648  		t.Run(tt.name, func(t *testing.T) {
   649  			g := NewWithT(t)
   650  
   651  			vars, errList := defaultClusterVariables(tt.values, tt.definitions, tt.createVariables,
   652  				field.NewPath("spec", "topology", "variables"))
   653  
   654  			if tt.wantErr {
   655  				g.Expect(errList).NotTo(BeEmpty())
   656  				return
   657  			}
   658  			g.Expect(errList).To(BeEmpty())
   659  			g.Expect(vars).To(BeComparableTo(tt.want))
   660  		})
   661  	}
   662  }
   663  
   664  func Test_DefaultClusterVariable(t *testing.T) {
   665  	tests := []struct {
   666  		name                 string
   667  		clusterVariable      *clusterv1.ClusterVariable
   668  		clusterClassVariable *statusVariableDefinition
   669  		createVariable       bool
   670  		want                 *clusterv1.ClusterVariable
   671  		wantErr              bool
   672  	}{
   673  		{
   674  			name: "Default new integer variable",
   675  			clusterClassVariable: &statusVariableDefinition{
   676  				Name: "cpu",
   677  				ClusterClassStatusVariableDefinition: &clusterv1.ClusterClassStatusVariableDefinition{
   678  					Required: true,
   679  					Schema: clusterv1.VariableSchema{
   680  						OpenAPIV3Schema: clusterv1.JSONSchemaProps{
   681  							Type:    "integer",
   682  							Default: &apiextensionsv1.JSON{Raw: []byte(`1`)},
   683  						},
   684  					},
   685  				},
   686  			},
   687  			createVariable: true,
   688  			want: &clusterv1.ClusterVariable{
   689  				Name: "cpu",
   690  				Value: apiextensionsv1.JSON{
   691  					Raw: []byte(`1`),
   692  				},
   693  			},
   694  		},
   695  		{
   696  			name: "Don't default new integer variable if variable creation is disabled",
   697  			clusterClassVariable: &statusVariableDefinition{
   698  				Name: "cpu",
   699  				ClusterClassStatusVariableDefinition: &clusterv1.ClusterClassStatusVariableDefinition{
   700  					Required: true,
   701  					Schema: clusterv1.VariableSchema{
   702  						OpenAPIV3Schema: clusterv1.JSONSchemaProps{
   703  							Type:    "integer",
   704  							Default: &apiextensionsv1.JSON{Raw: []byte(`1`)},
   705  						},
   706  					},
   707  				},
   708  			},
   709  			createVariable: false,
   710  			want:           nil,
   711  		},
   712  		{
   713  			name: "Don't default existing integer variable",
   714  			clusterClassVariable: &statusVariableDefinition{
   715  				Name: "cpu",
   716  				ClusterClassStatusVariableDefinition: &clusterv1.ClusterClassStatusVariableDefinition{
   717  					Required: true,
   718  					Schema: clusterv1.VariableSchema{
   719  						OpenAPIV3Schema: clusterv1.JSONSchemaProps{
   720  							Type:    "integer",
   721  							Default: &apiextensionsv1.JSON{Raw: []byte(`1`)},
   722  						},
   723  					},
   724  				},
   725  			},
   726  			clusterVariable: &clusterv1.ClusterVariable{
   727  				Name: "cpu",
   728  				Value: apiextensionsv1.JSON{
   729  					Raw: []byte(`2`),
   730  				},
   731  			},
   732  			createVariable: true,
   733  			want: &clusterv1.ClusterVariable{
   734  				Name: "cpu",
   735  				Value: apiextensionsv1.JSON{
   736  					Raw: []byte(`2`),
   737  				},
   738  			},
   739  		},
   740  		{
   741  			name: "Default new string variable",
   742  			clusterClassVariable: &statusVariableDefinition{
   743  				Name: "location",
   744  				ClusterClassStatusVariableDefinition: &clusterv1.ClusterClassStatusVariableDefinition{
   745  					Required: true,
   746  					Schema: clusterv1.VariableSchema{
   747  						OpenAPIV3Schema: clusterv1.JSONSchemaProps{
   748  							Type:    "string",
   749  							Default: &apiextensionsv1.JSON{Raw: []byte(`"us-east"`)},
   750  						},
   751  					},
   752  				},
   753  			},
   754  			createVariable: true,
   755  			want: &clusterv1.ClusterVariable{
   756  				Name: "location",
   757  				Value: apiextensionsv1.JSON{
   758  					Raw: []byte(`"us-east"`),
   759  				},
   760  			},
   761  		},
   762  		{
   763  			name: "Don't default existing string variable",
   764  			clusterClassVariable: &statusVariableDefinition{
   765  				Name: "location",
   766  				ClusterClassStatusVariableDefinition: &clusterv1.ClusterClassStatusVariableDefinition{
   767  					Required: true,
   768  					Schema: clusterv1.VariableSchema{
   769  						OpenAPIV3Schema: clusterv1.JSONSchemaProps{
   770  							Type:    "string",
   771  							Default: &apiextensionsv1.JSON{Raw: []byte(`"us-east"`)},
   772  						},
   773  					},
   774  				},
   775  			},
   776  			clusterVariable: &clusterv1.ClusterVariable{
   777  				Name: "location",
   778  				Value: apiextensionsv1.JSON{
   779  					Raw: []byte(`"us-west"`),
   780  				},
   781  			},
   782  			createVariable: true,
   783  			want: &clusterv1.ClusterVariable{
   784  				Name: "location",
   785  				Value: apiextensionsv1.JSON{
   786  					Raw: []byte(`"us-west"`),
   787  				},
   788  			},
   789  		},
   790  		{
   791  			name: "Default new number variable",
   792  			clusterClassVariable: &statusVariableDefinition{
   793  				Name: "cpu",
   794  				ClusterClassStatusVariableDefinition: &clusterv1.ClusterClassStatusVariableDefinition{
   795  					Required: true,
   796  					Schema: clusterv1.VariableSchema{
   797  						OpenAPIV3Schema: clusterv1.JSONSchemaProps{
   798  							Type:    "number",
   799  							Default: &apiextensionsv1.JSON{Raw: []byte(`0.1`)},
   800  						},
   801  					},
   802  				},
   803  			},
   804  			createVariable: true,
   805  			want: &clusterv1.ClusterVariable{
   806  				Name: "cpu",
   807  				Value: apiextensionsv1.JSON{
   808  					Raw: []byte(`0.1`),
   809  				},
   810  			},
   811  		},
   812  		{
   813  			name: "Don't default existing number variable",
   814  			clusterClassVariable: &statusVariableDefinition{
   815  				Name: "cpu",
   816  				ClusterClassStatusVariableDefinition: &clusterv1.ClusterClassStatusVariableDefinition{
   817  					Required: true,
   818  					Schema: clusterv1.VariableSchema{
   819  						OpenAPIV3Schema: clusterv1.JSONSchemaProps{
   820  							Type:    "number",
   821  							Default: &apiextensionsv1.JSON{Raw: []byte(`0.1`)},
   822  						},
   823  					},
   824  				},
   825  			},
   826  			clusterVariable: &clusterv1.ClusterVariable{
   827  				Name: "cpu",
   828  				Value: apiextensionsv1.JSON{
   829  					Raw: []byte(`2.1`),
   830  				},
   831  			},
   832  			createVariable: true,
   833  			want: &clusterv1.ClusterVariable{
   834  				Name: "cpu",
   835  				Value: apiextensionsv1.JSON{
   836  					Raw: []byte(`2.1`),
   837  				},
   838  			},
   839  		},
   840  		{
   841  			name: "Default new boolean variable",
   842  			clusterClassVariable: &statusVariableDefinition{
   843  				Name: "correct",
   844  				ClusterClassStatusVariableDefinition: &clusterv1.ClusterClassStatusVariableDefinition{
   845  					Required: true,
   846  					Schema: clusterv1.VariableSchema{
   847  						OpenAPIV3Schema: clusterv1.JSONSchemaProps{
   848  							Type:    "boolean",
   849  							Default: &apiextensionsv1.JSON{Raw: []byte(`true`)},
   850  						},
   851  					},
   852  				},
   853  			},
   854  			createVariable: true,
   855  			want: &clusterv1.ClusterVariable{
   856  				Name: "correct",
   857  				Value: apiextensionsv1.JSON{
   858  					Raw: []byte(`true`),
   859  				},
   860  			},
   861  		},
   862  		{
   863  			name: "Don't default existing boolean variable",
   864  			clusterClassVariable: &statusVariableDefinition{
   865  				Name: "correct",
   866  				ClusterClassStatusVariableDefinition: &clusterv1.ClusterClassStatusVariableDefinition{
   867  					Required: true,
   868  					Schema: clusterv1.VariableSchema{
   869  						OpenAPIV3Schema: clusterv1.JSONSchemaProps{
   870  							Type:    "boolean",
   871  							Default: &apiextensionsv1.JSON{Raw: []byte(`true`)},
   872  						},
   873  					},
   874  				},
   875  			},
   876  			clusterVariable: &clusterv1.ClusterVariable{
   877  				Name: "correct",
   878  				Value: apiextensionsv1.JSON{
   879  					Raw: []byte(`false`),
   880  				},
   881  			},
   882  			createVariable: true,
   883  			want: &clusterv1.ClusterVariable{
   884  				Name: "correct",
   885  				Value: apiextensionsv1.JSON{
   886  					Raw: []byte(`false`),
   887  				},
   888  			},
   889  		},
   890  		{
   891  			name: "Default new object variable",
   892  			clusterClassVariable: &statusVariableDefinition{
   893  				Name: "httpProxy",
   894  				ClusterClassStatusVariableDefinition: &clusterv1.ClusterClassStatusVariableDefinition{
   895  					Required: true,
   896  					Schema: clusterv1.VariableSchema{
   897  						OpenAPIV3Schema: clusterv1.JSONSchemaProps{
   898  							Type:    "object",
   899  							Default: &apiextensionsv1.JSON{Raw: []byte(`{"enabled": false}`)},
   900  							Properties: map[string]clusterv1.JSONSchemaProps{
   901  								"enabled": {
   902  									Type: "boolean",
   903  								},
   904  								"url": {
   905  									Type: "string",
   906  								},
   907  								"noProxy": {
   908  									Type: "string",
   909  								},
   910  							},
   911  						},
   912  					},
   913  				},
   914  			},
   915  			createVariable: true,
   916  			want: &clusterv1.ClusterVariable{
   917  				Name: "httpProxy",
   918  				Value: apiextensionsv1.JSON{
   919  					Raw: []byte(`{"enabled":false}`),
   920  				},
   921  			},
   922  		},
   923  		{
   924  			name: "Don't default new object variable if there is no top-level default",
   925  			clusterClassVariable: &statusVariableDefinition{
   926  				Name: "httpProxy",
   927  				ClusterClassStatusVariableDefinition: &clusterv1.ClusterClassStatusVariableDefinition{
   928  					Required: true,
   929  					Schema: clusterv1.VariableSchema{
   930  						OpenAPIV3Schema: clusterv1.JSONSchemaProps{
   931  							Type: "object",
   932  							Properties: map[string]clusterv1.JSONSchemaProps{
   933  								"enabled": {
   934  									Type:    "boolean",
   935  									Default: &apiextensionsv1.JSON{Raw: []byte(`false`)},
   936  								},
   937  								"url": {
   938  									Type: "string",
   939  								},
   940  								"noProxy": {
   941  									Type: "string",
   942  								},
   943  							},
   944  						},
   945  					},
   946  				},
   947  			},
   948  			createVariable: true,
   949  			want:           nil,
   950  		},
   951  		{
   952  			name: "Default new object variable if there is a top-level default",
   953  			clusterClassVariable: &statusVariableDefinition{
   954  				Name: "httpProxy",
   955  				ClusterClassStatusVariableDefinition: &clusterv1.ClusterClassStatusVariableDefinition{
   956  					Required: true,
   957  					Schema: clusterv1.VariableSchema{
   958  						OpenAPIV3Schema: clusterv1.JSONSchemaProps{
   959  							Type:    "object",
   960  							Default: &apiextensionsv1.JSON{Raw: []byte(`{"url":"test-url"}`)},
   961  							Properties: map[string]clusterv1.JSONSchemaProps{
   962  								"enabled": {
   963  									Type:    "boolean",
   964  									Default: &apiextensionsv1.JSON{Raw: []byte(`false`)},
   965  								},
   966  								"url": {
   967  									Type: "string",
   968  								},
   969  								"noProxy": {
   970  									Type: "string",
   971  								},
   972  							},
   973  						},
   974  					},
   975  				},
   976  			},
   977  			createVariable: true,
   978  			want: &clusterv1.ClusterVariable{
   979  				Name: "httpProxy",
   980  				Value: apiextensionsv1.JSON{
   981  					// Defaulting first adds the top-level object and then the enabled field.
   982  					Raw: []byte(`{"enabled":false,"url":"test-url"}`),
   983  				},
   984  			},
   985  		},
   986  		{
   987  			name: "Default new object variable if there is a top-level default (which is an empty object)",
   988  			clusterClassVariable: &statusVariableDefinition{
   989  				Name: "httpProxy",
   990  				ClusterClassStatusVariableDefinition: &clusterv1.ClusterClassStatusVariableDefinition{
   991  					Required: true,
   992  					Schema: clusterv1.VariableSchema{
   993  						OpenAPIV3Schema: clusterv1.JSONSchemaProps{
   994  							Type:    "object",
   995  							Default: &apiextensionsv1.JSON{Raw: []byte(`{}`)},
   996  							Properties: map[string]clusterv1.JSONSchemaProps{
   997  								"enabled": {
   998  									Type:    "boolean",
   999  									Default: &apiextensionsv1.JSON{Raw: []byte(`false`)},
  1000  								},
  1001  								"url": {
  1002  									Type: "string",
  1003  								},
  1004  								"noProxy": {
  1005  									Type: "string",
  1006  								},
  1007  							},
  1008  						},
  1009  					},
  1010  				},
  1011  			},
  1012  			createVariable: true,
  1013  			want: &clusterv1.ClusterVariable{
  1014  				Name: "httpProxy",
  1015  				Value: apiextensionsv1.JSON{
  1016  					// Defaulting first adds the top-level empty object and then the enabled field.
  1017  					Raw: []byte(`{"enabled":false}`),
  1018  				},
  1019  			},
  1020  		},
  1021  		{
  1022  			name: "Don't default existing object variable",
  1023  			clusterClassVariable: &statusVariableDefinition{
  1024  				Name: "httpProxy",
  1025  				ClusterClassStatusVariableDefinition: &clusterv1.ClusterClassStatusVariableDefinition{
  1026  					Required: true,
  1027  					Schema: clusterv1.VariableSchema{
  1028  						OpenAPIV3Schema: clusterv1.JSONSchemaProps{
  1029  							Type:    "object",
  1030  							Default: &apiextensionsv1.JSON{Raw: []byte(`{"enabled": false}`)},
  1031  							Properties: map[string]clusterv1.JSONSchemaProps{
  1032  								"enabled": {
  1033  									Type: "boolean",
  1034  								},
  1035  								"url": {
  1036  									Type: "string",
  1037  								},
  1038  								"noProxy": {
  1039  									Type: "string",
  1040  								},
  1041  							},
  1042  						},
  1043  					},
  1044  				},
  1045  			},
  1046  			clusterVariable: &clusterv1.ClusterVariable{
  1047  				Name: "httpProxy",
  1048  				Value: apiextensionsv1.JSON{
  1049  					Raw: []byte(`{"enabled":false,"url":"https://example.com"}`),
  1050  				},
  1051  			},
  1052  			createVariable: true,
  1053  			want: &clusterv1.ClusterVariable{
  1054  				Name: "httpProxy",
  1055  				Value: apiextensionsv1.JSON{
  1056  					Raw: []byte(`{"enabled":false,"url":"https://example.com"}`),
  1057  				},
  1058  			},
  1059  		},
  1060  		{
  1061  			name: "Default nested fields of existing object variable",
  1062  			clusterClassVariable: &statusVariableDefinition{
  1063  				Name: "httpProxy",
  1064  				ClusterClassStatusVariableDefinition: &clusterv1.ClusterClassStatusVariableDefinition{
  1065  
  1066  					Required: true,
  1067  					Schema: clusterv1.VariableSchema{
  1068  						OpenAPIV3Schema: clusterv1.JSONSchemaProps{
  1069  							Type:    "object",
  1070  							Default: &apiextensionsv1.JSON{Raw: []byte(`{"enabled": false}`)},
  1071  							Properties: map[string]clusterv1.JSONSchemaProps{
  1072  								"enabled": {
  1073  									Type: "boolean",
  1074  								},
  1075  								"url": {
  1076  									Type:    "string",
  1077  									Default: &apiextensionsv1.JSON{Raw: []byte(`"https://example.com"`)},
  1078  								},
  1079  								"noProxy": {
  1080  									Type: "string",
  1081  								},
  1082  							},
  1083  						},
  1084  					},
  1085  				},
  1086  			},
  1087  			clusterVariable: &clusterv1.ClusterVariable{
  1088  				Name: "httpProxy",
  1089  				Value: apiextensionsv1.JSON{
  1090  					Raw: []byte(`{"enabled":false}`),
  1091  				},
  1092  			},
  1093  			createVariable: true,
  1094  			want: &clusterv1.ClusterVariable{
  1095  				Name: "httpProxy",
  1096  				Value: apiextensionsv1.JSON{
  1097  					Raw: []byte(`{"enabled":false,"url":"https://example.com"}`),
  1098  				},
  1099  			},
  1100  		},
  1101  		{
  1102  			name: "Default new map variable",
  1103  			clusterClassVariable: &statusVariableDefinition{
  1104  				Name: "httpProxy",
  1105  				ClusterClassStatusVariableDefinition: &clusterv1.ClusterClassStatusVariableDefinition{
  1106  					Required: true,
  1107  					Schema: clusterv1.VariableSchema{
  1108  						OpenAPIV3Schema: clusterv1.JSONSchemaProps{
  1109  							Type:    "object",
  1110  							Default: &apiextensionsv1.JSON{Raw: []byte(`{"proxy":{"enabled":false}}`)},
  1111  							AdditionalProperties: &clusterv1.JSONSchemaProps{
  1112  								Type: "object",
  1113  								Properties: map[string]clusterv1.JSONSchemaProps{
  1114  									"enabled": {
  1115  										Type: "boolean",
  1116  									},
  1117  									"url": {
  1118  										Type:    "string",
  1119  										Default: &apiextensionsv1.JSON{Raw: []byte(`"https://example.com"`)},
  1120  									},
  1121  									"noProxy": {
  1122  										Type: "string",
  1123  									},
  1124  								},
  1125  							},
  1126  						},
  1127  					},
  1128  				},
  1129  			},
  1130  			createVariable: true,
  1131  			want: &clusterv1.ClusterVariable{
  1132  				Name: "httpProxy",
  1133  				Value: apiextensionsv1.JSON{
  1134  					Raw: []byte(`{"proxy":{"enabled":false,"url":"https://example.com"}}`),
  1135  				},
  1136  			},
  1137  		},
  1138  		{
  1139  			name: "Default nested fields of existing map variable",
  1140  			clusterClassVariable: &statusVariableDefinition{
  1141  				Name: "httpProxy",
  1142  				ClusterClassStatusVariableDefinition: &clusterv1.ClusterClassStatusVariableDefinition{
  1143  					Required: true,
  1144  					Schema: clusterv1.VariableSchema{
  1145  						OpenAPIV3Schema: clusterv1.JSONSchemaProps{
  1146  							Type: "object",
  1147  							AdditionalProperties: &clusterv1.JSONSchemaProps{
  1148  								Type: "object",
  1149  								Properties: map[string]clusterv1.JSONSchemaProps{
  1150  									"enabled": {
  1151  										Type: "boolean",
  1152  									},
  1153  									"url": {
  1154  										Type:    "string",
  1155  										Default: &apiextensionsv1.JSON{Raw: []byte(`"https://example.com"`)},
  1156  									},
  1157  									"noProxy": {
  1158  										Type: "string",
  1159  									},
  1160  								},
  1161  							},
  1162  						},
  1163  					},
  1164  				},
  1165  			},
  1166  			clusterVariable: &clusterv1.ClusterVariable{
  1167  				Name: "httpProxy",
  1168  				Value: apiextensionsv1.JSON{
  1169  					Raw: []byte(`{"proxy1":{"enabled":false},"proxy2":{"enabled":false}}`),
  1170  				},
  1171  			},
  1172  			createVariable: true,
  1173  			want: &clusterv1.ClusterVariable{
  1174  				Name: "httpProxy",
  1175  				Value: apiextensionsv1.JSON{
  1176  					Raw: []byte(`{"proxy1":{"enabled":false,"url":"https://example.com"},"proxy2":{"enabled":false,"url":"https://example.com"}}`),
  1177  				},
  1178  			},
  1179  		},
  1180  		{
  1181  			name: "Default new array variable",
  1182  			clusterClassVariable: &statusVariableDefinition{
  1183  				Name: "testVariable",
  1184  				ClusterClassStatusVariableDefinition: &clusterv1.ClusterClassStatusVariableDefinition{
  1185  					Required: true,
  1186  					Schema: clusterv1.VariableSchema{
  1187  						OpenAPIV3Schema: clusterv1.JSONSchemaProps{
  1188  							Type: "array",
  1189  							Items: &clusterv1.JSONSchemaProps{
  1190  								Type: "object",
  1191  								Properties: map[string]clusterv1.JSONSchemaProps{
  1192  									"enabled": {
  1193  										Type: "boolean",
  1194  									},
  1195  									"url": {
  1196  										Type: "string",
  1197  									},
  1198  								},
  1199  							},
  1200  							Default: &apiextensionsv1.JSON{Raw: []byte(`[{"enabled":false,"url":"123"},{"enabled":false,"url":"456"}]`)},
  1201  						},
  1202  					},
  1203  				},
  1204  			},
  1205  			createVariable: true,
  1206  			want: &clusterv1.ClusterVariable{
  1207  				Name: "testVariable",
  1208  				Value: apiextensionsv1.JSON{
  1209  					Raw: []byte(`[{"enabled":false,"url":"123"},{"enabled":false,"url":"456"}]`),
  1210  				},
  1211  			},
  1212  		},
  1213  		{
  1214  			name: "Don't default existing array variable",
  1215  			clusterClassVariable: &statusVariableDefinition{
  1216  				Name: "testVariable",
  1217  				ClusterClassStatusVariableDefinition: &clusterv1.ClusterClassStatusVariableDefinition{
  1218  
  1219  					Required: true,
  1220  					Schema: clusterv1.VariableSchema{
  1221  						OpenAPIV3Schema: clusterv1.JSONSchemaProps{
  1222  							Type: "array",
  1223  							Items: &clusterv1.JSONSchemaProps{
  1224  								Type: "object",
  1225  								Properties: map[string]clusterv1.JSONSchemaProps{
  1226  									"enabled": {
  1227  										Type: "boolean",
  1228  									},
  1229  									"url": {
  1230  										Type: "string",
  1231  									},
  1232  								},
  1233  							},
  1234  							Default: &apiextensionsv1.JSON{Raw: []byte(`[{"enabled":false,"url":"123"},{"enabled":false,"url":"456"}]`)},
  1235  						},
  1236  					},
  1237  				},
  1238  			},
  1239  			clusterVariable: &clusterv1.ClusterVariable{
  1240  				Name: "testVariable",
  1241  				Value: apiextensionsv1.JSON{
  1242  					Raw: []byte(`[]`),
  1243  				},
  1244  			},
  1245  			createVariable: true,
  1246  			want: &clusterv1.ClusterVariable{
  1247  				Name: "testVariable",
  1248  				Value: apiextensionsv1.JSON{
  1249  					Raw: []byte(`[]`),
  1250  				},
  1251  			},
  1252  		},
  1253  	}
  1254  	for _, tt := range tests {
  1255  		t.Run(tt.name, func(t *testing.T) {
  1256  			g := NewWithT(t)
  1257  
  1258  			defaultedVariable, errList := defaultValue(tt.clusterVariable, tt.clusterClassVariable,
  1259  				field.NewPath("spec", "topology", "variables").Index(0), tt.createVariable)
  1260  
  1261  			if tt.wantErr {
  1262  				g.Expect(errList).NotTo(BeEmpty())
  1263  				return
  1264  			}
  1265  			g.Expect(errList).To(BeEmpty())
  1266  
  1267  			g.Expect(defaultedVariable).To(BeComparableTo(tt.want))
  1268  		})
  1269  	}
  1270  }
  1271  
  1272  func Test_getAllVariables(t *testing.T) {
  1273  	g := NewWithT(t)
  1274  	t.Run("Expect values to be correctly consolidated in allVariables", func(*testing.T) {
  1275  		expectedValues := []clusterv1.ClusterVariable{
  1276  			// var1 has a value with no DefinitionFrom set and only one definition. It should be retained as is.
  1277  			{
  1278  				Name: "var1",
  1279  			},
  1280  
  1281  			// var2 has a value with DefinitionFrom "inline". It has a second definition with DefinitionFrom "somepatch".
  1282  			// The value for the first definition should be retained and a value for the second definition should be added.
  1283  			{
  1284  				Name:           "var2",
  1285  				DefinitionFrom: clusterv1.VariableDefinitionFromInline,
  1286  			},
  1287  			{
  1288  				Name:           "var2",
  1289  				DefinitionFrom: "somepatch",
  1290  			},
  1291  
  1292  			// var3 had no values. It has two conflicting definitions. A value for each definition should be added.
  1293  			{
  1294  				Name:           "var3",
  1295  				DefinitionFrom: clusterv1.VariableDefinitionFromInline,
  1296  			},
  1297  			{
  1298  				Name:           "var3",
  1299  				DefinitionFrom: "somepatch",
  1300  			},
  1301  
  1302  			// var4 had no values. It has two non-conflicting definitions. A single value with emptyDefinitionFrom should
  1303  			// be added.
  1304  			{
  1305  				Name:           "var4",
  1306  				DefinitionFrom: emptyDefinitionFrom,
  1307  			},
  1308  		}
  1309  
  1310  		values := []clusterv1.ClusterVariable{
  1311  			{
  1312  				Name: "var1",
  1313  			},
  1314  			{
  1315  				Name:           "var2",
  1316  				DefinitionFrom: clusterv1.VariableDefinitionFromInline,
  1317  			},
  1318  		}
  1319  		definitions := []clusterv1.ClusterClassStatusVariable{
  1320  			{
  1321  				Name: "var1",
  1322  				Definitions: []clusterv1.ClusterClassStatusVariableDefinition{
  1323  					{
  1324  						Required: false,
  1325  						From:     clusterv1.VariableDefinitionFromInline,
  1326  						Schema: clusterv1.VariableSchema{
  1327  							OpenAPIV3Schema: clusterv1.JSONSchemaProps{
  1328  								Type: "string",
  1329  							},
  1330  						},
  1331  					},
  1332  				},
  1333  			},
  1334  			{
  1335  				Name: "var2",
  1336  				Definitions: []clusterv1.ClusterClassStatusVariableDefinition{
  1337  					{
  1338  						Required: true,
  1339  						From:     clusterv1.VariableDefinitionFromInline,
  1340  						Schema: clusterv1.VariableSchema{
  1341  							OpenAPIV3Schema: clusterv1.JSONSchemaProps{
  1342  								Type: "string",
  1343  							},
  1344  						},
  1345  					},
  1346  					{
  1347  						Required: true,
  1348  						From:     "somepatch",
  1349  						Schema: clusterv1.VariableSchema{
  1350  							OpenAPIV3Schema: clusterv1.JSONSchemaProps{
  1351  								Type: "string",
  1352  							},
  1353  						},
  1354  					},
  1355  				},
  1356  			},
  1357  			{
  1358  				Name:                "var3",
  1359  				DefinitionsConflict: true,
  1360  				Definitions: []clusterv1.ClusterClassStatusVariableDefinition{
  1361  					{
  1362  						Required: false,
  1363  						From:     clusterv1.VariableDefinitionFromInline,
  1364  						Schema: clusterv1.VariableSchema{
  1365  							OpenAPIV3Schema: clusterv1.JSONSchemaProps{
  1366  								Type: "string",
  1367  							},
  1368  						},
  1369  					},
  1370  					{
  1371  						Required: true,
  1372  						From:     "somepatch",
  1373  						Schema: clusterv1.VariableSchema{
  1374  							OpenAPIV3Schema: clusterv1.JSONSchemaProps{
  1375  								Type: "string",
  1376  							},
  1377  						},
  1378  					},
  1379  				},
  1380  			},
  1381  			{
  1382  				Name: "var4",
  1383  				Definitions: []clusterv1.ClusterClassStatusVariableDefinition{
  1384  					{
  1385  						Required: true,
  1386  						From:     clusterv1.VariableDefinitionFromInline,
  1387  						Schema: clusterv1.VariableSchema{
  1388  							OpenAPIV3Schema: clusterv1.JSONSchemaProps{
  1389  								Type: "string",
  1390  							},
  1391  						},
  1392  					},
  1393  					{
  1394  						Required: true,
  1395  						From:     "somepatch",
  1396  						Schema: clusterv1.VariableSchema{
  1397  							OpenAPIV3Schema: clusterv1.JSONSchemaProps{
  1398  								Type: "string",
  1399  							},
  1400  						},
  1401  					},
  1402  				},
  1403  			},
  1404  		}
  1405  
  1406  		valuesIndex, err := newValuesIndex(values)
  1407  		g.Expect(err).ToNot(HaveOccurred())
  1408  		got := getAllVariables(values, valuesIndex, definitions)
  1409  		g.Expect(got).To(BeComparableTo(expectedValues))
  1410  	})
  1411  }