Skip to content

Commit b6d6b07

Browse files
committed
New target now 'k8s-pin', take ENVVAR for k8s ver
Also separate the target from make tiday and some code cleanup. Signed-off-by: Brett Tofel <[email protected]>
1 parent 6348b60 commit b6d6b07

File tree

2 files changed

+80
-31
lines changed

2 files changed

+80
-31
lines changed

Makefile

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -120,13 +120,13 @@ custom-linter-build: #EXHELP Build custom linter
120120
lint-custom: custom-linter-build #EXHELP Call custom linter for the project
121121
go vet -tags=$(GO_BUILD_TAGS) -vettool=./bin/custom-linter ./...
122122

123-
.PHONY: k8s-maintainer #EXHELP this tool also calls `go mod tidy` but also allows us maintain k8s.io/kubernetes` changes bumping related staging modules (e.g., `k8s.io/api`, `k8s.io/apimachinery) as needed
124-
k8s-maintainer:
125-
go run hack/tools/k8smaintainer/main.go
123+
.PHONY: k8s-pin #EXHELP Pin k8s staging modules based on k8s.io/kubernetes version (in go.mod or from K8S_IO_K8S_VERSION env var) and run go mod tidy.
124+
k8s-pin:
125+
K8S_IO_K8S_VERSION='$(K8S_IO_K8S_VERSION)' go run hack/tools/k8smaintainer/main.go
126126

127-
.PHONY: tidy
128-
tidy: k8s-maintainer #HELP Update dependencies.
129-
# k8s-maintainer calls go mod tidy
127+
.PHONY: tidy #HELP Run go mod tidy.
128+
tidy:
129+
go mod tidy
130130

131131
.PHONY: manifests
132132
KUSTOMIZE_CATD_CRDS_DIR := config/base/catalogd/crd/bases
@@ -154,7 +154,7 @@ generate: $(CONTROLLER_GEN) #EXHELP Generate code containing DeepCopy, DeepCopyI
154154
$(CONTROLLER_GEN) --load-build-tags=$(GO_BUILD_TAGS) object:headerFile="hack/boilerplate.go.txt" paths="./..."
155155

156156
.PHONY: verify
157-
verify: tidy fmt generate manifests crd-ref-docs generate-test-data #HELP Verify all generated code is up-to-date.
157+
verify: k8s-pin fmt generate manifests crd-ref-docs generate-test-data #HELP Verify all generated code is up-to-date. Runs k8s-pin instead of just tidy.
158158
git diff --exit-code
159159

160160
# Renders registry+v1 bundles in test/convert

hack/tools/k8smaintainer/main.go

Lines changed: 73 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const (
2424
goModFilePerms = fs.FileMode(0600)
2525
minGoListVersionFields = 2
2626
minPatchNumberToDecrementFrom = 1 // We can only decrement patch if it's 1 or greater (to get 0 or greater)
27+
k8sVersionEnvVar = "K8S_IO_K8S_VERSION"
2728
)
2829

2930
//nolint:gochecknoglobals
@@ -42,6 +43,42 @@ func readAndParseGoMod(filename string) (*modfile.File, error) {
4243
return modF, nil
4344
}
4445

