diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..35049cb --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,2 @@ +[alias] +xtask = "run --package xtask --" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b7eea0a..eacaef5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,6 +8,7 @@ on: - 'src/**' - 'Cargo.*' - 'flake.*' + - 'xtask/**' pull_request: branches: [main] paths: @@ -15,6 +16,7 @@ on: - 'src/**' - 'Cargo.*' - 'flake.*' + - 'xtask/**' concurrency: group: ci-${{ github.ref }} @@ -45,6 +47,18 @@ jobs: primary-key: nix-${{ runner.os }}-${{ hashFiles('**/Cargo.lock', 'flake.lock') }} - run: nix build .#checks.x86_64-linux.clippy + check-versions: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: cachix/install-nix-action@v31 + with: + github_access_token: ${{ secrets.GITHUB_TOKEN }} + - uses: nix-community/cache-nix-action@v7 + with: + primary-key: nix-${{ runner.os }}-${{ hashFiles('**/Cargo.lock', 'flake.lock') }} + - run: nix develop -c cargo xtask check + test: runs-on: ubuntu-latest steps: @@ -71,7 +85,7 @@ jobs: summary: runs-on: ubuntu-latest - needs: [fmt, clippy, test, build] + needs: [fmt, clippy, check-versions, test, build] if: always() steps: - name: Summary @@ -79,13 +93,14 @@ jobs: run: | fmt="${{ needs.fmt.result }}" clippy="${{ needs.clippy.result }}" + check_versions="${{ needs.check-versions.result }}" test="${{ needs.test.result }}" build="${{ needs.build.result }}" icon() { [[ "$1" == "success" ]] && echo "✅" || echo "❌"; } # Overall result - if [[ "$fmt" == "success" && "$clippy" == "success" && "$test" == "success" && "$build" == "success" ]]; then + if [[ "$fmt" == "success" && "$clippy" == "success" && "$check_versions" == "success" && "$test" == "success" && "$build" == "success" ]]; then header="## ✅ CI Passed" else header="## ❌ CI Failed" @@ -98,6 +113,7 @@ jobs: | --- | --- | | Formatting | $(icon "$fmt") \`cargo fmt\` | | Linting | $(icon "$clippy") \`cargo clippy\` | + | Versioning | $(icon "$check_versions") \`cargo xtask check\` | | Tests | $(icon "$test") \`cargo test\` | | Build | $(icon "$build") \`static musl binary\` | @@ -126,6 +142,17 @@ jobs: EOF fi + if [[ "$check_versions" != "success" ]]; then + cat >> "$GITHUB_STEP_SUMMARY" << 'EOF' + ### 🔧 Fix check versions warnings + + ```bash + cargo xtask check + ``` + + EOF + fi + if [[ "$test" != "success" ]]; then cat >> "$GITHUB_STEP_SUMMARY" << 'EOF' ### 🔧 Fix tests @@ -152,6 +179,94 @@ jobs: [[ \ "$fmt" == "success" && \ "$clippy" == "success" && \ + "$check_versions" == "success" && \ "$test" == "success" && \ "$build" == "success" \ ]] + + detect-release: + runs-on: ubuntu-latest + needs: [summary] + if: | + github.event_name == 'push' && + github.ref == 'refs/heads/main' && + needs.summary.result == 'success' + outputs: + is_release: ${{ steps.detect.outputs.is_release }} + version: ${{ steps.detect.outputs.version }} + steps: + - uses: actions/checkout@v6 + - name: Detect release commit + id: detect + run: | + MSG=$(git log -1 --format=%s) + if [[ "$MSG" =~ ^release\ v([0-9]+\.[0-9]+\.[0-9]+)$ ]]; + then + echo "is_release=true" >> "$GITHUB_OUTPUT" + echo "version=${BASH_REMATCH[1]}" >> "$GITHUB_OUTPUT" + else + echo "is_release=false" >> "$GITHUB_OUTPUT" + fi + + tag-and-release: + runs-on: ubuntu-latest + needs: [detect-release] + if: needs.detect-release.outputs.is_release == 'true' + permissions: + contents: write + steps: + - uses: actions/checkout@v6 + + - name: Create git tag + env: + TAG: v${{ needs.detect-release.outputs.version }} + run: | + if git ls-remote --tags origin "$TAG" | grep -qF "refs/tags/$TAG"; + then + echo "Tag $TAG already exists, skipping." + else + git tag "$TAG" + git push origin "$TAG" + fi + + - name: Setup Nix + uses: cachix/install-nix-action@v31 + with: + github_access_token: ${{ secrets.GITHUB_TOKEN }} + + - name: Restore Nix cache + uses: nix-community/cache-nix-action@v7 + with: + primary-key: nix-${{ runner.os }}-${{ hashFiles('**/Cargo.lock', 'flake.lock') }} + + - name: Build release image + run: nix build .#image + + - name: Push release image + id: push + env: + VERSION: ${{ needs.detect-release.outputs.version }} + run: | + TAG="v${VERSION}" + IMAGE="quay.io/upgrades/k8s-cloud-tagger:${TAG}" + + nix develop -c skopeo login quay.io \ + -u "${{ secrets.QUAY_USERNAME }}" \ + -p "${{ secrets.QUAY_PASSWORD }}" + + nix develop -c skopeo copy docker-archive:result "docker://${IMAGE}" + + echo "image=${IMAGE}" >> "$GITHUB_OUTPUT" + echo "tag=${TAG}" >> "$GITHUB_OUTPUT" + + - name: Summary + run: | + cat >> "$GITHUB_STEP_SUMMARY" << EOF + ## Release ${{ needs.detect-release.outputs.version }} + + | | | + | --- | --- | + | **Image** | \`${{ steps.push.outputs.image }}\` | + | **Tag** | \`${{ steps.push.outputs.tag }}\` | + | **Registry** | [Quay.io](https://quay.io/repository/upgrades/k8s-cloud-tagger?tab=tags) | + EOF \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..b3134c1 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,20 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added +- Kubernetes operator that propagates PVC labels to GCP persistent disks +- GCP label sanitisation (lowercasing, replacing invalid characters with hyphens) +- Helm chart for deployment +- Prometheus metrics for reconciliation and cloud API calls +- Health and readiness endpoints +- Kubernetes Events published on successful tagging +- Nix dev shell for reproducible development environments +- Dev image published to Quay.io +- Integration tests +- `cargo xtask` release tooling with version sync enforcement diff --git a/Cargo.lock b/Cargo.lock index 48d05d2..0f6c2ef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -41,9 +41,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.100" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "async-broadcast" @@ -173,9 +173,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bitflags" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" [[package]] name = "block-buffer" @@ -188,21 +188,21 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.19.1" +version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" [[package]] name = "bytes" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" [[package]] name = "cc" -version = "1.2.53" +version = "1.2.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "755d2fce177175ffca841e9a06afdb2c4ab0f593d53b4dee48147dfaade85932" +checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" dependencies = [ "find-msvc-tools", "shlex", @@ -459,9 +459,9 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "find-msvc-tools" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] name = "fnv" @@ -469,6 +469,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "foldhash" version = "0.2.0" @@ -486,9 +492,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" dependencies = [ "futures-channel", "futures-core", @@ -501,9 +507,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" dependencies = [ "futures-core", "futures-sink", @@ -511,15 +517,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" [[package]] name = "futures-executor" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" dependencies = [ "futures-core", "futures-task", @@ -528,15 +534,15 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" [[package]] name = "futures-macro" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", @@ -545,21 +551,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" [[package]] name = "futures-task" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" [[package]] name = "futures-util" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ "futures-channel", "futures-core", @@ -569,7 +575,6 @@ dependencies = [ "futures-task", "memchr", "pin-project-lite", - "pin-utils", "slab", ] @@ -632,6 +637,19 @@ dependencies = [ "wasip2", ] +[[package]] +name = "getrandom" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", + "wasip3", +] + [[package]] name = "gloo-timers" version = "0.3.0" @@ -663,6 +681,15 @@ dependencies = [ "tracing", ] +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash 0.1.5", +] + [[package]] name = "hashbrown" version = "0.16.1" @@ -671,9 +698,15 @@ checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" dependencies = [ "allocator-api2", "equivalent", - "foldhash", + "foldhash 0.2.0", ] +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hostname" version = "0.4.2" @@ -786,14 +819,13 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.19" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" dependencies = [ "base64", "bytes", "futures-channel", - "futures-core", "futures-util", "http", "http-body", @@ -913,6 +945,12 @@ dependencies = [ "zerovec", ] +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + [[package]] name = "ident_case" version = "1.0.1" @@ -947,7 +985,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.16.1", + "serde", + "serde_core", ] [[package]] @@ -974,9 +1014,9 @@ checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" [[package]] name = "jiff" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c867c356cc096b33f4981825ab281ecba3db0acefe60329f044c1789d94c6543" +checksum = "b3e3d65f018c6ae946ab16e80944b97096ed73c35b221d1c478a6c81d8f57940" dependencies = [ "jiff-static", "jiff-tzdb-platform", @@ -989,9 +1029,9 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7946b4325269738f270bb55b3c19ab5c5040525f83fd625259422a9d25d9be5" +checksum = "a17c2b211d863c7fde02cbea8a3c1a439b98e109286554f2860bdded7ff83818" dependencies = [ "proc-macro2", "quote", @@ -1037,9 +1077,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "js-sys" -version = "0.3.85" +version = "0.3.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" +checksum = "c7e709f3e3d22866f9c25b3aff01af289b18422cc8b4262fb19103ee80fe513d" dependencies = [ "once_cell", "wasm-bindgen", @@ -1125,9 +1165,9 @@ dependencies = [ [[package]] name = "kube" -version = "3.0.0" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dae7229247e4215781e5c5104a056e1e2163943e577f9084cf8bba7b5248f7a" +checksum = "f96b537b4c4f61fc183594edbecbbefa3037e403feac0701bb24e6eff78e0034" dependencies = [ "k8s-openapi", "kube-client", @@ -1138,9 +1178,9 @@ dependencies = [ [[package]] name = "kube-client" -version = "3.0.0" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "010875e291a9c0a4e076f4f9c35b97d82fd2372cb3bc713252c3d08b7e73ce5b" +checksum = "af97b8b696eb737e5694f087c498ca725b172c2a5bc3a6916328d160225537ee" dependencies = [ "base64", "bytes", @@ -1173,9 +1213,9 @@ dependencies = [ [[package]] name = "kube-core" -version = "3.0.0" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ac76281aa698dd34111e25b21f5f6561932a30feabab5357152be273f8a81bb" +checksum = "e7aeade7d2e9f165f96b3c1749ff01a8e2dc7ea954bd333bcfcecc37d5226bdd" dependencies = [ "derive_more", "form_urlencoded", @@ -1192,9 +1232,9 @@ dependencies = [ [[package]] name = "kube-derive" -version = "3.0.0" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "599c09721efcccc0e6a26e93df28c587da60ff5e099c657626fff2af0ae4cbb8" +checksum = "c98f59f4e68864624a0b993a1cc2424439ab7238eaede5c299e89943e2a093ff" dependencies = [ "darling", "proc-macro2", @@ -1206,9 +1246,9 @@ dependencies = [ [[package]] name = "kube-runtime" -version = "3.0.0" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db43d26700f564baf850f681f3cb0f1195d2699bd379bfa70750ecec4dcb209" +checksum = "fc158473d6d86ec22692874bd5ddccf07474eab5c6bb41f226c522e945da5244" dependencies = [ "ahash", "async-broadcast", @@ -1216,7 +1256,7 @@ dependencies = [ "backon", "educe", "futures", - "hashbrown", + "hashbrown 0.16.1", "hostname", "json-patch", "k8s-openapi", @@ -1237,17 +1277,23 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + [[package]] name = "libc" -version = "0.2.180" +version = "0.2.182" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" +checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" [[package]] name = "linux-raw-sys" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "litemap" @@ -1287,9 +1333,9 @@ checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" [[package]] name = "memchr" -version = "2.7.6" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "mime" @@ -1334,9 +1380,9 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "openssl-probe" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f50d9b3dabb09ecd771ad0aa242ca6894994c130308ca3d7684634df8037391" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" [[package]] name = "ordered-float" @@ -1394,9 +1440,9 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pest" -version = "2.8.5" +version = "2.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9eb05c21a464ea704b53158d358a31e6425db2f63a1a7312268b05fe2b75f7" +checksum = "e0848c601009d37dfa3430c4666e147e49cdcf1b92ecd3e63657d8a5f19da662" dependencies = [ "memchr", "ucd-trie", @@ -1404,9 +1450,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.8.5" +version = "2.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f9dbced329c441fa79d80472764b1a2c7e57123553b8519b36663a2fb234ed" +checksum = "11f486f1ea21e6c10ed15d5a7c77165d0ee443402f0780849d1768e7d9d6fe77" dependencies = [ "pest", "pest_generator", @@ -1414,9 +1460,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.8.5" +version = "2.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bb96d5051a78f44f43c8f712d8e810adb0ebf923fc9ed2655a7f66f63ba8ee5" +checksum = "8040c4647b13b210a963c1ed407c1ff4fdfa01c31d6d2a098218702e6664f94f" dependencies = [ "pest", "pest_meta", @@ -1427,9 +1473,9 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.8.5" +version = "2.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "602113b5b5e8621770cfd490cfd90b9f84ab29bd2b0e49ad83eb6d186cef2365" +checksum = "89815c69d36021a140146f26659a81d6c2afa33d216d736dd4be5381a7362220" dependencies = [ "pest", "sha2", @@ -1469,15 +1515,15 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "portable-atomic" -version = "1.13.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f89776e4d69bb58bc6993e99ffa1d11f228b839984854c7daeb5d37f87cbe950" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" [[package]] name = "portable-atomic-util" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +checksum = "7a9db96d7fa8782dd8c15ce32ffe8680bbd1e978a43bf51a34d39483540495f5" dependencies = [ "portable-atomic", ] @@ -1491,11 +1537,21 @@ dependencies = [ "zerovec", ] +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "proc-macro2" -version = "1.0.103" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] @@ -1537,9 +1593,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.42" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" dependencies = [ "proc-macro2", ] @@ -1581,9 +1637,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.12.2" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" dependencies = [ "aho-corasick", "memchr", @@ -1593,9 +1649,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", @@ -1604,9 +1660,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" +checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" [[package]] name = "reqwest" @@ -1671,9 +1727,9 @@ dependencies = [ [[package]] name = "rustix" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ "bitflags", "errno", @@ -1764,9 +1820,9 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" [[package]] name = "same-file" @@ -1788,9 +1844,9 @@ dependencies = [ [[package]] name = "schemars" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54e910108742c57a770f492731f99be216a52fadd361b06c8fb59d74ccc267d2" +checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" dependencies = [ "dyn-clone", "ref-cast", @@ -1801,9 +1857,9 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4908ad288c5035a8eb12cfdf0d49270def0a268ee162b75eeee0f85d155a7c45" +checksum = "7d115b50f4aaeea07e79c1912f645c7513d81715d0420f8bc77a18c6260b307f" dependencies = [ "proc-macro2", "quote", @@ -1828,9 +1884,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "3.5.1" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ "bitflags", "core-foundation", @@ -1841,9 +1897,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.15.0" +version = "2.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" dependencies = [ "core-foundation-sys", "libc", @@ -1993,9 +2049,9 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "smallvec" @@ -2005,9 +2061,9 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "socket2" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" +checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" dependencies = [ "libc", "windows-sys 0.60.2", @@ -2033,9 +2089,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.111" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", @@ -2069,7 +2125,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0136791f7c95b1f6dd99f9cc786b91bb81c3800b639b3478e561ddb7be95e5f1" dependencies = [ "fastrand", - "getrandom 0.3.4", + "getrandom 0.4.1", "once_cell", "rustix", "windows-sys 0.61.2", @@ -2363,9 +2419,15 @@ checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" [[package]] name = "unicode-ident" -version = "1.0.22" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-xid" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "unsafe-libyaml" @@ -2443,11 +2505,20 @@ dependencies = [ "wit-bindgen", ] +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen", +] + [[package]] name = "wasm-bindgen" -version = "0.2.108" +version = "0.2.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" +checksum = "ec1adf1535672f5b7824f817792b1afd731d7e843d2d04ec8f27e8cb51edd8ac" dependencies = [ "cfg-if", "once_cell", @@ -2458,9 +2529,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.58" +version = "0.4.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f" +checksum = "fe88540d1c934c4ec8e6db0afa536876c5441289d7f9f9123d4f065ac1250a6b" dependencies = [ "cfg-if", "futures-util", @@ -2472,9 +2543,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.108" +version = "0.2.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" +checksum = "19e638317c08b21663aed4d2b9a2091450548954695ff4efa75bff5fa546b3b1" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2482,9 +2553,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.108" +version = "0.2.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" +checksum = "2c64760850114d03d5f65457e96fc988f11f01d38fbaa51b254e4ab5809102af" dependencies = [ "bumpalo", "proc-macro2", @@ -2495,18 +2566,52 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.108" +version = "0.2.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" +checksum = "60eecd4fe26177cfa3339eb00b4a36445889ba3ad37080c2429879718e20ca41" dependencies = [ "unicode-ident", ] +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + [[package]] name = "web-sys" -version = "0.3.85" +version = "0.3.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598" +checksum = "9d6bb20ed2d9572df8584f6dc81d68a41a625cadc6f15999d649a70ce7e3597a" dependencies = [ "js-sys", "wasm-bindgen", @@ -2825,6 +2930,88 @@ name = "wit-bindgen" version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] [[package]] name = "writeable" @@ -2832,6 +3019,10 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" +[[package]] +name = "xtask" +version = "0.1.0" + [[package]] name = "yoke" version = "0.8.1" @@ -2857,18 +3048,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.31" +version = "0.8.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" +checksum = "db6d35d663eadb6c932438e763b262fe1a70987f9ae936e60158176d710cae4a" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.31" +version = "0.8.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" +checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517" dependencies = [ "proc-macro2", "quote", @@ -2937,6 +3128,6 @@ dependencies = [ [[package]] name = "zmij" -version = "1.0.16" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfcd145825aace48cff44a8844de64bf75feec3080e0aa5cdbde72961ae51a65" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/Cargo.toml b/Cargo.toml index 41b512f..05785df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,4 +41,7 @@ serde_json = "1.0.149" tower = "0.5.3" bytes = "1.11.0" tower-test = "0.4.0" -jiff = "0.2.20" \ No newline at end of file +jiff = "0.2.20" + +[workspace] +members = ["xtask"] diff --git a/README.md b/README.md index 7e8e0c0..539f3b5 100644 --- a/README.md +++ b/README.md @@ -35,8 +35,6 @@ nix build ### Integration tests -#### Run an e2e test locally - **Note to Mac users**: The e2e runs on Mac, but the Dockerimage is built by Docker instead of Nix, due to compatibility issues. Linux users can opt in to use Docker build with `USE_DOCKER_BUILD=true`, but that is mainly for troubleshooting since it's generally slower than Nix. @@ -61,8 +59,6 @@ IMAGE=quay.io/upgrades/k8s-cloud-tagger-dev:sha-6f4cbfe nix run .#kind-test `KEEP_CLUSTER=true` prints a message saying how to use `kubectl` in case you want to inspect the cluster. Otherwise, the cluster is deleted after the test. -## Helm - To get the raw Kubernetes manifests: ```bash @@ -70,7 +66,9 @@ nix develop helm template k8s-cloud-tagger helm/k8s-cloud-tagger/ --set serviceMonitor.enabled=true ``` -### To deploy to a GKE cluster +## Deploy + +### GKE Create a low cost, minimal cluster for development: @@ -98,43 +96,10 @@ gcloud container clusters get-credentials cluster-1 \ --project "${GCP_PROJECT}" ``` -Build and push an image from your branch with the [push-dev-image](https://github.com/upgrades-dev/k8s-cloud-tagger/actions/workflows/push-dev-image.yml) GHA job. - -Install Helm chart: - -```bash -helm install k8s-cloud-tagger helm/k8s-cloud-tagger \ - --set deployment.env.RUST_LOG="debug" \ - --set cloudProvider=gcp \ - --set image.repository=quay.io/upgrades/k8s-cloud-tagger-dev \ - --set image.tag="sha-$(git rev-parse --short HEAD)" -``` - -Where the value for `image.tag` matches the tag of the image pushed to [Quay](https://quay.io/repository/upgrades/k8s-cloud-tagger-dev?tab=tags). - -#### Useful commands for GKE - -Scale down the cluster to zero (stop paying for compute): - -```bash -gcloud container clusters resize cluster-1 \ - --node-pool default-pool \ - --num-nodes 0 \ - --zone "${GCP_ZONE}" \ - --project "${GCP_PROJECT}" -``` - -Scale up again: - -```bash -gcloud container clusters resize cluster-1 \ - --node-pool default-pool \ - --num-nodes 1 \ - --zone "${GCP_ZONE}" \ - --project "${GCP_PROJECT}" -``` +#### IAM setup Grant IAM permissions to the controller's service account: + ```bash # Set your project ID once export PROJECT_ID="" @@ -162,9 +127,25 @@ kubectl annotate serviceaccount k8s-cloud-tagger \ --overwrite ``` -## Google Artifact Registry +#### Helm install -If you use an autopilot cluster or just have private nodes, then it's easiest to use pkg.dev. +You can let GitHub CI build and push your branch's image and push it to Quay, and have the cluster pull the image from there. If you use an autopilot cluster or just have private nodes, then it's easiest to use Google Artifact Registry. + +##### Push image to Quay + +Build and push an image from your branch with the [push-dev-image](https://github.com/upgrades-dev/k8s-cloud-tagger/actions/workflows/push-dev-image.yml) GHA job. + +```bash +helm install k8s-cloud-tagger helm/k8s-cloud-tagger \ + --set deployment.env.RUST_LOG="debug" \ + --set cloudProvider=gcp \ + --set image.repository=quay.io/upgrades/k8s-cloud-tagger-dev \ + --set image.tag="sha-$(git rev-parse --short HEAD)" +``` + +Where the value for `image.tag` matches the tag of the image pushed to [Quay](https://quay.io/repository/upgrades/k8s-cloud-tagger-dev?tab=tags). + +##### Push image to Google Artifact Registry ```bash nix develop @@ -176,13 +157,33 @@ docker tag quay.io/upgrades/k8s-cloud-tagger-dev:dev \ helm upgrade k8s-cloud-tagger helm/k8s-cloud-tagger -n k8s-cloud-tagger \ --set deployment.env.RUST_LOG="debug" --set cloudProvider=gcp \ - --set image.repository="${REGION}-docker.pkg.dev/${PROJECT_ID}/k8s-cloud-tagger/controller" + --set image.repository="${REGION}-docker.pkg.dev/${PROJECT_ID}/k8s-cloud-tagger/controller" \ --set image.tag="YOUR-FEATURE" ``` -# GCP +#### Useful commands + +Scale down the cluster to zero (stop paying for compute): + +```bash +gcloud container clusters resize cluster-1 \ + --node-pool default-pool \ + --num-nodes 0 \ + --zone "${GCP_ZONE}" \ + --project "${GCP_PROJECT}" +``` + +Scale up again: + +```bash +gcloud container clusters resize cluster-1 \ + --node-pool default-pool \ + --num-nodes 1 \ + --zone "${GCP_ZONE}" \ + --project "${GCP_PROJECT}" +``` -## GCP label sanitisation +#### Label sanitisation Kubernetes label keys and values can contain characters that are not valid in GCP labels. GCP labels only allow lowercase letters, digits, hyphens, and underscores (`[a-z0-9_-]`), @@ -200,3 +201,11 @@ For more detail on GCP label requirements, see the [Google Cloud labeling best p | `env: production` | `env: production` | | `upgrades.dev/managed-by: k8s-cloud-tagger` | `upgrades-dev-managed-by: k8s-cloud-tagger` | | `Team: Platform` | `team: platform` | + +## Release + +1. Check out a new branch +1. Update [CHANGELOG.md](./CHANGELOG.md) following the [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) format and [SemVer](https://semver.org/) conventions. +1. Run `cargo xtask release `. This updates and commits the relevant files to git. +1. Raise PR, merge PR +1. When the PR is merged, a job will run that adds a git tag, and builds and pushes the release Docker image diff --git a/flake.nix b/flake.nix index 2618b4d..724de06 100644 --- a/flake.nix +++ b/flake.nix @@ -194,6 +194,16 @@ User = "65532:65532"; }; }; + + image = pkgs.dockerTools.buildImage { + name = "quay.io/upgrades/k8s-cloud-tagger"; + tag = "latest"; + fromImage = chainguardStatic; + config = { + Entrypoint = [ "${binaryMusl}/bin/k8s-cloud-tagger" ]; + User = "65532:65532"; + }; + }; }; # ====================================================================== diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml new file mode 100644 index 0000000..37518d7 --- /dev/null +++ b/xtask/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "xtask" +version = "0.1.0" +edition = "2024" diff --git a/xtask/src/main.rs b/xtask/src/main.rs new file mode 100644 index 0000000..9f40d98 --- /dev/null +++ b/xtask/src/main.rs @@ -0,0 +1,209 @@ +use std::process; + +const CARGO_FILE_PATH: &str = "Cargo.toml"; +const CARGO_LOCK_FILE_PATH: &str = "Cargo.lock"; +const HELM_CHART_FILE_PATH: &str = "helm/k8s-cloud-tagger/Chart.yaml"; +const CHANGELOG_FILE_PATH: &str = "CHANGELOG.md"; + +fn main() { + let task = std::env::args().nth(1); + match task.as_deref() { + Some("check") => check_versions(), + Some("release") => { + let version = std::env::args().nth(2).unwrap_or_else(|| { + eprintln!("Usage: cargo xtask release "); + process::exit(1); + }); + release(&version); + } + _ => { + eprintln!("Usage: cargo xtask "); + process::exit(1); + } + } +} + +fn read_file(path: &str) -> String { + std::fs::read_to_string(path).unwrap_or_else(|e| { + eprintln!("Failed to read {path}: {e}"); + process::exit(1); + }) +} + +fn extract_version<'a>(content: &'a str, prefix: &str) -> Option<&'a str> { + content.lines().find_map(|line| { + let line = line.trim(); + let rest = line.strip_prefix(prefix)?; + Some(rest.trim().trim_matches('"')) + }) +} + +fn check_versions() { + let cargo = read_file(CARGO_FILE_PATH); + let chart = read_file(HELM_CHART_FILE_PATH); + + let cargo_version = extract_version(&cargo, "version = ").unwrap_or_else(|| { + eprintln!("No version found in {CARGO_FILE_PATH}"); + process::exit(1) + }); + let chart_version = extract_version(&chart, "version:").unwrap_or_else(|| { + eprintln!("No version found in {HELM_CHART_FILE_PATH}"); + process::exit(1) + }); + let app_version = extract_version(&chart, "appVersion:").unwrap_or_else(|| { + eprintln!("No appVersion found in {HELM_CHART_FILE_PATH}"); + process::exit(1) + }); + + let ok = cargo_version == chart_version && cargo_version == app_version; + + println!("Cargo.toml: {cargo_version}"); + println!("Chart version: {chart_version}"); + println!("Chart appVersion: {app_version}"); + + if ok { + println!("OK: all versions match"); + } else { + eprintln!("ERROR: versions do not match"); + process::exit(1); + } +} + +fn release(version: &str) { + // Parse the version strings to ints + let new = parse_semver(version).unwrap_or_else(|| { + eprintln!("Invalid version: {version}. Expected format: x.y.z"); + process::exit(1); + }); + let current_content = read_file(CARGO_FILE_PATH); + let current_str = extract_version(¤t_content, "version = ").unwrap_or_else(|| { + eprintln!("Could not read current version from {CARGO_FILE_PATH}"); + process::exit(1); + }); + let current = parse_semver(current_str).unwrap_or_else(|| { + eprintln!("Could not parse current version: {current_str}"); + process::exit(1); + }); + + // Check that the new version is an upgrade (downgrade not allowed) + if current >= new { + // Equality is lexicographical and works as long as we stick to x.y.z + // If we start doing 1.0.0-alpha releases we should us a parsing lib + eprintln!("New version {version} must be greater than current {current_str}"); + process::exit(1); + } + + let bump_type = if new.0 > current.0 { + "major" + } else if new.1 > current.1 { + "minor" + } else { + "patch" + }; + println!("Bumping {current_str} -> {version} ({bump_type})"); + + // Check that the changelog file is updated with the new version + let changelog = read_file(CHANGELOG_FILE_PATH); + let expected_heading = format!("## [{version}] - "); + if !changelog + .lines() + .any(|line| line.starts_with(&expected_heading)) + { + eprintln!("{CHANGELOG_FILE_PATH} does not contain a section for [{version}]"); + eprintln!("Add '## [{version}] - YYYY-MM-DD' before releasing."); + process::exit(1); + } + bump_file(CARGO_FILE_PATH, "version", version); + bump_file(HELM_CHART_FILE_PATH, "version", version); + bump_file(HELM_CHART_FILE_PATH, "appVersion", version); + + run("cargo", &["generate-lockfile"]); + run( + "git", + &[ + "add", + CARGO_FILE_PATH, + CARGO_LOCK_FILE_PATH, + HELM_CHART_FILE_PATH, + CHANGELOG_FILE_PATH, + ], + ); + run( + "git", + &["commit", "--message", &format!("release v{version}")], + ); + + println!("Done. Open a PR with this commit. CI will tag and release on merge."); +} + +fn parse_semver(v: &str) -> Option<(u32, u32, u32)> { + let parts: Vec<&str> = v.split('.').collect(); + if parts.len() != 3 { + return None; + } + Some(( + parts[0].parse().ok()?, + parts[1].parse().ok()?, + parts[2].parse().ok()?, + )) +} + +fn bump_file(path: &str, key: &str, version: &str) { + let content = read_file(path); + let prefix = if path.ends_with(".toml") { + format!("{key} = ") + } else { + format!("{key}: ") + }; + let new_content = content + .lines() + .map(|line| { + if line.trim().starts_with(&prefix) { + let indent = &line[..line.len() - line.trim_start().len()]; + if path.ends_with(".toml") { + format!("{indent}{key} = \"{version}\"") + } else { + format!("{indent}{key}: {version}") + } + } else { + line.to_string() + } + }) + .collect::>() + .join("\n") + + "\n"; + + std::fs::write(path, new_content).unwrap_or_else(|e| { + eprintln!("Failed to write {path}: {e}"); + process::exit(1); + }); + println!("Updated {path}"); +} + +fn run(cmd: &str, args: &[&str]) { + let status = process::Command::new(cmd) + .args(args) + .status() + .unwrap_or_else(|e| { + eprintln!("Failed to run {cmd}: {e}"); + process::exit(1); + }); + if !status.success() { + eprintln!("Command failed: {cmd} {}", args.join(" ")); + process::exit(1); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_parse_semver() { + assert_eq!(parse_semver("1.2.3"), Some((1, 2, 3))); + assert_eq!(parse_semver("0.1.0"), Some((0, 1, 0))); + assert_eq!(parse_semver("1.2"), None); + assert_eq!(parse_semver("1.2.x"), None); + assert_eq!(parse_semver(""), None); + } +}