diff --git a/pkg/providers/tinkerbell/assert_test.go b/pkg/providers/tinkerbell/assert_test.go index c306360a90e0..4e4bde9e1a2b 100644 --- a/pkg/providers/tinkerbell/assert_test.go +++ b/pkg/providers/tinkerbell/assert_test.go @@ -337,7 +337,7 @@ func TestK8sVersionInCPMachineConfigOSImageURL_Error(t *testing.T) { clusterSpec.MachineConfigs[builder.ControlPlaneMachineName].Spec.OSImageURL = "test-url" clusterSpec.MachineConfigs[builder.ExternalEtcdMachineName].Spec.OSImageURL = "test-url-122" clusterSpec.MachineConfigs[builder.WorkerNodeGroupMachineName].Spec.OSImageURL = "test-url-122" - g.Expect(tinkerbell.AssertOSImageURL(clusterSpec)).To(gomega.MatchError(gomega.ContainSubstring("missing kube version from control plane machine config OSImageURL:"))) + g.Expect(tinkerbell.AssertOSImageURL(clusterSpec)).To(gomega.MatchError(gomega.ContainSubstring("missing kube version from control plane machine config OSImageURL"))) } func TestK8sVersionInWorkerMachineConfigOSImageURL_Error(t *testing.T) { @@ -350,7 +350,7 @@ func TestK8sVersionInWorkerMachineConfigOSImageURL_Error(t *testing.T) { clusterSpec.MachineConfigs[builder.ControlPlaneMachineName].Spec.OSImageURL = "test-url-122" clusterSpec.MachineConfigs[builder.ExternalEtcdMachineName].Spec.OSImageURL = "test-url-122" clusterSpec.MachineConfigs[builder.WorkerNodeGroupMachineName].Spec.OSImageURL = "test-url" - g.Expect(tinkerbell.AssertOSImageURL(clusterSpec)).To(gomega.MatchError(gomega.ContainSubstring("missing kube version from worker node group machine config OSImageURL:"))) + g.Expect(tinkerbell.AssertOSImageURL(clusterSpec)).To(gomega.MatchError(gomega.ContainSubstring("missing kube version from worker node group machine config OSImageURL"))) } func TestK8sVersionForBRAutoImport_Succeed(t *testing.T) { diff --git a/pkg/providers/tinkerbell/validate.go b/pkg/providers/tinkerbell/validate.go index 6a956f0235aa..1229e3c6cd55 100644 --- a/pkg/providers/tinkerbell/validate.go +++ b/pkg/providers/tinkerbell/validate.go @@ -132,8 +132,8 @@ func validateK8sVersionInOSImageURLs(spec *ClusterSpec) error { kvs := spec.Cluster.KubernetesVersions() for _, v := range kvs { if !containsK8sVersion(spec.DatacenterConfig.Spec.OSImageURL, string(v)) { - return fmt.Errorf("missing kube version from OSImageURL: url=%v, version=%v", - spec.DatacenterConfig.Spec.OSImageURL, v) + return fmt.Errorf("missing kube version from OSImageURL, the image url must include %v: url=%v, version=%v", + permutations(string(v), []string{"-", "_", ""}), spec.DatacenterConfig.Spec.OSImageURL, v) } } } else { @@ -150,8 +150,8 @@ func validateK8sVersionInOSImageURLs(spec *ClusterSpec) error { } if !containsK8sVersion(spec.ControlPlaneMachineConfig().Spec.OSImageURL, string(spec.Cluster.Spec.KubernetesVersion)) { - return fmt.Errorf("missing kube version from control plane machine config OSImageURL: url=%v, version=%v", - spec.ControlPlaneMachineConfig().Spec.OSImageURL, spec.Cluster.Spec.KubernetesVersion) + return fmt.Errorf("missing kube version from control plane machine config OSImageURL, the image url must include %v: url=%v, version=%v", + permutations(string(spec.Cluster.Spec.KubernetesVersion), []string{"-", "_", ""}), spec.ControlPlaneMachineConfig().Spec.OSImageURL, spec.Cluster.Spec.KubernetesVersion) } for _, wng := range spec.WorkerNodeGroupConfigurations() { @@ -162,14 +162,82 @@ func validateK8sVersionInOSImageURLs(spec *ClusterSpec) error { } if !containsK8sVersion(url, string(version)) { - return fmt.Errorf("missing kube version from worker node group machine config OSImageURL: url=%v, version=%v", - url, version) + return fmt.Errorf("missing kube version from worker node group machine config OSImageURL, the image url must include %v: url=%v, version=%v", + permutations(string(version), []string{"-", "_", ""}), url, version) } } } return nil } +// permutations takes a version in dot format and +// returns a human readable string with the permutations of the version +// using the passed in separators. +// +// For example, when v = 1.29 and separators = []string{"-", "_", ""} +// the result will be "1.29, 1-29, 1_29, or 129". +func permutations(v string, separators []string) string { + result := []string{v} + split := strings.Split(v, ".") + if len(split) == 1 { + return v + } + + seps := remove(".", deduplicate(separators)) + for idx, sep := range seps { + var elem string + for idx, s := range split { + if idx != len(split)-1 { + elem = elem + s + sep + } else { + elem = elem + s + } + } + + if idx == len(seps)-1 { + result = append(result, "or") + } else if idx != len(seps)-2 { + elem = elem + "," + } + result = append(result, elem) + } + + if len(seps) > 1 { + result[0] = result[0] + "," + } + + return strings.Join(result, " ") +} + +// deduplicate returns a new slice with duplicates values removed. +func deduplicate(s []string) []string { + if len(s) <= 1 { + return s + } + result := []string{} + seen := make(map[string]struct{}) + for _, val := range s { + val := strings.ToLower(val) + if _, ok := seen[val]; !ok { + result = append(result, val) + seen[val] = struct{}{} + } + } + return result +} + +// removes all values equal to val from in. +func remove(val string, in []string) []string { + result := []string{} + for _, i := range in { + if i != val { + result = append(result, i) + } + } + + return result +} + func defaultBottlerocketOSImageURLs(spec *ClusterSpec) { if spec.ControlPlaneMachineConfig().Spec.OSImageURL == "" { spec.ControlPlaneMachineConfig().Spec.OSImageURL = spec.RootVersionsBundle().EksD.Raw.Bottlerocket.URI diff --git a/pkg/providers/tinkerbell/validate_test.go b/pkg/providers/tinkerbell/validate_test.go new file mode 100644 index 000000000000..88a0ebba7eec --- /dev/null +++ b/pkg/providers/tinkerbell/validate_test.go @@ -0,0 +1,31 @@ +package tinkerbell + +import ( + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestPermuatations(t *testing.T) { + tests := []struct { + initial string + separators []string + want string + }{ + {"125", []string{"-", "_", ""}, "125"}, + {"1.26", []string{"-", "_", ""}, "1.26, 1-26, 1_26 or 126"}, + {"1.27", []string{"."}, "1.27"}, + {"1.28", []string{".", "."}, "1.28"}, + {"1.29", []string{"-", "-", ""}, "1.29, 1-29 or 129"}, + {"1.29.1", []string{"-", "_", "_", ""}, "1.29.1, 1-29-1, 1_29_1 or 1291"}, + } + + for _, tt := range tests { + t.Run(tt.initial, func(t *testing.T) { + got := permutations(tt.initial, tt.separators) + if diff := cmp.Diff(got, tt.want); diff != "" { + t.Errorf("permutations() mismatch (-got +want):\n%s", diff) + } + }) + } +}