46+
// getK8sVersionFromEnv processes the version specified via environment variable.
47+
// It validates the version and runs `go get` to update the dependency.
48+
func getK8sVersionFromEnv(targetK8sVer string) (string, error) {
49+
log.Printf("Found target %s version override from env var %s: %s", k8sRepo, k8sVersionEnvVar, targetK8sVer)
50+
if !semver.IsValid(targetK8sVer) {
51+
return "", fmt.Errorf("invalid semver specified in %s: %s", k8sVersionEnvVar, targetK8sVer)
52+
}
53+
// Update the go.mod file first
54+
log.Printf("Running 'go get %s@%s' to update the main dependency...", k8sRepo, targetK8sVer)
55+
getArgs := fmt.Sprintf("%s@%s", k8sRepo, targetK8sVer)
56+
if _, err := runGoCommand("get", getArgs); err != nil {
57+
return "", fmt.Errorf("error running 'go get %s': %w", getArgs, err)
58+
}
59+
return targetK8sVer, nil // Return the validated version
60+
}
61+
62+
// getK8sVersionFromMod reads the go.mod file to find the current version of k8s.io/kubernetes.
63+
// It returns the version string if found, or an empty string (and nil error) if not found.
64+
func getK8sVersionFromMod() (string, error) {
65+
modF, err := readAndParseGoMod(goModFilename)
66+
if err != nil {
67+
return "", err // Propagate error from reading/parsing
68+
}
69+
70+
// Find k8s.io/kubernetes version
71+
for _, req := range modF.Require {
72+
if req.Mod.Path == k8sRepo {
73+
log.Printf("Found existing %s version in %s: %s", k8sRepo, goModFilename, req.Mod.Version)
74+
return req.Mod.Version, nil // Return found version
75+
}
76+
}
77+
// Not found case - return empty string, no error (as per original logic)
78+
log.Printf("INFO: %s not found in %s require block. Nothing to do.", k8sRepo, goModFilename)
79+
return "", nil
80+
}
81+
4582
func main() {
4683
log.SetFlags(0)
4784
if os.Getenv("GOEXE") != "" {
@@ -61,23 +98,27 @@ func main() {
6198
}
6299
log.Printf("Running in module root: %s", modRoot)
63100

64-
modF, err := readAndParseGoMod(goModFilename)
65-
if err != nil {
66-
log.Fatal(err) // Error already formatted by helper function
67-
}
101+
var k8sVer string
68102

69-
// Find k8s.io/kubernetes version
70-
k8sVer := ""
71-
for _, req := range modF.Require {
72-
if req.Mod.Path == k8sRepo {
73-
k8sVer = req.Mod.Version
74-
break
103+
// Determine the target k8s version using helper functions
104+
targetK8sVerEnv := os.Getenv(k8sVersionEnvVar)
105+
if targetK8sVerEnv != "" {
106+
// Process version from environment variable
107+
k8sVer, err = getK8sVersionFromEnv(targetK8sVerEnv)
108+
if err != nil {
109+
log.Fatalf("Failed to process k8s version from environment variable %s: %v", k8sVersionEnvVar, err)
110+
}
111+
} else {
112+
// Process version from go.mod file
113+
k8sVer, err = getK8sVersionFromMod()
114+
if err != nil {
115+
log.Fatalf("Failed to get k8s version from %s: %v", goModFilename, err)
116+
}
117+
// Handle the "not found" case where getK8sVersionFromMod returns "", nil
118+
if k8sVer == "" {
119+
os.Exit(0) // Exit gracefully as requested
75120
}
76121
}
77-
if k8sVer == "" {
78-
log.Fatalf("Could not find %s in %s require block", k8sRepo, goModFilename)
79-
}
80-
log.Printf("Found %s version: %s", k8sRepo, k8sVer)
81122

82123
// Calculate target staging version
83124
if !semver.IsValid(k8sVer) {
@@ -92,7 +133,7 @@ func main() {
92133
// targetStagingVer becomes "v0" + ".32" + "." + "3" => "v0.32.3"
93134
targetStagingVer := "v0" + strings.TrimPrefix(majorMinor, "v1") + "." + patch
94135
if !semver.IsValid(targetStagingVer) {
95-
log.Fatalf("Calculated invalid staging semver: %s", targetStagingVer)
136+
log.Fatalf("Calculated invalid staging semver: %s from k8s version %s", targetStagingVer, k8sVer)
96137
}
97138
log.Printf("Target staging version calculated: %s", targetStagingVer)
98139

@@ -158,7 +199,7 @@ func main() {
158199
}
159200

160201
if determinedVer != targetStagingVer {
161-
log.Printf("WARNING: Target version %s not found for %s. Using existing version %s.", targetStagingVer, effectivePath, determinedVer)
202+
log.Printf("INFO: Target version %s not found for %s. Using existing predecessor version %s.", targetStagingVer, effectivePath, determinedVer)
162203
}
163204

164205
// map the original module path (as seen in the dependency graph) to the desired version for the 'replace' directive
@@ -169,18 +210,18 @@ func main() {
169210
pins[k8sRepo] = k8sVer
170211
log.Printf("Identified %d k8s.io/* modules to manage.", len(pins))
171212

172-
// Parse go.mod again (needed in case `go list` modified it)
173-
modF, err = readAndParseGoMod(goModFilename)
213+
// Parse go.mod again (needed in case `go list` or `go get` modified it)
214+
modF, err := readAndParseGoMod(goModFilename)
174215
if err != nil {
175216
log.Fatal(err) // Error already formatted by helper function
176217
}
177218

178-
// Remove all existing k8s.io/* replaces
179-
log.Println("Removing existing k8s.io/* replace directives...")
219+
// Remove all existing k8s.io/* replaces that target other modules (not local paths)
220+
log.Println("Removing existing k8s.io/* module replace directives...")
180221
var replacesToRemove []string
181222
for _, rep := range modF.Replace {
182223
// Only remove replaces targeting k8s.io/* modules (not local replacements like ../staging)
183-
// assumes standard module paths contain '.'
224+
// Check that the old path starts with k8s.io/ and the new path looks like a module path (contains '.')
184225
if strings.HasPrefix(rep.Old.Path, "k8s.io/") && strings.Contains(rep.New.Path, ".") {
185226
replacesToRemove = append(replacesToRemove, rep.Old.Path)
186227
} else if strings.HasPrefix(rep.Old.Path, "k8s.io/") {
@@ -274,8 +315,12 @@ func runGoCommand(args ...string) ([]byte, error) {
274315
var stdout, stderr bytes.Buffer
275316
cmd.Stdout = &stdout
276317
cmd.Stderr = &stderr
318+
log.Printf("Executing: %s %s", goExe, strings.Join(args, " "))
277319
if err := cmd.Run(); err != nil {
278-
return nil, fmt.Errorf("command '%s %s' failed: %v\nStderr:\n%s", goExe, strings.Join(args, " "), err, stderr.String())
320+
if stderr.Len() > 0 {
321+
log.Printf("Stderr:\n%s", stderr.String())
322+
}
323+
return nil, fmt.Errorf("command '%s %s' failed: %w", goExe, strings.Join(args, " "), err)
279324
}
280325
return stdout.Bytes(), nil
281326
}
@@ -286,11 +331,14 @@ func getModuleVersions(modulePath string) ([]string, error) {
286331
// Combine output and error message for checking because 'go list' sometimes writes errors to stdout
287332
combinedOutput := string(output)
288333
if err != nil {
289-
combinedOutput += err.Error()
334+
if !strings.Contains(combinedOutput, err.Error()) {
335+
combinedOutput += err.Error()
336+
}
290337
}
291338

292339
// Check if the error/output indicates "no matching versions" - this is not a fatal error for our logic
293-
if strings.Contains(combinedOutput, "no matching versions") {
340+
if strings.Contains(combinedOutput, "no matching versions") || strings.Contains(combinedOutput, "no required module provides package") {
341+
log.Printf("INFO: No versions found for module %s via 'go list'.", modulePath)
294342
return []string{}, nil // Return empty list, not an error
295343
}
296344
// If there was an actual error beyond "no matching versions"
@@ -300,6 +348,7 @@ func getModuleVersions(modulePath string) ([]string, error) {
300348

301349
fields := strings.Fields(string(output))
302350
if len(fields) < minGoListVersionFields {
351+
log.Printf("INFO: No versions listed for module %s (output: '%s')", modulePath, string(output))
303352
return []string{}, nil // No versions listed (e.g., just the module path)
304353
}
305354
return fields[1:], nil // First field is the module path

0 commit comments

Comments
 (0)