├── .github ├── FUNDING.yml ├── dependabot.yml └── workflows │ ├── ci.yaml │ └── release.yaml ├── .gitignore ├── .krew.yaml ├── .mise.toml ├── .vscode └── settings.json ├── Cargo.lock ├── Cargo.toml ├── LICENSE.txt ├── Makefile.toml ├── README.md ├── examples └── pod_api.rs ├── justfile ├── release.toml ├── scripts └── getLatest.sh ├── src ├── lib.rs ├── main.rs ├── metrics.rs ├── qty.rs └── tree.rs └── tests ├── metrics-server-components.yaml ├── run_ubuntu_16.sh ├── test_first_run.sh └── ubuntu_16.dockerfile /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [davidB] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "cargo" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | allow: 8 | - dependency-type: "direct" 9 | - package-ecosystem: "github-actions" 10 | # Workflow files stored in the 11 | # default location of `.github/workflows` 12 | directory: "/" 13 | schedule: 14 | interval: "weekly" 15 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: ci-flow 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - master 8 | - "releases/*" 9 | # tags-ignore: 10 | # - "[0-9]+.[0-9]+.[0-9]+*" 11 | 12 | permissions: 13 | contents: read 14 | 15 | env: 16 | SCCACHE_GHA_ENABLED: "true" 17 | RUSTC_WRAPPER: "sccache" 18 | 19 | jobs: 20 | build: 21 | runs-on: ${{ matrix.os.imageName }} 22 | strategy: 23 | matrix: 24 | rust_toolchain: ["stable"] 25 | os: 26 | - imageName: ubuntu-latest 27 | profile_rustup: default 28 | profile_ci_flow: ci-static-code-analysis-tasks 29 | target_platform: x86_64-unknown-linux-gnu 30 | - target_platform: x86_64-apple-darwin 31 | imageName: "macOS-latest" 32 | profile_rustup: minimal 33 | profile_ci_flow: none 34 | # - imageName: "macOS-latest" 35 | # profile_rustup: minimal 36 | # profile_ci_flow: none 37 | # target_platform: aarch64-apple-darwin 38 | # - imageName: windows-latest 39 | # profile_rustup: minimal 40 | # profile_ci_flow: none 41 | # target_platform: 42 | 43 | steps: 44 | - uses: actions/checkout@v4 45 | - uses: actions/cache@v4 46 | with: 47 | path: | 48 | ~/.cargo/bin/ 49 | ~/.cargo/registry/index/ 50 | ~/.cargo/registry/cache/ 51 | ~/.cargo/git/db/ 52 | target/ 53 | key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} 54 | - uses: dtolnay/rust-toolchain@stable 55 | with: 56 | toolchain: ${{ matrix.rust_toolchain }} 57 | targets: ${{ matrix.os.target_platform }} 58 | - uses: mozilla-actions/sccache-action@v0.0.9 59 | - uses: davidB/rust-cargo-make@v1 60 | - name: Run tests 61 | run: cargo make --disable-check-for-updates --profile "${{ matrix.os.profile_ci_flow }}" ci-flow 62 | env: 63 | TARGET: ${{ matrix.os.target_paddlatform }} 64 | # for list of xcode sdk see https://help.github.com/en/actions/automating-your-workflow-with-github-actions/software-installed-on-github-hosted-runners#xcode 65 | # DEVELOPER_DIR: "/Applications/Xcode_11.app/Contents/Developer" 66 | CARGO_MAKE_RUN_CODECOV: "false" 67 | # to have CODECOV_TOKEN go to https://codecov.io/gh/${GITHUB_USER}/${GITHUB_REPO} 68 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 69 | - run: ${SCCACHE_PATH} --show-stats 70 | shell: bash 71 | 72 | integration: 73 | # Integration tests are linux only 74 | runs-on: ubuntu-latest 75 | steps: 76 | - uses: nolar/setup-k3d-k3s@v1 77 | with: 78 | # version: v1.20 79 | # k3d-kube 80 | k3d-name: kube 81 | # Used to avoid rate limits when fetching the releases from k3s repo. 82 | # Anonymous access is limited to 60 requests / hour / worker 83 | # github-token: ${{ secrets.GITHUB_TOKEN }} 84 | - uses: actions/checkout@v4 85 | - uses: dtolnay/rust-toolchain@stable 86 | - uses: mozilla-actions/sccache-action@v0.0.9 87 | - name: Run on k3d 88 | run: cargo run 89 | - run: ${SCCACHE_PATH} --show-stats 90 | shell: bash 91 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: release-flow 2 | 3 | # https://help.github.com/en/articles/events-that-trigger-workflows#release-event-release 4 | # on: release 5 | 6 | on: 7 | workflow_dispatch: 8 | push: 9 | tags: 10 | - "[0-9]+.[0-9]+.[0-9]+*" 11 | 12 | permissions: 13 | contents: write 14 | 15 | env: 16 | SCCACHE_GHA_ENABLED: "true" 17 | RUSTC_WRAPPER: "sccache" 18 | 19 | jobs: 20 | build: 21 | runs-on: ${{ matrix.os.imageName }} 22 | strategy: 23 | matrix: 24 | rust_toolchain: ["stable"] 25 | os: 26 | - target_platform: x86_64-unknown-linux-gnu 27 | imageName: ubuntu-latest 28 | cross: "true" 29 | - target_platform: x86_64-unknown-linux-musl 30 | imageName: ubuntu-latest 31 | cross: "true" 32 | - target_platform: aarch64-unknown-linux-musl 33 | imageName: ubuntu-latest 34 | cross: "true" 35 | - target_platform: x86_64-apple-darwin 36 | imageName: "macOS-latest" 37 | - target_platform: aarch64-apple-darwin 38 | imageName: "macOS-latest" 39 | - target_platform: x86_64-pc-windows-msvc 40 | imageName: windows-latest 41 | 42 | steps: 43 | - uses: actions/checkout@v4 44 | - uses: dtolnay/rust-toolchain@stable 45 | with: 46 | toolchain: ${{ matrix.rust_toolchain }} 47 | targets: ${{ matrix.os.target_platform }} 48 | - uses: mozilla-actions/sccache-action@v0.0.9 49 | - uses: davidB/rust-cargo-make@v1 50 | - name: Make zip-release-ci-flow 51 | id: zip-release-ci-flow 52 | run: cargo make --disable-check-for-updates zip-release-ci-flow 53 | env: 54 | TARGET: ${{ matrix.os.target_platform }} 55 | CROSS: ${{ matrix.os.cross }} 56 | # DEVELOPER_DIR: "/Applications/Xcode_11.app/Contents/Developer" 57 | - name: Upload binaries to release 58 | # if: startsWith(github.ref, 'refs/tags/') 59 | uses: svenstaro/upload-release-action@v2 60 | with: 61 | repo_token: ${{ secrets.GITHUB_TOKEN }} 62 | file: ${{ steps.zip-release-ci-flow.outputs.dist_file_path }} 63 | # asset_name: ${{ steps.zip-release-ci-flow.outputs.dist_file_name }} 64 | # tag: ${{ github.ref }} 65 | tag: ${{ steps.zip-release-ci-flow.outputs.dist_version }} 66 | prerelease: false # ${{ github.ref == format('refs/tags/{0}', steps.zip-release-ci-flow.outputs.dist_version) }} 67 | overwrite: true 68 | - run: ${SCCACHE_PATH} --show-stats 69 | shell: bash 70 | 71 | krew-update: 72 | needs: [build] 73 | runs-on: ubuntu-latest 74 | steps: 75 | - uses: actions/checkout@v4 76 | - name: Update new version in krew-index 77 | uses: rajatjindal/krew-release-bot@v0.0.47 78 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/git,rust,macos,visualstudiocode 2 | # Edit at https://www.gitignore.io/?templates=git,rust,macos,visualstudiocode 3 | 4 | ### Git ### 5 | # Created by git for backups. To disable backups in Git: 6 | # $ git config --global mergetool.keepBackup false 7 | *.orig 8 | 9 | # Created by git when using merge tools for conflicts 10 | *.BACKUP.* 11 | *.BASE.* 12 | *.LOCAL.* 13 | *.REMOTE.* 14 | *_BACKUP_*.txt 15 | *_BASE_*.txt 16 | *_LOCAL_*.txt 17 | *_REMOTE_*.txt 18 | 19 | ### macOS ### 20 | # General 21 | .DS_Store 22 | .AppleDouble 23 | .LSOverride 24 | 25 | # Icon must end with two \r 26 | Icon 27 | 28 | # Thumbnails 29 | ._* 30 | 31 | # Files that might appear in the root of a volume 32 | .DocumentRevisions-V100 33 | .fseventsd 34 | .Spotlight-V100 35 | .TemporaryItems 36 | .Trashes 37 | .VolumeIcon.icns 38 | .com.apple.timemachine.donotpresent 39 | 40 | # Directories potentially created on remote AFP share 41 | .AppleDB 42 | .AppleDesktop 43 | Network Trash Folder 44 | Temporary Items 45 | .apdisk 46 | 47 | ### Rust ### 48 | # Generated by Cargo 49 | # will have compiled files and executables 50 | /target/ 51 | 52 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 53 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 54 | Cargo.lock 55 | 56 | # These are backup files generated by rustfmt 57 | **/*.rs.bk 58 | 59 | ### VisualStudioCode ### 60 | .vscode/* 61 | !.vscode/settings.json 62 | !.vscode/tasks.json 63 | !.vscode/launch.json 64 | !.vscode/extensions.json 65 | 66 | ### VisualStudioCode Patch ### 67 | # Ignore all local history of files 68 | .history 69 | 70 | # End of https://www.gitignore.io/api/git,rust,macos,visualstudiocode 71 | 72 | !Cargo.lock 73 | -------------------------------------------------------------------------------- /.krew.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: krew.googlecontainertools.github.com/v1alpha2 2 | kind: Plugin 3 | metadata: 4 | name: view-allocations 5 | spec: 6 | version: "v{{ .TagName }}" 7 | platforms: 8 | - selector: 9 | matchLabels: 10 | os: darwin 11 | arch: amd64 12 | {{addURIAndSha "https://github.com/davidB/kubectl-view-allocations/releases/download/{{ .TagName }}/kubectl-view-allocations_{{ .TagName }}-x86_64-apple-darwin.tar.gz" .TagName | indent 6 }} 13 | bin: "./kubectl-view-allocations" 14 | - selector: 15 | matchLabels: 16 | os: darwin 17 | arch: arm64 18 | {{addURIAndSha "https://github.com/davidB/kubectl-view-allocations/releases/download/{{ .TagName }}/kubectl-view-allocations_{{ .TagName }}-aarch64-apple-darwin.tar.gz" .TagName | indent 6}} 19 | bin: "./kubectl-view-allocations" 20 | - selector: 21 | matchLabels: 22 | os: linux 23 | arch: amd64 24 | {{addURIAndSha "https://github.com/davidB/kubectl-view-allocations/releases/download/{{ .TagName }}/kubectl-view-allocations_{{ .TagName }}-x86_64-unknown-linux-musl.tar.gz" .TagName | indent 6}} 25 | bin: "./kubectl-view-allocations" 26 | - selector: 27 | matchLabels: 28 | os: linux 29 | arch: arm64 30 | {{addURIAndSha "https://github.com/davidB/kubectl-view-allocations/releases/download/{{ .TagName }}/kubectl-view-allocations_{{ .TagName }}-aarch64-unknown-linux-musl.tar.gz" .TagName | indent 6}} 31 | bin: "./kubectl-view-allocations" 32 | - selector: 33 | matchLabels: 34 | os: windows 35 | arch: amd64 36 | {{addURIAndSha "https://github.com/davidB/kubectl-view-allocations/releases/download/{{ .TagName }}/kubectl-view-allocations_{{ .TagName }}-x86_64-pc-windows-msvc.zip" .TagName | indent 6}} 37 | bin: "./kubectl-view-allocations.exe" 38 | shortDescription: List allocations per resources, nodes, pods. 39 | homepage: https://github.com/davidB/kubectl-view-allocations 40 | description: | 41 | This plugin lists resources (cpu, memory, gpu,...) allocations (requested, 42 | limit, allocatable) as defined in the manifest of nodes and running pods, 43 | and utilization from metrics-server. 44 | try `kubectl view-allocations -h`, `kubectl view-allocations` 45 | -------------------------------------------------------------------------------- /.mise.toml: -------------------------------------------------------------------------------- 1 | [env] 2 | CLUSTER_NAME = "demo-kube" 3 | DOCKER_BUILDKIT = "1" 4 | # BP_RUST_TOOLCHAIN = "1.86" 5 | # KIND_EXPERIMENTAL_PROVIDER = "podman" # to use podman and podman-docker 6 | 7 | [tools] 8 | # docker (or podman + some config) should be available for some of thoses tools 9 | # helm = '3.12' 10 | kubectl = '1.24' 11 | # helmfile = '0.155' 12 | kind = '0.20' 13 | just = '1.14' 14 | rust = '1.86' 15 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "spellright.language": [], 3 | "spellright.documentTypes": [ 4 | "latex", 5 | "plaintext" 6 | ] 7 | } -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.24.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler2" 16 | version = "2.0.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" 19 | 20 | [[package]] 21 | name = "ahash" 22 | version = "0.8.12" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" 25 | dependencies = [ 26 | "cfg-if", 27 | "getrandom 0.3.3", 28 | "once_cell", 29 | "version_check", 30 | "zerocopy", 31 | ] 32 | 33 | [[package]] 34 | name = "aho-corasick" 35 | version = "1.1.3" 36 | source = "registry+https://github.com/rust-lang/crates.io-index" 37 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 38 | dependencies = [ 39 | "memchr", 40 | ] 41 | 42 | [[package]] 43 | name = "android-tzdata" 44 | version = "0.1.1" 45 | source = "registry+https://github.com/rust-lang/crates.io-index" 46 | checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" 47 | 48 | [[package]] 49 | name = "android_system_properties" 50 | version = "0.1.5" 51 | source = "registry+https://github.com/rust-lang/crates.io-index" 52 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 53 | dependencies = [ 54 | "libc", 55 | ] 56 | 57 | [[package]] 58 | name = "anstream" 59 | version = "0.6.18" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" 62 | dependencies = [ 63 | "anstyle", 64 | "anstyle-parse", 65 | "anstyle-query", 66 | "anstyle-wincon", 67 | "colorchoice", 68 | "is_terminal_polyfill", 69 | "utf8parse", 70 | ] 71 | 72 | [[package]] 73 | name = "anstyle" 74 | version = "1.0.10" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" 77 | 78 | [[package]] 79 | name = "anstyle-parse" 80 | version = "0.2.6" 81 | source = "registry+https://github.com/rust-lang/crates.io-index" 82 | checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" 83 | dependencies = [ 84 | "utf8parse", 85 | ] 86 | 87 | [[package]] 88 | name = "anstyle-query" 89 | version = "1.1.2" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" 92 | dependencies = [ 93 | "windows-sys 0.59.0", 94 | ] 95 | 96 | [[package]] 97 | name = "anstyle-wincon" 98 | version = "3.0.7" 99 | source = "registry+https://github.com/rust-lang/crates.io-index" 100 | checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" 101 | dependencies = [ 102 | "anstyle", 103 | "once_cell", 104 | "windows-sys 0.59.0", 105 | ] 106 | 107 | [[package]] 108 | name = "anyhow" 109 | version = "1.0.98" 110 | source = "registry+https://github.com/rust-lang/crates.io-index" 111 | checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" 112 | 113 | [[package]] 114 | name = "async-compression" 115 | version = "0.4.23" 116 | source = "registry+https://github.com/rust-lang/crates.io-index" 117 | checksum = "b37fc50485c4f3f736a4fb14199f6d5f5ba008d7f28fe710306c92780f004c07" 118 | dependencies = [ 119 | "flate2", 120 | "futures-core", 121 | "memchr", 122 | "pin-project-lite", 123 | "tokio", 124 | ] 125 | 126 | [[package]] 127 | name = "async-socks5" 128 | version = "0.6.0" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | checksum = "8da2537846e16b96d2972ee52a3b355663872a1a687ce6d57a3b6f6b6a181c89" 131 | dependencies = [ 132 | "thiserror 1.0.69", 133 | "tokio", 134 | ] 135 | 136 | [[package]] 137 | name = "autocfg" 138 | version = "1.4.0" 139 | source = "registry+https://github.com/rust-lang/crates.io-index" 140 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 141 | 142 | [[package]] 143 | name = "backtrace" 144 | version = "0.3.75" 145 | source = "registry+https://github.com/rust-lang/crates.io-index" 146 | checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" 147 | dependencies = [ 148 | "addr2line", 149 | "cfg-if", 150 | "libc", 151 | "miniz_oxide", 152 | "object", 153 | "rustc-demangle", 154 | "windows-targets", 155 | ] 156 | 157 | [[package]] 158 | name = "base64" 159 | version = "0.21.7" 160 | source = "registry+https://github.com/rust-lang/crates.io-index" 161 | checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" 162 | 163 | [[package]] 164 | name = "base64" 165 | version = "0.22.1" 166 | source = "registry+https://github.com/rust-lang/crates.io-index" 167 | checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" 168 | 169 | [[package]] 170 | name = "bitflags" 171 | version = "2.9.0" 172 | source = "registry+https://github.com/rust-lang/crates.io-index" 173 | checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" 174 | 175 | [[package]] 176 | name = "block-buffer" 177 | version = "0.10.4" 178 | source = "registry+https://github.com/rust-lang/crates.io-index" 179 | checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" 180 | dependencies = [ 181 | "generic-array", 182 | ] 183 | 184 | [[package]] 185 | name = "bumpalo" 186 | version = "3.17.0" 187 | source = "registry+https://github.com/rust-lang/crates.io-index" 188 | checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" 189 | 190 | [[package]] 191 | name = "bytes" 192 | version = "1.10.1" 193 | source = "registry+https://github.com/rust-lang/crates.io-index" 194 | checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" 195 | 196 | [[package]] 197 | name = "cc" 198 | version = "1.2.22" 199 | source = "registry+https://github.com/rust-lang/crates.io-index" 200 | checksum = "32db95edf998450acc7881c932f94cd9b05c87b4b2599e8bab064753da4acfd1" 201 | dependencies = [ 202 | "shlex", 203 | ] 204 | 205 | [[package]] 206 | name = "cfg-if" 207 | version = "1.0.0" 208 | source = "registry+https://github.com/rust-lang/crates.io-index" 209 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 210 | 211 | [[package]] 212 | name = "chrono" 213 | version = "0.4.41" 214 | source = "registry+https://github.com/rust-lang/crates.io-index" 215 | checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" 216 | dependencies = [ 217 | "android-tzdata", 218 | "iana-time-zone", 219 | "js-sys", 220 | "num-traits", 221 | "serde", 222 | "wasm-bindgen", 223 | "windows-link", 224 | ] 225 | 226 | [[package]] 227 | name = "clap" 228 | version = "4.5.39" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | checksum = "fd60e63e9be68e5fb56422e397cf9baddded06dae1d2e523401542383bc72a9f" 231 | dependencies = [ 232 | "clap_builder", 233 | "clap_derive", 234 | ] 235 | 236 | [[package]] 237 | name = "clap_builder" 238 | version = "4.5.39" 239 | source = "registry+https://github.com/rust-lang/crates.io-index" 240 | checksum = "89cc6392a1f72bbeb820d71f32108f61fdaf18bc526e1d23954168a67759ef51" 241 | dependencies = [ 242 | "anstream", 243 | "anstyle", 244 | "clap_lex", 245 | "strsim", 246 | ] 247 | 248 | [[package]] 249 | name = "clap_derive" 250 | version = "4.5.32" 251 | source = "registry+https://github.com/rust-lang/crates.io-index" 252 | checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" 253 | dependencies = [ 254 | "heck", 255 | "proc-macro2", 256 | "quote", 257 | "syn", 258 | ] 259 | 260 | [[package]] 261 | name = "clap_lex" 262 | version = "0.7.4" 263 | source = "registry+https://github.com/rust-lang/crates.io-index" 264 | checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" 265 | 266 | [[package]] 267 | name = "color-eyre" 268 | version = "0.6.5" 269 | source = "registry+https://github.com/rust-lang/crates.io-index" 270 | checksum = "e5920befb47832a6d61ee3a3a846565cfa39b331331e68a3b1d1116630f2f26d" 271 | dependencies = [ 272 | "backtrace", 273 | "color-spantrace", 274 | "eyre", 275 | "indenter", 276 | "once_cell", 277 | "owo-colors", 278 | "tracing-error", 279 | ] 280 | 281 | [[package]] 282 | name = "color-spantrace" 283 | version = "0.3.0" 284 | source = "registry+https://github.com/rust-lang/crates.io-index" 285 | checksum = "b8b88ea9df13354b55bc7234ebcce36e6ef896aca2e42a15de9e10edce01b427" 286 | dependencies = [ 287 | "once_cell", 288 | "owo-colors", 289 | "tracing-core", 290 | "tracing-error", 291 | ] 292 | 293 | [[package]] 294 | name = "colorchoice" 295 | version = "1.0.3" 296 | source = "registry+https://github.com/rust-lang/crates.io-index" 297 | checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" 298 | 299 | [[package]] 300 | name = "core-foundation" 301 | version = "0.9.4" 302 | source = "registry+https://github.com/rust-lang/crates.io-index" 303 | checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" 304 | dependencies = [ 305 | "core-foundation-sys", 306 | "libc", 307 | ] 308 | 309 | [[package]] 310 | name = "core-foundation" 311 | version = "0.10.0" 312 | source = "registry+https://github.com/rust-lang/crates.io-index" 313 | checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" 314 | dependencies = [ 315 | "core-foundation-sys", 316 | "libc", 317 | ] 318 | 319 | [[package]] 320 | name = "core-foundation-sys" 321 | version = "0.8.7" 322 | source = "registry+https://github.com/rust-lang/crates.io-index" 323 | checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 324 | 325 | [[package]] 326 | name = "cpufeatures" 327 | version = "0.2.17" 328 | source = "registry+https://github.com/rust-lang/crates.io-index" 329 | checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" 330 | dependencies = [ 331 | "libc", 332 | ] 333 | 334 | [[package]] 335 | name = "crc32fast" 336 | version = "1.4.2" 337 | source = "registry+https://github.com/rust-lang/crates.io-index" 338 | checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" 339 | dependencies = [ 340 | "cfg-if", 341 | ] 342 | 343 | [[package]] 344 | name = "crypto-common" 345 | version = "0.1.6" 346 | source = "registry+https://github.com/rust-lang/crates.io-index" 347 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" 348 | dependencies = [ 349 | "generic-array", 350 | "typenum", 351 | ] 352 | 353 | [[package]] 354 | name = "data-encoding" 355 | version = "2.9.0" 356 | source = "registry+https://github.com/rust-lang/crates.io-index" 357 | checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" 358 | 359 | [[package]] 360 | name = "deranged" 361 | version = "0.4.0" 362 | source = "registry+https://github.com/rust-lang/crates.io-index" 363 | checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" 364 | dependencies = [ 365 | "powerfmt", 366 | ] 367 | 368 | [[package]] 369 | name = "derive_more" 370 | version = "2.0.1" 371 | source = "registry+https://github.com/rust-lang/crates.io-index" 372 | checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" 373 | dependencies = [ 374 | "derive_more-impl", 375 | ] 376 | 377 | [[package]] 378 | name = "derive_more-impl" 379 | version = "2.0.1" 380 | source = "registry+https://github.com/rust-lang/crates.io-index" 381 | checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" 382 | dependencies = [ 383 | "proc-macro2", 384 | "quote", 385 | "syn", 386 | ] 387 | 388 | [[package]] 389 | name = "diff" 390 | version = "0.1.13" 391 | source = "registry+https://github.com/rust-lang/crates.io-index" 392 | checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" 393 | 394 | [[package]] 395 | name = "digest" 396 | version = "0.10.7" 397 | source = "registry+https://github.com/rust-lang/crates.io-index" 398 | checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" 399 | dependencies = [ 400 | "block-buffer", 401 | "crypto-common", 402 | ] 403 | 404 | [[package]] 405 | name = "dirs-next" 406 | version = "2.0.0" 407 | source = "registry+https://github.com/rust-lang/crates.io-index" 408 | checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" 409 | dependencies = [ 410 | "cfg-if", 411 | "dirs-sys-next", 412 | ] 413 | 414 | [[package]] 415 | name = "dirs-sys-next" 416 | version = "0.1.2" 417 | source = "registry+https://github.com/rust-lang/crates.io-index" 418 | checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" 419 | dependencies = [ 420 | "libc", 421 | "redox_users", 422 | "winapi", 423 | ] 424 | 425 | [[package]] 426 | name = "displaydoc" 427 | version = "0.2.5" 428 | source = "registry+https://github.com/rust-lang/crates.io-index" 429 | checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" 430 | dependencies = [ 431 | "proc-macro2", 432 | "quote", 433 | "syn", 434 | ] 435 | 436 | [[package]] 437 | name = "either" 438 | version = "1.15.0" 439 | source = "registry+https://github.com/rust-lang/crates.io-index" 440 | checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" 441 | 442 | [[package]] 443 | name = "encode_unicode" 444 | version = "1.0.0" 445 | source = "registry+https://github.com/rust-lang/crates.io-index" 446 | checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" 447 | 448 | [[package]] 449 | name = "equivalent" 450 | version = "1.0.2" 451 | source = "registry+https://github.com/rust-lang/crates.io-index" 452 | checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" 453 | 454 | [[package]] 455 | name = "eyre" 456 | version = "0.6.12" 457 | source = "registry+https://github.com/rust-lang/crates.io-index" 458 | checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" 459 | dependencies = [ 460 | "indenter", 461 | "once_cell", 462 | ] 463 | 464 | [[package]] 465 | name = "flate2" 466 | version = "1.1.1" 467 | source = "registry+https://github.com/rust-lang/crates.io-index" 468 | checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" 469 | dependencies = [ 470 | "crc32fast", 471 | "miniz_oxide", 472 | ] 473 | 474 | [[package]] 475 | name = "fnv" 476 | version = "1.0.7" 477 | source = "registry+https://github.com/rust-lang/crates.io-index" 478 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 479 | 480 | [[package]] 481 | name = "form_urlencoded" 482 | version = "1.2.1" 483 | source = "registry+https://github.com/rust-lang/crates.io-index" 484 | checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" 485 | dependencies = [ 486 | "percent-encoding", 487 | ] 488 | 489 | [[package]] 490 | name = "futures" 491 | version = "0.3.31" 492 | source = "registry+https://github.com/rust-lang/crates.io-index" 493 | checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" 494 | dependencies = [ 495 | "futures-channel", 496 | "futures-core", 497 | "futures-io", 498 | "futures-sink", 499 | "futures-task", 500 | "futures-util", 501 | ] 502 | 503 | [[package]] 504 | name = "futures-channel" 505 | version = "0.3.31" 506 | source = "registry+https://github.com/rust-lang/crates.io-index" 507 | checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" 508 | dependencies = [ 509 | "futures-core", 510 | "futures-sink", 511 | ] 512 | 513 | [[package]] 514 | name = "futures-core" 515 | version = "0.3.31" 516 | source = "registry+https://github.com/rust-lang/crates.io-index" 517 | checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" 518 | 519 | [[package]] 520 | name = "futures-io" 521 | version = "0.3.31" 522 | source = "registry+https://github.com/rust-lang/crates.io-index" 523 | checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" 524 | 525 | [[package]] 526 | name = "futures-sink" 527 | version = "0.3.31" 528 | source = "registry+https://github.com/rust-lang/crates.io-index" 529 | checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" 530 | 531 | [[package]] 532 | name = "futures-task" 533 | version = "0.3.31" 534 | source = "registry+https://github.com/rust-lang/crates.io-index" 535 | checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" 536 | 537 | [[package]] 538 | name = "futures-util" 539 | version = "0.3.31" 540 | source = "registry+https://github.com/rust-lang/crates.io-index" 541 | checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" 542 | dependencies = [ 543 | "futures-channel", 544 | "futures-core", 545 | "futures-io", 546 | "futures-sink", 547 | "futures-task", 548 | "memchr", 549 | "pin-project-lite", 550 | "pin-utils", 551 | "slab", 552 | ] 553 | 554 | [[package]] 555 | name = "generic-array" 556 | version = "0.14.7" 557 | source = "registry+https://github.com/rust-lang/crates.io-index" 558 | checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" 559 | dependencies = [ 560 | "typenum", 561 | "version_check", 562 | ] 563 | 564 | [[package]] 565 | name = "gethostname" 566 | version = "0.2.3" 567 | source = "registry+https://github.com/rust-lang/crates.io-index" 568 | checksum = "c1ebd34e35c46e00bb73e81363248d627782724609fe1b6396f553f68fe3862e" 569 | dependencies = [ 570 | "libc", 571 | "winapi", 572 | ] 573 | 574 | [[package]] 575 | name = "getrandom" 576 | version = "0.2.16" 577 | source = "registry+https://github.com/rust-lang/crates.io-index" 578 | checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" 579 | dependencies = [ 580 | "cfg-if", 581 | "libc", 582 | "wasi 0.11.0+wasi-snapshot-preview1", 583 | ] 584 | 585 | [[package]] 586 | name = "getrandom" 587 | version = "0.3.3" 588 | source = "registry+https://github.com/rust-lang/crates.io-index" 589 | checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" 590 | dependencies = [ 591 | "cfg-if", 592 | "libc", 593 | "r-efi", 594 | "wasi 0.14.2+wasi-0.2.4", 595 | ] 596 | 597 | [[package]] 598 | name = "gimli" 599 | version = "0.31.1" 600 | source = "registry+https://github.com/rust-lang/crates.io-index" 601 | checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" 602 | 603 | [[package]] 604 | name = "hashbrown" 605 | version = "0.15.3" 606 | source = "registry+https://github.com/rust-lang/crates.io-index" 607 | checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" 608 | 609 | [[package]] 610 | name = "headers" 611 | version = "0.4.0" 612 | source = "registry+https://github.com/rust-lang/crates.io-index" 613 | checksum = "322106e6bd0cba2d5ead589ddb8150a13d7c4217cf80d7c4f682ca994ccc6aa9" 614 | dependencies = [ 615 | "base64 0.21.7", 616 | "bytes", 617 | "headers-core", 618 | "http", 619 | "httpdate", 620 | "mime", 621 | "sha1", 622 | ] 623 | 624 | [[package]] 625 | name = "headers-core" 626 | version = "0.3.0" 627 | source = "registry+https://github.com/rust-lang/crates.io-index" 628 | checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" 629 | dependencies = [ 630 | "http", 631 | ] 632 | 633 | [[package]] 634 | name = "heck" 635 | version = "0.5.0" 636 | source = "registry+https://github.com/rust-lang/crates.io-index" 637 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 638 | 639 | [[package]] 640 | name = "hermit-abi" 641 | version = "0.5.1" 642 | source = "registry+https://github.com/rust-lang/crates.io-index" 643 | checksum = "f154ce46856750ed433c8649605bf7ed2de3bc35fd9d2a9f30cddd873c80cb08" 644 | 645 | [[package]] 646 | name = "home" 647 | version = "0.5.11" 648 | source = "registry+https://github.com/rust-lang/crates.io-index" 649 | checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" 650 | dependencies = [ 651 | "windows-sys 0.59.0", 652 | ] 653 | 654 | [[package]] 655 | name = "http" 656 | version = "1.3.1" 657 | source = "registry+https://github.com/rust-lang/crates.io-index" 658 | checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" 659 | dependencies = [ 660 | "bytes", 661 | "fnv", 662 | "itoa", 663 | ] 664 | 665 | [[package]] 666 | name = "http-body" 667 | version = "1.0.1" 668 | source = "registry+https://github.com/rust-lang/crates.io-index" 669 | checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" 670 | dependencies = [ 671 | "bytes", 672 | "http", 673 | ] 674 | 675 | [[package]] 676 | name = "http-body-util" 677 | version = "0.1.3" 678 | source = "registry+https://github.com/rust-lang/crates.io-index" 679 | checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" 680 | dependencies = [ 681 | "bytes", 682 | "futures-core", 683 | "http", 684 | "http-body", 685 | "pin-project-lite", 686 | ] 687 | 688 | [[package]] 689 | name = "httparse" 690 | version = "1.10.1" 691 | source = "registry+https://github.com/rust-lang/crates.io-index" 692 | checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" 693 | 694 | [[package]] 695 | name = "httpdate" 696 | version = "1.0.3" 697 | source = "registry+https://github.com/rust-lang/crates.io-index" 698 | checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" 699 | 700 | [[package]] 701 | name = "hyper" 702 | version = "1.6.0" 703 | source = "registry+https://github.com/rust-lang/crates.io-index" 704 | checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" 705 | dependencies = [ 706 | "bytes", 707 | "futures-channel", 708 | "futures-util", 709 | "http", 710 | "http-body", 711 | "httparse", 712 | "itoa", 713 | "pin-project-lite", 714 | "smallvec", 715 | "tokio", 716 | "want", 717 | ] 718 | 719 | [[package]] 720 | name = "hyper-http-proxy" 721 | version = "1.1.0" 722 | source = "registry+https://github.com/rust-lang/crates.io-index" 723 | checksum = "7ad4b0a1e37510028bc4ba81d0e38d239c39671b0f0ce9e02dfa93a8133f7c08" 724 | dependencies = [ 725 | "bytes", 726 | "futures-util", 727 | "headers", 728 | "http", 729 | "hyper", 730 | "hyper-rustls", 731 | "hyper-util", 732 | "pin-project-lite", 733 | "rustls-native-certs 0.7.3", 734 | "tokio", 735 | "tokio-rustls", 736 | "tower-service", 737 | ] 738 | 739 | [[package]] 740 | name = "hyper-rustls" 741 | version = "0.27.5" 742 | source = "registry+https://github.com/rust-lang/crates.io-index" 743 | checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" 744 | dependencies = [ 745 | "futures-util", 746 | "http", 747 | "hyper", 748 | "hyper-util", 749 | "log", 750 | "rustls", 751 | "rustls-native-certs 0.8.1", 752 | "rustls-pki-types", 753 | "tokio", 754 | "tokio-rustls", 755 | "tower-service", 756 | "webpki-roots 0.26.11", 757 | ] 758 | 759 | [[package]] 760 | name = "hyper-socks2" 761 | version = "0.9.1" 762 | source = "registry+https://github.com/rust-lang/crates.io-index" 763 | checksum = "51c227614c208f7e7c2e040526912604a1a957fe467c9c2f5b06c5d032337dab" 764 | dependencies = [ 765 | "async-socks5", 766 | "http", 767 | "hyper", 768 | "hyper-util", 769 | "thiserror 1.0.69", 770 | "tokio", 771 | "tower-service", 772 | ] 773 | 774 | [[package]] 775 | name = "hyper-timeout" 776 | version = "0.5.2" 777 | source = "registry+https://github.com/rust-lang/crates.io-index" 778 | checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" 779 | dependencies = [ 780 | "hyper", 781 | "hyper-util", 782 | "pin-project-lite", 783 | "tokio", 784 | "tower-service", 785 | ] 786 | 787 | [[package]] 788 | name = "hyper-util" 789 | version = "0.1.11" 790 | source = "registry+https://github.com/rust-lang/crates.io-index" 791 | checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" 792 | dependencies = [ 793 | "bytes", 794 | "futures-channel", 795 | "futures-util", 796 | "http", 797 | "http-body", 798 | "hyper", 799 | "libc", 800 | "pin-project-lite", 801 | "socket2", 802 | "tokio", 803 | "tower-service", 804 | "tracing", 805 | ] 806 | 807 | [[package]] 808 | name = "iana-time-zone" 809 | version = "0.1.63" 810 | source = "registry+https://github.com/rust-lang/crates.io-index" 811 | checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" 812 | dependencies = [ 813 | "android_system_properties", 814 | "core-foundation-sys", 815 | "iana-time-zone-haiku", 816 | "js-sys", 817 | "log", 818 | "wasm-bindgen", 819 | "windows-core", 820 | ] 821 | 822 | [[package]] 823 | name = "iana-time-zone-haiku" 824 | version = "0.1.2" 825 | source = "registry+https://github.com/rust-lang/crates.io-index" 826 | checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 827 | dependencies = [ 828 | "cc", 829 | ] 830 | 831 | [[package]] 832 | name = "icu_collections" 833 | version = "2.0.0" 834 | source = "registry+https://github.com/rust-lang/crates.io-index" 835 | checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" 836 | dependencies = [ 837 | "displaydoc", 838 | "potential_utf", 839 | "yoke", 840 | "zerofrom", 841 | "zerovec", 842 | ] 843 | 844 | [[package]] 845 | name = "icu_locale_core" 846 | version = "2.0.0" 847 | source = "registry+https://github.com/rust-lang/crates.io-index" 848 | checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" 849 | dependencies = [ 850 | "displaydoc", 851 | "litemap", 852 | "tinystr", 853 | "writeable", 854 | "zerovec", 855 | ] 856 | 857 | [[package]] 858 | name = "icu_normalizer" 859 | version = "2.0.0" 860 | source = "registry+https://github.com/rust-lang/crates.io-index" 861 | checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" 862 | dependencies = [ 863 | "displaydoc", 864 | "icu_collections", 865 | "icu_normalizer_data", 866 | "icu_properties", 867 | "icu_provider", 868 | "smallvec", 869 | "zerovec", 870 | ] 871 | 872 | [[package]] 873 | name = "icu_normalizer_data" 874 | version = "2.0.0" 875 | source = "registry+https://github.com/rust-lang/crates.io-index" 876 | checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" 877 | 878 | [[package]] 879 | name = "icu_properties" 880 | version = "2.0.0" 881 | source = "registry+https://github.com/rust-lang/crates.io-index" 882 | checksum = "2549ca8c7241c82f59c80ba2a6f415d931c5b58d24fb8412caa1a1f02c49139a" 883 | dependencies = [ 884 | "displaydoc", 885 | "icu_collections", 886 | "icu_locale_core", 887 | "icu_properties_data", 888 | "icu_provider", 889 | "potential_utf", 890 | "zerotrie", 891 | "zerovec", 892 | ] 893 | 894 | [[package]] 895 | name = "icu_properties_data" 896 | version = "2.0.0" 897 | source = "registry+https://github.com/rust-lang/crates.io-index" 898 | checksum = "8197e866e47b68f8f7d95249e172903bec06004b18b2937f1095d40a0c57de04" 899 | 900 | [[package]] 901 | name = "icu_provider" 902 | version = "2.0.0" 903 | source = "registry+https://github.com/rust-lang/crates.io-index" 904 | checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" 905 | dependencies = [ 906 | "displaydoc", 907 | "icu_locale_core", 908 | "stable_deref_trait", 909 | "tinystr", 910 | "writeable", 911 | "yoke", 912 | "zerofrom", 913 | "zerotrie", 914 | "zerovec", 915 | ] 916 | 917 | [[package]] 918 | name = "idna" 919 | version = "1.0.3" 920 | source = "registry+https://github.com/rust-lang/crates.io-index" 921 | checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" 922 | dependencies = [ 923 | "idna_adapter", 924 | "smallvec", 925 | "utf8_iter", 926 | ] 927 | 928 | [[package]] 929 | name = "idna_adapter" 930 | version = "1.2.1" 931 | source = "registry+https://github.com/rust-lang/crates.io-index" 932 | checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" 933 | dependencies = [ 934 | "icu_normalizer", 935 | "icu_properties", 936 | ] 937 | 938 | [[package]] 939 | name = "indenter" 940 | version = "0.3.3" 941 | source = "registry+https://github.com/rust-lang/crates.io-index" 942 | checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" 943 | 944 | [[package]] 945 | name = "indexmap" 946 | version = "2.9.0" 947 | source = "registry+https://github.com/rust-lang/crates.io-index" 948 | checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" 949 | dependencies = [ 950 | "equivalent", 951 | "hashbrown", 952 | ] 953 | 954 | [[package]] 955 | name = "is-terminal" 956 | version = "0.4.16" 957 | source = "registry+https://github.com/rust-lang/crates.io-index" 958 | checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" 959 | dependencies = [ 960 | "hermit-abi", 961 | "libc", 962 | "windows-sys 0.59.0", 963 | ] 964 | 965 | [[package]] 966 | name = "is_terminal_polyfill" 967 | version = "1.70.1" 968 | source = "registry+https://github.com/rust-lang/crates.io-index" 969 | checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" 970 | 971 | [[package]] 972 | name = "itertools" 973 | version = "0.14.0" 974 | source = "registry+https://github.com/rust-lang/crates.io-index" 975 | checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" 976 | dependencies = [ 977 | "either", 978 | ] 979 | 980 | [[package]] 981 | name = "itoa" 982 | version = "1.0.15" 983 | source = "registry+https://github.com/rust-lang/crates.io-index" 984 | checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 985 | 986 | [[package]] 987 | name = "js-sys" 988 | version = "0.3.77" 989 | source = "registry+https://github.com/rust-lang/crates.io-index" 990 | checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" 991 | dependencies = [ 992 | "once_cell", 993 | "wasm-bindgen", 994 | ] 995 | 996 | [[package]] 997 | name = "jsonpath-rust" 998 | version = "0.7.5" 999 | source = "registry+https://github.com/rust-lang/crates.io-index" 1000 | checksum = "0c00ae348f9f8fd2d09f82a98ca381c60df9e0820d8d79fce43e649b4dc3128b" 1001 | dependencies = [ 1002 | "pest", 1003 | "pest_derive", 1004 | "regex", 1005 | "serde_json", 1006 | "thiserror 2.0.12", 1007 | ] 1008 | 1009 | [[package]] 1010 | name = "k8s-openapi" 1011 | version = "0.25.0" 1012 | source = "registry+https://github.com/rust-lang/crates.io-index" 1013 | checksum = "aa60a41b57ae1a0a071af77dbcf89fc9819cfe66edaf2beeb204c34459dcf0b2" 1014 | dependencies = [ 1015 | "base64 0.22.1", 1016 | "chrono", 1017 | "serde", 1018 | "serde_json", 1019 | ] 1020 | 1021 | [[package]] 1022 | name = "kube" 1023 | version = "1.1.0" 1024 | source = "registry+https://github.com/rust-lang/crates.io-index" 1025 | checksum = "778f98664beaf4c3c11372721e14310d1ae00f5e2d9aabcf8906c881aa4e9f51" 1026 | dependencies = [ 1027 | "k8s-openapi", 1028 | "kube-client", 1029 | "kube-core", 1030 | ] 1031 | 1032 | [[package]] 1033 | name = "kube-client" 1034 | version = "1.1.0" 1035 | source = "registry+https://github.com/rust-lang/crates.io-index" 1036 | checksum = "7cb276b85b6e94ded00ac8ea2c68fcf4697ea0553cb25fddc35d4a0ab718db8d" 1037 | dependencies = [ 1038 | "base64 0.22.1", 1039 | "bytes", 1040 | "chrono", 1041 | "either", 1042 | "form_urlencoded", 1043 | "futures", 1044 | "home", 1045 | "http", 1046 | "http-body", 1047 | "http-body-util", 1048 | "hyper", 1049 | "hyper-http-proxy", 1050 | "hyper-rustls", 1051 | "hyper-socks2", 1052 | "hyper-timeout", 1053 | "hyper-util", 1054 | "jsonpath-rust", 1055 | "k8s-openapi", 1056 | "kube-core", 1057 | "pem", 1058 | "rustls", 1059 | "secrecy", 1060 | "serde", 1061 | "serde_json", 1062 | "serde_yaml", 1063 | "tame-oauth", 1064 | "thiserror 2.0.12", 1065 | "tokio", 1066 | "tokio-util", 1067 | "tower", 1068 | "tower-http", 1069 | "tracing", 1070 | ] 1071 | 1072 | [[package]] 1073 | name = "kube-core" 1074 | version = "1.1.0" 1075 | source = "registry+https://github.com/rust-lang/crates.io-index" 1076 | checksum = "e3c56ff45deb0031f2a476017eed60c06872251f271b8387ad8020b8fef60960" 1077 | dependencies = [ 1078 | "chrono", 1079 | "derive_more", 1080 | "form_urlencoded", 1081 | "http", 1082 | "k8s-openapi", 1083 | "serde", 1084 | "serde-value", 1085 | "serde_json", 1086 | "thiserror 2.0.12", 1087 | ] 1088 | 1089 | [[package]] 1090 | name = "kubectl-view-allocations" 1091 | version = "0.21.2" 1092 | dependencies = [ 1093 | "anyhow", 1094 | "chrono", 1095 | "clap", 1096 | "color-eyre", 1097 | "itertools", 1098 | "k8s-openapi", 1099 | "kube", 1100 | "pretty_assertions", 1101 | "prettytable-rs", 1102 | "serde", 1103 | "serde_json", 1104 | "thiserror 2.0.12", 1105 | "tokio", 1106 | "tracing", 1107 | "tracing-bunyan-formatter", 1108 | "tracing-error", 1109 | "tracing-subscriber", 1110 | ] 1111 | 1112 | [[package]] 1113 | name = "lazy_static" 1114 | version = "1.5.0" 1115 | source = "registry+https://github.com/rust-lang/crates.io-index" 1116 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 1117 | 1118 | [[package]] 1119 | name = "libc" 1120 | version = "0.2.172" 1121 | source = "registry+https://github.com/rust-lang/crates.io-index" 1122 | checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" 1123 | 1124 | [[package]] 1125 | name = "libredox" 1126 | version = "0.1.3" 1127 | source = "registry+https://github.com/rust-lang/crates.io-index" 1128 | checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" 1129 | dependencies = [ 1130 | "bitflags", 1131 | "libc", 1132 | ] 1133 | 1134 | [[package]] 1135 | name = "litemap" 1136 | version = "0.8.0" 1137 | source = "registry+https://github.com/rust-lang/crates.io-index" 1138 | checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" 1139 | 1140 | [[package]] 1141 | name = "lock_api" 1142 | version = "0.4.12" 1143 | source = "registry+https://github.com/rust-lang/crates.io-index" 1144 | checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" 1145 | dependencies = [ 1146 | "autocfg", 1147 | "scopeguard", 1148 | ] 1149 | 1150 | [[package]] 1151 | name = "log" 1152 | version = "0.4.27" 1153 | source = "registry+https://github.com/rust-lang/crates.io-index" 1154 | checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" 1155 | 1156 | [[package]] 1157 | name = "matchers" 1158 | version = "0.1.0" 1159 | source = "registry+https://github.com/rust-lang/crates.io-index" 1160 | checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" 1161 | dependencies = [ 1162 | "regex-automata 0.1.10", 1163 | ] 1164 | 1165 | [[package]] 1166 | name = "memchr" 1167 | version = "2.7.4" 1168 | source = "registry+https://github.com/rust-lang/crates.io-index" 1169 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 1170 | 1171 | [[package]] 1172 | name = "mime" 1173 | version = "0.3.17" 1174 | source = "registry+https://github.com/rust-lang/crates.io-index" 1175 | checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" 1176 | 1177 | [[package]] 1178 | name = "miniz_oxide" 1179 | version = "0.8.8" 1180 | source = "registry+https://github.com/rust-lang/crates.io-index" 1181 | checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" 1182 | dependencies = [ 1183 | "adler2", 1184 | ] 1185 | 1186 | [[package]] 1187 | name = "mio" 1188 | version = "1.0.3" 1189 | source = "registry+https://github.com/rust-lang/crates.io-index" 1190 | checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" 1191 | dependencies = [ 1192 | "libc", 1193 | "wasi 0.11.0+wasi-snapshot-preview1", 1194 | "windows-sys 0.52.0", 1195 | ] 1196 | 1197 | [[package]] 1198 | name = "num-conv" 1199 | version = "0.1.0" 1200 | source = "registry+https://github.com/rust-lang/crates.io-index" 1201 | checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" 1202 | 1203 | [[package]] 1204 | name = "num-traits" 1205 | version = "0.2.19" 1206 | source = "registry+https://github.com/rust-lang/crates.io-index" 1207 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 1208 | dependencies = [ 1209 | "autocfg", 1210 | ] 1211 | 1212 | [[package]] 1213 | name = "object" 1214 | version = "0.36.7" 1215 | source = "registry+https://github.com/rust-lang/crates.io-index" 1216 | checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" 1217 | dependencies = [ 1218 | "memchr", 1219 | ] 1220 | 1221 | [[package]] 1222 | name = "once_cell" 1223 | version = "1.21.3" 1224 | source = "registry+https://github.com/rust-lang/crates.io-index" 1225 | checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 1226 | 1227 | [[package]] 1228 | name = "openssl-probe" 1229 | version = "0.1.6" 1230 | source = "registry+https://github.com/rust-lang/crates.io-index" 1231 | checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" 1232 | 1233 | [[package]] 1234 | name = "ordered-float" 1235 | version = "2.10.1" 1236 | source = "registry+https://github.com/rust-lang/crates.io-index" 1237 | checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" 1238 | dependencies = [ 1239 | "num-traits", 1240 | ] 1241 | 1242 | [[package]] 1243 | name = "owo-colors" 1244 | version = "4.2.0" 1245 | source = "registry+https://github.com/rust-lang/crates.io-index" 1246 | checksum = "1036865bb9422d3300cf723f657c2851d0e9ab12567854b1f4eba3d77decf564" 1247 | 1248 | [[package]] 1249 | name = "parking_lot" 1250 | version = "0.12.3" 1251 | source = "registry+https://github.com/rust-lang/crates.io-index" 1252 | checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" 1253 | dependencies = [ 1254 | "lock_api", 1255 | "parking_lot_core", 1256 | ] 1257 | 1258 | [[package]] 1259 | name = "parking_lot_core" 1260 | version = "0.9.10" 1261 | source = "registry+https://github.com/rust-lang/crates.io-index" 1262 | checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" 1263 | dependencies = [ 1264 | "cfg-if", 1265 | "libc", 1266 | "redox_syscall", 1267 | "smallvec", 1268 | "windows-targets", 1269 | ] 1270 | 1271 | [[package]] 1272 | name = "pem" 1273 | version = "3.0.5" 1274 | source = "registry+https://github.com/rust-lang/crates.io-index" 1275 | checksum = "38af38e8470ac9dee3ce1bae1af9c1671fffc44ddfd8bd1d0a3445bf349a8ef3" 1276 | dependencies = [ 1277 | "base64 0.22.1", 1278 | "serde", 1279 | ] 1280 | 1281 | [[package]] 1282 | name = "percent-encoding" 1283 | version = "2.3.1" 1284 | source = "registry+https://github.com/rust-lang/crates.io-index" 1285 | checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 1286 | 1287 | [[package]] 1288 | name = "pest" 1289 | version = "2.8.0" 1290 | source = "registry+https://github.com/rust-lang/crates.io-index" 1291 | checksum = "198db74531d58c70a361c42201efde7e2591e976d518caf7662a47dc5720e7b6" 1292 | dependencies = [ 1293 | "memchr", 1294 | "thiserror 2.0.12", 1295 | "ucd-trie", 1296 | ] 1297 | 1298 | [[package]] 1299 | name = "pest_derive" 1300 | version = "2.8.0" 1301 | source = "registry+https://github.com/rust-lang/crates.io-index" 1302 | checksum = "d725d9cfd79e87dccc9341a2ef39d1b6f6353d68c4b33c177febbe1a402c97c5" 1303 | dependencies = [ 1304 | "pest", 1305 | "pest_generator", 1306 | ] 1307 | 1308 | [[package]] 1309 | name = "pest_generator" 1310 | version = "2.8.0" 1311 | source = "registry+https://github.com/rust-lang/crates.io-index" 1312 | checksum = "db7d01726be8ab66ab32f9df467ae8b1148906685bbe75c82d1e65d7f5b3f841" 1313 | dependencies = [ 1314 | "pest", 1315 | "pest_meta", 1316 | "proc-macro2", 1317 | "quote", 1318 | "syn", 1319 | ] 1320 | 1321 | [[package]] 1322 | name = "pest_meta" 1323 | version = "2.8.0" 1324 | source = "registry+https://github.com/rust-lang/crates.io-index" 1325 | checksum = "7f9f832470494906d1fca5329f8ab5791cc60beb230c74815dff541cbd2b5ca0" 1326 | dependencies = [ 1327 | "once_cell", 1328 | "pest", 1329 | "sha2", 1330 | ] 1331 | 1332 | [[package]] 1333 | name = "pin-project-lite" 1334 | version = "0.2.16" 1335 | source = "registry+https://github.com/rust-lang/crates.io-index" 1336 | checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" 1337 | 1338 | [[package]] 1339 | name = "pin-utils" 1340 | version = "0.1.0" 1341 | source = "registry+https://github.com/rust-lang/crates.io-index" 1342 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 1343 | 1344 | [[package]] 1345 | name = "potential_utf" 1346 | version = "0.1.2" 1347 | source = "registry+https://github.com/rust-lang/crates.io-index" 1348 | checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" 1349 | dependencies = [ 1350 | "zerovec", 1351 | ] 1352 | 1353 | [[package]] 1354 | name = "powerfmt" 1355 | version = "0.2.0" 1356 | source = "registry+https://github.com/rust-lang/crates.io-index" 1357 | checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" 1358 | 1359 | [[package]] 1360 | name = "pretty_assertions" 1361 | version = "1.4.1" 1362 | source = "registry+https://github.com/rust-lang/crates.io-index" 1363 | checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" 1364 | dependencies = [ 1365 | "diff", 1366 | "yansi", 1367 | ] 1368 | 1369 | [[package]] 1370 | name = "prettytable-rs" 1371 | version = "0.10.0" 1372 | source = "registry+https://github.com/rust-lang/crates.io-index" 1373 | checksum = "eea25e07510aa6ab6547308ebe3c036016d162b8da920dbb079e3ba8acf3d95a" 1374 | dependencies = [ 1375 | "encode_unicode", 1376 | "is-terminal", 1377 | "lazy_static", 1378 | "term", 1379 | "unicode-width", 1380 | ] 1381 | 1382 | [[package]] 1383 | name = "proc-macro2" 1384 | version = "1.0.95" 1385 | source = "registry+https://github.com/rust-lang/crates.io-index" 1386 | checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" 1387 | dependencies = [ 1388 | "unicode-ident", 1389 | ] 1390 | 1391 | [[package]] 1392 | name = "quote" 1393 | version = "1.0.40" 1394 | source = "registry+https://github.com/rust-lang/crates.io-index" 1395 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 1396 | dependencies = [ 1397 | "proc-macro2", 1398 | ] 1399 | 1400 | [[package]] 1401 | name = "r-efi" 1402 | version = "5.2.0" 1403 | source = "registry+https://github.com/rust-lang/crates.io-index" 1404 | checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" 1405 | 1406 | [[package]] 1407 | name = "redox_syscall" 1408 | version = "0.5.12" 1409 | source = "registry+https://github.com/rust-lang/crates.io-index" 1410 | checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" 1411 | dependencies = [ 1412 | "bitflags", 1413 | ] 1414 | 1415 | [[package]] 1416 | name = "redox_users" 1417 | version = "0.4.6" 1418 | source = "registry+https://github.com/rust-lang/crates.io-index" 1419 | checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" 1420 | dependencies = [ 1421 | "getrandom 0.2.16", 1422 | "libredox", 1423 | "thiserror 1.0.69", 1424 | ] 1425 | 1426 | [[package]] 1427 | name = "regex" 1428 | version = "1.11.1" 1429 | source = "registry+https://github.com/rust-lang/crates.io-index" 1430 | checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" 1431 | dependencies = [ 1432 | "aho-corasick", 1433 | "memchr", 1434 | "regex-automata 0.4.9", 1435 | "regex-syntax 0.8.5", 1436 | ] 1437 | 1438 | [[package]] 1439 | name = "regex-automata" 1440 | version = "0.1.10" 1441 | source = "registry+https://github.com/rust-lang/crates.io-index" 1442 | checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" 1443 | dependencies = [ 1444 | "regex-syntax 0.6.29", 1445 | ] 1446 | 1447 | [[package]] 1448 | name = "regex-automata" 1449 | version = "0.4.9" 1450 | source = "registry+https://github.com/rust-lang/crates.io-index" 1451 | checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" 1452 | dependencies = [ 1453 | "aho-corasick", 1454 | "memchr", 1455 | "regex-syntax 0.8.5", 1456 | ] 1457 | 1458 | [[package]] 1459 | name = "regex-syntax" 1460 | version = "0.6.29" 1461 | source = "registry+https://github.com/rust-lang/crates.io-index" 1462 | checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" 1463 | 1464 | [[package]] 1465 | name = "regex-syntax" 1466 | version = "0.8.5" 1467 | source = "registry+https://github.com/rust-lang/crates.io-index" 1468 | checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 1469 | 1470 | [[package]] 1471 | name = "ring" 1472 | version = "0.17.14" 1473 | source = "registry+https://github.com/rust-lang/crates.io-index" 1474 | checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" 1475 | dependencies = [ 1476 | "cc", 1477 | "cfg-if", 1478 | "getrandom 0.2.16", 1479 | "libc", 1480 | "untrusted", 1481 | "windows-sys 0.52.0", 1482 | ] 1483 | 1484 | [[package]] 1485 | name = "rustc-demangle" 1486 | version = "0.1.24" 1487 | source = "registry+https://github.com/rust-lang/crates.io-index" 1488 | checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" 1489 | 1490 | [[package]] 1491 | name = "rustls" 1492 | version = "0.23.27" 1493 | source = "registry+https://github.com/rust-lang/crates.io-index" 1494 | checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321" 1495 | dependencies = [ 1496 | "log", 1497 | "once_cell", 1498 | "ring", 1499 | "rustls-pki-types", 1500 | "rustls-webpki", 1501 | "subtle", 1502 | "zeroize", 1503 | ] 1504 | 1505 | [[package]] 1506 | name = "rustls-native-certs" 1507 | version = "0.7.3" 1508 | source = "registry+https://github.com/rust-lang/crates.io-index" 1509 | checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" 1510 | dependencies = [ 1511 | "openssl-probe", 1512 | "rustls-pemfile", 1513 | "rustls-pki-types", 1514 | "schannel", 1515 | "security-framework 2.11.1", 1516 | ] 1517 | 1518 | [[package]] 1519 | name = "rustls-native-certs" 1520 | version = "0.8.1" 1521 | source = "registry+https://github.com/rust-lang/crates.io-index" 1522 | checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" 1523 | dependencies = [ 1524 | "openssl-probe", 1525 | "rustls-pki-types", 1526 | "schannel", 1527 | "security-framework 3.2.0", 1528 | ] 1529 | 1530 | [[package]] 1531 | name = "rustls-pemfile" 1532 | version = "2.2.0" 1533 | source = "registry+https://github.com/rust-lang/crates.io-index" 1534 | checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" 1535 | dependencies = [ 1536 | "rustls-pki-types", 1537 | ] 1538 | 1539 | [[package]] 1540 | name = "rustls-pki-types" 1541 | version = "1.12.0" 1542 | source = "registry+https://github.com/rust-lang/crates.io-index" 1543 | checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" 1544 | dependencies = [ 1545 | "zeroize", 1546 | ] 1547 | 1548 | [[package]] 1549 | name = "rustls-webpki" 1550 | version = "0.103.3" 1551 | source = "registry+https://github.com/rust-lang/crates.io-index" 1552 | checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" 1553 | dependencies = [ 1554 | "ring", 1555 | "rustls-pki-types", 1556 | "untrusted", 1557 | ] 1558 | 1559 | [[package]] 1560 | name = "rustversion" 1561 | version = "1.0.20" 1562 | source = "registry+https://github.com/rust-lang/crates.io-index" 1563 | checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" 1564 | 1565 | [[package]] 1566 | name = "ryu" 1567 | version = "1.0.20" 1568 | source = "registry+https://github.com/rust-lang/crates.io-index" 1569 | checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" 1570 | 1571 | [[package]] 1572 | name = "schannel" 1573 | version = "0.1.27" 1574 | source = "registry+https://github.com/rust-lang/crates.io-index" 1575 | checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" 1576 | dependencies = [ 1577 | "windows-sys 0.59.0", 1578 | ] 1579 | 1580 | [[package]] 1581 | name = "scopeguard" 1582 | version = "1.2.0" 1583 | source = "registry+https://github.com/rust-lang/crates.io-index" 1584 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 1585 | 1586 | [[package]] 1587 | name = "secrecy" 1588 | version = "0.10.3" 1589 | source = "registry+https://github.com/rust-lang/crates.io-index" 1590 | checksum = "e891af845473308773346dc847b2c23ee78fe442e0472ac50e22a18a93d3ae5a" 1591 | dependencies = [ 1592 | "zeroize", 1593 | ] 1594 | 1595 | [[package]] 1596 | name = "security-framework" 1597 | version = "2.11.1" 1598 | source = "registry+https://github.com/rust-lang/crates.io-index" 1599 | checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" 1600 | dependencies = [ 1601 | "bitflags", 1602 | "core-foundation 0.9.4", 1603 | "core-foundation-sys", 1604 | "libc", 1605 | "security-framework-sys", 1606 | ] 1607 | 1608 | [[package]] 1609 | name = "security-framework" 1610 | version = "3.2.0" 1611 | source = "registry+https://github.com/rust-lang/crates.io-index" 1612 | checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" 1613 | dependencies = [ 1614 | "bitflags", 1615 | "core-foundation 0.10.0", 1616 | "core-foundation-sys", 1617 | "libc", 1618 | "security-framework-sys", 1619 | ] 1620 | 1621 | [[package]] 1622 | name = "security-framework-sys" 1623 | version = "2.14.0" 1624 | source = "registry+https://github.com/rust-lang/crates.io-index" 1625 | checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" 1626 | dependencies = [ 1627 | "core-foundation-sys", 1628 | "libc", 1629 | ] 1630 | 1631 | [[package]] 1632 | name = "serde" 1633 | version = "1.0.219" 1634 | source = "registry+https://github.com/rust-lang/crates.io-index" 1635 | checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" 1636 | dependencies = [ 1637 | "serde_derive", 1638 | ] 1639 | 1640 | [[package]] 1641 | name = "serde-value" 1642 | version = "0.7.0" 1643 | source = "registry+https://github.com/rust-lang/crates.io-index" 1644 | checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" 1645 | dependencies = [ 1646 | "ordered-float", 1647 | "serde", 1648 | ] 1649 | 1650 | [[package]] 1651 | name = "serde_derive" 1652 | version = "1.0.219" 1653 | source = "registry+https://github.com/rust-lang/crates.io-index" 1654 | checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" 1655 | dependencies = [ 1656 | "proc-macro2", 1657 | "quote", 1658 | "syn", 1659 | ] 1660 | 1661 | [[package]] 1662 | name = "serde_json" 1663 | version = "1.0.140" 1664 | source = "registry+https://github.com/rust-lang/crates.io-index" 1665 | checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" 1666 | dependencies = [ 1667 | "itoa", 1668 | "memchr", 1669 | "ryu", 1670 | "serde", 1671 | ] 1672 | 1673 | [[package]] 1674 | name = "serde_yaml" 1675 | version = "0.9.34+deprecated" 1676 | source = "registry+https://github.com/rust-lang/crates.io-index" 1677 | checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" 1678 | dependencies = [ 1679 | "indexmap", 1680 | "itoa", 1681 | "ryu", 1682 | "serde", 1683 | "unsafe-libyaml", 1684 | ] 1685 | 1686 | [[package]] 1687 | name = "sha1" 1688 | version = "0.10.6" 1689 | source = "registry+https://github.com/rust-lang/crates.io-index" 1690 | checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" 1691 | dependencies = [ 1692 | "cfg-if", 1693 | "cpufeatures", 1694 | "digest", 1695 | ] 1696 | 1697 | [[package]] 1698 | name = "sha2" 1699 | version = "0.10.9" 1700 | source = "registry+https://github.com/rust-lang/crates.io-index" 1701 | checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" 1702 | dependencies = [ 1703 | "cfg-if", 1704 | "cpufeatures", 1705 | "digest", 1706 | ] 1707 | 1708 | [[package]] 1709 | name = "sharded-slab" 1710 | version = "0.1.7" 1711 | source = "registry+https://github.com/rust-lang/crates.io-index" 1712 | checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" 1713 | dependencies = [ 1714 | "lazy_static", 1715 | ] 1716 | 1717 | [[package]] 1718 | name = "shlex" 1719 | version = "1.3.0" 1720 | source = "registry+https://github.com/rust-lang/crates.io-index" 1721 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 1722 | 1723 | [[package]] 1724 | name = "signal-hook-registry" 1725 | version = "1.4.5" 1726 | source = "registry+https://github.com/rust-lang/crates.io-index" 1727 | checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" 1728 | dependencies = [ 1729 | "libc", 1730 | ] 1731 | 1732 | [[package]] 1733 | name = "slab" 1734 | version = "0.4.9" 1735 | source = "registry+https://github.com/rust-lang/crates.io-index" 1736 | checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" 1737 | dependencies = [ 1738 | "autocfg", 1739 | ] 1740 | 1741 | [[package]] 1742 | name = "smallvec" 1743 | version = "1.15.0" 1744 | source = "registry+https://github.com/rust-lang/crates.io-index" 1745 | checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" 1746 | 1747 | [[package]] 1748 | name = "socket2" 1749 | version = "0.5.9" 1750 | source = "registry+https://github.com/rust-lang/crates.io-index" 1751 | checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" 1752 | dependencies = [ 1753 | "libc", 1754 | "windows-sys 0.52.0", 1755 | ] 1756 | 1757 | [[package]] 1758 | name = "stable_deref_trait" 1759 | version = "1.2.0" 1760 | source = "registry+https://github.com/rust-lang/crates.io-index" 1761 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 1762 | 1763 | [[package]] 1764 | name = "static_assertions" 1765 | version = "1.1.0" 1766 | source = "registry+https://github.com/rust-lang/crates.io-index" 1767 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 1768 | 1769 | [[package]] 1770 | name = "strsim" 1771 | version = "0.11.1" 1772 | source = "registry+https://github.com/rust-lang/crates.io-index" 1773 | checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 1774 | 1775 | [[package]] 1776 | name = "subtle" 1777 | version = "2.6.1" 1778 | source = "registry+https://github.com/rust-lang/crates.io-index" 1779 | checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" 1780 | 1781 | [[package]] 1782 | name = "syn" 1783 | version = "2.0.101" 1784 | source = "registry+https://github.com/rust-lang/crates.io-index" 1785 | checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" 1786 | dependencies = [ 1787 | "proc-macro2", 1788 | "quote", 1789 | "unicode-ident", 1790 | ] 1791 | 1792 | [[package]] 1793 | name = "sync_wrapper" 1794 | version = "1.0.2" 1795 | source = "registry+https://github.com/rust-lang/crates.io-index" 1796 | checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" 1797 | 1798 | [[package]] 1799 | name = "synstructure" 1800 | version = "0.13.2" 1801 | source = "registry+https://github.com/rust-lang/crates.io-index" 1802 | checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" 1803 | dependencies = [ 1804 | "proc-macro2", 1805 | "quote", 1806 | "syn", 1807 | ] 1808 | 1809 | [[package]] 1810 | name = "tame-oauth" 1811 | version = "0.10.0" 1812 | source = "registry+https://github.com/rust-lang/crates.io-index" 1813 | checksum = "c206bbecfbc0aea8bf35f57bf34e8be060d2cf7efe3937f8d0bdfdd4205ed771" 1814 | dependencies = [ 1815 | "data-encoding", 1816 | "http", 1817 | "ring", 1818 | "serde", 1819 | "serde_json", 1820 | "twox-hash", 1821 | "url", 1822 | ] 1823 | 1824 | [[package]] 1825 | name = "term" 1826 | version = "0.7.0" 1827 | source = "registry+https://github.com/rust-lang/crates.io-index" 1828 | checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" 1829 | dependencies = [ 1830 | "dirs-next", 1831 | "rustversion", 1832 | "winapi", 1833 | ] 1834 | 1835 | [[package]] 1836 | name = "thiserror" 1837 | version = "1.0.69" 1838 | source = "registry+https://github.com/rust-lang/crates.io-index" 1839 | checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" 1840 | dependencies = [ 1841 | "thiserror-impl 1.0.69", 1842 | ] 1843 | 1844 | [[package]] 1845 | name = "thiserror" 1846 | version = "2.0.12" 1847 | source = "registry+https://github.com/rust-lang/crates.io-index" 1848 | checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" 1849 | dependencies = [ 1850 | "thiserror-impl 2.0.12", 1851 | ] 1852 | 1853 | [[package]] 1854 | name = "thiserror-impl" 1855 | version = "1.0.69" 1856 | source = "registry+https://github.com/rust-lang/crates.io-index" 1857 | checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" 1858 | dependencies = [ 1859 | "proc-macro2", 1860 | "quote", 1861 | "syn", 1862 | ] 1863 | 1864 | [[package]] 1865 | name = "thiserror-impl" 1866 | version = "2.0.12" 1867 | source = "registry+https://github.com/rust-lang/crates.io-index" 1868 | checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" 1869 | dependencies = [ 1870 | "proc-macro2", 1871 | "quote", 1872 | "syn", 1873 | ] 1874 | 1875 | [[package]] 1876 | name = "thread_local" 1877 | version = "1.1.8" 1878 | source = "registry+https://github.com/rust-lang/crates.io-index" 1879 | checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" 1880 | dependencies = [ 1881 | "cfg-if", 1882 | "once_cell", 1883 | ] 1884 | 1885 | [[package]] 1886 | name = "time" 1887 | version = "0.3.41" 1888 | source = "registry+https://github.com/rust-lang/crates.io-index" 1889 | checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" 1890 | dependencies = [ 1891 | "deranged", 1892 | "itoa", 1893 | "num-conv", 1894 | "powerfmt", 1895 | "serde", 1896 | "time-core", 1897 | "time-macros", 1898 | ] 1899 | 1900 | [[package]] 1901 | name = "time-core" 1902 | version = "0.1.4" 1903 | source = "registry+https://github.com/rust-lang/crates.io-index" 1904 | checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" 1905 | 1906 | [[package]] 1907 | name = "time-macros" 1908 | version = "0.2.22" 1909 | source = "registry+https://github.com/rust-lang/crates.io-index" 1910 | checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" 1911 | dependencies = [ 1912 | "num-conv", 1913 | "time-core", 1914 | ] 1915 | 1916 | [[package]] 1917 | name = "tinystr" 1918 | version = "0.8.1" 1919 | source = "registry+https://github.com/rust-lang/crates.io-index" 1920 | checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" 1921 | dependencies = [ 1922 | "displaydoc", 1923 | "zerovec", 1924 | ] 1925 | 1926 | [[package]] 1927 | name = "tokio" 1928 | version = "1.45.1" 1929 | source = "registry+https://github.com/rust-lang/crates.io-index" 1930 | checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" 1931 | dependencies = [ 1932 | "backtrace", 1933 | "bytes", 1934 | "libc", 1935 | "mio", 1936 | "parking_lot", 1937 | "pin-project-lite", 1938 | "signal-hook-registry", 1939 | "socket2", 1940 | "tokio-macros", 1941 | "windows-sys 0.52.0", 1942 | ] 1943 | 1944 | [[package]] 1945 | name = "tokio-macros" 1946 | version = "2.5.0" 1947 | source = "registry+https://github.com/rust-lang/crates.io-index" 1948 | checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" 1949 | dependencies = [ 1950 | "proc-macro2", 1951 | "quote", 1952 | "syn", 1953 | ] 1954 | 1955 | [[package]] 1956 | name = "tokio-rustls" 1957 | version = "0.26.2" 1958 | source = "registry+https://github.com/rust-lang/crates.io-index" 1959 | checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" 1960 | dependencies = [ 1961 | "rustls", 1962 | "tokio", 1963 | ] 1964 | 1965 | [[package]] 1966 | name = "tokio-util" 1967 | version = "0.7.15" 1968 | source = "registry+https://github.com/rust-lang/crates.io-index" 1969 | checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" 1970 | dependencies = [ 1971 | "bytes", 1972 | "futures-core", 1973 | "futures-sink", 1974 | "pin-project-lite", 1975 | "tokio", 1976 | ] 1977 | 1978 | [[package]] 1979 | name = "tower" 1980 | version = "0.5.2" 1981 | source = "registry+https://github.com/rust-lang/crates.io-index" 1982 | checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" 1983 | dependencies = [ 1984 | "futures-core", 1985 | "futures-util", 1986 | "pin-project-lite", 1987 | "sync_wrapper", 1988 | "tokio", 1989 | "tokio-util", 1990 | "tower-layer", 1991 | "tower-service", 1992 | "tracing", 1993 | ] 1994 | 1995 | [[package]] 1996 | name = "tower-http" 1997 | version = "0.6.4" 1998 | source = "registry+https://github.com/rust-lang/crates.io-index" 1999 | checksum = "0fdb0c213ca27a9f57ab69ddb290fd80d970922355b83ae380b395d3986b8a2e" 2000 | dependencies = [ 2001 | "async-compression", 2002 | "base64 0.22.1", 2003 | "bitflags", 2004 | "bytes", 2005 | "futures-core", 2006 | "http", 2007 | "http-body", 2008 | "http-body-util", 2009 | "mime", 2010 | "pin-project-lite", 2011 | "tokio", 2012 | "tokio-util", 2013 | "tower-layer", 2014 | "tower-service", 2015 | "tracing", 2016 | ] 2017 | 2018 | [[package]] 2019 | name = "tower-layer" 2020 | version = "0.3.3" 2021 | source = "registry+https://github.com/rust-lang/crates.io-index" 2022 | checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" 2023 | 2024 | [[package]] 2025 | name = "tower-service" 2026 | version = "0.3.3" 2027 | source = "registry+https://github.com/rust-lang/crates.io-index" 2028 | checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" 2029 | 2030 | [[package]] 2031 | name = "tracing" 2032 | version = "0.1.41" 2033 | source = "registry+https://github.com/rust-lang/crates.io-index" 2034 | checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" 2035 | dependencies = [ 2036 | "log", 2037 | "pin-project-lite", 2038 | "tracing-attributes", 2039 | "tracing-core", 2040 | ] 2041 | 2042 | [[package]] 2043 | name = "tracing-attributes" 2044 | version = "0.1.28" 2045 | source = "registry+https://github.com/rust-lang/crates.io-index" 2046 | checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" 2047 | dependencies = [ 2048 | "proc-macro2", 2049 | "quote", 2050 | "syn", 2051 | ] 2052 | 2053 | [[package]] 2054 | name = "tracing-bunyan-formatter" 2055 | version = "0.3.10" 2056 | source = "registry+https://github.com/rust-lang/crates.io-index" 2057 | checksum = "2d637245a0d8774bd48df6482e086c59a8b5348a910c3b0579354045a9d82411" 2058 | dependencies = [ 2059 | "ahash", 2060 | "gethostname", 2061 | "log", 2062 | "serde", 2063 | "serde_json", 2064 | "time", 2065 | "tracing", 2066 | "tracing-core", 2067 | "tracing-log", 2068 | "tracing-subscriber", 2069 | ] 2070 | 2071 | [[package]] 2072 | name = "tracing-core" 2073 | version = "0.1.33" 2074 | source = "registry+https://github.com/rust-lang/crates.io-index" 2075 | checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" 2076 | dependencies = [ 2077 | "once_cell", 2078 | "valuable", 2079 | ] 2080 | 2081 | [[package]] 2082 | name = "tracing-error" 2083 | version = "0.2.1" 2084 | source = "registry+https://github.com/rust-lang/crates.io-index" 2085 | checksum = "8b1581020d7a273442f5b45074a6a57d5757ad0a47dac0e9f0bd57b81936f3db" 2086 | dependencies = [ 2087 | "tracing", 2088 | "tracing-subscriber", 2089 | ] 2090 | 2091 | [[package]] 2092 | name = "tracing-log" 2093 | version = "0.1.4" 2094 | source = "registry+https://github.com/rust-lang/crates.io-index" 2095 | checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2" 2096 | dependencies = [ 2097 | "log", 2098 | "once_cell", 2099 | "tracing-core", 2100 | ] 2101 | 2102 | [[package]] 2103 | name = "tracing-subscriber" 2104 | version = "0.3.19" 2105 | source = "registry+https://github.com/rust-lang/crates.io-index" 2106 | checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" 2107 | dependencies = [ 2108 | "matchers", 2109 | "once_cell", 2110 | "regex", 2111 | "sharded-slab", 2112 | "thread_local", 2113 | "tracing", 2114 | "tracing-core", 2115 | ] 2116 | 2117 | [[package]] 2118 | name = "try-lock" 2119 | version = "0.2.5" 2120 | source = "registry+https://github.com/rust-lang/crates.io-index" 2121 | checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" 2122 | 2123 | [[package]] 2124 | name = "twox-hash" 2125 | version = "1.6.3" 2126 | source = "registry+https://github.com/rust-lang/crates.io-index" 2127 | checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" 2128 | dependencies = [ 2129 | "cfg-if", 2130 | "static_assertions", 2131 | ] 2132 | 2133 | [[package]] 2134 | name = "typenum" 2135 | version = "1.18.0" 2136 | source = "registry+https://github.com/rust-lang/crates.io-index" 2137 | checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" 2138 | 2139 | [[package]] 2140 | name = "ucd-trie" 2141 | version = "0.1.7" 2142 | source = "registry+https://github.com/rust-lang/crates.io-index" 2143 | checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" 2144 | 2145 | [[package]] 2146 | name = "unicode-ident" 2147 | version = "1.0.18" 2148 | source = "registry+https://github.com/rust-lang/crates.io-index" 2149 | checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 2150 | 2151 | [[package]] 2152 | name = "unicode-width" 2153 | version = "0.1.14" 2154 | source = "registry+https://github.com/rust-lang/crates.io-index" 2155 | checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" 2156 | 2157 | [[package]] 2158 | name = "unsafe-libyaml" 2159 | version = "0.2.11" 2160 | source = "registry+https://github.com/rust-lang/crates.io-index" 2161 | checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" 2162 | 2163 | [[package]] 2164 | name = "untrusted" 2165 | version = "0.9.0" 2166 | source = "registry+https://github.com/rust-lang/crates.io-index" 2167 | checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" 2168 | 2169 | [[package]] 2170 | name = "url" 2171 | version = "2.5.4" 2172 | source = "registry+https://github.com/rust-lang/crates.io-index" 2173 | checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" 2174 | dependencies = [ 2175 | "form_urlencoded", 2176 | "idna", 2177 | "percent-encoding", 2178 | ] 2179 | 2180 | [[package]] 2181 | name = "utf8_iter" 2182 | version = "1.0.4" 2183 | source = "registry+https://github.com/rust-lang/crates.io-index" 2184 | checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" 2185 | 2186 | [[package]] 2187 | name = "utf8parse" 2188 | version = "0.2.2" 2189 | source = "registry+https://github.com/rust-lang/crates.io-index" 2190 | checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 2191 | 2192 | [[package]] 2193 | name = "valuable" 2194 | version = "0.1.1" 2195 | source = "registry+https://github.com/rust-lang/crates.io-index" 2196 | checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" 2197 | 2198 | [[package]] 2199 | name = "version_check" 2200 | version = "0.9.5" 2201 | source = "registry+https://github.com/rust-lang/crates.io-index" 2202 | checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 2203 | 2204 | [[package]] 2205 | name = "want" 2206 | version = "0.3.1" 2207 | source = "registry+https://github.com/rust-lang/crates.io-index" 2208 | checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" 2209 | dependencies = [ 2210 | "try-lock", 2211 | ] 2212 | 2213 | [[package]] 2214 | name = "wasi" 2215 | version = "0.11.0+wasi-snapshot-preview1" 2216 | source = "registry+https://github.com/rust-lang/crates.io-index" 2217 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 2218 | 2219 | [[package]] 2220 | name = "wasi" 2221 | version = "0.14.2+wasi-0.2.4" 2222 | source = "registry+https://github.com/rust-lang/crates.io-index" 2223 | checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" 2224 | dependencies = [ 2225 | "wit-bindgen-rt", 2226 | ] 2227 | 2228 | [[package]] 2229 | name = "wasm-bindgen" 2230 | version = "0.2.100" 2231 | source = "registry+https://github.com/rust-lang/crates.io-index" 2232 | checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" 2233 | dependencies = [ 2234 | "cfg-if", 2235 | "once_cell", 2236 | "rustversion", 2237 | "wasm-bindgen-macro", 2238 | ] 2239 | 2240 | [[package]] 2241 | name = "wasm-bindgen-backend" 2242 | version = "0.2.100" 2243 | source = "registry+https://github.com/rust-lang/crates.io-index" 2244 | checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" 2245 | dependencies = [ 2246 | "bumpalo", 2247 | "log", 2248 | "proc-macro2", 2249 | "quote", 2250 | "syn", 2251 | "wasm-bindgen-shared", 2252 | ] 2253 | 2254 | [[package]] 2255 | name = "wasm-bindgen-macro" 2256 | version = "0.2.100" 2257 | source = "registry+https://github.com/rust-lang/crates.io-index" 2258 | checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" 2259 | dependencies = [ 2260 | "quote", 2261 | "wasm-bindgen-macro-support", 2262 | ] 2263 | 2264 | [[package]] 2265 | name = "wasm-bindgen-macro-support" 2266 | version = "0.2.100" 2267 | source = "registry+https://github.com/rust-lang/crates.io-index" 2268 | checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" 2269 | dependencies = [ 2270 | "proc-macro2", 2271 | "quote", 2272 | "syn", 2273 | "wasm-bindgen-backend", 2274 | "wasm-bindgen-shared", 2275 | ] 2276 | 2277 | [[package]] 2278 | name = "wasm-bindgen-shared" 2279 | version = "0.2.100" 2280 | source = "registry+https://github.com/rust-lang/crates.io-index" 2281 | checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" 2282 | dependencies = [ 2283 | "unicode-ident", 2284 | ] 2285 | 2286 | [[package]] 2287 | name = "webpki-roots" 2288 | version = "0.26.11" 2289 | source = "registry+https://github.com/rust-lang/crates.io-index" 2290 | checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" 2291 | dependencies = [ 2292 | "webpki-roots 1.0.0", 2293 | ] 2294 | 2295 | [[package]] 2296 | name = "webpki-roots" 2297 | version = "1.0.0" 2298 | source = "registry+https://github.com/rust-lang/crates.io-index" 2299 | checksum = "2853738d1cc4f2da3a225c18ec6c3721abb31961096e9dbf5ab35fa88b19cfdb" 2300 | dependencies = [ 2301 | "rustls-pki-types", 2302 | ] 2303 | 2304 | [[package]] 2305 | name = "winapi" 2306 | version = "0.3.9" 2307 | source = "registry+https://github.com/rust-lang/crates.io-index" 2308 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 2309 | dependencies = [ 2310 | "winapi-i686-pc-windows-gnu", 2311 | "winapi-x86_64-pc-windows-gnu", 2312 | ] 2313 | 2314 | [[package]] 2315 | name = "winapi-i686-pc-windows-gnu" 2316 | version = "0.4.0" 2317 | source = "registry+https://github.com/rust-lang/crates.io-index" 2318 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 2319 | 2320 | [[package]] 2321 | name = "winapi-x86_64-pc-windows-gnu" 2322 | version = "0.4.0" 2323 | source = "registry+https://github.com/rust-lang/crates.io-index" 2324 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 2325 | 2326 | [[package]] 2327 | name = "windows-core" 2328 | version = "0.61.0" 2329 | source = "registry+https://github.com/rust-lang/crates.io-index" 2330 | checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" 2331 | dependencies = [ 2332 | "windows-implement", 2333 | "windows-interface", 2334 | "windows-link", 2335 | "windows-result", 2336 | "windows-strings", 2337 | ] 2338 | 2339 | [[package]] 2340 | name = "windows-implement" 2341 | version = "0.60.0" 2342 | source = "registry+https://github.com/rust-lang/crates.io-index" 2343 | checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" 2344 | dependencies = [ 2345 | "proc-macro2", 2346 | "quote", 2347 | "syn", 2348 | ] 2349 | 2350 | [[package]] 2351 | name = "windows-interface" 2352 | version = "0.59.1" 2353 | source = "registry+https://github.com/rust-lang/crates.io-index" 2354 | checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" 2355 | dependencies = [ 2356 | "proc-macro2", 2357 | "quote", 2358 | "syn", 2359 | ] 2360 | 2361 | [[package]] 2362 | name = "windows-link" 2363 | version = "0.1.1" 2364 | source = "registry+https://github.com/rust-lang/crates.io-index" 2365 | checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" 2366 | 2367 | [[package]] 2368 | name = "windows-result" 2369 | version = "0.3.2" 2370 | source = "registry+https://github.com/rust-lang/crates.io-index" 2371 | checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" 2372 | dependencies = [ 2373 | "windows-link", 2374 | ] 2375 | 2376 | [[package]] 2377 | name = "windows-strings" 2378 | version = "0.4.0" 2379 | source = "registry+https://github.com/rust-lang/crates.io-index" 2380 | checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" 2381 | dependencies = [ 2382 | "windows-link", 2383 | ] 2384 | 2385 | [[package]] 2386 | name = "windows-sys" 2387 | version = "0.52.0" 2388 | source = "registry+https://github.com/rust-lang/crates.io-index" 2389 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 2390 | dependencies = [ 2391 | "windows-targets", 2392 | ] 2393 | 2394 | [[package]] 2395 | name = "windows-sys" 2396 | version = "0.59.0" 2397 | source = "registry+https://github.com/rust-lang/crates.io-index" 2398 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 2399 | dependencies = [ 2400 | "windows-targets", 2401 | ] 2402 | 2403 | [[package]] 2404 | name = "windows-targets" 2405 | version = "0.52.6" 2406 | source = "registry+https://github.com/rust-lang/crates.io-index" 2407 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 2408 | dependencies = [ 2409 | "windows_aarch64_gnullvm", 2410 | "windows_aarch64_msvc", 2411 | "windows_i686_gnu", 2412 | "windows_i686_gnullvm", 2413 | "windows_i686_msvc", 2414 | "windows_x86_64_gnu", 2415 | "windows_x86_64_gnullvm", 2416 | "windows_x86_64_msvc", 2417 | ] 2418 | 2419 | [[package]] 2420 | name = "windows_aarch64_gnullvm" 2421 | version = "0.52.6" 2422 | source = "registry+https://github.com/rust-lang/crates.io-index" 2423 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 2424 | 2425 | [[package]] 2426 | name = "windows_aarch64_msvc" 2427 | version = "0.52.6" 2428 | source = "registry+https://github.com/rust-lang/crates.io-index" 2429 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 2430 | 2431 | [[package]] 2432 | name = "windows_i686_gnu" 2433 | version = "0.52.6" 2434 | source = "registry+https://github.com/rust-lang/crates.io-index" 2435 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 2436 | 2437 | [[package]] 2438 | name = "windows_i686_gnullvm" 2439 | version = "0.52.6" 2440 | source = "registry+https://github.com/rust-lang/crates.io-index" 2441 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 2442 | 2443 | [[package]] 2444 | name = "windows_i686_msvc" 2445 | version = "0.52.6" 2446 | source = "registry+https://github.com/rust-lang/crates.io-index" 2447 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 2448 | 2449 | [[package]] 2450 | name = "windows_x86_64_gnu" 2451 | version = "0.52.6" 2452 | source = "registry+https://github.com/rust-lang/crates.io-index" 2453 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 2454 | 2455 | [[package]] 2456 | name = "windows_x86_64_gnullvm" 2457 | version = "0.52.6" 2458 | source = "registry+https://github.com/rust-lang/crates.io-index" 2459 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 2460 | 2461 | [[package]] 2462 | name = "windows_x86_64_msvc" 2463 | version = "0.52.6" 2464 | source = "registry+https://github.com/rust-lang/crates.io-index" 2465 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 2466 | 2467 | [[package]] 2468 | name = "wit-bindgen-rt" 2469 | version = "0.39.0" 2470 | source = "registry+https://github.com/rust-lang/crates.io-index" 2471 | checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" 2472 | dependencies = [ 2473 | "bitflags", 2474 | ] 2475 | 2476 | [[package]] 2477 | name = "writeable" 2478 | version = "0.6.1" 2479 | source = "registry+https://github.com/rust-lang/crates.io-index" 2480 | checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" 2481 | 2482 | [[package]] 2483 | name = "yansi" 2484 | version = "1.0.1" 2485 | source = "registry+https://github.com/rust-lang/crates.io-index" 2486 | checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" 2487 | 2488 | [[package]] 2489 | name = "yoke" 2490 | version = "0.8.0" 2491 | source = "registry+https://github.com/rust-lang/crates.io-index" 2492 | checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" 2493 | dependencies = [ 2494 | "serde", 2495 | "stable_deref_trait", 2496 | "yoke-derive", 2497 | "zerofrom", 2498 | ] 2499 | 2500 | [[package]] 2501 | name = "yoke-derive" 2502 | version = "0.8.0" 2503 | source = "registry+https://github.com/rust-lang/crates.io-index" 2504 | checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" 2505 | dependencies = [ 2506 | "proc-macro2", 2507 | "quote", 2508 | "syn", 2509 | "synstructure", 2510 | ] 2511 | 2512 | [[package]] 2513 | name = "zerocopy" 2514 | version = "0.8.25" 2515 | source = "registry+https://github.com/rust-lang/crates.io-index" 2516 | checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" 2517 | dependencies = [ 2518 | "zerocopy-derive", 2519 | ] 2520 | 2521 | [[package]] 2522 | name = "zerocopy-derive" 2523 | version = "0.8.25" 2524 | source = "registry+https://github.com/rust-lang/crates.io-index" 2525 | checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" 2526 | dependencies = [ 2527 | "proc-macro2", 2528 | "quote", 2529 | "syn", 2530 | ] 2531 | 2532 | [[package]] 2533 | name = "zerofrom" 2534 | version = "0.1.6" 2535 | source = "registry+https://github.com/rust-lang/crates.io-index" 2536 | checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" 2537 | dependencies = [ 2538 | "zerofrom-derive", 2539 | ] 2540 | 2541 | [[package]] 2542 | name = "zerofrom-derive" 2543 | version = "0.1.6" 2544 | source = "registry+https://github.com/rust-lang/crates.io-index" 2545 | checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" 2546 | dependencies = [ 2547 | "proc-macro2", 2548 | "quote", 2549 | "syn", 2550 | "synstructure", 2551 | ] 2552 | 2553 | [[package]] 2554 | name = "zeroize" 2555 | version = "1.8.1" 2556 | source = "registry+https://github.com/rust-lang/crates.io-index" 2557 | checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" 2558 | 2559 | [[package]] 2560 | name = "zerotrie" 2561 | version = "0.2.2" 2562 | source = "registry+https://github.com/rust-lang/crates.io-index" 2563 | checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" 2564 | dependencies = [ 2565 | "displaydoc", 2566 | "yoke", 2567 | "zerofrom", 2568 | ] 2569 | 2570 | [[package]] 2571 | name = "zerovec" 2572 | version = "0.11.2" 2573 | source = "registry+https://github.com/rust-lang/crates.io-index" 2574 | checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" 2575 | dependencies = [ 2576 | "yoke", 2577 | "zerofrom", 2578 | "zerovec-derive", 2579 | ] 2580 | 2581 | [[package]] 2582 | name = "zerovec-derive" 2583 | version = "0.11.1" 2584 | source = "registry+https://github.com/rust-lang/crates.io-index" 2585 | checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" 2586 | dependencies = [ 2587 | "proc-macro2", 2588 | "quote", 2589 | "syn", 2590 | ] 2591 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kubectl-view-allocations" 3 | version = "0.21.2" 4 | authors = ["David Bernard"] 5 | edition = "2024" 6 | description = "kubectl plugin to list allocations (cpu, memory, gpu,...) X (utilization, requested, limit, allocatable,...)" 7 | readme = "README.md" 8 | license = "CC0-1.0" 9 | keywords = ["kubectl", "k8s"] 10 | repository = "https://github.com/davidB/kubectl-view-allocations" 11 | homepage = "https://github.com/davidB/kubectl-view-allocations" 12 | exclude = ["/.github", ".gitignore"] 13 | 14 | [dependencies] 15 | chrono = "0.4" 16 | clap = { version = "4.5", features = ["derive"] } 17 | color-eyre = "0.6" 18 | itertools = "0.14" 19 | k8s-openapi = { version = "0.25", default-features = false } 20 | kube = { version = "1", features = [ 21 | "ring", # or "aws-lc-rs" for rustls-tls, "ring" is also a dependency of tame-oauth 22 | "client", 23 | "gzip", 24 | "http-proxy", 25 | "oauth", 26 | "oidc", 27 | "rustls-tls", 28 | "socks5", 29 | "webpki-roots", 30 | ], default-features = false } 31 | prettytable-rs = { version = "0.10", default-features = false, optional = true } 32 | serde = "1.0" 33 | serde_json = "1.0" 34 | thiserror = "2.0" 35 | tokio = { version = "1", features = ["full"], optional = true } 36 | tracing = "0.1" 37 | tracing-bunyan-formatter = { version = "0.3", optional = true } 38 | tracing-error = "0.2" 39 | tracing-subscriber = { version = "0.3", optional = true, default-features = false, features = [ 40 | "env-filter", 41 | ] } 42 | 43 | [features] 44 | default = ["cli"] 45 | cli = [ 46 | "dep:tokio", 47 | "k8s-openapi/earliest", 48 | "dep:tracing-subscriber", 49 | "prettytable", 50 | ] 51 | prettytable = ["dep:prettytable-rs"] 52 | 53 | [[bin]] 54 | name = "kubectl-view-allocations" 55 | path = "src/main.rs" 56 | doc = false 57 | # HACK to define dependencies only for cli 58 | # see https://github.com/rust-lang/cargo/issues/1982 59 | required-features = ["cli"] 60 | 61 | [dev-dependencies] 62 | anyhow = "1" 63 | pretty_assertions = "1" 64 | 65 | [profile.release] 66 | lto = true 67 | panic = 'abort' 68 | opt-level = 'z' # Optimize for size. 69 | codegen-units = 1 70 | strip = true 71 | 72 | [profile.dev.package.backtrace] 73 | opt-level = 3 74 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | 48 | ii. moral rights retained by the original author(s) and/or performer(s); 49 | 50 | iii. publicity and privacy rights pertaining to a person's image or 51 | likeness depicted in a Work; 52 | 53 | iv. rights protecting against unfair competition in regards to a Work, 54 | subject to the limitations in paragraph 4(a), below; 55 | 56 | v. rights protecting the extraction, dissemination, use and reuse of data 57 | in a Work; 58 | 59 | vi. database rights (such as those arising under Directive 96/9/EC of the 60 | European Parliament and of the Council of 11 March 1996 on the legal 61 | protection of databases, and under any national implementation 62 | thereof, including any amended or successor version of such 63 | directive); and 64 | 65 | vii. other similar, equivalent or corresponding rights throughout the 66 | world based on applicable law or treaty, and any national 67 | implementations thereof. 68 | 69 | 2. Waiver. To the greatest extent permitted by, but not in contravention 70 | of, applicable law, Affirmer hereby overtly, fully, permanently, 71 | irrevocably and unconditionally waives, abandons, and surrenders all of 72 | Affirmer's Copyright and Related Rights and associated claims and causes 73 | of action, whether now known or unknown (including existing as well as 74 | future claims and causes of action), in the Work (i) in all territories 75 | worldwide, (ii) for the maximum duration provided by applicable law or 76 | treaty (including future time extensions), (iii) in any current or future 77 | medium and for any number of copies, and (iv) for any purpose whatsoever, 78 | including without limitation commercial, advertising or promotional 79 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 80 | member of the public at large and to the detriment of Affirmer's heirs and 81 | successors, fully intending that such Waiver shall not be subject to 82 | revocation, rescission, cancellation, termination, or any other legal or 83 | equitable action to disrupt the quiet enjoyment of the Work by the public 84 | as contemplated by Affirmer's express Statement of Purpose. 85 | 86 | 3. Public License Fallback. Should any part of the Waiver for any reason 87 | be judged legally invalid or ineffective under applicable law, then the 88 | Waiver shall be preserved to the maximum extent permitted taking into 89 | account Affirmer's express Statement of Purpose. In addition, to the 90 | extent the Waiver is so judged Affirmer hereby grants to each affected 91 | person a royalty-free, non transferable, non sublicensable, non exclusive, 92 | irrevocable and unconditional license to exercise Affirmer's Copyright and 93 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 94 | maximum duration provided by applicable law or treaty (including future 95 | time extensions), (iii) in any current or future medium and for any number 96 | of copies, and (iv) for any purpose whatsoever, including without 97 | limitation commercial, advertising or promotional purposes (the 98 | "License"). The License shall be deemed effective as of the date CC0 was 99 | applied by Affirmer to the Work. Should any part of the License for any 100 | reason be judged legally invalid or ineffective under applicable law, such 101 | partial invalidity or ineffectiveness shall not invalidate the remainder 102 | of the License, and in such case Affirmer hereby affirms that he or she 103 | will not (i) exercise any of his or her remaining Copyright and Related 104 | Rights in the Work or (ii) assert any associated claims and causes of 105 | action with respect to the Work, in either case contrary to Affirmer's 106 | express Statement of Purpose. 107 | 108 | 4. Limitations and Disclaimers. 109 | 110 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 111 | surrendered, licensed or otherwise affected by this document. 112 | 113 | b. Affirmer offers the Work as-is and makes no representations or 114 | warranties of any kind concerning the Work, express, implied, 115 | statutory or otherwise, including without limitation warranties of 116 | title, merchantability, fitness for a particular purpose, non 117 | infringement, or the absence of latent or other defects, accuracy, or 118 | the present or absence of errors, whether or not discoverable, all to 119 | the greatest extent permissible under applicable law. 120 | 121 | c. Affirmer disclaims responsibility for clearing rights of other persons 122 | that may apply to the Work or any use thereof, including without 123 | limitation any person's Copyright and Related Rights in the Work. 124 | Further, Affirmer disclaims responsibility for obtaining any necessary 125 | consents, permissions or other rights required for any use of the 126 | Work. 127 | 128 | d. Affirmer understands and acknowledges that Creative Commons is not a 129 | party to this document and has no duty or obligation with respect to 130 | this CC0 or use of the Work. 131 | -------------------------------------------------------------------------------- /Makefile.toml: -------------------------------------------------------------------------------- 1 | # see https://crates.io/crates/cargo-make 2 | 3 | [env] 4 | RUST_TEST_THREADS = "1" 5 | CARGO_MAKE_TEST_COVERAGE_BINARY_FILTER = "cli-[a-z0-9]*$\\|makers-[a-z0-9]*$\\|${CARGO_MAKE_TEST_COVERAGE_DEFAULT_BINARY_FILTER}" 6 | # TARGET is {arch}-{vendor}-{sys}-{abi} the output of rustc --print target-list 7 | TARGET_AUTO = { source = "${CARGO_MAKE_RUST_TARGET_ARCH}-${CARGO_MAKE_RUST_TARGET_VENDOR}-${CARGO_MAKE_RUST_TARGET_OS}-${CARGO_MAKE_RUST_TARGET_ENV}", mapping = { x86_64-apple-macos- = "x86_64-apple-darwin" } } 8 | TARGET = { condition = { env_not_set = ["TARGET"] }, value = "${TARGET_AUTO}" } 9 | LIBZ_SYS_STATIC = "1" 10 | PKG_CONFIG_ALLOW_CROSS = "1" 11 | # see https://github.com/naftulikay/rust-openssl-static-example 12 | OPENSSL_STATIC = "1" 13 | # OPENSSL_LIB_DIR="/usr/lib64" 14 | # OPENSSL_INCLUDE_DIR="/usr/include/openssl" 15 | CARGO_BUILD_CMD = { source = "${CROSS}", default_value = "cargo", mapping = { true = "cross" } } 16 | 17 | [tasks.init.mac.env] 18 | # workaround for "____chkstk_darwin ... which was built for Mac OS X 10.15 /usr/lib/libSystem.B.dylib" 19 | # see https://github.com/sharkdp/bat/issues/680 20 | # if DevelopmentTools.clang_build_version >= 1010 21 | CFLAGS = "-fno-stack-check" 22 | #RUSTFLAGS = "-Clink-args=-Wl,-bind_at_load" 23 | # XCode --mmacosx-version-min=10.9 ? 24 | MACOSX_DEPLOYMENT_TARGET = "10.9" 25 | 26 | [tasks.dist_env.env] 27 | DIST_VERSION = "${CARGO_MAKE_CRATE_VERSION}" 28 | DIST_NAME = "${CARGO_MAKE_CRATE_NAME}_${DIST_VERSION}-${TARGET}" 29 | DIST_EXT = { source = "${CARGO_MAKE_RUST_TARGET_OS}", default_value = "tar.gz", mapping = { windows = "zip" } } 30 | DIST_PATH = "target/dist/${DIST_NAME}" 31 | 32 | [tasks.debug] 33 | dependencies = ["dist_env"] 34 | script_runner = "@duckscript" 35 | script = [''' 36 | echo "PROFILE=${PROFILE}" 37 | echo "MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET}" 38 | echo "FILE=${DIST_PATH}.${DIST_EXT}" 39 | echo "TARGET=${TARGET}" 40 | echo "DIST_VERSION=${DIST_VERSION}" 41 | echo "TAG=${TAG}" 42 | echo "GITHUB_REF=${GITHUB_REF}" 43 | echo "CARGO_BUILD_CMD=${CARGO_BUILD_CMD}" 44 | echo ${CARGO_MAKE_PROFILE} : ${DIST_PATH} : ${platform} 45 | '''] 46 | 47 | [tasks.zip-release-ci-flow] 48 | description = "Compiles the binary in release mode and zips it up" 49 | windows_alias = "zip-release-ci-flow_windows" 50 | category = "CI" 51 | condition = { env_set = ["TARGET"] } 52 | dependencies = [ 53 | # "clean", 54 | "dist_env", 55 | "build-release-for-target", 56 | "zip-release-binary-for-target", 57 | ] 58 | script_runner = "@shell" 59 | script = [''' 60 | echo "dist_file_path=${DIST_PATH}.${DIST_EXT}" >> $GITHUB_OUTPUT 61 | echo "dist_file_name=${DIST_NAME}.${DIST_EXT}" >> $GITHUB_OUTPUT 62 | echo "dist_version=${DIST_VERSION}" >> $GITHUB_OUTPUT 63 | '''] 64 | 65 | [tasks.zip-release-ci-flow_windows] 66 | description = "Compiles the binary in release mode and zips it up" 67 | category = "CI" 68 | condition = { env_set = ["TARGET"] } 69 | dependencies = [ 70 | # "clean", 71 | "dist_env", 72 | "build-release-for-target", 73 | "zip-release-binary-for-target", 74 | ] 75 | script_runner = "powershell" 76 | script_extension = "ps1" 77 | script = [''' 78 | echo "dist_file_path=${env:DIST_PATH}.${env:DIST_EXT}" >> $env:GITHUB_OUTPUT 79 | echo "dist_file_name=${env:DIST_NAME}.${env:DIST_EXT}" >> $env:GITHUB_OUTPUT 80 | echo "dist_version=${env:DIST_VERSION}" >> $env:GITHUB_OUTPUT 81 | '''] 82 | 83 | [tasks.setup-cross] 84 | description = "Install cross from https://github.com/cross-rs/cross" 85 | condition = { env_set = ["TARGET", "CROSS"] } 86 | install_crate = { crate_name = "cross", binary = "cross", test_arg = "--help" } 87 | 88 | [tasks.build-release-for-target] 89 | description = "Makes a release build for a given target" 90 | condition = { env_set = ["TARGET"] } 91 | dependencies = ["setup-cross"] 92 | command = "${CARGO_BUILD_CMD}" 93 | args = ["build", "--release", "--all-features", "--target", "${TARGET}"] 94 | 95 | [tasks.zip-release-binary-for-target] 96 | windows_alias = "zip-release-binary-for-target_windows" 97 | description = "Zips up the release binary, README, and license(s)" 98 | category = "Publish" 99 | condition = { env_set = ["TARGET"] } 100 | # env = { "LIBZ_SYS_STATIC" = "1", "PKG_CONFIG_ALLOW_CROSS" = "1" } 101 | script_runner = "@shell" 102 | script = [ 103 | ''' 104 | rm -Rf ${DIST_PATH}* 105 | mkdir -p ${DIST_PATH} 106 | cp target/${TARGET}/release/${CARGO_MAKE_CRATE_NAME} ${DIST_PATH}/ 107 | cp LICENSE* ${DIST_PATH}/ || echo "ignore if no LICENSE" 108 | # "cp README* ${DIST_PATH}/ 109 | tar -czvf "${DIST_PATH}.${DIST_EXT}" -C "${DIST_PATH}" "${CARGO_MAKE_CRATE_NAME}" "LICENSE.txt" 110 | ''', 111 | ] 112 | 113 | [tasks.zip-release-binary-for-target_windows] 114 | script_runner = "powershell" 115 | script_extension = "ps1" 116 | script = [ 117 | ''' 118 | Set-PSDebug -Strict # -Trace 1 119 | $ErrorActionPreference = "Stop" 120 | Remove-Item -Recurse -Force "${env:DIST_PATH}" -ErrorAction SilentlyContinue 121 | New-Item -ItemType directory -Path "${env:DIST_PATH}" 122 | Copy-Item -Path "target\\${env:TARGET}\\release\\${env:CARGO_MAKE_CRATE_NAME}.exe" -Destination "${env:DIST_PATH}" 123 | Copy-Item -Path "LICENSE*" -Destination "${env:DIST_PATH}" 124 | # Copy-Item -Path "README*" -Destination "${env:DIST_PATH}" 125 | Compress-Archive -Path "${env:DIST_PATH}\\*" -DestinationPath "${env:DIST_PATH}.${env:DIST_EXT}" -CompressionLevel Optimal -Force 126 | ''', 127 | ] 128 | 129 | [tasks.update-changelog] 130 | category = "Publish" 131 | install_crate = { crate_name = "gitmoji-changelog", binary = "gitmoji-changelog", test_arg = "--help" } 132 | script = [''' 133 | rm -Rf CHANGELOG.md 134 | gitmoji-changelog -r x.y.z-dev -o CHANGELOG.md . 135 | '''] 136 | 137 | [tasks.update-book] 138 | category = "Publish" 139 | install_crate = { crate_name = "mdbook", binary = "mdbook", test_arg = "--help" } 140 | script = [''' 141 | cd docs 142 | mdbook clean 143 | mdbook build 144 | '''] 145 | 146 | [tasks.update-bom] 147 | category = "Publish" 148 | install_crate = { crate_name = "cargo-bom", binary = "cargo", test_arg = "bom" } 149 | script = ["cargo bom > BOM.txt"] 150 | 151 | [tasks.pre-publish] 152 | dependencies = ["update-docs"] 153 | 154 | [tasks.update-docs] 155 | env = { COMMIT_MSG = ":pencil: pre-publish update book, changelog" } 156 | run_task = "_update-docs-do" 157 | 158 | [tasks._update-docs-do] 159 | dependencies = [ 160 | "update-changelog", 161 | "update-bom", 162 | "git-add", 163 | "git-commit-message", 164 | ] 165 | 166 | [tasks.publish] 167 | command = "cargo" 168 | args = ["release", "${@}"] 169 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # kubectl-view-allocations 2 | 3 | [![Crates.io](https://img.shields.io/crates/l/kubectl-view-allocations.svg)](http://creativecommons.org/publicdomain/zero/1.0/) 4 | [![Crates.io](https://img.shields.io/crates/v/kubectl-view-allocations.svg)](https://crates.io/crates/kubectl-view-allocations) 5 | 6 | [![Project Status: WIP – Initial development is in progress, but there has not yet been a stable, usable release suitable for the public.](https://www.repostatus.org/badges/latest/wip.svg)](https://www.repostatus.org/#wip) 7 | [![Actions Status](https://github.com/davidB/kubectl-view-allocations/workflows/ci-flow/badge.svg)](https://github.com/davidB/kubectl-view-allocations/actions) 8 | [![Documentation](https://docs.rs/kubectl-view-allocations/badge.svg)](https://docs.rs/kubectl-view-allocations/) 9 | 10 | [![Crates.io](https://img.shields.io/crates/d/kubectl-view-allocations.svg)](https://crates.io/crates/kubectl-view-allocations) 11 | ![GitHub All Releases](https://img.shields.io/github/downloads/davidB/kubectl-view-allocations/total.svg) 12 | 13 | `kubectl` plugin lists allocations for resources (cpu, memory, gpu,...) as defined into the manifest of nodes and running pods. It doesn't list usage like `kubectl top`. It can provide result grouped by namespaces, nodes, pods and filtered by resources'name. 14 | 15 | Columns displayed : 16 | 17 | - `Requested` : Quantity of resources requested by the container in the pod's manifest. It's the sum group by pod, namespace, node where container is running. With percentage of resources requested over what is allocatable in the group. 18 | - `Limit` : Quantity of resources max (limit) requestable by the container in the pod's manifest. It's the sum group by pod, namespace, node where container is running. With percentage of resources max / limit over what is allocatable in the group. 19 | - `Allocatable` : Allocatable resources defined (or detected) on nodes. 20 | - `Free` : `Allocatable - max (Limit, Requested)` (by default, see options `--used-mode`) 21 | - `Utilization` : Quantity of resources (cpu & memory only) used as reported by Metrics API. It's disable by default, [metrics-server](https://github.com/kubernetes-incubator/metrics-server) is optional and should be setup into the cluster. 22 | 23 | ## Install 24 | 25 | ### Via download binary 26 | 27 | Download from [github's release](https://github.com/davidB/kubectl-view-allocations/releases/latest) or use script 28 | 29 | ```sh 30 | curl https://raw.githubusercontent.com/davidB/kubectl-view-allocations/master/scripts/getLatest.sh | bash 31 | ``` 32 | 33 | ### Via krew (kubectl plugin manager) 34 | 35 | [Krew – kubectl plugin manager](https://krew.sigs.k8s.io/) 36 | 37 | ```sh 38 | kubectl krew install view-allocations 39 | ``` 40 | 41 | ### Via cargo 42 | 43 | ```sh 44 | cargo install kubectl-view-allocations 45 | ``` 46 | 47 | ### As lib in Cargo.toml 48 | 49 | If you want to embed some function or struct of the plugin into an other rust code: 50 | 51 | ```toml 52 | [dependencies] 53 | kubectl-view-allocations = { version = "0.14", default-features = false } 54 | 55 | [features] 56 | default = ["k8s-openapi/v1_20"] 57 | ``` 58 | 59 | ## Usage 60 | 61 | ### Show help 62 | 63 | ```sh 64 | > kubectl-view-allocations -h 65 | kubectl plugin to list allocations (cpu, memory, gpu,...) X (utilization, requested, limit, allocatable,...) 66 | 67 | Usage: kubectl-view-allocations [OPTIONS] 68 | 69 | Options: 70 | --context 71 | The name of the kubeconfig context to use 72 | -n, --namespace 73 | Show only pods from this namespace 74 | -l, --selector 75 | Show only nodes match this label selector 76 | -u, --utilization 77 | Force to retrieve utilization (for cpu and memory), requires having metrics-server https://github.com/kubernetes-sigs/metrics-server 78 | -z, --show-zero 79 | Show lines with zero requested AND zero limit AND zero allocatable, OR pods with unset requested AND limit for `cpu` and `memory` 80 | --used-mode 81 | The way to compute the `used` part for free (`allocatable - used`) [default: max-request-limit] [possible values: max-request-limit, only-request] 82 | --precheck 83 | Pre-check access and refresh token on kubeconfig by running `kubectl cluster-info` 84 | --accept-invalid-certs 85 | Accept invalid certificates (dangerous) 86 | -r, --resource-name ... 87 | Filter resources shown by name(s), by default all resources are listed 88 | -g, --group-by ... 89 | Group information hierarchically (default: `-g resource -g node -g pod`) [possible values: resource, node, pod, namespace] 90 | -o, --output 91 | Output format [default: table] [possible values: table, csv] 92 | -h, --help 93 | Print help 94 | -V, --version 95 | Print version 96 | 97 | https://github.com/davidB/kubectl-view-allocations 98 | ``` 99 | 100 | ### Show gpu allocation 101 | 102 | ```sh 103 | 104 | > kubectl-view-allocations -r gpu 105 | 106 | Resource Requested Limit Allocatable Free 107 | nvidia.com/gpu (71%) 10.0 (71%) 10.0 14.0 4.0 108 | ├─ node-gpu1 (0%) __ (0%) __ 2.0 2.0 109 | ├─ node-gpu2 (0%) __ (0%) __ 2.0 2.0 110 | ├─ node-gpu3 (100%) 2.0 (100%) 2.0 2.0 __ 111 | │ └─ fah-gpu-cpu-d29sc 2.0 2.0 __ __ 112 | ├─ node-gpu4 (100%) 2.0 (100%) 2.0 2.0 __ 113 | │ └─ fah-gpu-cpu-hkg59 2.0 2.0 __ __ 114 | ├─ node-gpu5 (100%) 2.0 (100%) 2.0 2.0 __ 115 | │ └─ fah-gpu-cpu-nw9fc 2.0 2.0 __ __ 116 | ├─ node-gpu6 (100%) 2.0 (100%) 2.0 2.0 __ 117 | │ └─ fah-gpu-cpu-gtwsf 2.0 2.0 __ __ 118 | └─ node-gpu7 (100%) 2.0 (100%) 2.0 2.0 __ 119 | └─ fah-gpu-cpu-x7zfb 2.0 2.0 __ __ 120 | ``` 121 | 122 | ### Overview only 123 | 124 | ```sh 125 | > kubectl-view-allocations -g resource 126 | 127 | Resource Requested Limit Allocatable Free 128 | cpu (21%) 56.7 (65%) 176.1 272.0 95.9 129 | ephemeral-storage (0%) __ (0%) __ 38.4T 38.4T 130 | memory (8%) 52.7Gi (15%) 101.3Gi 675.6Gi 574.3Gi 131 | nvidia.com/gpu (71%) 10.0 (71%) 10.0 14.0 4.0 132 | pods (9%) 147.0 (9%) 147.0 1.6k 1.5k 133 | ``` 134 | 135 | ### Show utilization 136 | 137 | - Utilization information are retrieve from [metrics-server](https://github.com/kubernetes-incubator/metrics-server) (should be setup on your cluster). 138 | - Only report cpu and memory utilization 139 | 140 | ```sh 141 | > kubectl-view-allocations -u 142 | 143 | Resource Utilization Requested Limit Allocatable Free 144 | cpu (0%) 9.0m (10%) 200.0m __ 2.0 1.8 145 | └─ lima-rancher-desktop (0%) 9.0m (10%) 200.0m __ 2.0 1.8 146 | ├─ coredns-96cc4f57d-57cj9 1.0m 100.0m __ __ __ 147 | ├─ local-path-provisioner-84bb864455-czzcg 1.0m __ __ __ __ 148 | ├─ metrics-server-ff9dbcb6c-kb7x9 4.0m 100.0m __ __ __ 149 | ├─ svclb-traefik-ggd2q 2.0m __ __ __ __ 150 | └─ traefik-55fdc6d984-sqp57 1.0m __ __ __ __ 151 | ephemeral-storage __ __ __ 99.8G __ 152 | └─ lima-rancher-desktop __ __ __ 99.8G __ 153 | memory (1%) 51.0Mi (2%) 140.0Mi (3%) 170.0Mi 5.8Gi 5.6Gi 154 | └─ lima-rancher-desktop (1%) 51.0Mi (2%) 140.0Mi (3%) 170.0Mi 5.8Gi 5.6Gi 155 | ├─ coredns-96cc4f57d-57cj9 11.5Mi 70.0Mi 170.0Mi __ __ 156 | ├─ local-path-provisioner-84bb864455-czzcg 6.2Mi __ __ __ __ 157 | ├─ metrics-server-ff9dbcb6c-kb7x9 14.9Mi 70.0Mi __ __ __ 158 | ├─ svclb-traefik-ggd2q 548.0Ki __ __ __ __ 159 | └─ traefik-55fdc6d984-sqp57 17.9Mi __ __ __ __ 160 | pods __ (5%) 5.0 (5%) 5.0 110.0 105.0 161 | └─ lima-rancher-desktop __ (5%) 5.0 (5%) 5.0 110.0 105.0 162 | ``` 163 | 164 | ### Group by namespaces 165 | 166 | ```sh 167 | > kubectl-view-allocations -g namespace 168 | 169 | Resource Requested Limit Allocatable Free 170 | cpu (10%) 200.0m __ 2.0 1.8 171 | └─ kube-system 200.0m __ __ __ 172 | ephemeral-storage __ __ 99.8G __ 173 | memory (2%) 140.0Mi (3%) 170.0Mi 5.8Gi 5.6Gi 174 | └─ kube-system 140.0Mi 170.0Mi __ __ 175 | pods (5%) 5.0 (5%) 5.0 110.0 105.0 176 | └─ kube-system 5.0 5.0 __ __ 177 | ``` 178 | 179 | ### Show as csv 180 | 181 | In this case value as expanded as float (with 2 decimal) 182 | 183 | ```sh 184 | kubectl-view-allocations -o csv 185 | Date,Kind,resource,node,pod,Requested,%Requested,Limit,%Limit,Allocatable,Free 186 | 2020-08-19T19:12:48.326605746+00:00,resource,cpu,,,59.94,22%,106.10,39%,272.00,165.90 187 | 2020-08-19T19:12:48.326605746+00:00,node,cpu,node-gpu1,,2.31,19%,4.47,37%,12.00,7.53 188 | 2020-08-19T19:12:48.326605746+00:00,pod,cpu,node-gpu1,yyy-b8bd56fbd-5x8vq,1.00,,2.00,,, 189 | 2020-08-19T19:12:48.326605746+00:00,pod,cpu,node-gpu1,kube-flannel-ds-amd64-7dz9z,0.10,,0.10,,, 190 | 2020-08-19T19:12:48.326605746+00:00,pod,cpu,node-gpu1,node-exporter-gpu-b4w7s,0.11,,0.22,,, 191 | 2020-08-19T19:12:48.326605746+00:00,pod,cpu,node-gpu1,xxx-backend-7d84544458-46qnh,1.00,,2.00,,, 192 | 2020-08-19T19:12:48.326605746+00:00,pod,cpu,node-gpu1,weave-scope-agent-bbdnz,0.10,,0.15,,, 193 | 2020-08-19T19:12:48.326605746+00:00,node,cpu,node-gpu2,,0.31,1%,0.47,2%,24.00,23.53 194 | 2020-08-19T19:12:48.326605746+00:00,pod,cpu,node-gpu2,kube-flannel-ds-amd64-b5b4v,0.10,,0.10,,, 195 | 2020-08-19T19:12:48.326605746+00:00,pod,cpu,node-gpu2,node-exporter-gpu-796jz,0.11,,0.22,,, 196 | 2020-08-19T19:12:48.326605746+00:00,pod,cpu,node-gpu2,weave-scope-agent-8rhnd,0.10,,0.15,,, 197 | 2020-08-19T19:12:48.326605746+00:00,node,cpu,node-gpu3,,3.41,11%,6.67,21%,32.00,25.33 198 | ... 199 | ``` 200 | 201 | It can be combined with "group-by" options. 202 | 203 | ```sh 204 | kubectl-view-allocations -g resource -o csv 205 | Date,Kind,resource,Requested,%Requested,Limit,%Limit,Allocatable,Free 206 | 2020-08-19T19:11:49.630864028+00:00,resource,cpu,59.94,22%,106.10,39%,272.00,165.90 207 | 2020-08-19T19:11:49.630864028+00:00,resource,ephemeral-storage,0.00,0%,0.00,0%,34462898618662.00,34462898618662.00 208 | 2020-08-19T19:11:49.630864028+00:00,resource,hugepages-1Gi,0.00,,0.00,,, 209 | 2020-08-19T19:11:49.630864028+00:00,resource,hugepages-2Mi,0.00,,0.00,,, 210 | 2020-08-19T19:11:49.630864028+00:00,resource,memory,69063409664.00,10%,224684670976.00,31%,722318667776.00,497633996800.00 211 | 2020-08-19T19:11:49.630864028+00:00,resource,nvidia.com/gpu,3.00,27%,3.00,27%,11.00,8.00 212 | 2020-08-19T19:11:49.630864028+00:00,resource,pods,0.00,0%,0.00,0%,1540.00,1540.00 213 | ``` 214 | 215 | ## Alternatives & Similars 216 | 217 | - see the discussion [Need simple kubectl command to see cluster resource usage · Issue #17512 · kubernetes/kubernetes](https://github.com/kubernetes/kubernetes/issues/17512) 218 | - For CPU & Memory only 219 | - [ahmetb/kubectl-node\_resource: Query node allocations/utilization in kubectl](https://github.com/ahmetb/kubectl-node_resource) 220 | - [robscott/kube-capacity: A simple CLI that provides an overview of the resource requests, limits, and utilization in a Kubernetes cluster](https://github.com/robscott/kube-capacity), 221 | - [hjacobs/kube-resource-report: Report Kubernetes cluster and pod resource requests vs usage and generate static HTML](https://github.com/hjacobs/kube-resource-report) 222 | - [etopeter/kubectl-view-utilization: kubectl plugin to show cluster CPU and Memory requests utilization](https://github.com/etopeter/kubectl-view-utilization) 223 | - For CPU & Memory utilization only 224 | - `kubectl top pods` 225 | - [LeastAuthority/kubetop: A top(1)-like tool for Kubernetes.](https://github.com/LeastAuthority/kubetop) 226 | -------------------------------------------------------------------------------- /examples/pod_api.rs: -------------------------------------------------------------------------------- 1 | use k8s_openapi::api::core::v1::Pod; 2 | 3 | use kube::{ 4 | Client, 5 | api::{Api, ListParams}, 6 | }; 7 | 8 | #[tokio::main] 9 | async fn main() -> anyhow::Result<()> { 10 | //std::env::set_var("RUST_LOG", "info,kube=debug"); 11 | tracing_subscriber::fmt::init(); 12 | let client = Client::try_default().await?; 13 | let pods: Api = Api::all(client); 14 | // let pods: Api = Api::namespaced(client, "kube-system"); 15 | 16 | let lp = ListParams::default().timeout(10); 17 | let pods = pods.list(&lp).await?; 18 | 19 | eprintln!("pods: {:?}", pods); 20 | 21 | Ok(()) 22 | } 23 | -------------------------------------------------------------------------------- /justfile: -------------------------------------------------------------------------------- 1 | default: 2 | just --list 3 | 4 | k8s_create_kind: 5 | # k3d cluster create "$CLUSTER_NAME" --agents 2 6 | sudo systemctl start docker 7 | kind create cluster --name "$CLUSTER_NAME" 8 | kubectl cluster-info --context kind-"$CLUSTER_NAME" 9 | kubectl apply -f tests/metrics-server-components.yaml 10 | sleep 5 11 | kubectl top node 12 | cargo run 13 | 14 | k8s_delete_kind: 15 | # k3d cluster delete "$CLUSTER_NAME" 16 | kind delete cluster --name "$CLUSTER_NAME" 17 | 18 | # k8s_create_kwok_in_container: 19 | # cp tests/kube_config-kwokcontainer.yaml $HOME/.kube/config-kwokcontainer.yaml 20 | # kubectl config --kubeconfig=config-kwokcontainer use-context kwok 21 | # podman run --rm -it -p 8080:8080 registry.k8s.io/kwok/cluster:v0.4.0-k8s.v1.28.0 22 | 23 | k8s_create_kwok: 24 | # echo "require docker, with podman I got timeout on my machine" 25 | kwokctl create cluster --name="$CLUSTER_NAME" 26 | kwokctl get clusters 27 | kubectl cluster-info --context kwok-"$CLUSTER_NAME" 28 | kwokctl scale node --replicas 2 --name="$CLUSTER_NAME" 29 | kubectl get node 30 | kubectl create deployment pod --image=pod --replicas=5 31 | kubectl get pods -o wide 32 | echo "use '--accept-invalid-certs' with kube view-allocations" 33 | cargo run -- --accept-invalid-certs 34 | 35 | k8s_delete_kwok: 36 | kwokctl delete cluster --name="$CLUSTER_NAME" 37 | -------------------------------------------------------------------------------- /release.toml: -------------------------------------------------------------------------------- 1 | pre-release-commit-message = ":rocket: (cargo-release) version {{version}}" 2 | # post-release-commit-message = ":construction: (cargo-release) start next development iteration {{next_version}}" 3 | tag-prefix = "" 4 | tag-name = "{{prefix}}{{version}}" 5 | tag-message = ":bookmark: {{version}}" 6 | # dev-version-ext = "dev" 7 | # dev-version = true 8 | # pre-release-replacements = [ 9 | # {file="CHANGELOG.md", search="x.y.z-dev", replace="{{version}}"}, 10 | # {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}"} 11 | # ] 12 | -------------------------------------------------------------------------------- /scripts/getLatest.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # The install script is licensed under the CC-0 1.0 license. 4 | 5 | # See https://github.com/davidB/kubectl-view-allocations/blob/master/LICENSE for more details. 6 | # 7 | # To run this script execute: 8 | # `curl https://raw.githubusercontent.com/davidB/kubectl-view-allocations/master/scripts/getLatest.sh | sh` 9 | 10 | GITHUB_REPO="kubectl-view-allocations" 11 | GITHUB_USER="davidB" 12 | EXE_FILENAME="kubectl-view-allocations" 13 | 14 | bye() { 15 | result=$? 16 | if [ "$result" != "0" ]; then 17 | echo "Fail to install ${GITHUB_USER}/${GITHUB_REPO}" 18 | fi 19 | exit $result 20 | } 21 | 22 | fail() { 23 | echo "$1" 24 | exit 1 25 | } 26 | 27 | find_download_url() { 28 | local SUFFIX=$1 29 | URL=$(curl -s https://api.github.com/repos/${GITHUB_USER}/${GITHUB_REPO}/releases/latest | 30 | grep "browser_download_url.*${SUFFIX}" | 31 | cut -d : -f 2,3 | 32 | tr -d \" | 33 | head -n 1) 34 | echo "${URL//[[:space:]]/}" 35 | } 36 | 37 | find_arch() { 38 | ARCH=$(uname -m) 39 | case $ARCH in 40 | armv5*) ARCH="armv5" ;; 41 | armv6*) ARCH="armv6" ;; 42 | armv7*) ARCH="armv7" ;; 43 | aarch64) ARCH="arm64" ;; 44 | x86) ARCH="386" ;; 45 | # x86_64) ARCH="amd64";; 46 | i686) ARCH="386" ;; 47 | i386) ARCH="386" ;; 48 | esac 49 | echo $ARCH 50 | } 51 | 52 | find_os() { 53 | UNAME=$(uname) 54 | OS=$(echo "$UNAME" | tr '[:upper:]' '[:lower:]') 55 | 56 | case "$OS" in 57 | # Minimalist GNU for Windows 58 | mingw*) OS='windows' ;; 59 | msys*) OS='windows' ;; 60 | esac 61 | echo "$OS" 62 | } 63 | 64 | find_suffix() { 65 | local ARCH=$1 66 | local OS=$2 67 | local SUFFIX="$ARCH-$OS.tar.gz" 68 | case "$SUFFIX" in 69 | "x86_64-darwin.tar.gz") SUFFIX='x86_64-apple-darwin.tar.gz' ;; 70 | "arm64-darwin.tar.gz") SUFFIX='aarch64-apple-darwin.tar.gz' ;; 71 | "x86_64-linux.tar.gz") SUFFIX='x86_64-unknown-linux-musl.tar.gz' ;; 72 | "arm64-linux.tar.gz") SUFFIX='aarch64-unknown-linux-musl.tar.gz' ;; 73 | # "x86_64-windows.tar.gz") SUFFIX='x86_64-pc-windows-msvc.zip';; 74 | esac 75 | echo "$SUFFIX" 76 | } 77 | 78 | download_file() { 79 | local FILE_URL="$1" 80 | local FILE_PATH="$2" 81 | echo "Getting $FILE_URL" 82 | httpStatusCode=$(curl -s -w '%{http_code}' -L "$FILE_URL" -o "$FILE_PATH") 83 | if [ "$httpStatusCode" != 200 ]; then 84 | echo "failed to download '${URL}'" 85 | fail "Request fail with http status code $httpStatusCode" 86 | fi 87 | } 88 | 89 | find_exec_dest_path() { 90 | local DEST_DIR="/usr/local/bin" 91 | if [ ! -w $DEST_DIR ]; then 92 | DEST_DIR=$(pwd) 93 | fi 94 | echo "${DEST_DIR}" 95 | } 96 | 97 | install_file() { 98 | local FILE_PATH=$1 99 | local EXE_DEST_FILE=$2 100 | TMP_DIR="/tmp/${GITHUB_USER}_${GITHUB_REPO}" 101 | mkdir -p "$TMP_DIR" || true 102 | tar xf "$FILE_PATH" -C "$TMP_DIR" 103 | if [ -f "$TMP_DIR/${EXE_FILENAME}" ]; then 104 | cp "$TMP_DIR/${EXE_FILENAME}" "${EXE_DEST_FILE}" 105 | else 106 | for dir in "$TMP_DIR"/*/; do 107 | if [ -f "$dir${EXE_FILENAME}" ]; then 108 | cp "$dir${EXE_FILENAME}" "${EXE_DEST_FILE}" 109 | break 110 | fi 111 | done 112 | fi 113 | 114 | chmod +x "${EXE_DEST_FILE}" 115 | rm -rf "$TMP_DIR" 116 | } 117 | 118 | main() { 119 | EXE_DEST_DIR=$(find_exec_dest_path) 120 | EXE_DEST_FILE="${EXE_DEST_DIR}/${EXE_FILENAME}" 121 | ARCH=$(find_arch) 122 | OS=$(find_os) 123 | SUFFIX=$(find_suffix "$ARCH" "$OS") 124 | FILE_URL=$(find_download_url "$SUFFIX") 125 | FILE_PATH="/tmp/${GITHUB_USER}-${GITHUB_REPO}-latest-${SUFFIX}" 126 | if [ -z "${FILE_URL}" ]; then 127 | fail "Did not find a release for your system: $OS $ARCH" 128 | fi 129 | download_file "${FILE_URL}" "${FILE_PATH}" 130 | install_file "${FILE_PATH}" "${EXE_DEST_FILE}" 131 | rm -Rf "${FILE_PATH}" 132 | echo "executable installed at ${EXE_DEST_FILE}" 133 | bye 134 | } 135 | 136 | #Stop execution on any error 137 | trap "bye" EXIT 138 | set -e 139 | # set -x 140 | main 141 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod metrics; 2 | pub mod qty; 3 | pub mod tree; 4 | 5 | // mod human_format; 6 | use chrono::prelude::*; 7 | use clap::{Parser, ValueEnum}; 8 | use core::convert::TryFrom; 9 | use itertools::Itertools; 10 | use k8s_openapi::api::core::v1::{Node, Pod}; 11 | use kube::api::{Api, ListParams, ObjectList}; 12 | #[cfg(feature = "prettytable")] 13 | use prettytable::{Cell, Row, Table, format, row}; 14 | use qty::Qty; 15 | use std::collections::BTreeMap; 16 | use std::str::FromStr; 17 | use tracing::{info, instrument, warn}; 18 | 19 | #[derive(thiserror::Error, Debug)] 20 | pub enum Error { 21 | #[error("Failed to run '{cmd}'")] 22 | CmdError { 23 | cmd: String, 24 | output: Option, 25 | source: Option, 26 | }, 27 | 28 | #[error("Failed to read Qty of location {location:?} / {qualifier:?} {kind}={input}")] 29 | ResourceQtyParseError { 30 | location: Location, 31 | qualifier: ResourceQualifier, 32 | kind: String, 33 | input: String, 34 | source: qty::Error, 35 | }, 36 | 37 | #[error("Failed to process Qty")] 38 | QtyError { 39 | #[from] 40 | source: qty::Error, 41 | }, 42 | 43 | #[error("Failed to {context}")] 44 | KubeError { 45 | context: String, 46 | source: kube::Error, 47 | }, 48 | 49 | #[error("Failed to {context}")] 50 | KubeConfigError { 51 | context: String, 52 | source: kube::config::KubeconfigError, 53 | }, 54 | 55 | #[error("Failed to {context}")] 56 | KubeInferConfigError { 57 | context: String, 58 | source: kube::config::InferConfigError, 59 | }, 60 | } 61 | 62 | #[derive(Debug, Clone, Default)] 63 | pub struct Location { 64 | pub node_name: String, 65 | pub namespace: Option, 66 | pub pod_name: Option, 67 | } 68 | 69 | #[derive(Debug, Clone)] 70 | pub struct Resource { 71 | pub kind: String, 72 | pub quantity: Qty, 73 | pub location: Location, 74 | pub qualifier: ResourceQualifier, 75 | } 76 | 77 | #[derive(Debug, Clone)] 78 | pub enum ResourceQualifier { 79 | Limit, 80 | Requested, 81 | Allocatable, 82 | Utilization, 83 | // HACK special qualifier, used to show zero/undef cpu & memory 84 | Present, 85 | } 86 | 87 | #[derive(Debug, Clone, Default)] 88 | pub struct QtyByQualifier { 89 | pub limit: Option, 90 | pub requested: Option, 91 | pub allocatable: Option, 92 | pub utilization: Option, 93 | pub present: Option, 94 | } 95 | 96 | fn add(lhs: Option, rhs: &Qty) -> Option { 97 | lhs.map(|l| &l + rhs).or_else(|| Some(rhs.clone())) 98 | } 99 | 100 | impl QtyByQualifier { 101 | pub fn calc_free(&self, used_mode: UsedMode) -> Option { 102 | let total_used = match used_mode { 103 | UsedMode::max_request_limit => { 104 | std::cmp::max(self.limit.as_ref(), self.requested.as_ref()) 105 | } 106 | UsedMode::only_request => self.requested.as_ref(), 107 | }; 108 | self.allocatable 109 | .as_ref() 110 | .zip(total_used) 111 | .map(|(allocatable, total_used)| { 112 | if allocatable > total_used { 113 | allocatable - total_used 114 | } else { 115 | Qty::default() 116 | } 117 | }) 118 | } 119 | } 120 | 121 | pub fn sum_by_qualifier(rsrcs: &[&Resource]) -> Option { 122 | if !rsrcs.is_empty() { 123 | let kind = rsrcs 124 | .get(0) 125 | .expect("group contains at least 1 element") 126 | .kind 127 | .clone(); 128 | 129 | if rsrcs.iter().all(|i| i.kind == kind) { 130 | let sum = rsrcs.iter().fold(QtyByQualifier::default(), |mut acc, v| { 131 | match &v.qualifier { 132 | ResourceQualifier::Limit => acc.limit = add(acc.limit, &v.quantity), 133 | ResourceQualifier::Requested => acc.requested = add(acc.requested, &v.quantity), 134 | ResourceQualifier::Allocatable => { 135 | acc.allocatable = add(acc.allocatable, &v.quantity) 136 | } 137 | ResourceQualifier::Utilization => { 138 | acc.utilization = add(acc.utilization, &v.quantity) 139 | } 140 | ResourceQualifier::Present => acc.present = add(acc.present, &v.quantity), 141 | }; 142 | acc 143 | }); 144 | Some(sum) 145 | } else { 146 | None 147 | } 148 | } else { 149 | None 150 | } 151 | } 152 | 153 | pub fn make_qualifiers( 154 | rsrcs: &[Resource], 155 | group_by: &[GroupBy], 156 | resource_names: &[String], 157 | ) -> Vec<(Vec, Option)> { 158 | let group_by_fct = group_by.iter().map(GroupBy::to_fct).collect::>(); 159 | let mut out = make_group_x_qualifier( 160 | &(rsrcs 161 | .iter() 162 | .filter(|a| accept_resource(&a.kind, resource_names)) 163 | .collect::>()), 164 | &[], 165 | &group_by_fct, 166 | 0, 167 | ); 168 | out.sort_by_key(|i| i.0.clone()); 169 | out 170 | } 171 | 172 | fn make_group_x_qualifier( 173 | rsrcs: &[&Resource], 174 | prefix: &[String], 175 | group_by_fct: &[fn(&Resource) -> Option], 176 | group_by_depth: usize, 177 | ) -> Vec<(Vec, Option)> { 178 | // Note: The `&` is significant here, `GroupBy` is iterable 179 | // only by reference. You can also call `.into_iter()` explicitly. 180 | let mut out = vec![]; 181 | if let Some(group_by) = group_by_fct.get(group_by_depth) { 182 | for (key, group) in rsrcs 183 | .iter() 184 | .filter_map(|e| group_by(e).map(|k| (k, *e))) 185 | .into_group_map() 186 | { 187 | let mut key_full = prefix.to_vec(); 188 | key_full.push(key); 189 | let children = 190 | make_group_x_qualifier(&group, &key_full, group_by_fct, group_by_depth + 1); 191 | out.push((key_full, sum_by_qualifier(&group))); 192 | out.extend(children); 193 | } 194 | } 195 | // let kg = &rsrcs.into_iter().group_by(|v| v.kind); 196 | // kg.into_iter().map(|(key, group)| ).collect() 197 | out 198 | } 199 | 200 | fn accept_resource(name: &str, resource_filter: &[String]) -> bool { 201 | resource_filter.is_empty() || resource_filter.iter().any(|x| name.contains(x)) 202 | } 203 | 204 | #[instrument(skip(client, resources))] 205 | pub async fn collect_from_nodes( 206 | client: kube::Client, 207 | resources: &mut Vec, 208 | selector: &Option, 209 | ) -> Result, Error> { 210 | let api_nodes: Api = Api::all(client); 211 | let mut lp = ListParams::default(); 212 | if let Some(labels) = &selector { 213 | lp = lp.labels(labels); 214 | } 215 | let nodes = api_nodes 216 | .list(&lp) 217 | .await 218 | .map_err(|source| Error::KubeError { 219 | context: "list nodes".to_string(), 220 | source, 221 | })? 222 | .items; 223 | let node_names = nodes 224 | .iter() 225 | .filter_map(|node| node.metadata.name.clone()) 226 | .collect(); 227 | extract_allocatable_from_nodes(nodes, resources).await?; 228 | Ok(node_names) 229 | } 230 | 231 | #[instrument(skip(node_list, resources))] 232 | pub async fn extract_allocatable_from_nodes( 233 | node_list: Vec, 234 | resources: &mut Vec, 235 | ) -> Result<(), Error> { 236 | for node in node_list { 237 | let location = Location { 238 | node_name: node.metadata.name.unwrap_or_default(), 239 | ..Location::default() 240 | }; 241 | if let Some(als) = node.status.and_then(|v| v.allocatable) { 242 | // add_resource(resources, &location, ResourceUsage::Allocatable, &als)? 243 | for (kind, value) in als.iter() { 244 | let quantity = 245 | Qty::from_str(&(value).0).map_err(|source| Error::ResourceQtyParseError { 246 | location: location.clone(), 247 | qualifier: ResourceQualifier::Allocatable, 248 | kind: kind.to_string(), 249 | input: value.0.to_string(), 250 | source, 251 | })?; 252 | resources.push(Resource { 253 | kind: kind.clone(), 254 | qualifier: ResourceQualifier::Allocatable, 255 | quantity, 256 | location: location.clone(), 257 | }); 258 | } 259 | } 260 | } 261 | Ok(()) 262 | } 263 | 264 | /* 265 | The phase of a Pod is a simple, high-level summary of where the Pod is in its lifecycle. The conditions array, the reason and message fields, and the individual container status arrays contain more detail about the pod's status. 266 | 267 | There are five possible phase values: 268 | Pending: The pod has been accepted by the Kubernetes system, but one or more of the container images has not been created. This includes time before being scheduled as well as time spent downloading images over the network, which could take a while. 269 | Running: The pod has been bound to a node, and all of the containers have been created. At least one container is still running, or is in the process of starting or restarting. 270 | Succeeded: All containers in the pod have terminated in success, and will not be restarted. 271 | Failed: All containers in the pod have terminated, and at least one container has terminated in failure. The container either exited with non-zero status or was terminated by the system. 272 | Unknown: For some reason the state of the pod could not be obtained, typically due to an error in communicating with the host of the pod. 273 | 274 | More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-phase 275 | */ 276 | 277 | pub fn is_scheduled(pod: &Pod) -> bool { 278 | pod.status 279 | .as_ref() 280 | .and_then(|ps| { 281 | ps.phase.as_ref().and_then(|phase| { 282 | match &phase[..] { 283 | "Succeeded" | "Failed" => Some(false), 284 | "Running" => Some(true), 285 | "Unknown" => None, // this is the case when a node is down (kubelet is not responding) 286 | "Pending" => ps.conditions.as_ref().map(|o| { 287 | o.iter() 288 | .any(|c| c.type_ == "PodScheduled" && c.status == "True") 289 | }), 290 | &_ => None, // should not happen 291 | } 292 | }) 293 | }) 294 | .unwrap_or(false) 295 | } 296 | 297 | fn push_resources( 298 | resources: &mut Vec, 299 | location: &Location, 300 | qualifier: ResourceQualifier, 301 | resource_list: &BTreeMap, 302 | ) -> Result<(), Error> { 303 | for (key, quantity) in resource_list.iter() { 304 | resources.push(Resource { 305 | kind: key.clone(), 306 | qualifier: qualifier.clone(), 307 | quantity: quantity.clone(), 308 | location: location.clone(), 309 | }); 310 | } 311 | // add a "pods" resource as well 312 | resources.push(Resource { 313 | kind: "pods".to_string(), 314 | qualifier, 315 | quantity: Qty::from_str("1")?, 316 | location: location.clone(), 317 | }); 318 | Ok(()) 319 | } 320 | 321 | fn process_resources( 322 | effective_resources: &mut BTreeMap, 323 | resource_list: &BTreeMap, 324 | op: F, 325 | ) -> Result<(), Error> 326 | where 327 | F: Fn(Qty, Qty) -> Qty, 328 | { 329 | for (key, value) in resource_list.iter() { 330 | let quantity = Qty::from_str(&(value).0)?; 331 | if let Some(current_quantity) = effective_resources.get_mut(key) { 332 | *current_quantity = op(current_quantity.clone(), quantity).clone(); 333 | } else { 334 | effective_resources.insert(key.clone(), quantity.clone()); 335 | } 336 | } 337 | Ok(()) 338 | } 339 | 340 | #[instrument(skip(client, resources))] 341 | pub async fn collect_from_pods( 342 | client: kube::Client, 343 | resources: &mut Vec, 344 | namespace: &Option, 345 | selected_node_names: &[String], 346 | ) -> Result<(), Error> { 347 | let api_pods: Api = if let Some(ns) = namespace { 348 | Api::namespaced(client, ns) 349 | } else { 350 | Api::all(client) 351 | }; 352 | let pods = api_pods 353 | .list(&ListParams::default()) 354 | .await 355 | .map_err(|source| Error::KubeError { 356 | context: "list pods".to_string(), 357 | source, 358 | })? 359 | .items; 360 | extract_allocatable_from_pods(pods, resources, selected_node_names).await?; 361 | Ok(()) 362 | } 363 | 364 | #[instrument(skip(pod_list, resources))] 365 | pub async fn extract_allocatable_from_pods( 366 | pod_list: Vec, 367 | resources: &mut Vec, 368 | selected_node_names: &[String], 369 | ) -> Result<(), Error> { 370 | for pod in pod_list.into_iter().filter(is_scheduled) { 371 | let spec = pod.spec.as_ref(); 372 | let node_name = spec.and_then(|s| s.node_name.clone()).unwrap_or_default(); 373 | if !selected_node_names.contains(&node_name) { 374 | continue; 375 | } 376 | let metadata = &pod.metadata; 377 | let location = Location { 378 | node_name: node_name.clone(), 379 | namespace: metadata.namespace.clone(), 380 | pod_name: metadata.name.clone(), 381 | }; 382 | // compute the effective resource qualifier 383 | // see https://kubernetes.io/docs/concepts/workloads/pods/init-containers/#resources 384 | let mut resource_requests: BTreeMap = BTreeMap::new(); 385 | let mut resource_limits: BTreeMap = BTreeMap::new(); 386 | // handle regular containers 387 | let containers = spec.map(|s| s.containers.clone()).unwrap_or_default(); 388 | for container in containers.into_iter() { 389 | if let Some(requirements) = container.resources { 390 | if let Some(r) = requirements.requests { 391 | process_resources(&mut resource_requests, &r, std::ops::Add::add)?; 392 | } 393 | if let Some(r) = requirements.limits { 394 | process_resources(&mut resource_limits, &r, std::ops::Add::add)?; 395 | } 396 | } 397 | } 398 | // handle initContainers 399 | let init_containers = spec 400 | .and_then(|s| s.init_containers.clone()) 401 | .unwrap_or_default(); 402 | for container in init_containers.into_iter() { 403 | if let Some(requirements) = container.resources { 404 | if let Some(r) = requirements.requests { 405 | process_resources(&mut resource_requests, &r, std::cmp::max)?; 406 | } 407 | if let Some(r) = requirements.limits { 408 | process_resources(&mut resource_limits, &r, std::cmp::max)?; 409 | } 410 | } 411 | } 412 | // handler overhead (add to both requests and limits) 413 | if let Some(ref overhead) = spec.and_then(|s| s.overhead.clone()) { 414 | process_resources(&mut resource_requests, overhead, std::ops::Add::add)?; 415 | process_resources(&mut resource_limits, overhead, std::ops::Add::add)?; 416 | } 417 | // push these onto resources 418 | push_resources( 419 | resources, 420 | &location, 421 | ResourceQualifier::Requested, 422 | &resource_requests, 423 | )?; 424 | push_resources( 425 | resources, 426 | &location, 427 | ResourceQualifier::Limit, 428 | &resource_limits, 429 | )?; 430 | // HACK add zero/None cpu & memory, to allow show-zero to display them 431 | resources.push(Resource { 432 | kind: "cpu".to_string(), 433 | qualifier: ResourceQualifier::Present, 434 | quantity: Qty::zero(), 435 | location: location.clone(), 436 | }); 437 | resources.push(Resource { 438 | kind: "memory".to_string(), 439 | qualifier: ResourceQualifier::Present, 440 | quantity: Qty::zero(), 441 | location: location.clone(), 442 | }); 443 | } 444 | Ok(()) 445 | } 446 | 447 | pub fn extract_locations( 448 | resources: &[Resource], 449 | ) -> std::collections::HashMap<(String, String), Location> { 450 | resources 451 | .iter() 452 | .filter_map(|resource| { 453 | let loc = &resource.location; 454 | loc.pod_name.as_ref().map(|n| { 455 | ( 456 | (loc.namespace.clone().unwrap_or_default(), n.to_owned()), 457 | loc.clone(), 458 | ) 459 | }) 460 | }) 461 | .collect() 462 | } 463 | 464 | //TODO need location of pods (aka node because its not part of metrics) 465 | //TODO filter to only retreive info from node's selector 466 | #[instrument(skip(client, resources))] 467 | pub async fn collect_from_metrics( 468 | client: kube::Client, 469 | resources: &mut Vec, 470 | ) -> Result<(), Error> { 471 | let api_pod_metrics: Api = Api::all(client); 472 | let pod_metrics = api_pod_metrics 473 | .list(&ListParams::default()) 474 | .await 475 | .map_err(|source| Error::KubeError { 476 | context: "list podmetrics, maybe Metrics API not available".to_string(), 477 | source, 478 | })?; 479 | 480 | extract_utilizations_from_pod_metrics(pod_metrics, resources).await?; 481 | Ok(()) 482 | } 483 | 484 | #[instrument(skip(pod_metrics, resources))] 485 | pub async fn extract_utilizations_from_pod_metrics( 486 | pod_metrics: ObjectList, 487 | resources: &mut Vec, 488 | ) -> Result<(), Error> { 489 | let cpu_kind = "cpu"; 490 | let memory_kind = "memory"; 491 | let locations = extract_locations(resources); 492 | for pod_metric in pod_metrics.items { 493 | let metadata = &pod_metric.metadata; 494 | let key = ( 495 | metadata.namespace.clone().unwrap_or_default(), 496 | metadata.name.clone().unwrap_or_default(), 497 | ); 498 | let location = locations.get(&key).cloned().unwrap_or_else(|| Location { 499 | // node_name: node_name.clone(), 500 | namespace: metadata.namespace.clone(), 501 | pod_name: metadata.name.clone(), 502 | ..Location::default() 503 | }); 504 | let mut cpu_utilization = Qty::default(); 505 | let mut memory_utilization = Qty::default(); 506 | for container in pod_metric.containers.into_iter() { 507 | cpu_utilization += &Qty::from_str(&container.usage.cpu) 508 | .map_err(|source| Error::ResourceQtyParseError { 509 | location: location.clone(), 510 | qualifier: ResourceQualifier::Utilization, 511 | kind: cpu_kind.to_string(), 512 | input: container.usage.cpu.clone(), 513 | source, 514 | })? 515 | .max(Qty::lowest_positive()); 516 | memory_utilization += &Qty::from_str(&container.usage.memory) 517 | .map_err(|source| Error::ResourceQtyParseError { 518 | location: location.clone(), 519 | qualifier: ResourceQualifier::Utilization, 520 | kind: memory_kind.to_string(), 521 | input: container.usage.memory.clone(), 522 | source, 523 | })? 524 | .max(Qty::lowest_positive()); 525 | } 526 | resources.push(Resource { 527 | kind: cpu_kind.to_string(), 528 | qualifier: ResourceQualifier::Utilization, 529 | quantity: cpu_utilization, 530 | location: location.clone(), 531 | }); 532 | resources.push(Resource { 533 | kind: memory_kind.to_string(), 534 | qualifier: ResourceQualifier::Utilization, 535 | quantity: memory_utilization, 536 | location: location.clone(), 537 | }); 538 | } 539 | Ok(()) 540 | } 541 | 542 | #[derive(Debug, Eq, PartialEq, ValueEnum, Clone)] 543 | #[allow(non_camel_case_types)] 544 | pub enum GroupBy { 545 | resource, 546 | node, 547 | pod, 548 | namespace, 549 | } 550 | 551 | impl GroupBy { 552 | pub fn to_fct(&self) -> fn(&Resource) -> Option { 553 | match self { 554 | Self::resource => Self::extract_kind, 555 | Self::node => Self::extract_node_name, 556 | Self::pod => Self::extract_pod_name, 557 | Self::namespace => Self::extract_namespace, 558 | } 559 | } 560 | 561 | fn extract_kind(e: &Resource) -> Option { 562 | Some(e.kind.clone()) 563 | } 564 | 565 | fn extract_node_name(e: &Resource) -> Option { 566 | Some(e.location.node_name.to_string()).filter(|s| !s.is_empty()) 567 | } 568 | 569 | fn extract_pod_name(e: &Resource) -> Option { 570 | // We do not need to display "pods" resource types when grouping by pods 571 | if e.kind == "pods" { 572 | return None; 573 | } 574 | e.location.pod_name.clone() 575 | } 576 | 577 | fn extract_namespace(e: &Resource) -> Option { 578 | e.location.namespace.clone() 579 | } 580 | } 581 | 582 | impl std::fmt::Display for GroupBy { 583 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 584 | let s = match self { 585 | Self::resource => "resource", 586 | Self::node => "node", 587 | Self::pod => "pod", 588 | Self::namespace => "namespace", 589 | }; 590 | f.write_str(s) 591 | } 592 | } 593 | 594 | #[derive(Debug, Eq, PartialEq, ValueEnum, Clone, Copy, Default)] 595 | #[allow(non_camel_case_types)] 596 | pub enum Output { 597 | #[default] 598 | table, 599 | csv, 600 | } 601 | 602 | #[derive(Debug, Eq, PartialEq, ValueEnum, Clone, Copy, Default)] 603 | #[allow(non_camel_case_types)] 604 | pub enum UsedMode { 605 | #[default] 606 | max_request_limit, 607 | only_request, 608 | } 609 | 610 | #[derive(Parser, Debug)] 611 | #[command( 612 | version, about, 613 | after_help(env!("CARGO_PKG_HOMEPAGE")), 614 | propagate_version = true 615 | )] 616 | pub struct CliOpts { 617 | /// The name of the kubeconfig context to use 618 | #[arg(long, value_parser)] 619 | pub context: Option, 620 | 621 | /// Show only pods from this namespace 622 | #[arg(short, long, value_parser)] 623 | pub namespace: Option, 624 | 625 | /// Show only nodes match this label selector 626 | #[arg(short = 'l', long, value_parser)] 627 | pub selector: Option, 628 | 629 | /// Force to retrieve utilization (for cpu and memory), requires 630 | /// having metrics-server https://github.com/kubernetes-sigs/metrics-server 631 | #[arg(short = 'u', long, value_parser)] 632 | pub utilization: bool, 633 | 634 | /// Show lines with zero requested AND zero limit AND zero allocatable, 635 | /// OR pods with unset requested AND limit for `cpu` and `memory` 636 | #[arg(short = 'z', long, value_parser)] 637 | pub show_zero: bool, 638 | 639 | /// The way to compute the `used` part for free (`allocatable - used`) 640 | #[arg( 641 | long, 642 | value_enum, 643 | ignore_case = true, 644 | default_value = "max-request-limit", 645 | value_parser 646 | )] 647 | pub used_mode: UsedMode, 648 | 649 | /// Pre-check access and refresh token on kubeconfig by running `kubectl cluster-info` 650 | #[arg(long, value_parser)] 651 | pub precheck: bool, 652 | 653 | /// Accept invalid certificates (dangerous) 654 | #[arg(long, value_parser)] 655 | pub accept_invalid_certs: bool, 656 | 657 | /// Filter resources shown by name(s), by default all resources are listed 658 | #[arg(short, long, value_parser, value_delimiter= ',', num_args = 1..)] 659 | pub resource_name: Vec, 660 | 661 | /// Group information hierarchically (default: `-g resource -g node -g pod`) 662 | #[arg(short, long, value_enum, ignore_case = true, value_parser, value_delimiter= ',', num_args = 1..)] 663 | pub group_by: Vec, 664 | 665 | /// Output format 666 | #[arg( 667 | short, 668 | long, 669 | value_enum, 670 | ignore_case = true, 671 | default_value = "table", 672 | value_parser 673 | )] 674 | pub output: Output, 675 | } 676 | 677 | pub async fn refresh_kube_config(cli_opts: &CliOpts) -> Result<(), Error> { 678 | //HACK force refresh token by calling "kubectl cluster-info before loading configuration" 679 | use std::process::Command; 680 | let mut cmd = Command::new("kubectl"); 681 | cmd.arg("cluster-info"); 682 | if let Some(ref context) = cli_opts.context { 683 | cmd.arg("--context").arg(context); 684 | } 685 | let output = cmd.output().map_err(|source| Error::CmdError { 686 | cmd: "kubectl cluster-info".to_owned(), 687 | output: None, 688 | source: Some(source), 689 | })?; 690 | if !output.status.success() { 691 | return Err(Error::CmdError { 692 | cmd: "kubectl cluster-info".to_owned(), 693 | output: Some(output), 694 | source: None, 695 | }); 696 | } 697 | Ok(()) 698 | } 699 | 700 | pub async fn new_client(cli_opts: &CliOpts) -> Result { 701 | if cli_opts.precheck { 702 | refresh_kube_config(cli_opts).await?; 703 | } 704 | let mut client_config = match cli_opts.context { 705 | Some(ref context) => kube::Config::from_kubeconfig(&kube::config::KubeConfigOptions { 706 | context: Some(context.clone()), 707 | ..Default::default() 708 | }) 709 | .await 710 | .map_err(|source| Error::KubeConfigError { 711 | context: "create the kube client config".to_string(), 712 | source, 713 | })?, 714 | None => kube::Config::infer() 715 | .await 716 | .map_err(|source| Error::KubeInferConfigError { 717 | context: "create the kube client config".to_string(), 718 | source, 719 | })?, 720 | }; 721 | info!(cluster_url = client_config.cluster_url.to_string().as_str()); 722 | client_config.accept_invalid_certs = cli_opts.accept_invalid_certs; 723 | kube::Client::try_from(client_config).map_err(|source| Error::KubeError { 724 | context: "create the kube client".to_string(), 725 | source, 726 | }) 727 | } 728 | 729 | #[instrument] 730 | pub async fn do_main(cli_opts: &CliOpts) -> Result<(), Error> { 731 | let client = new_client(cli_opts).await?; 732 | let mut resources: Vec = vec![]; 733 | let node_names = collect_from_nodes(client.clone(), &mut resources, &cli_opts.selector).await?; 734 | collect_from_pods( 735 | client.clone(), 736 | &mut resources, 737 | &cli_opts.namespace, 738 | &node_names, 739 | ) 740 | .await?; 741 | 742 | let show_utilization = if cli_opts.utilization { 743 | match collect_from_metrics(client.clone(), &mut resources).await { 744 | Ok(_) => true, 745 | Err(err) => { 746 | warn!(?err); 747 | false 748 | } 749 | } 750 | } else { 751 | false 752 | }; 753 | 754 | let res = make_qualifiers(&resources, &cli_opts.group_by, &cli_opts.resource_name); 755 | match &cli_opts.output { 756 | Output::table => display_with_prettytable( 757 | &res, 758 | !&cli_opts.show_zero, 759 | show_utilization, 760 | cli_opts.used_mode, 761 | ), 762 | Output::csv => display_as_csv( 763 | &res, 764 | &cli_opts.group_by, 765 | show_utilization, 766 | cli_opts.used_mode, 767 | ), 768 | } 769 | Ok(()) 770 | } 771 | 772 | pub fn display_as_csv( 773 | data: &[(Vec, Option)], 774 | group_by: &[GroupBy], 775 | show_utilization: bool, 776 | used_mode: UsedMode, 777 | ) { 778 | // print header 779 | println!( 780 | "Date,Kind,{}{},Requested,%Requested,Limit,%Limit,Allocatable,Free", 781 | group_by.iter().map(|x| x.to_string()).join(","), 782 | if show_utilization { 783 | ",Utilization,%Utilization" 784 | } else { 785 | "" 786 | } 787 | ); 788 | 789 | // print data 790 | let empty = "".to_string(); 791 | let datetime = Utc::now().to_rfc3339(); 792 | for (k, oqtys) in data { 793 | if let Some(qtys) = oqtys { 794 | let mut row = vec![ 795 | datetime.clone(), 796 | group_by 797 | .get(k.len() - 1) 798 | .map(|x| x.to_string()) 799 | .unwrap_or_else(|| empty.clone()), 800 | ]; 801 | for i in 0..group_by.len() { 802 | row.push(k.get(i).cloned().unwrap_or_else(|| empty.clone())); 803 | } 804 | 805 | if show_utilization { 806 | add_cells_for_cvs(&mut row, &qtys.utilization, &qtys.allocatable); 807 | } 808 | add_cells_for_cvs(&mut row, &qtys.requested, &qtys.allocatable); 809 | add_cells_for_cvs(&mut row, &qtys.limit, &qtys.allocatable); 810 | 811 | row.push( 812 | qtys.allocatable 813 | .as_ref() 814 | .map(|qty| format!("{:.2}", f64::from(qty))) 815 | .unwrap_or_else(|| empty.clone()), 816 | ); 817 | row.push( 818 | qtys.calc_free(used_mode) 819 | .as_ref() 820 | .map(|qty| format!("{:.2}", f64::from(qty))) 821 | .unwrap_or_else(|| empty.clone()), 822 | ); 823 | println!("{}", &row.join(",")); 824 | } 825 | } 826 | } 827 | 828 | fn add_cells_for_cvs(row: &mut Vec, oqty: &Option, o100: &Option) { 829 | match oqty { 830 | None => { 831 | row.push("".to_string()); 832 | row.push("".to_string()); 833 | } 834 | Some(qty) => { 835 | row.push(format!("{:.2}", f64::from(qty))); 836 | row.push(match o100 { 837 | None => "".to_string(), 838 | Some(q100) => format!("{:.0}%", qty.calc_percentage(q100)), 839 | }); 840 | } 841 | }; 842 | } 843 | 844 | #[cfg(not(feature = "prettytable"))] 845 | pub fn display_with_prettytable( 846 | _data: &[(Vec, Option)], 847 | _filter_full_zero: bool, 848 | _show_utilization: bool, 849 | _used_mode: UsedMode, 850 | ) { 851 | warn!("feature 'prettytable' not enabled"); 852 | } 853 | 854 | #[cfg(feature = "prettytable")] 855 | pub fn display_with_prettytable( 856 | data: &[(Vec, Option)], 857 | filter_full_zero: bool, 858 | show_utilization: bool, 859 | used_mode: UsedMode, 860 | ) { 861 | // Create the table 862 | let mut table = Table::new(); 863 | let format = format::FormatBuilder::new() 864 | // .column_separator('|') 865 | // .borders('|') 866 | // .separators(&[format::LinePosition::Top, 867 | // format::LinePosition::Bottom], 868 | // format::LineSeparator::new('-', '+', '+', '+')) 869 | .separators(&[], format::LineSeparator::new('-', '+', '+', '+')) 870 | .padding(1, 1) 871 | .build(); 872 | table.set_format(format); 873 | let mut row_titles = row![bl->"Resource", br->"Utilization", br->"Requested", br->"Limit", br->"Allocatable", br->"Free"]; 874 | if !show_utilization { 875 | row_titles.remove_cell(1); 876 | } 877 | table.set_titles(row_titles); 878 | let data2 = data 879 | .iter() 880 | .filter(|d| { 881 | !filter_full_zero 882 | || !d 883 | .1 884 | .as_ref() 885 | .map(|x| { 886 | x.utilization.is_none() 887 | && is_empty(&x.requested) 888 | && is_empty(&x.limit) 889 | && is_empty(&x.allocatable) 890 | }) 891 | .unwrap_or(false) 892 | }) 893 | .collect::>(); 894 | let prefixes = tree::provide_prefix(&data2, |parent, item| parent.0.len() + 1 == item.0.len()); 895 | 896 | for ((k, oqtys), prefix) in data2.iter().zip(prefixes.iter()) { 897 | let column0 = format!( 898 | "{} {}", 899 | prefix, 900 | k.last().map(|x| x.as_str()).unwrap_or("???") 901 | ); 902 | if let Some(qtys) = oqtys { 903 | let style = if qtys.requested > qtys.limit 904 | || qtys.utilization > qtys.limit 905 | || is_empty(&qtys.requested) 906 | || is_empty(&qtys.limit) 907 | { 908 | "rFy" 909 | } else { 910 | "rFg" 911 | }; 912 | let mut row = Row::new(vec![ 913 | Cell::new(&column0), 914 | make_cell_for_prettytable(&qtys.utilization, &qtys.allocatable).style_spec(style), 915 | make_cell_for_prettytable(&qtys.requested, &qtys.allocatable).style_spec(style), 916 | make_cell_for_prettytable(&qtys.limit, &qtys.allocatable).style_spec(style), 917 | make_cell_for_prettytable(&qtys.allocatable, &None).style_spec(style), 918 | make_cell_for_prettytable(&qtys.calc_free(used_mode), &None).style_spec(style), 919 | ]); 920 | if !show_utilization { 921 | row.remove_cell(1); 922 | } 923 | table.add_row(row); 924 | } else { 925 | table.add_row(Row::new(vec![Cell::new(&column0)])); 926 | } 927 | } 928 | 929 | // Print the table to stdout 930 | table.printstd(); 931 | } 932 | 933 | #[cfg(feature = "prettytable")] 934 | fn is_empty(oqty: &Option) -> bool { 935 | match oqty { 936 | Some(qty) => qty.is_zero(), 937 | None => true, 938 | } 939 | } 940 | 941 | #[cfg(feature = "prettytable")] 942 | fn make_cell_for_prettytable(oqty: &Option, o100: &Option) -> Cell { 943 | let txt = match oqty { 944 | None => "__".to_string(), 945 | Some(qty) => match o100 { 946 | None => format!("{}", qty.adjust_scale()), 947 | Some(q100) => format!("({:.0}%) {}", qty.calc_percentage(q100), qty.adjust_scale()), 948 | }, 949 | }; 950 | Cell::new(&txt) 951 | } 952 | 953 | #[cfg(test)] 954 | mod tests { 955 | use super::*; 956 | 957 | #[test] 958 | fn test_accept_resource() { 959 | assert!(accept_resource("cpu", &[])); 960 | assert!(accept_resource("cpu", &["c".to_string()])); 961 | assert!(accept_resource("cpu", &["cpu".to_string()])); 962 | assert!(!accept_resource("cpu", &["cpu3".to_string()])); 963 | assert!(accept_resource("gpu", &["gpu".to_string()])); 964 | assert!(accept_resource("nvidia.com/gpu", &["gpu".to_string()])); 965 | } 966 | } 967 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use clap::Parser; 2 | use color_eyre::eyre::Result; 3 | use kubectl_view_allocations::{CliOpts, GroupBy, do_main}; 4 | 5 | fn init_tracing() { 6 | use tracing_error::ErrorLayer; 7 | use tracing_subscriber::prelude::*; 8 | use tracing_subscriber::{EnvFilter, fmt}; 9 | 10 | let fmt_layer = fmt::layer().with_target(false); 11 | let filter_layer = EnvFilter::try_from_default_env() 12 | .or_else(|_| EnvFilter::try_new("warn")) 13 | .unwrap(); 14 | 15 | tracing_subscriber::registry() 16 | .with(filter_layer) 17 | .with(fmt_layer) 18 | .with(ErrorLayer::default()) 19 | .init(); 20 | } 21 | 22 | #[tokio::main] 23 | async fn main() -> Result<()> { 24 | init_tracing(); 25 | color_eyre::config::HookBuilder::default() 26 | .panic_section("consider reporting the bug on github") 27 | .install()?; 28 | let mut cli_opts = CliOpts::parse(); 29 | //HACK because I didn't find how to default a multiple opts 30 | if cli_opts.group_by.is_empty() { 31 | cli_opts.group_by.push(GroupBy::resource); 32 | cli_opts.group_by.push(GroupBy::node); 33 | cli_opts.group_by.push(GroupBy::pod); 34 | } 35 | if !cli_opts.group_by.contains(&GroupBy::resource) { 36 | cli_opts.group_by.insert(0, GroupBy::resource) 37 | } 38 | cli_opts.group_by.dedup(); 39 | // dbg!(&cli_opts); 40 | 41 | do_main(&cli_opts).await?; 42 | Ok(()) 43 | } 44 | -------------------------------------------------------------------------------- /src/metrics.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | // kubectl get --raw /apis/metrics.k8s.io/v1beta1/pods | jq . 3 | 4 | #[derive(Debug, Clone, Serialize, Deserialize)] 5 | pub struct Usage { 6 | pub cpu: String, 7 | pub memory: String, 8 | } 9 | 10 | #[derive(Debug, Clone, Serialize, Deserialize)] 11 | pub struct Container { 12 | pub name: String, 13 | pub usage: Usage, 14 | } 15 | 16 | #[derive(Debug, Clone, Serialize, Deserialize)] 17 | pub struct PodMetrics { 18 | pub metadata: kube::api::ObjectMeta, 19 | pub containers: Vec, 20 | pub timestamp: String, 21 | pub window: String, 22 | } 23 | 24 | // custom impl since metrics API doesn't exist on kube-rs 25 | impl k8s_openapi::Resource for PodMetrics { 26 | const GROUP: &'static str = "metrics.k8s.io"; 27 | const KIND: &'static str = "pod"; 28 | const VERSION: &'static str = "v1beta1"; 29 | const API_VERSION: &'static str = "metrics.k8s.io/v1beta1"; 30 | const URL_PATH_SEGMENT: &'static str = "pods"; 31 | type Scope = k8s_openapi::NamespaceResourceScope; 32 | } 33 | 34 | impl k8s_openapi::Metadata for PodMetrics { 35 | type Ty = kube::api::ObjectMeta; 36 | 37 | fn metadata(&self) -> &Self::Ty { 38 | &self.metadata 39 | } 40 | 41 | fn metadata_mut(&mut self) -> &mut Self::Ty { 42 | &mut self.metadata 43 | } 44 | } 45 | 46 | // #[derive(Debug, Clone, Serialize, Deserialize)] 47 | // struct PodMetricsList { 48 | // metadata: kube::api::ObjectMeta, 49 | // api_version: String, 50 | // kind: String, 51 | // items: Vec, 52 | // } 53 | -------------------------------------------------------------------------------- /src/qty.rs: -------------------------------------------------------------------------------- 1 | // see [Definitions of the SI units: The binary prefixes](https://physics.nist.gov/cuu/Units/binary.html) 2 | // see [Managing Compute Resources for Containers - Kubernetes](https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/) 3 | //TODO rewrite to support exponent, ... see [apimachinery/quantity.go at master · kubernetes/apimachinery](https://github.com/kubernetes/apimachinery/blob/master/pkg/api/resource/quantity.go) 4 | 5 | use std::cmp::Ordering; 6 | use std::str::FromStr; 7 | 8 | #[derive(thiserror::Error, Debug)] 9 | pub enum Error { 10 | #[error("Failed to parse scale in '{0}'")] 11 | ScaleParseError(String), 12 | 13 | #[error("Failed to read Qty (num) from '{input}'")] 14 | QtyNumberParseError { 15 | input: String, 16 | #[source] // optional if field name is `source` 17 | source: std::num::ParseFloatError, 18 | }, 19 | } 20 | 21 | #[derive(Debug, Clone, Eq, PartialEq, Default)] 22 | pub struct Scale { 23 | label: &'static str, 24 | base: u32, 25 | pow: i32, 26 | } 27 | 28 | // should be sorted in DESC 29 | #[rustfmt::skip] 30 | static SCALES: [Scale;15] = [ 31 | Scale{ label:"Pi", base: 2, pow: 50}, 32 | Scale{ label:"Ti", base: 2, pow: 40}, 33 | Scale{ label:"Gi", base: 2, pow: 30}, 34 | Scale{ label:"Mi", base: 2, pow: 20}, 35 | Scale{ label:"Ki", base: 2, pow: 10}, 36 | Scale{ label:"P", base: 10, pow: 15}, 37 | Scale{ label:"T", base: 10, pow: 12}, 38 | Scale{ label:"G", base: 10, pow: 9}, 39 | Scale{ label:"M", base: 10, pow: 6}, 40 | Scale{ label:"k", base: 10, pow: 3}, 41 | Scale{ label:"", base: 10, pow: 0}, 42 | Scale{ label:"m", base: 10, pow: -3}, 43 | Scale{ label:"u", base: 10, pow: -6}, 44 | Scale{ label:"μ", base: 10, pow: -6}, 45 | Scale{ label:"n", base: 10, pow: -9}, 46 | ]; 47 | 48 | impl FromStr for Scale { 49 | type Err = Error; 50 | fn from_str(s: &str) -> Result { 51 | SCALES 52 | .iter() 53 | .find(|v| v.label == s) 54 | .cloned() 55 | .ok_or_else(|| Error::ScaleParseError(s.to_owned())) 56 | } 57 | } 58 | 59 | impl From<&Scale> for f64 { 60 | fn from(v: &Scale) -> f64 { 61 | if v.pow == 0 || v.base == 0 { 62 | 1.0 63 | } else { 64 | f64::from(v.base).powf(f64::from(v.pow)) 65 | } 66 | } 67 | } 68 | 69 | impl PartialOrd for Scale { 70 | //TODO optimize accuracy with big number 71 | fn partial_cmp(&self, other: &Self) -> Option { 72 | let v1 = f64::from(self); 73 | let v2 = f64::from(other); 74 | if v1 > v2 { 75 | Some(Ordering::Greater) 76 | } else if v1 < v2 { 77 | Some(Ordering::Less) 78 | } else if (v1 - v2).abs() < std::f64::EPSILON { 79 | Some(Ordering::Equal) 80 | } else { 81 | None 82 | } 83 | } 84 | } 85 | 86 | impl Scale { 87 | pub fn min(&self, other: &Scale) -> Scale { 88 | if self < other { 89 | self.clone() 90 | } else { 91 | other.clone() 92 | } 93 | } 94 | } 95 | 96 | #[derive(Debug, Clone, Eq, PartialEq, Default)] 97 | pub struct Qty { 98 | pub value: i64, 99 | pub scale: Scale, 100 | } 101 | 102 | impl From<&Qty> for f64 { 103 | fn from(v: &Qty) -> f64 { 104 | (v.value as f64) * 0.001 105 | } 106 | } 107 | 108 | impl Qty { 109 | pub fn zero() -> Self { 110 | Self { 111 | value: 0, 112 | scale: Scale::from_str("").unwrap(), 113 | } 114 | } 115 | 116 | pub fn lowest_positive() -> Self { 117 | Self { 118 | value: 1, 119 | scale: Scale::from_str("m").unwrap(), 120 | } 121 | } 122 | 123 | pub fn is_zero(&self) -> bool { 124 | self.value == 0 125 | } 126 | 127 | pub fn calc_percentage(&self, base100: &Self) -> f64 { 128 | if base100.value != 0 { 129 | f64::from(self) * 100f64 / f64::from(base100) 130 | } else { 131 | core::f64::NAN 132 | } 133 | } 134 | 135 | pub fn adjust_scale(&self) -> Self { 136 | let valuef64 = f64::from(self); 137 | let scale = SCALES 138 | .iter() 139 | .filter(|s| s.base == self.scale.base || self.scale.base == 0) 140 | .find(|s| f64::from(*s) <= valuef64); 141 | match scale { 142 | Some(scale) => Self { 143 | value: self.value, 144 | scale: scale.clone(), 145 | }, 146 | None => self.clone(), 147 | } 148 | } 149 | } 150 | 151 | impl FromStr for Qty { 152 | type Err = Error; 153 | fn from_str(s: &str) -> Result { 154 | let (num_str, scale_str): (&str, &str) = match s.find(|c: char| { 155 | !c.is_ascii_digit() && c != 'E' && c != 'e' && c != '+' && c != '-' && c != '.' 156 | }) { 157 | Some(pos) => (&s[..pos], &s[pos..]), 158 | None => (s, ""), 159 | }; 160 | let scale = Scale::from_str(scale_str.trim())?; 161 | let num = f64::from_str(num_str).map_err(|source| Error::QtyNumberParseError { 162 | input: num_str.to_owned(), 163 | source, 164 | })?; 165 | let value = (num * f64::from(&scale) * 1000f64) as i64; 166 | Ok(Qty { value, scale }) 167 | } 168 | } 169 | 170 | impl std::fmt::Display for Qty { 171 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 172 | write!( 173 | f, 174 | "{:.1}{}", 175 | (self.value as f64 / (f64::from(&self.scale) * 1000f64)), 176 | self.scale.label 177 | ) 178 | } 179 | } 180 | 181 | impl PartialOrd for Qty { 182 | //TODO optimize accuracy with big number 183 | fn partial_cmp(&self, other: &Self) -> Option { 184 | let v1 = self.value; // f64::from(self); 185 | let v2 = other.value; // f64::from(other); 186 | v1.partial_cmp(&v2) 187 | } 188 | } 189 | 190 | impl Ord for Qty { 191 | //TODO optimize accuracy with big number 192 | fn cmp(&self, other: &Self) -> Ordering { 193 | let v1 = self.value; // f64::from(self); 194 | let v2 = other.value; // f64::from(other); 195 | v1.partial_cmp(&v2).unwrap() // i64 should always be comparable (no NaNs or anything crazy like that) 196 | } 197 | } 198 | 199 | pub fn select_scale_for_add(v1: &Qty, v2: &Qty) -> Scale { 200 | if v2.value == 0 { 201 | v1.scale.clone() 202 | } else if v1.value == 0 { 203 | v2.scale.clone() 204 | } else { 205 | v1.scale.min(&v2.scale) 206 | } 207 | } 208 | 209 | impl std::ops::Add for Qty { 210 | type Output = Qty; 211 | fn add(self, other: Self) -> Qty { 212 | &self + &other 213 | } 214 | } 215 | 216 | impl std::ops::Add for &Qty { 217 | type Output = Qty; 218 | fn add(self, other: Self) -> Qty { 219 | Qty { 220 | value: self.value + other.value, 221 | scale: select_scale_for_add(self, other), 222 | } 223 | } 224 | } 225 | 226 | impl<'b> std::ops::AddAssign<&'b Qty> for Qty { 227 | fn add_assign(&mut self, other: &'b Self) { 228 | *self = Qty { 229 | value: self.value + other.value, 230 | scale: select_scale_for_add(self, other), 231 | } 232 | } 233 | } 234 | 235 | impl std::ops::Sub for Qty { 236 | type Output = Qty; 237 | fn sub(self, other: Self) -> Qty { 238 | &self - &other 239 | } 240 | } 241 | 242 | impl std::ops::Sub for &Qty { 243 | type Output = Qty; 244 | fn sub(self, other: Self) -> Qty { 245 | Qty { 246 | value: self.value - other.value, 247 | scale: select_scale_for_add(self, other), 248 | } 249 | } 250 | } 251 | 252 | impl<'b> std::ops::SubAssign<&'b Qty> for Qty { 253 | fn sub_assign(&mut self, other: &'b Self) { 254 | *self = Qty { 255 | value: self.value - other.value, 256 | scale: select_scale_for_add(self, other), 257 | }; 258 | } 259 | } 260 | 261 | #[cfg(test)] 262 | mod tests { 263 | use super::*; 264 | use pretty_assertions::assert_eq; 265 | 266 | macro_rules! assert_is_close { 267 | ($x:expr, $y:expr, $range:expr) => { 268 | assert!($x >= ($y - $range)); 269 | assert!($x <= ($y + $range)); 270 | }; 271 | } 272 | 273 | #[test] 274 | fn test_to_base() -> Result<(), Box> { 275 | assert_is_close!( 276 | f64::from(&Qty::from_str("1k")?), 277 | f64::from(&Qty::from_str("1000000m")?), 278 | 0.01 279 | ); 280 | assert_eq!( 281 | Qty::from_str("1Ki")?, 282 | Qty { 283 | value: 1024000, 284 | scale: Scale { 285 | label: "Ki", 286 | base: 2, 287 | pow: 10, 288 | }, 289 | } 290 | ); 291 | Ok(()) 292 | } 293 | 294 | #[test] 295 | fn expectation_ok_for_adjust_scale() -> Result<(), Box> { 296 | let cases = vec![ 297 | ("1k", "1.0k"), 298 | ("10k", "10.0k"), 299 | ("100k", "100.0k"), 300 | ("999k", "999.0k"), 301 | ("1000k", "1.0M"), 302 | ("1999k", "2.0M"), //TODO 1.9M should be better ? 303 | ("1Ki", "1.0Ki"), 304 | ("10Ki", "10.0Ki"), 305 | ("100Ki", "100.0Ki"), 306 | ("1000Ki", "1000.0Ki"), 307 | ("1024Ki", "1.0Mi"), 308 | ("25641877504", "25.6G"), 309 | ("1770653738944", "1.8T"), 310 | ("1000m", "1.0"), 311 | ("100m", "100.0m"), 312 | ("1m", "1.0m"), 313 | ]; 314 | for (input, expected) in cases { 315 | assert_eq!( 316 | format!("{}", &Qty::from_str(input)?.adjust_scale()), 317 | expected.to_string() 318 | ); 319 | } 320 | Ok(()) 321 | } 322 | 323 | #[test] 324 | fn test_display() -> Result<(), Box> { 325 | let cases = vec![ 326 | ("1k", "1.0k"), 327 | ("10k", "10.0k"), 328 | ("100k", "100.0k"), 329 | ("999k", "999.0k"), 330 | ("1000k", "1000.0k"), 331 | ("1999k", "1999.0k"), 332 | ("1Ki", "1.0Ki"), 333 | ("10Ki", "10.0Ki"), 334 | ("100Ki", "100.0Ki"), 335 | ("1000Ki", "1000.0Ki"), 336 | ("1024Ki", "1024.0Ki"), 337 | ("25641877504", "25641877504.0"), 338 | ("1000m", "1000.0m"), 339 | ("100m", "100.0m"), 340 | ("1m", "1.0m"), 341 | ("1000000n", "1000000.0n"), 342 | // lowest precision is m, under 1m value is trunked 343 | ("1u", "0.0u"), 344 | ("1μ", "0.0μ"), 345 | ("1n", "0.0n"), 346 | ("999999n", "0.0n"), 347 | ]; 348 | for input in cases { 349 | assert_eq!(format!("{}", &Qty::from_str(input.0)?), input.1.to_string()); 350 | assert_eq!(format!("{}", &Qty::from_str(input.1)?), input.1.to_string()); 351 | } 352 | Ok(()) 353 | } 354 | 355 | #[test] 356 | fn test_f64_from_scale() -> Result<(), Box> { 357 | assert_is_close!(f64::from(&Scale::from_str("m")?), 0.001, 0.00001); 358 | Ok(()) 359 | } 360 | 361 | #[test] 362 | fn test_f64_from_qty() -> Result<(), Box> { 363 | assert_is_close!(f64::from(&Qty::from_str("20m")?), 0.020, 0.00001); 364 | assert_is_close!(f64::from(&Qty::from_str("300m")?), 0.300, 0.00001); 365 | assert_is_close!(f64::from(&Qty::from_str("1000m")?), 1.000, 0.00001); 366 | assert_is_close!(f64::from(&Qty::from_str("+1000m")?), 1.000, 0.00001); 367 | assert_is_close!(f64::from(&Qty::from_str("-1000m")?), -1.000, 0.00001); 368 | assert_is_close!( 369 | f64::from(&Qty::from_str("3145728e3")?), 370 | 3145728000.000, 371 | 0.00001 372 | ); 373 | Ok(()) 374 | } 375 | 376 | #[test] 377 | fn test_add() -> Result<(), Box> { 378 | assert_eq!( 379 | (Qty::from_str("1")? 380 | + Qty::from_str("300m")? 381 | + Qty::from_str("300m")? 382 | + Qty::from_str("300m")? 383 | + Qty::from_str("300m")?), 384 | Qty::from_str("2200m")? 385 | ); 386 | assert_eq!( 387 | Qty::default() + Qty::from_str("300m")?, 388 | Qty::from_str("300m")? 389 | ); 390 | assert_eq!( 391 | Qty::default() + Qty::from_str("16Gi")?, 392 | Qty::from_str("16Gi")? 393 | ); 394 | assert_eq!( 395 | Qty::from_str("20m")? + Qty::from_str("300m")?, 396 | Qty::from_str("320m")? 397 | ); 398 | assert_eq!( 399 | &(Qty::from_str("1k")? + Qty::from_str("300m")?), 400 | &Qty::from_str("1000300m")? 401 | ); 402 | assert_eq!( 403 | &(Qty::from_str("1Ki")? + Qty::from_str("1Ki")?), 404 | &Qty::from_str("2Ki")? 405 | ); 406 | assert_eq!( 407 | &(Qty::from_str("1Ki")? + Qty::from_str("1k")?), 408 | &Qty { 409 | value: 2024000, 410 | scale: Scale { 411 | label: "k", 412 | base: 10, 413 | pow: 3, 414 | }, 415 | } 416 | ); 417 | Ok(()) 418 | } 419 | } 420 | -------------------------------------------------------------------------------- /src/tree.rs: -------------------------------------------------------------------------------- 1 | //! module to make tree-table or tree from a list, 2 | //! by computing the string prefix that contains line of to link item of the list 3 | //! like a tree (multi-root) 4 | //! 5 | //! ```rust 6 | //! use kubectl_view_allocations::tree::provide_prefix; 7 | //! 8 | //! let items = vec![ 9 | //! "1/2", 10 | //! "1/2/3", 11 | //! "1/2/3/4", 12 | //! "1/2/5", 13 | //! "6", 14 | //! "7", 15 | //! "7/8", 16 | //! "7/9", 17 | //! ]; 18 | //! 19 | //! let prefixes = provide_prefix(&items, |parent, item| { 20 | //! let pi = item.split("/"); 21 | //! let pp = parent.split("/"); 22 | //! (pi.count() == pp.count() + 1) && item.starts_with(parent) 23 | //! }); 24 | //! 25 | //! let mut actual = String::new(); 26 | //! prefixes.iter().zip(items).for_each(|(p, i)| 27 | //! actual.push_str(&format!("{} {}\n", p, i)) 28 | //! ); 29 | //! 30 | //! let expected = r#" 1/2 31 | //! ├─ 1/2/3 32 | //! │ └─ 1/2/3/4 33 | //! └─ 1/2/5 34 | //! 6 35 | //! 7 36 | //! ├─ 7/8 37 | //! └─ 7/9 38 | //! "#; 39 | //! //dbg!(&actual); 40 | //! assert_eq!(actual, expected); 41 | //! ``` 42 | //! 43 | 44 | #[derive(Debug, Clone)] 45 | struct TreeNode { 46 | parent: Option, 47 | level: Vec, 48 | children: Vec, 49 | } 50 | 51 | fn level_to_string(level: &[bool]) -> String { 52 | const EMPTY: &str = " "; 53 | const EDGE: &str = " └─"; 54 | const PIPE: &str = " │ "; 55 | const BRANCH: &str = " ├─"; 56 | 57 | let mut prefix = String::new(); 58 | if !level.is_empty() { 59 | let last_col = level.len() - 1; 60 | for (col, is_last_child) in level.iter().enumerate() { 61 | let is_last_col = col == last_col; 62 | let s = match (*is_last_child, is_last_col) { 63 | (true, false) => EMPTY, 64 | (true, true) => EDGE, 65 | (false, false) => PIPE, 66 | (false, true) => BRANCH, 67 | }; 68 | prefix.push_str(s); 69 | } 70 | } 71 | prefix 72 | } 73 | 74 | fn write_tree_level_of_children(nodes: &mut [TreeNode], idx: usize) { 75 | if let Some(node) = nodes.get(idx) { 76 | let treenode = node.clone(); 77 | let mut d = treenode.children.len(); 78 | for s in treenode.children.iter() { 79 | if let Some(child) = nodes.get_mut(*s) { 80 | let mut lnext = treenode.level.clone(); 81 | lnext.push(d == 1); 82 | d -= 1; 83 | child.level = lnext; 84 | } 85 | } 86 | } 87 | } 88 | 89 | fn make_tree_by_reverse_depth_first(items: &[I], is_parent_of: F) -> Vec 90 | where 91 | F: Fn(&I, &I) -> bool, 92 | { 93 | let mut current: Option = None; 94 | let mut nodes: Vec = vec![]; 95 | for (i, item) in items.iter().enumerate() { 96 | while current.is_some() && !is_parent_of(&items[current.unwrap()], item) { 97 | current = nodes.get_mut(current.unwrap()).and_then(|n| n.parent); 98 | } 99 | let treenode = TreeNode { 100 | parent: current, 101 | level: vec![], 102 | children: vec![], 103 | }; 104 | if let Some(parent) = current { 105 | if let Some(node) = nodes.get_mut(parent) { 106 | node.children.push(i); 107 | } 108 | } 109 | nodes.push(treenode); 110 | current = Some(i); 111 | } 112 | nodes 113 | } 114 | 115 | /// Generate a list of prefix to display items as a tree (multi-root) 116 | /// - the input should be sorted in the target display order 117 | /// - the input order of ìtems is preserved into output 118 | /// - the input order is used to ask for parent of item (parent should be before child) 119 | /// - output as the same number of element than input `items` 120 | /// - output can be zipped with input ìtems 121 | /// - is_parent_of(maybe_parent, item) 122 | pub fn provide_prefix(items: &[I], is_parent_of: F) -> Vec 123 | where 124 | F: Fn(&I, &I) -> bool, 125 | { 126 | let mut nodes: Vec = make_tree_by_reverse_depth_first(items, is_parent_of); 127 | //dbg!(&nodes); 128 | for i in 0..nodes.len() { 129 | write_tree_level_of_children(&mut nodes, i); 130 | } 131 | //dbg!(&nodes); 132 | nodes.iter().map(|n| level_to_string(&n.level)).collect() 133 | } 134 | 135 | #[cfg(test)] 136 | mod tests { 137 | use super::*; 138 | 139 | #[test] 140 | fn test_() { 141 | let items = vec!["1/2", "1/2/3", "1/2/3/4", "1/2/5", "6", "7", "7/8", "7/9"]; 142 | 143 | let prefixes = provide_prefix(&items, |parent, item| { 144 | let pi = item.split('/'); 145 | let pp = parent.split('/'); 146 | (pi.count() == pp.count() + 1) && item.starts_with(parent) 147 | }); 148 | 149 | let mut actual = String::new(); 150 | prefixes 151 | .iter() 152 | .zip(items) 153 | .for_each(|(p, i)| actual.push_str(&format!("{} {}\n", p, i))); 154 | 155 | let expected = r#" 1/2 156 | ├─ 1/2/3 157 | │ └─ 1/2/3/4 158 | └─ 1/2/5 159 | 6 160 | 7 161 | ├─ 7/8 162 | └─ 7/9 163 | "#; 164 | //dbg!(&actual); 165 | assert_eq!(actual, expected); 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /tests/metrics-server-components.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | labels: 5 | k8s-app: metrics-server 6 | name: metrics-server 7 | namespace: kube-system 8 | --- 9 | apiVersion: rbac.authorization.k8s.io/v1 10 | kind: ClusterRole 11 | metadata: 12 | labels: 13 | k8s-app: metrics-server 14 | rbac.authorization.k8s.io/aggregate-to-admin: "true" 15 | rbac.authorization.k8s.io/aggregate-to-edit: "true" 16 | rbac.authorization.k8s.io/aggregate-to-view: "true" 17 | name: system:aggregated-metrics-reader 18 | rules: 19 | - apiGroups: 20 | - metrics.k8s.io 21 | resources: 22 | - pods 23 | - nodes 24 | verbs: 25 | - get 26 | - list 27 | - watch 28 | --- 29 | apiVersion: rbac.authorization.k8s.io/v1 30 | kind: ClusterRole 31 | metadata: 32 | labels: 33 | k8s-app: metrics-server 34 | name: system:metrics-server 35 | rules: 36 | - apiGroups: 37 | - "" 38 | resources: 39 | - pods 40 | - nodes 41 | - nodes/stats 42 | - namespaces 43 | - configmaps 44 | verbs: 45 | - get 46 | - list 47 | - watch 48 | --- 49 | apiVersion: rbac.authorization.k8s.io/v1 50 | kind: RoleBinding 51 | metadata: 52 | labels: 53 | k8s-app: metrics-server 54 | name: metrics-server-auth-reader 55 | namespace: kube-system 56 | roleRef: 57 | apiGroup: rbac.authorization.k8s.io 58 | kind: Role 59 | name: extension-apiserver-authentication-reader 60 | subjects: 61 | - kind: ServiceAccount 62 | name: metrics-server 63 | namespace: kube-system 64 | --- 65 | apiVersion: rbac.authorization.k8s.io/v1 66 | kind: ClusterRoleBinding 67 | metadata: 68 | labels: 69 | k8s-app: metrics-server 70 | name: metrics-server:system:auth-delegator 71 | roleRef: 72 | apiGroup: rbac.authorization.k8s.io 73 | kind: ClusterRole 74 | name: system:auth-delegator 75 | subjects: 76 | - kind: ServiceAccount 77 | name: metrics-server 78 | namespace: kube-system 79 | --- 80 | apiVersion: rbac.authorization.k8s.io/v1 81 | kind: ClusterRoleBinding 82 | metadata: 83 | labels: 84 | k8s-app: metrics-server 85 | name: system:metrics-server 86 | roleRef: 87 | apiGroup: rbac.authorization.k8s.io 88 | kind: ClusterRole 89 | name: system:metrics-server 90 | subjects: 91 | - kind: ServiceAccount 92 | name: metrics-server 93 | namespace: kube-system 94 | --- 95 | apiVersion: v1 96 | kind: Service 97 | metadata: 98 | labels: 99 | k8s-app: metrics-server 100 | name: metrics-server 101 | namespace: kube-system 102 | spec: 103 | ports: 104 | - name: https 105 | port: 443 106 | protocol: TCP 107 | targetPort: https 108 | selector: 109 | k8s-app: metrics-server 110 | --- 111 | apiVersion: apps/v1 112 | kind: Deployment 113 | metadata: 114 | labels: 115 | k8s-app: metrics-server 116 | name: metrics-server 117 | namespace: kube-system 118 | spec: 119 | selector: 120 | matchLabels: 121 | k8s-app: metrics-server 122 | strategy: 123 | rollingUpdate: 124 | maxUnavailable: 0 125 | template: 126 | metadata: 127 | labels: 128 | k8s-app: metrics-server 129 | spec: 130 | containers: 131 | - args: 132 | - --cert-dir=/tmp 133 | - --secure-port=4443 134 | - --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname 135 | - --kubelet-use-node-status-port 136 | - --kubelet-insecure-tls 137 | image: k8s.gcr.io/metrics-server/metrics-server:v0.4.2 138 | imagePullPolicy: IfNotPresent 139 | livenessProbe: 140 | failureThreshold: 3 141 | httpGet: 142 | path: /livez 143 | port: https 144 | scheme: HTTPS 145 | periodSeconds: 10 146 | name: metrics-server 147 | ports: 148 | - containerPort: 4443 149 | name: https 150 | protocol: TCP 151 | readinessProbe: 152 | failureThreshold: 3 153 | httpGet: 154 | path: /readyz 155 | port: https 156 | scheme: HTTPS 157 | periodSeconds: 10 158 | securityContext: 159 | readOnlyRootFilesystem: true 160 | runAsNonRoot: true 161 | runAsUser: 1000 162 | volumeMounts: 163 | - mountPath: /tmp 164 | name: tmp-dir 165 | nodeSelector: 166 | kubernetes.io/os: linux 167 | priorityClassName: system-cluster-critical 168 | serviceAccountName: metrics-server 169 | volumes: 170 | - emptyDir: {} 171 | name: tmp-dir 172 | --- 173 | apiVersion: apiregistration.k8s.io/v1 174 | kind: APIService 175 | metadata: 176 | labels: 177 | k8s-app: metrics-server 178 | name: v1beta1.metrics.k8s.io 179 | spec: 180 | group: metrics.k8s.io 181 | groupPriorityMinimum: 100 182 | insecureSkipTLSVerify: true 183 | service: 184 | name: metrics-server 185 | namespace: kube-system 186 | version: v1beta1 187 | versionPriority: 100 188 | -------------------------------------------------------------------------------- /tests/run_ubuntu_16.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" 4 | 5 | docker build -t ubuntu_curl:16.04 -f "${DIR}/ubuntu_16.dockerfile" "${DIR}" 6 | docker run --rm \ 7 | -v ${DIR}/..:/prj \ 8 | -v $HOME/.kube:/root/.kube \ 9 | -w /tmp \ 10 | ubuntu_curl:16.04 \ 11 | /prj/tests/test_first_run.sh 12 | 13 | # docker run --rm -it \ 14 | # -v ${DIR}/..:/prj \ 15 | # -v $HOME/.kube:/root/.kube \ 16 | # -w /tmp \ 17 | # ubuntu:16.04 \ 18 | # bash 19 | -------------------------------------------------------------------------------- /tests/test_first_run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #set -ex 4 | 5 | # apt-get update && apt-get install -y curl 6 | #curl https://raw.githubusercontent.com/davidB/kubectl-view-allocations/master/scripts/getLatest.sh | sh 7 | #sh /prj/scripts/getLatest.sh 8 | bash /prj/scripts/getLatest.sh 9 | 10 | ls -l /tmp 11 | kubectl-view-allocations 12 | -------------------------------------------------------------------------------- /tests/ubuntu_16.dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:16.04 2 | 3 | RUN apt-get update \ 4 | && apt-get install -y curl \ 5 | && rm -rf /var/lib/apt/lists/* 6 | --------------------------------------------------------------------------------