diff --git a/.bingo/Variables.mk b/.bingo/Variables.mk index 16a0e58a2..273cfb709 100644 --- a/.bingo/Variables.mk +++ b/.bingo/Variables.mk @@ -53,11 +53,11 @@ $(GORELEASER): $(BINGO_DIR)/goreleaser.mod @echo "(re)installing $(GOBIN)/goreleaser-v1.26.2" @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=goreleaser.mod -o=$(GOBIN)/goreleaser-v1.26.2 "github.com/goreleaser/goreleaser" -KIND := $(GOBIN)/kind-v0.27.0 +KIND := $(GOBIN)/kind-v0.29.0 $(KIND): $(BINGO_DIR)/kind.mod @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. - @echo "(re)installing $(GOBIN)/kind-v0.27.0" - @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=kind.mod -o=$(GOBIN)/kind-v0.27.0 "sigs.k8s.io/kind" + @echo "(re)installing $(GOBIN)/kind-v0.29.0" + @cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=kind.mod -o=$(GOBIN)/kind-v0.29.0 "sigs.k8s.io/kind" KUSTOMIZE := $(GOBIN)/kustomize-v5.6.0 $(KUSTOMIZE): $(BINGO_DIR)/kustomize.mod diff --git a/.bingo/kind.mod b/.bingo/kind.mod index 3037dbbaa..90ef6aa18 100644 --- a/.bingo/kind.mod +++ b/.bingo/kind.mod @@ -2,4 +2,4 @@ module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT go 1.20 -require sigs.k8s.io/kind v0.27.0 +require sigs.k8s.io/kind v0.29.0 diff --git a/.bingo/kind.sum b/.bingo/kind.sum index b4115fe16..30e183508 100644 --- a/.bingo/kind.sum +++ b/.bingo/kind.sum @@ -64,6 +64,8 @@ sigs.k8s.io/kind v0.26.0 h1:8fS6I0Q5WGlmLprSpH0DarlOSdcsv0txnwc93J2BP7M= sigs.k8s.io/kind v0.26.0/go.mod h1:t7ueEpzPYJvHA8aeLtI52rtFftNgUYUaCwvxjk7phfw= sigs.k8s.io/kind v0.27.0 h1:PQ3f0iAWNIj66LYkZ1ivhEg/+Zb6UPMbO+qVei/INZA= sigs.k8s.io/kind v0.27.0/go.mod h1:RZVFmy6qcwlSWwp6xeIUv7kXCPF3i8MXsEXxW/J+gJY= +sigs.k8s.io/kind v0.29.0 h1:3TpCsyh908IkXXpcSnsMjWdwdWjIl7o9IMZImZCWFnI= +sigs.k8s.io/kind v0.29.0/go.mod h1:ldWQisw2NYyM6k64o/tkZng/1qQW7OlzcN5a8geJX3o= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= diff --git a/.bingo/variables.env b/.bingo/variables.env index 07e40961f..b773bd006 100644 --- a/.bingo/variables.env +++ b/.bingo/variables.env @@ -20,7 +20,7 @@ GOLANGCI_LINT="${GOBIN}/golangci-lint-v1.64.6" GORELEASER="${GOBIN}/goreleaser-v1.26.2" -KIND="${GOBIN}/kind-v0.27.0" +KIND="${GOBIN}/kind-v0.29.0" KUSTOMIZE="${GOBIN}/kustomize-v5.6.0" diff --git a/Makefile b/Makefile index a576df128..b587f7c7a 100644 --- a/Makefile +++ b/Makefile @@ -40,12 +40,6 @@ endif # Ensure ENVTEST_VERSION follows correct "X.Y.x" format ENVTEST_VERSION := $(K8S_VERSION).x -# Not guaranteed to have patch releases available and node image tags are full versions (i.e v1.28.0 - no v1.28, v1.29, etc.) -# The K8S_VERSION is set by getting the version of the k8s.io/client-go dependency from the go.mod -# and sets major version to "1" and the patch version to "0". For example, a client-go version of v0.28.5 -# will map to a K8S_VERSION of 1.28.0 -KIND_CLUSTER_IMAGE := kindest/node:v$(K8S_VERSION).0 - # Define dependency versions (use go.mod if we also use Go code from dependency) export CERT_MGR_VERSION := v1.17.1 export WAIT_TIMEOUT := 60s @@ -160,7 +154,7 @@ generate: $(CONTROLLER_GEN) #EXHELP Generate code containing DeepCopy, DeepCopyI $(CONTROLLER_GEN) --load-build-tags=$(GO_BUILD_TAGS) object:headerFile="hack/boilerplate.go.txt" paths="./..." .PHONY: verify -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. +verify: k8s-version-check 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. git diff --exit-code # Renders registry+v1 bundles in test/convert @@ -321,7 +315,7 @@ kind-deploy: manifests .PHONY: kind-cluster kind-cluster: $(KIND) #EXHELP Standup a kind cluster. -$(KIND) delete cluster --name $(KIND_CLUSTER_NAME) - $(KIND) create cluster --name $(KIND_CLUSTER_NAME) --image $(KIND_CLUSTER_IMAGE) --config ./kind-config.yaml + $(KIND) create cluster --name $(KIND_CLUSTER_NAME) --config ./kind-config.yaml $(KIND) export kubeconfig --name $(KIND_CLUSTER_NAME) .PHONY: kind-clean @@ -450,4 +444,8 @@ update-demos: ./hack/demo/generate-asciidemo.sh -u -n $$nm $$(basename $$script); \ done +.PHONY: k8s-version-check +k8s-version-check: #EXHELP verify tooling aligns with k8s MAJOR.MINOR version + go run hack/tools/k8smaintainer/main.go --kube-version $(K8S_VERSION) --kind-mod-path ./.bingo/kind.mod + include Makefile.venv diff --git a/hack/tools/k8saligner/main.go b/hack/tools/k8saligner/main.go new file mode 100644 index 000000000..42a5d8672 --- /dev/null +++ b/hack/tools/k8saligner/main.go @@ -0,0 +1,110 @@ +package main + +import ( + "bufio" + "errors" + "fmt" + "io" + "net/http" + "os" + "regexp" + "strings" + + "github.com/Masterminds/semver/v3" + "github.com/spf13/cobra" +) + +func main() { + var kubeVersionString, kindModPath string + + var rootCmd = &cobra.Command{ + Use: "k8saligner", + Short: "Compares Kubernetes version with kind image version", + RunE: func(cmd *cobra.Command, args []string) error { + kindVersion, err := parseKindVersion(kindModPath) + if err != nil { + return fmt.Errorf("failed to parse kind version: %w", err) + } + + imageVersion, err := fetchKindImageVersion(kindVersion) + if err != nil { + return fmt.Errorf("failed to fetch kind image version: %w", err) + } + + kubeVersion, err := semver.NewVersion(kubeVersionString) + if err != nil { + return fmt.Errorf("invalid Kubernetes version format: %w", err) + } + match := ((kubeVersion.Major() == imageVersion.Major()) && (kubeVersion.Minor() == imageVersion.Minor())) + + fmt.Printf("Kubernetes version: %s\nKind image version: %s\n", kubeVersion, imageVersion) + if match { + fmt.Println("major and minor versions match.") + os.Exit(0) + } else { + fmt.Println("major and minor versions do NOT match.") + os.Exit(1) + } + return nil + }, + } + + rootCmd.Flags().StringVar(&kubeVersionString, "kube-version", "", "Kubernetes version string (e.g. v1.29.0)") + rootCmd.Flags().StringVar(&kindModPath, "kind-mod-path", "", "Path to .bingo/kind.mod file") + _ = rootCmd.MarkFlagRequired("kube-version") + _ = rootCmd.MarkFlagRequired("kind-mod-path") + + if err := rootCmd.Execute(); err != nil { + fmt.Println(err) + os.Exit(1) + } +} + +func parseKindVersion(path string) (string, error) { + f, err := os.Open(path) + if err != nil { + return "", err + } + defer f.Close() + + scanner := bufio.NewScanner(f) + re := regexp.MustCompile(`require sigs\.k8s\.io/kind\s+([^\s]+)`) + for scanner.Scan() { + line := scanner.Text() + if m := re.FindStringSubmatch(line); m != nil { + fmt.Printf("Found kind version: %s\n", m[1]) + return m[1], nil + } + } + return "", errors.New("kind version not found in mod file") +} + +func fetchKindImageVersion(kindVersion string) (*semver.Version, error) { + url := fmt.Sprintf("https://github.com/kubernetes-sigs/kind/raw/refs/tags/%s/pkg/apis/config/defaults/image.go", kindVersion) + resp, err := http.Get(url) + if err != nil { + return nil, err + } + defer resp.Body.Close() + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to fetch file: %s", resp.Status) + } + return extractImageVersion(resp.Body) +} + +func extractImageVersion(r io.Reader) (*semver.Version, error) { + scanner := bufio.NewScanner(r) + re := regexp.MustCompile(`^const\s+Image\s+=\s+"[^:]+:?(.*)"`) + for scanner.Scan() { + line := scanner.Text() + if m := re.FindStringSubmatch(line); m != nil { + tagString := strings.Split(m[1], "@")[0] // floating tag part before any hash + v, err := semver.NewVersion(tagString) + if err != nil { + return nil, fmt.Errorf("invalid version format: %s", m[1]) + } + return v, nil + } + } + return nil, errors.New("image constant not found") +}