Skip to content

Commit 443c5f2

Browse files
fix(tools): Ensure GetCoreDNSVersion handles canonical semver
this allows us to parse and ignore patch version, etc. in params.
1 parent a2ab3f5 commit 443c5f2

File tree

2 files changed

+116
-53
lines changed

2 files changed

+116
-53
lines changed

api/versions/coredns.go

Lines changed: 30 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

hack/tools/coredns-versions/main.go

Lines changed: 86 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -25,21 +25,22 @@ import (
2525
var (
2626
// Command-line flags
2727
outputFile = flag.String("output", "api/versions/coredns.go", "Output file path")
28-
minKubernetesVersion = flag.String("min-kubernetes-version", "v1.22", "Minimum Kubernetes version to include (semver format)")
28+
minKubernetesVersion = flag.String("min-kubernetes-version", "v1.22.0", "Minimum Kubernetes version to include (semver format)")
2929
)
3030

3131
const (
3232
constantsURLTemplate = "https://raw.githubusercontent.com/kubernetes/kubernetes/%s/cmd/kubeadm/app/constants/constants.go"
3333
branchesAPIURL = "https://api.github.com/repos/kubernetes/kubernetes/branches?per_page=100&page=%d"
3434
)
3535

36-
var goTemplate = `
37-
// Copyright 2024 Nutanix. All rights reserved.
38-
// SPDX-License-Identifier: Apache-2.0
39-
// Code generated by script; DO NOT EDIT. Run 'make coredns.sync' instead
36+
var goTemplate = `// Code generated by script; DO NOT EDIT. Run 'make coredns.sync' instead
4037
4138
package versions
4239
40+
import (
41+
"golang.org/x/mod/semver"
42+
)
43+
4344
// Kubernetes versions
4445
const (
4546
{{- range .KubernetesConstants }}
@@ -55,26 +56,40 @@ const (
5556
)
5657
5758
// kubernetesToCoreDNSVersion maps Kubernetes versions to CoreDNS versions.
59+
// This map is unexported to prevent external modification.
5860
var kubernetesToCoreDNSVersion = map[string]string{
5961
{{- range .VersionMap }}
6062
{{ .KubernetesConst }}: {{ .CoreDNSConst }},
6163
{{- end }}
6264
}
6365
6466
// GetCoreDNSVersion returns the CoreDNS version for a given Kubernetes version.
67+
// It accepts versions with or without the "v" prefix and handles full semver versions.
68+
// The function maps based on the major and minor versions (e.g., "v1.27").
6569
// If the Kubernetes version is not found, it returns an empty string and false.
6670
func GetCoreDNSVersion(kubernetesVersion string) (string, bool) {
67-
version, found := kubernetesToCoreDNSVersion[kubernetesVersion]
71+
// Normalize the version using semver
72+
normalizedVersion := semver.Canonical(kubernetesVersion)
73+
if normalizedVersion == "" {
74+
// Handle invalid version strings
75+
return "", false
76+
}
77+
78+
// Extract major and minor versions
79+
majorMinor := semver.MajorMinor(normalizedVersion) // e.g., "v1.27"
80+
81+
// Lookup the CoreDNS version using the major and minor version
82+
version, found := kubernetesToCoreDNSVersion[majorMinor]
6883
return version, found
6984
}
7085
7186
// GetKubernetesToCoreDNSVersionMap returns a copy of the Kubernetes to CoreDNS version mapping.
87+
// The map keys are Kubernetes versions in "vMAJOR.MINOR" format.
7288
func GetKubernetesToCoreDNSVersionMap() map[string]string {
7389
copyMap := make(map[string]string, len(kubernetesToCoreDNSVersion))
7490
for k, v := range kubernetesToCoreDNSVersion {
7591
copyMap[k] = v
7692
}
77-
7893
return copyMap
7994
}
8095
`
@@ -86,6 +101,10 @@ func main() {
86101
if !strings.HasPrefix(*minKubernetesVersion, "v") {
87102
*minKubernetesVersion = "v" + *minKubernetesVersion
88103
}
104+
// Ensure minKubernetesVersion has patch version
105+
if semver.Prerelease(*minKubernetesVersion) == "" && semver.Build(*minKubernetesVersion) == "" && len(strings.Split(semver.Canonical(*minKubernetesVersion), ".")) == 2 {
106+
*minKubernetesVersion += ".0"
107+
}
89108

90109
versions, err := fetchKubernetesVersions(*minKubernetesVersion)
91110
if err != nil {
@@ -117,20 +136,24 @@ func fetchKubernetesVersions(minVersion string) ([]string, error) {
117136
if err != nil {
118137
return nil, err
119138
}
139+
120140
if len(branchNames) == 0 {
121141
break
122142
}
143+
123144
for _, branch := range branchNames {
124145
if strings.HasPrefix(branch, "release-1.") {
125146
version := strings.TrimPrefix(branch, "release-")
126-
semverVersion := "v" + version
147+
semverVersion := "v" + version + ".0"
127148
if semver.Compare(semverVersion, minVersion) >= 0 {
128149
versions = append(versions, version)
129150
}
130151
}
131152
}
153+
132154
page++
133155
}
156+
134157
if len(versions) == 0 {
135158
return nil, errors.New("no Kubernetes versions found")
136159
}
@@ -139,13 +162,16 @@ func fetchKubernetesVersions(minVersion string) ([]string, error) {
139162
for _, v := range versions {
140163
versionSet[v] = struct{}{}
141164
}
165+
142166
versions = nil
143167
for v := range versionSet {
144168
versions = append(versions, v)
145169
}
170+
146171
sort.Slice(versions, func(i, j int) bool {
147-
return semver.Compare("v"+versions[i], "v"+versions[j]) < 0
172+
return semver.Compare("v"+versions[i]+".0", "v"+versions[j]+".0") < 0
148173
})
174+
149175
return versions, nil
150176
}
151177

@@ -155,6 +181,7 @@ func fetchBranchNames(url string) ([]string, error) {
155181
if err != nil {
156182
return nil, fmt.Errorf("HTTP GET error: %w", err)
157183
}
184+
158185
defer resp.Body.Close()
159186

160187
if resp.StatusCode != http.StatusOK {
@@ -164,6 +191,7 @@ func fetchBranchNames(url string) ([]string, error) {
164191
var branches []struct {
165192
Name string `json:"name"`
166193
}
194+
167195
if err := json.NewDecoder(resp.Body).Decode(&branches); err != nil {
168196
return nil, fmt.Errorf("decoding JSON error: %w", err)
169197
}
@@ -172,13 +200,13 @@ func fetchBranchNames(url string) ([]string, error) {
172200
for _, branch := range branches {
173201
branchNames = append(branchNames, branch.Name)
174202
}
203+
175204
return branchNames, nil
176205
}
177206

178207
func fetchCoreDNSVersions(versions []string) (map[string]string, error) {
179208
versionMap := make(map[string]string)
180209
re := regexp.MustCompile(`CoreDNSVersion\s*=\s*"([^"]+)"`)
181-
182210
for _, k8sVersion := range versions {
183211
branch := "release-" + k8sVersion
184212
url := fmt.Sprintf(constantsURLTemplate, branch)
@@ -187,7 +215,17 @@ func fetchCoreDNSVersions(versions []string) (map[string]string, error) {
187215
fmt.Fprintf(os.Stderr, "Warning: Failed for Kubernetes %s: %v\n", k8sVersion, err)
188216
continue
189217
}
190-
versionMap[k8sVersion] = coreDNSVersion
218+
219+
// Normalize CoreDNS version using semver
220+
coreDNSVersion = semver.Canonical(coreDNSVersion)
221+
if coreDNSVersion == "" {
222+
fmt.Fprintf(os.Stderr, "Warning: Invalid CoreDNS version '%s' for Kubernetes %s\n", coreDNSVersion, k8sVersion)
223+
continue
224+
}
225+
// Prefix Kubernetes version with "v" and add ".0" for patch version
226+
fullVersion := "v" + k8sVersion + ".0"
227+
majorMinor := semver.MajorMinor(fullVersion)
228+
versionMap[majorMinor] = coreDNSVersion
191229
}
192230

