diff --git a/go.mod b/go.mod index b977c8e2cb..be17da518a 100644 --- a/go.mod +++ b/go.mod @@ -171,6 +171,7 @@ require ( github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect github.com/mdlayher/socket v0.4.1 // indirect github.com/mdlayher/vsock v1.2.1 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect @@ -187,6 +188,7 @@ require ( github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/procfs v0.16.1 // indirect + github.com/rivo/uniseg v0.4.7 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect github.com/sagikazarmark/locafero v0.7.0 // indirect github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect @@ -259,6 +261,7 @@ tool ( github.com/campoy/embedmd github.com/google/go-jsonnet/cmd/jsonnet github.com/hairyhenderson/gomplate/v4/cmd/gomplate + github.com/itchyny/gojq/cmd/gojq github.com/jsonnet-bundler/jsonnet-bundler/cmd/jb golang.org/x/perf/cmd/benchstat ) diff --git a/go.sum b/go.sum index 9a835b3933..483c5cce1e 100644 --- a/go.sum +++ b/go.sum @@ -491,6 +491,8 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U= github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= @@ -579,6 +581,9 @@ github.com/protocolbuffers/txtpbfmt v0.0.0-20240823084532-8e6b51fa9bef h1:ej+64j github.com/protocolbuffers/txtpbfmt v0.0.0-20240823084532-8e6b51fa9bef/go.mod h1:jgxiZysxFPM+iWKwQwPR+y+Jvo54ARd4EisXxKYpB5c= github.com/redis/go-redis/v9 v9.6.1 h1:HHDteefn6ZkTtY5fGUE8tj8uy85AHk6zP7CpzIAM0y4= github.com/redis/go-redis/v9 v9.6.1/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= diff --git a/scripts/tests/update-data-yaml/inputs/add_new_no_prune.yaml b/scripts/tests/update-data-yaml/inputs/add_new_no_prune.yaml new file mode 100644 index 0000000000..dc16509c34 --- /dev/null +++ b/scripts/tests/update-data-yaml/inputs/add_new_no_prune.yaml @@ -0,0 +1,17 @@ +# The purpose of this config is to keep all versions in a single file and make them machine accessible + +# Marks the latest release +version: "2.17.0" + +# List at max 5 releases here + the main branch +compat: + - version: "v2.14.0" + kubernetes: "1.31" + - version: "v2.15.0" + kubernetes: "1.32" + - version: "v2.16.0" + kubernetes: "1.32" + - version: "v2.17.0" + kubernetes: "1.33" + - version: "main" + kubernetes: "1.33" diff --git a/scripts/tests/update-data-yaml/inputs/add_new_with_prune.yaml b/scripts/tests/update-data-yaml/inputs/add_new_with_prune.yaml new file mode 100644 index 0000000000..2d8aa8d2eb --- /dev/null +++ b/scripts/tests/update-data-yaml/inputs/add_new_with_prune.yaml @@ -0,0 +1,19 @@ +# The purpose of this config is to keep all versions in a single file and make them machine accessible + +# Marks the latest release +version: "2.17.0" + +# List at max 5 releases here + the main branch +compat: + - version: "v2.13.0" + kubernetes: "1.30" + - version: "v2.14.0" + kubernetes: "1.31" + - version: "v2.15.0" + kubernetes: "1.32" + - version: "v2.16.0" + kubernetes: "1.32" + - version: "v2.17.0" + kubernetes: "1.33" + - version: "main" + kubernetes: "1.33" diff --git a/scripts/tests/update-data-yaml/inputs/exact_match_exists.yaml b/scripts/tests/update-data-yaml/inputs/exact_match_exists.yaml new file mode 100644 index 0000000000..f24aeb681b --- /dev/null +++ b/scripts/tests/update-data-yaml/inputs/exact_match_exists.yaml @@ -0,0 +1,19 @@ +# The purpose of this config is to keep all versions in a single file and make them machine accessible + +# Marks the latest release +version: "2.17.0" + +# List at max 5 releases here + the main branch +compat: + - version: "v2.14.0" + kubernetes: "1.31" + - version: "v2.15.0" + kubernetes: "1.32" + - version: "v2.16.0" + kubernetes: "1.32" + - version: "v2.17.0" + kubernetes: "1.33" + - version: "v2.18.0" + kubernetes: "1.33" + - version: "main" + kubernetes: "1.33" diff --git a/scripts/tests/update-data-yaml/outputs/add_new_no_prune.out.yaml b/scripts/tests/update-data-yaml/outputs/add_new_no_prune.out.yaml new file mode 100644 index 0000000000..812ee98cc0 --- /dev/null +++ b/scripts/tests/update-data-yaml/outputs/add_new_no_prune.out.yaml @@ -0,0 +1,19 @@ +# This configuration tracks the last five releases, and acts as the source of truth for those entries in the README. +# +# This marks the latest release. +version: "2.18.0" + +# List KSM-to-K8s version mapping for the last five releases here, and the default branch. +compat: + - version: "v2.14.0" + kubernetes: "1.31" + - version: "v2.15.0" + kubernetes: "1.32" + - version: "v2.16.0" + kubernetes: "1.32" + - version: "v2.17.0" + kubernetes: "1.33" + - version: "v2.18.0" + kubernetes: "1.33" + - version: "main" + kubernetes: "1.33" diff --git a/scripts/tests/update-data-yaml/outputs/add_new_with_prune.out.yaml b/scripts/tests/update-data-yaml/outputs/add_new_with_prune.out.yaml new file mode 100644 index 0000000000..812ee98cc0 --- /dev/null +++ b/scripts/tests/update-data-yaml/outputs/add_new_with_prune.out.yaml @@ -0,0 +1,19 @@ +# This configuration tracks the last five releases, and acts as the source of truth for those entries in the README. +# +# This marks the latest release. +version: "2.18.0" + +# List KSM-to-K8s version mapping for the last five releases here, and the default branch. +compat: + - version: "v2.14.0" + kubernetes: "1.31" + - version: "v2.15.0" + kubernetes: "1.32" + - version: "v2.16.0" + kubernetes: "1.32" + - version: "v2.17.0" + kubernetes: "1.33" + - version: "v2.18.0" + kubernetes: "1.33" + - version: "main" + kubernetes: "1.33" diff --git a/scripts/tests/update-data-yaml/outputs/exact_match_exists.out.yaml b/scripts/tests/update-data-yaml/outputs/exact_match_exists.out.yaml new file mode 100644 index 0000000000..f24aeb681b --- /dev/null +++ b/scripts/tests/update-data-yaml/outputs/exact_match_exists.out.yaml @@ -0,0 +1,19 @@ +# The purpose of this config is to keep all versions in a single file and make them machine accessible + +# Marks the latest release +version: "2.17.0" + +# List at max 5 releases here + the main branch +compat: + - version: "v2.14.0" + kubernetes: "1.31" + - version: "v2.15.0" + kubernetes: "1.32" + - version: "v2.16.0" + kubernetes: "1.32" + - version: "v2.17.0" + kubernetes: "1.33" + - version: "v2.18.0" + kubernetes: "1.33" + - version: "main" + kubernetes: "1.33" diff --git a/scripts/tests/update-data-yaml/test-data-yaml-script.sh b/scripts/tests/update-data-yaml/test-data-yaml-script.sh new file mode 100755 index 0000000000..9f48d71041 --- /dev/null +++ b/scripts/tests/update-data-yaml/test-data-yaml-script.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &>/dev/null && pwd ) +PROJECT_ROOT=$( cd -- "${SCRIPT_DIR}/../../.." &>/dev/null && pwd ) + +INPUT_DIR="${SCRIPT_DIR}/inputs" +OUTPUT_DIR="${SCRIPT_DIR}/outputs" +mkdir -p "${OUTPUT_DIR}" + +# Backup root data.yaml once +ROOT_DATA="${PROJECT_ROOT}/data.yaml" +TMP_DATA=$(mktemp) +cp "${ROOT_DATA}" "${TMP_DATA}" + +for input_file in "${INPUT_DIR}"/*.yaml; do + base=$(basename "${input_file}" .yaml) + echo "Processing ${base}.yaml ..." + + cp "${input_file}" "${ROOT_DATA}" + "${PROJECT_ROOT}/scripts/update-data-yaml.sh" v2.18.0 + cp "${ROOT_DATA}" "${OUTPUT_DIR}/${base}.out.yaml" +done + +# Restore original data.yaml +mv "${TMP_DATA}" "${ROOT_DATA}" + +echo "All done. Results in ${OUTPUT_DIR}" diff --git a/scripts/update-data-yaml.sh b/scripts/update-data-yaml.sh new file mode 100755 index 0000000000..27a3cf2983 --- /dev/null +++ b/scripts/update-data-yaml.sh @@ -0,0 +1,150 @@ +#!/bin/bash +set -euo pipefail + +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +REPO_ROOT=$( cd -- "${SCRIPT_DIR}/.." &> /dev/null && pwd ) +DATA_FILE="${REPO_ROOT}/data.yaml" +NUM_RELEASES_TO_KEEP=5 + +GOJQ="go tool github.com/itchyny/gojq/cmd/gojq" + +check_command() { + if ! command -v "$1" &> /dev/null; then + echo "Error: Required command '$1' not found. Please install it to continue." >&2 + exit 1 + fi +} + +check_command "go" +check_command "sort" + +if [ -z "$1" ]; then + echo "Usage: $0 " + echo "Example: $0 v2.17.0" + exit 1 +fi + +if [ ! -f "${DATA_FILE}" ]; then + echo "Error: Data file not found at ${DATA_FILE}" >&2 + exit 1 +fi + +NEW_VERSION_WITH_V=$1 +NEW_VERSION_WITHOUT_V=${NEW_VERSION_WITH_V#v} + +echo "Starting update process for version ${NEW_VERSION_WITH_V}..." + +echo "Checking k8s.io/client-go version from go.mod..." +GO_MOD="${REPO_ROOT}/go.mod" + +if [ ! -f "${GO_MOD}" ]; then + echo "Error: go.mod file not found at ${GO_MOD}" >&2 + exit 1 +fi + +CLIENT_GO_VERSION=$(go list -m -f '{{.Version}}' k8s.io/client-go) + +if [ -z "$CLIENT_GO_VERSION" ]; then + echo "Error: Could not find k8s.io/client-go version in go.mod." >&2 + exit 1 +fi + +K8S_MINOR=$(echo "${CLIENT_GO_VERSION}" | cut -d. -f2) + +K8S_VERSION_FOR_NEW_RELEASE="1.${K8S_MINOR}" +echo "New release ${NEW_VERSION_WITH_V} will be mapped to Kubernetes: ${K8S_VERSION_FOR_NEW_RELEASE}" + +# Convert YAML data file to JSON format +DATA_FILE_JSON_DATA=$(${GOJQ} -r --yaml-input '.' "${DATA_FILE}") + +EXISTING_EXACT_MATCH=$(\ +echo "${DATA_FILE_JSON_DATA}" |\ +# Query for existing entry with same version and k8s mapping +${GOJQ} -r --arg version "${NEW_VERSION_WITH_V}" --arg k8s "${K8S_VERSION_FOR_NEW_RELEASE}" ".compat[] | select(.version == \$version and .kubernetes == \$k8s) | .version // empty"\ +) + +if [ -n "${EXISTING_EXACT_MATCH}" ]; then + echo "Entry for ${NEW_VERSION_WITH_V} with Kubernetes ${K8S_VERSION_FOR_NEW_RELEASE} already exists. No changes needed." + exit 0 +fi + +EXISTING_KSM_VERSION_ENTRY=$(\ +echo "${DATA_FILE_JSON_DATA}" |\ +# Check if KSM version already exists (with different k8s mapping) +${GOJQ} -r --arg version "${NEW_VERSION_WITH_V}" ".compat[] | select(.version == \$version) | .version // empty"\ +) + +if [ -n "${EXISTING_KSM_VERSION_ENTRY}" ]; then + echo "Version ${NEW_VERSION_WITH_V} found with a different K8s mapping. Updating..." + cat > "${DATA_FILE}" << EOF +# This configuration tracks the last five releases, and acts as the source of truth for those entries in the README. +# +# This marks the latest release. +version: "${NEW_VERSION_WITHOUT_V}" + +# List KSM-to-K8s version mapping for the last five releases here, and the default branch. +compat: +EOF + + + { + echo "${DATA_FILE_JSON_DATA}" |\ + # Filter out the version being updated and format as YAML + ${GOJQ} -r --arg version "${NEW_VERSION_WITH_V}" --arg k8s "${K8S_VERSION_FOR_NEW_RELEASE}" \ + ".compat[] | select(.version != \$version) | \" - kubernetes: \\\"\" + .kubernetes + \"\\\"\\n version: \" + .version" + + echo " - kubernetes: \"${K8S_VERSION_FOR_NEW_RELEASE}\"" + echo " version: ${NEW_VERSION_WITH_V}" + } >> "${DATA_FILE}" + + echo "Successfully updated existing entry for ${NEW_VERSION_WITH_V}." + echo "--- Final ${DATA_FILE} content ---" + cat "${DATA_FILE}" + exit 0 +fi + +echo "Adding new version ${NEW_VERSION_WITH_V} and pruning old releases..." + +VERSIONS_LIST_WITHOUT_MAIN=$(\ +echo "${DATA_FILE_JSON_DATA}" |\ +# Extract version|kubernetes pairs, excluding main branch +${GOJQ} -r ".compat[] | select(.version != \"main\") | \"\(.version)|\(.kubernetes)\"" 2>/dev/null || true\ +) + +FULL_VERSIONS_LIST=$(printf "%s\n%s|%s" "${VERSIONS_LIST_WITHOUT_MAIN}" "${NEW_VERSION_WITH_V}" "${K8S_VERSION_FOR_NEW_RELEASE}") + +SORTED_VERSIONS=$(\ +echo "${FULL_VERSIONS_LIST}" |\ +# Remove empty lines +grep -v '^$' |\ +# Sort by version (ascending) using version sort +sort -t'|' -k1,1 -V |\ +# Keep only the most recent N releases +tail -n "${NUM_RELEASES_TO_KEEP}"\ +) + +cat > "${DATA_FILE}" << EOF +# This configuration tracks the last five releases, and acts as the source of truth for those entries in the README. +# +# This marks the latest release. +version: "${NEW_VERSION_WITHOUT_V}" + +# List KSM-to-K8s version mapping for the last five releases here, and the default branch. +compat: +EOF + +while IFS='|' read -r version k8s_ver; do + if [ -n "${version}" ]; then + echo " - version: \"${version}\"" >> "${DATA_FILE}" + echo " kubernetes: \"${k8s_ver}\"" >> "${DATA_FILE}" + fi +done <<< "${SORTED_VERSIONS}" + +echo " - version: \"main\"" >> "${DATA_FILE}" +echo " kubernetes: \"${K8S_VERSION_FOR_NEW_RELEASE}\"" >> "${DATA_FILE}" + +echo "Successfully updated and pruned ${DATA_FILE}." +echo "New release (${NEW_VERSION_WITH_V}) is mapped to Kubernetes: ${K8S_VERSION_FOR_NEW_RELEASE}" +echo "Main branch is mapped to Kubernetes: ${K8S_VERSION_FOR_NEW_RELEASE}" +echo "--- Final ${DATA_FILE} content ---" +cat "${DATA_FILE}"