193231
if len(versionMap) == 0 {
@@ -202,6 +240,7 @@ func extractCoreDNSVersion(url string, re *regexp.Regexp) (string, error) {
202240
if err != nil {
203241
return "", fmt.Errorf("HTTP GET error: %w", err)
204242
}
243+
205244
defer resp.Body.Close()
206245

207246
if resp.StatusCode != http.StatusOK {
@@ -218,7 +257,13 @@ func extractCoreDNSVersion(url string, re *regexp.Regexp) (string, error) {
218257
return "", errors.New("CoreDNSVersion not found")
219258
}
220259

221-
return matches[1], nil
260+
// Ensure CoreDNS version includes "v" prefix
261+
coreDNSVersion := matches[1]
262+
if !strings.HasPrefix(coreDNSVersion, "v") {
263+
coreDNSVersion = "v" + coreDNSVersion
264+
}
265+
266+
return coreDNSVersion, nil
222267
}
223268

224269
func generateGoFile(versionMap map[string]string, outputPath string) error {
@@ -255,13 +300,13 @@ func prepareTemplateData(versionMap map[string]string) map[string]interface{} {
255300
Name string
256301
Version string
257302
}
258-
var k8sConstants []Const
259-
var coreDNSConstants []Const
303+
260304
type versionMapEntry struct {
261305
KubernetesVersion string
262306
KubernetesConst string
263307
CoreDNSConst string
264308
}
309+
265310
var versionMapList []versionMapEntry
266311

267312
// Maps for deduplication
@@ -274,46 +319,48 @@ func prepareTemplateData(versionMap map[string]string) map[string]interface{} {
274319
uniqueCoreDNSVersions[coreDNSVersion] = struct{}{}
275320
}
276321

277-
// Generate constants for Kubernetes versions
278-
for k8sVersion := range versionMap {
279-
constName := versionToConst("Kubernetes", k8sVersion)
280-
k8sConstMap[k8sVersion] = constName
281-
}
282-
322+
var k8sConstants []Const
323+
var coreDNSConstants []Const
283324
// Generate constants for CoreDNS versions
284325
for coreDNSVersion := range uniqueCoreDNSVersions {
285326
constName := versionToConst("CoreDNS", coreDNSVersion)
286327
coreDNSConstMap[coreDNSVersion] = constName
328+
coreDNSConstants = append(coreDNSConstants, Const{Name: constName, Version: coreDNSVersion})
287329
}
288330

289-
// Prepare constants slices
290-
for k8sVersion, constName := range k8sConstMap {
291-
k8sConstants = append(k8sConstants, Const{Name: constName, Version: k8sVersion})
331+
// Generate constants and mapping for Kubernetes versions
332+
for k8sVersion := range versionMap {
333+
// Constants and mapping use major.minor versions
334+
majorMinor := semver.MajorMinor(k8sVersion)
335+
if _, exists := k8sConstMap[majorMinor]; !exists {
336+
constName := versionToConst("Kubernetes", majorMinor)
337+
k8sConstMap[majorMinor] = constName
338+
k8sConstants = append(k8sConstants, Const{Name: constName, Version: majorMinor})
339+
}
292340
}
293-
for coreDNSVersion, constName := range coreDNSConstMap {
294-
coreDNSConstants = append(coreDNSConstants, Const{Name: constName, Version: coreDNSVersion})
341+
342+
// Map Kubernetes constants to CoreDNS constants
343+
for k8sVersion, coreDNSVersion := range versionMap {
344+
majorMinor := semver.MajorMinor(k8sVersion)
345+
versionMapList = append(versionMapList, versionMapEntry{
346+
KubernetesVersion: majorMinor,
347+
KubernetesConst: k8sConstMap[majorMinor],
348+
CoreDNSConst: coreDNSConstMap[coreDNSVersion],
349+
})
295350
}
296351

297352
// Sort constants
298353
sort.Slice(k8sConstants, func(i, j int) bool {
299-
return semver.Compare("v"+k8sConstants[i].Version, "v"+k8sConstants[j].Version) < 0
354+
return semver.Compare(k8sConstants[i].Version, k8sConstants[j].Version) < 0
300355
})
356+
301357
sort.Slice(coreDNSConstants, func(i, j int) bool {
302358
return semver.Compare(coreDNSConstants[i].Version, coreDNSConstants[j].Version) < 0
303359
})
304360

305-
// Map Kubernetes constants to CoreDNS constants
306-
for k8sVersion, coreDNSVersion := range versionMap {
307-
versionMapList = append(versionMapList, versionMapEntry{
308-
KubernetesVersion: k8sVersion,
309-
KubernetesConst: k8sConstMap[k8sVersion],
310-
CoreDNSConst: coreDNSConstMap[coreDNSVersion],
311-
})
312-
}
313-
314361
// Sort version map
315362
sort.Slice(versionMapList, func(i, j int) bool {
316-
return semver.Compare("v"+versionMapList[i].KubernetesVersion, "v"+versionMapList[j].KubernetesVersion) < 0
363+
return semver.Compare(versionMapList[i].KubernetesVersion, versionMapList[j].KubernetesVersion) < 0
317364
})
318365

319366
data := map[string]interface{}{
@@ -327,9 +374,9 @@ func prepareTemplateData(versionMap map[string]string) map[string]interface{} {
327374

328375
func versionToConst(prefix, version string) string {
329376
// Remove 'v' prefix if present
330-
version = strings.TrimPrefix(version, "v")
377+
versionNoV := strings.TrimPrefix(version, "v")
331378
// Replace dots with underscores
332-
version = strings.ReplaceAll(version, ".", "_")
379+
versionNoV = strings.ReplaceAll(versionNoV, ".", "_")
333380
// Prepend the prefix and 'V'
334-
return prefix + "_V" + version
381+
return prefix + "_V" + versionNoV
335382
}

0 commit comments

Comments
 (0)