├── tests ├── ubuntu_16.dockerfile ├── test_first_run.sh ├── run_ubuntu_16.sh └── metrics-server-components.yaml ├── .vscode └── settings.json ├── .github ├── dependabot.yml ├── FUNDING.yml └── workflows │ ├── ci.yaml │ └── release.yaml ├── release.toml ├── examples └── pod_api.rs ├── src ├── main.rs ├── metrics.rs ├── tree.rs ├── qty.rs └── lib.rs ├── .gitignore ├── .krew.yaml ├── Cargo.toml ├── scripts └── getLatest.sh ├── .mise.toml ├── MIGRATION_CARGO_MAKE_TO_MISE.md ├── LICENSE.txt ├── README.md ├── CHANGELOG.md └── Cargo.lock /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 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "spellright.language": [], 3 | "spellright.documentTypes": [ 4 | "latex", 5 | "plaintext" 6 | ] 7 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "cargo" 4 | directory: "/" 5 | schedule: 6 | interval: "monthly" 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: "monthly" 15 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /release.toml: -------------------------------------------------------------------------------- 1 | pre-release-commit-message = "🚀 (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 = "🔖 {{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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | os: 25 | - imageName: ubuntu-latest 26 | - imageName: macOS-latest 27 | # - imageName: windows-latest 28 | 29 | steps: 30 | - uses: actions/checkout@v6 31 | - uses: mozilla-actions/sccache-action@v0.0.9 32 | - uses: jdx/mise-action@v3 33 | with: 34 | cache: false 35 | cache_save: false 36 | - run: mise run ci 37 | # 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 38 | # DEVELOPER_DIR: "/Applications/Xcode_11.app/Contents/Developer" 39 | 40 | integration: 41 | # Integration tests are linux only 42 | runs-on: ubuntu-latest 43 | steps: 44 | - uses: nolar/setup-k3d-k3s@v1 45 | with: 46 | # version: v1.20 47 | # k3d-kube 48 | k3d-name: kube 49 | # Used to avoid rate limits when fetching the releases from k3s repo. 50 | # Anonymous access is limited to 60 requests / hour / worker 51 | # github-token: ${{ secrets.GITHUB_TOKEN }} 52 | - uses: actions/checkout@v6 53 | - uses: dtolnay/rust-toolchain@stable 54 | - uses: mozilla-actions/sccache-action@v0.0.9 55 | - uses: jdx/mise-action@v3 56 | - name: Run on k3d 57 | run: cargo run 58 | -------------------------------------------------------------------------------- /.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 | shortDescription: List allocations per resources, nodes, pods. 33 | homepage: https://github.com/davidB/kubectl-view-allocations 34 | description: | 35 | This plugin lists resources (cpu, memory, gpu,...) allocations (requested, 36 | limit, allocatable) as defined in the manifest of nodes and running pods, 37 | and utilization from metrics-server. 38 | try `kubectl view-allocations -h`, `kubectl view-allocations` 39 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kubectl-view-allocations" 3 | version = "1.0.0" 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 | futures = "0.3" 19 | itertools = "0.14" 20 | k8s-openapi = { version = "0.26", default-features = false } 21 | kube = { version = "2", features = [ 22 | "ring", # or "aws-lc-rs" for rustls-tls, "ring" is also a dependency of tame-oauth 23 | "client", 24 | "gzip", 25 | "http-proxy", 26 | "oauth", 27 | "oidc", 28 | "rustls-tls", 29 | "socks5", 30 | "webpki-roots", 31 | ], default-features = false } 32 | prettytable-rs = { version = "0.10", default-features = false, optional = true } 33 | serde = "1.0" 34 | serde_json = "1.0" 35 | thiserror = "2.0" 36 | tokio = { version = "1", features = ["full"], optional = true } 37 | tracing = "0.1" 38 | tracing-bunyan-formatter = { version = "0.3", optional = true } 39 | tracing-error = "0.2" 40 | tracing-subscriber = { version = "0.3", optional = true, default-features = false, features = [ 41 | "env-filter", 42 | ] } 43 | 44 | [features] 45 | default = ["cli"] 46 | cli = [ 47 | "dep:tokio", 48 | "k8s-openapi/earliest", 49 | "dep:tracing-subscriber", 50 | "prettytable", 51 | ] 52 | prettytable = ["dep:prettytable-rs"] 53 | 54 | [[bin]] 55 | name = "kubectl-view-allocations" 56 | path = "src/main.rs" 57 | doc = false 58 | # HACK to define dependencies only for cli 59 | # see https://github.com/rust-lang/cargo/issues/1982 60 | required-features = ["cli"] 61 | 62 | [dev-dependencies] 63 | anyhow = "1" 64 | pretty_assertions = "1" 65 | 66 | [profile.release] 67 | lto = true 68 | panic = 'abort' 69 | opt-level = 'z' # Optimize for size. 70 | codegen-units = 1 71 | strip = true 72 | 73 | [profile.dev.package.backtrace] 74 | opt-level = 3 75 | -------------------------------------------------------------------------------- /.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@v6 44 | - uses: mozilla-actions/sccache-action@v0.0.9 45 | - uses: jdx/mise-action@v3 46 | # - uses: dtolnay/rust-toolchain@stable 47 | # with: 48 | # toolchain: ${{ matrix.rust_toolchain }} 49 | # targets: ${{ matrix.os.target_platform }} 50 | - name: Make zip-release-ci-flow 51 | id: zip-release-ci-flow 52 | run: mise run 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@v6 76 | - name: Update new version in krew-index 77 | uses: rajatjindal/krew-release-bot@v0.0.47 78 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | && let Some(node) = nodes.get_mut(parent) 106 | { 107 | node.children.push(i); 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 | -------------------------------------------------------------------------------- /.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 | RUST_TEST_THREADS = "1" 7 | TARGET_AUTO = "x86_64-unknown-linux-gnu" 8 | LIBZ_SYS_STATIC = "1" 9 | PKG_CONFIG_ALLOW_CROSS = "1" 10 | OPENSSL_STATIC = "1" 11 | 12 | [tools] 13 | # docker (or podman + some config) should be available for some of thoses tools 14 | kubectl = "1.24" 15 | kind = "0.20" 16 | rust = { version = "1.89.0", profile="minimal", components="cargo,rustfmt,clippy"} # the rust tool stack (with cargo, fmt, clippy) to build source 17 | jq = "latest" 18 | "cargo:cross" = "latest" 19 | 20 | [tasks.clean] 21 | description = "Clean build artifacts" 22 | run = "cargo clean" 23 | 24 | [tasks.build] 25 | description = "Build the project" 26 | run = "cargo build" 27 | depends = ["clean"] 28 | 29 | [tasks.build-release] 30 | description = "Build the project in release mode" 31 | run = "cargo build --release" 32 | 33 | [tasks.doc] 34 | description = "Generate documentation" 35 | run = "cargo doc --no-deps" 36 | 37 | [tasks.format] 38 | alias = "fmt" 39 | description = "Format the code" 40 | run = "cargo fmt" 41 | 42 | # CI Flow tasks 43 | [tasks.ci] 44 | description = "Main CI flow - format, check, test, clippy" 45 | run = """ 46 | cargo check 47 | cargo test 48 | cargo clippy -- -D warnings 49 | """ 50 | 51 | # Build tasks for different targets 52 | [tasks.build-release-for-target] 53 | description = "Build release for specific target" 54 | run = """ 55 | if [ -n "$TARGET" ]; then 56 | rustup toolchain install stable --target "$TARGET" --profile minimal --no-self-update 57 | rustup target add "$TARGET" 58 | if [ "$CROSS" = "true" ]; then 59 | cross build --release --all-features --target "$TARGET" 60 | else 61 | cargo build --release --all-features --target "$TARGET" 62 | fi 63 | else 64 | cargo build --release --all-features 65 | fi 66 | """ 67 | 68 | # Distribution tasks 69 | [tasks.zip-release-ci-flow] 70 | description = "Complete release build and packaging" 71 | depends = ["build-release-for-target"] 72 | run = """ 73 | DIST_VERSION=$(cargo metadata --format-version 1 --no-deps | jq -r '.packages[0].version') 74 | DIST_NAME="kubectl-view-allocations_${DIST_VERSION}-${TARGET:-x86_64-unknown-linux-gnu}" 75 | DIST_EXT="tar.gz" 76 | DIST_PATH="target/dist/${DIST_NAME}" 77 | 78 | rm -rf "${DIST_PATH}" 79 | mkdir -p "${DIST_PATH}" 80 | 81 | if [ -n "$TARGET" ]; then 82 | cp target/${TARGET}/release/kubectl-view-allocations "${DIST_PATH}/" 83 | else 84 | cp target/release/kubectl-view-allocations "${DIST_PATH}/" 85 | fi 86 | 87 | cp LICENSE.txt "${DIST_PATH}/" || echo "LICENSE.txt not found" 88 | tar -czvf "${DIST_PATH}.${DIST_EXT}" -C "${DIST_PATH}" "kubectl-view-allocations" "LICENSE.txt" 89 | echo "Created: ${DIST_PATH}.${DIST_EXT}" 90 | 91 | if [ -n "$GITHUB_OUTPUT" ]; then 92 | echo "dist_file_path=${DIST_PATH}.${DIST_EXT}" >> $GITHUB_OUTPUT 93 | echo "dist_file_name=${DIST_NAME}.${DIST_EXT}" >> $GITHUB_OUTPUT 94 | echo "dist_version=${DIST_VERSION}" >> $GITHUB_OUTPUT 95 | fi 96 | """ 97 | 98 | # Documentation tasks 99 | [tasks.update-changelog] 100 | description = "Update changelog using gitmoji-changelog" 101 | run = """ 102 | if ! command -v gitmoji-changelog &> /dev/null; then 103 | echo "Installing gitmoji-changelog..." 104 | cargo install gitmoji-changelog 105 | fi 106 | rm -rf CHANGELOG.md 107 | gitmoji-changelog -r x.y.z-dev -o CHANGELOG.md . 108 | """ 109 | 110 | [tasks.update-bom] 111 | description = "Update Bill of Materials" 112 | run = """ 113 | if ! cargo bom --help &> /dev/null; then 114 | echo "Installing cargo-bom..." 115 | cargo install cargo-bom 116 | fi 117 | cargo bom > BOM.txt 118 | """ 119 | 120 | [tasks.update-docs] 121 | description = "Update all documentation" 122 | depends = ["update-changelog", "update-bom"] 123 | 124 | [tasks.publish] 125 | description = "Publish to crates.io" 126 | # depends = ["update-docs"] 127 | run = """ 128 | if ! command -v cargo-release &> /dev/null; then 129 | echo "Installing cargo-release..." 130 | cargo install cargo-release 131 | fi 132 | cargo release --execute {{option(name="level", default="minor")}} 133 | """ 134 | 135 | # Debug task 136 | [tasks.debug] 137 | description = "Print debug information" 138 | run = """ 139 | echo "TARGET=${TARGET:-x86_64-unknown-linux-gnu}" 140 | echo "CROSS=${CROSS:-false}" 141 | echo "RUST_TEST_THREADS=${RUST_TEST_THREADS}" 142 | echo "LIBZ_SYS_STATIC=${LIBZ_SYS_STATIC}" 143 | echo "PKG_CONFIG_ALLOW_CROSS=${PKG_CONFIG_ALLOW_CROSS}" 144 | echo "OPENSSL_STATIC=${OPENSSL_STATIC}" 145 | DIST_VERSION=$(cargo metadata --format-version 1 --no-deps | jq -r '.packages[0].version') 146 | echo "DIST_VERSION=${DIST_VERSION}" 147 | """ 148 | 149 | # Kubernetes testing tasks 150 | [tasks.k8s_create_kind] 151 | description = "Create kind cluster with metrics-server and run kubectl-view-allocations" 152 | run = """ 153 | # k3d cluster create "$CLUSTER_NAME" --agents 2 154 | sudo systemctl start docker 155 | kind create cluster --name "$CLUSTER_NAME" 156 | kubectl cluster-info --context kind-"$CLUSTER_NAME" 157 | kubectl apply -f tests/metrics-server-components.yaml 158 | sleep 5 159 | kubectl top node 160 | cargo run 161 | """ 162 | 163 | [tasks.k8s_delete_kind] 164 | description = "Delete kind cluster" 165 | run = """ 166 | # k3d cluster delete "$CLUSTER_NAME" 167 | kind delete cluster --name "$CLUSTER_NAME" 168 | """ 169 | 170 | [tasks.k8s_create_kwok] 171 | description = "Create KWOK cluster with test pods and run kubectl-view-allocations" 172 | run = """ 173 | # echo "require docker, with podman I got timeout on my machine" 174 | kwokctl create cluster --name="$CLUSTER_NAME" 175 | kwokctl get clusters 176 | kubectl cluster-info --context kwok-"$CLUSTER_NAME" 177 | kwokctl scale node --replicas 2 --name="$CLUSTER_NAME" 178 | kubectl get node 179 | kubectl create deployment pod --image=pod --replicas=5 180 | kubectl get pods -o wide 181 | echo "use '--accept-invalid-certs' with kube view-allocations" 182 | cargo run -- --accept-invalid-certs 183 | """ 184 | 185 | [tasks.k8s_delete_kwok] 186 | description = "Delete KWOK cluster" 187 | run = """ 188 | kwokctl delete cluster --name="$CLUSTER_NAME" 189 | """ 190 | -------------------------------------------------------------------------------- /MIGRATION_CARGO_MAKE_TO_MISE.md: -------------------------------------------------------------------------------- 1 | # Migration from cargo-make to mise 2 | 3 | This document describes the migration from `cargo-make` to `mise` for task management in the kubectl-view-allocations project. 4 | 5 | ## Overview 6 | 7 | The project has been migrated from using `cargo-make` with `Makefile.toml` to using `mise` with tasks defined in `.mise.toml`. This migration provides better integration with the development environment and eliminates the need for an external task runner dependency. 8 | 9 | ## Task Mapping 10 | 11 | ### Basic Development Tasks 12 | 13 | | cargo-make (old) | mise (new) | Description | 14 | |------------------|------------|-------------| 15 | | `cargo make format` | `mise run format` | Format code with rustfmt | 16 | | `cargo make clean` | `mise run clean` | Clean build artifacts | 17 | | `cargo make build` | `mise run build` | Build the project | 18 | | `cargo make test` | `mise run test` | Run tests | 19 | | `cargo make check` | `mise run check` | Check the project for errors | 20 | | `cargo make clippy` | `mise run clippy` | Run clippy linter | 21 | 22 | ### CI/Build Tasks 23 | 24 | | cargo-make (old) | mise (new) | Description | 25 | |------------------|------------|-------------| 26 | | `cargo make ci-flow` | `mise run ci-flow` | Main CI flow - format, check, test, clippy | 27 | | `cargo make ci-static-code-analysis-tasks` | `mise run ci-static-code-analysis-tasks` | Static code analysis tasks for CI | 28 | | `cargo make build-release-for-target` | `mise run build-release-for-target` | Build release for specific target | 29 | 30 | ### Release Tasks 31 | 32 | | cargo-make (old) | mise (new) | Description | 33 | |------------------|------------|-------------| 34 | | `cargo make zip-release-ci-flow` | `mise run zip-release-ci-flow` | Complete release build and packaging | 35 | | `cargo make zip-release-binary-for-target` | `mise run zip-release-binary-for-target` | Create release archive for target | 36 | | `cargo make dist_env` | `mise run dist_env` | Set up distribution environment variables | 37 | 38 | ### Documentation Tasks 39 | 40 | | cargo-make (old) | mise (new) | Description | 41 | |------------------|------------|-------------| 42 | | `cargo make update-changelog` | `mise run update-changelog` | Update changelog using gitmoji-changelog | 43 | | `cargo make update-bom` | `mise run update-bom` | Update Bill of Materials | 44 | | `cargo make update-docs` | `mise run update-docs` | Update all documentation | 45 | 46 | ### Publishing Tasks 47 | 48 | | cargo-make (old) | mise (new) | Description | 49 | |------------------|------------|-------------| 50 | | `cargo make pre-publish` | `mise run pre-publish` | Pre-publish tasks | 51 | | `cargo make publish` | `mise run publish` | Publish to crates.io | 52 | 53 | ### Kubernetes Testing Tasks 54 | 55 | | cargo-make (old) | mise (new) | Description | 56 | |------------------|------------|-------------| 57 | | `just k8s_create_kind` | `mise run k8s_create_kind` | Create kind cluster with metrics-server | 58 | | `just k8s_delete_kind` | `mise run k8s_delete_kind` | Delete kind cluster | 59 | | `just k8s_create_kwok` | `mise run k8s_create_kwok` | Create KWOK cluster with test pods | 60 | | `just k8s_delete_kwok` | `mise run k8s_delete_kwok` | Delete KWOK cluster | 61 | 62 | ### Utility Tasks 63 | 64 | | cargo-make (old) | mise (new) | Description | 65 | |------------------|------------|-------------| 66 | | `cargo make debug` | `mise run debug` | Print debug information | 67 | | `cargo make default` | `mise run default` | List available tasks | 68 | 69 | ## Key Changes 70 | 71 | ### 1. Removed Dependencies 72 | 73 | - **cargo-make**: No longer needed as a development dependency 74 | - **just**: Replaced with mise's native task runner 75 | - **Makefile.toml**: Removed in favor of `.mise.toml` configuration 76 | 77 | ### 2. Added Dependencies 78 | 79 | - **jq**: Added as a mise tool for JSON processing in release tasks 80 | - **mise**: Now used as the primary task runner 81 | 82 | ### 3. Environment Variables 83 | 84 | Environment variables are now defined in the `[env]` section of `.mise.toml`: 85 | 86 | ```toml 87 | [env] 88 | CLUSTER_NAME = "demo-kube" 89 | DOCKER_BUILDKIT = "1" 90 | RUST_TEST_THREADS = "1" 91 | TARGET_AUTO = "x86_64-unknown-linux-gnu" 92 | LIBZ_SYS_STATIC = "1" 93 | PKG_CONFIG_ALLOW_CROSS = "1" 94 | OPENSSL_STATIC = "1" 95 | ``` 96 | 97 | ### 4. Task Dependencies 98 | 99 | - **Sequential execution**: Tasks like `ci-flow` now run commands sequentially to avoid filesystem conflicts 100 | - **Simplified dependencies**: Dependencies are handled through explicit command sequences rather than cargo-make's dependency system 101 | 102 | ### 5. GitHub Actions Integration 103 | 104 | The GitHub workflows have been updated to use `mise` instead of `cargo-make`: 105 | 106 | **Before:** 107 | ```yaml 108 | - uses: davidB/rust-cargo-make@v1 109 | - run: cargo make --disable-check-for-updates ci-flow 110 | ``` 111 | 112 | **After:** 113 | ```yaml 114 | - uses: jdx/mise-action@v2 115 | - run: mise run ci-flow 116 | ``` 117 | 118 | ## Benefits of Migration 119 | 120 | 1. **Unified tooling**: Everything is managed through mise (tools + tasks) 121 | 2. **Better performance**: No external dependency on cargo-make 122 | 3. **Simpler configuration**: Single `.mise.toml` file for both tools and tasks 123 | 4. **Native integration**: Better integration with the development environment 124 | 5. **Reduced complexity**: Eliminated cargo-make's complex dependency system 125 | 126 | ## Usage Examples 127 | 128 | ```bash 129 | # List all available tasks 130 | mise tasks ls 131 | 132 | # Run CI flow 133 | mise run ci-flow 134 | 135 | # Build release for specific target 136 | TARGET=x86_64-unknown-linux-gnu mise run build-release-for-target 137 | 138 | # Create release package 139 | TARGET=x86_64-unknown-linux-gnu mise run zip-release-ci-flow 140 | 141 | # Run Kubernetes tests 142 | mise run k8s_create_kind 143 | ``` 144 | 145 | ## Migration Verification 146 | 147 | All original functionality has been preserved and tested: 148 | 149 | - ✅ Basic development tasks (build, test, format, etc.) 150 | - ✅ CI workflows for GitHub Actions 151 | - ✅ Release packaging and distribution 152 | - ✅ Cross-compilation support 153 | - ✅ Environment variable handling 154 | - ✅ Kubernetes testing workflows 155 | - ✅ Documentation generation tasks 156 | 157 | The migration maintains full backward compatibility in terms of functionality while providing a more streamlined and integrated development experience. 158 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | --kubeconfig 71 | Path to the kubeconfig file to use for requests to kubernetes cluster 72 | --context 73 | The name of the kubeconfig context to use 74 | -n, --namespace ... 75 | Filter pods by namespace(s), by default pods in all namespaces are listed (comma separated list or multiple calls) 76 | -l, --selector 77 | Show only nodes match this label selector 78 | -u, --utilization 79 | Force to retrieve utilization (for cpu and memory), requires having metrics-server https://github.com/kubernetes-sigs/metrics-server 80 | -z, --show-zero 81 | Show lines with zero requested AND zero limit AND zero allocatable, OR pods with unset requested AND limit for `cpu` and `memory` 82 | --used-mode 83 | The way to compute the `used` part for free (`allocatable - used`) [default: max-request-limit] [possible values: max-request-limit, only-request] 84 | --precheck 85 | Pre-check access and refresh token on kubeconfig by running `kubectl cluster-info` 86 | --accept-invalid-certs 87 | Accept invalid certificates (dangerous) 88 | -r, --resource-name ... 89 | Filter resources shown by name(s), by default all resources are listed (comma separated list or multiple calls) 90 | -g, --group-by ... 91 | Group information in a hierarchical manner; defaults to `-g resource,node,pod` (comma-separated list or multiple calls) [possible values: resource, node, pod, namespace] 92 | -o, --output 93 | Output format [default: table] [possible values: table, csv] 94 | -h, --help 95 | Print help 96 | -V, --version 97 | Print version 98 | 99 | https://github.com/davidB/kubectl-view-allocations 100 | ``` 101 | 102 | ### Show gpu allocation 103 | 104 | ```sh 105 | 106 | > kubectl-view-allocations -r gpu 107 | 108 | Resource Requested Limit Allocatable Free 109 | nvidia.com/gpu (71%) 10.0 (71%) 10.0 14.0 4.0 110 | ├─ node-gpu1 (0%) __ (0%) __ 2.0 2.0 111 | ├─ node-gpu2 (0%) __ (0%) __ 2.0 2.0 112 | ├─ node-gpu3 (100%) 2.0 (100%) 2.0 2.0 __ 113 | │ └─ fah-gpu-cpu-d29sc 2.0 2.0 __ __ 114 | ├─ node-gpu4 (100%) 2.0 (100%) 2.0 2.0 __ 115 | │ └─ fah-gpu-cpu-hkg59 2.0 2.0 __ __ 116 | ├─ node-gpu5 (100%) 2.0 (100%) 2.0 2.0 __ 117 | │ └─ fah-gpu-cpu-nw9fc 2.0 2.0 __ __ 118 | ├─ node-gpu6 (100%) 2.0 (100%) 2.0 2.0 __ 119 | │ └─ fah-gpu-cpu-gtwsf 2.0 2.0 __ __ 120 | └─ node-gpu7 (100%) 2.0 (100%) 2.0 2.0 __ 121 | └─ fah-gpu-cpu-x7zfb 2.0 2.0 __ __ 122 | ``` 123 | 124 | ### Overview only 125 | 126 | ```sh 127 | > kubectl-view-allocations -g resource 128 | 129 | Resource Requested Limit Allocatable Free 130 | cpu (21%) 56.7 (65%) 176.1 272.0 95.9 131 | ephemeral-storage (0%) __ (0%) __ 38.4T 38.4T 132 | memory (8%) 52.7Gi (15%) 101.3Gi 675.6Gi 574.3Gi 133 | nvidia.com/gpu (71%) 10.0 (71%) 10.0 14.0 4.0 134 | pods (9%) 147.0 (9%) 147.0 1.6k 1.5k 135 | ``` 136 | 137 | ### Show utilization 138 | 139 | - Utilization information are retrieve from [metrics-server](https://github.com/kubernetes-incubator/metrics-server) (should be setup on your cluster). 140 | - Only report cpu and memory utilization 141 | 142 | ```sh 143 | > kubectl-view-allocations -u 144 | 145 | Resource Utilization Requested Limit Allocatable Free 146 | cpu (0%) 9.0m (10%) 200.0m __ 2.0 1.8 147 | └─ lima-rancher-desktop (0%) 9.0m (10%) 200.0m __ 2.0 1.8 148 | ├─ coredns-96cc4f57d-57cj9 1.0m 100.0m __ __ __ 149 | ├─ local-path-provisioner-84bb864455-czzcg 1.0m __ __ __ __ 150 | ├─ metrics-server-ff9dbcb6c-kb7x9 4.0m 100.0m __ __ __ 151 | ├─ svclb-traefik-ggd2q 2.0m __ __ __ __ 152 | └─ traefik-55fdc6d984-sqp57 1.0m __ __ __ __ 153 | ephemeral-storage __ __ __ 99.8G __ 154 | └─ lima-rancher-desktop __ __ __ 99.8G __ 155 | memory (1%) 51.0Mi (2%) 140.0Mi (3%) 170.0Mi 5.8Gi 5.6Gi 156 | └─ lima-rancher-desktop (1%) 51.0Mi (2%) 140.0Mi (3%) 170.0Mi 5.8Gi 5.6Gi 157 | ├─ coredns-96cc4f57d-57cj9 11.5Mi 70.0Mi 170.0Mi __ __ 158 | ├─ local-path-provisioner-84bb864455-czzcg 6.2Mi __ __ __ __ 159 | ├─ metrics-server-ff9dbcb6c-kb7x9 14.9Mi 70.0Mi __ __ __ 160 | ├─ svclb-traefik-ggd2q 548.0Ki __ __ __ __ 161 | └─ traefik-55fdc6d984-sqp57 17.9Mi __ __ __ __ 162 | pods __ (5%) 5.0 (5%) 5.0 110.0 105.0 163 | └─ lima-rancher-desktop __ (5%) 5.0 (5%) 5.0 110.0 105.0 164 | ``` 165 | 166 | ### Group by namespaces 167 | 168 | ```sh 169 | > kubectl-view-allocations -g namespace 170 | 171 | Resource Requested Limit Allocatable Free 172 | cpu (10%) 200.0m __ 2.0 1.8 173 | └─ kube-system 200.0m __ __ __ 174 | ephemeral-storage __ __ 99.8G __ 175 | memory (2%) 140.0Mi (3%) 170.0Mi 5.8Gi 5.6Gi 176 | └─ kube-system 140.0Mi 170.0Mi __ __ 177 | pods (5%) 5.0 (5%) 5.0 110.0 105.0 178 | └─ kube-system 5.0 5.0 __ __ 179 | ``` 180 | 181 | ### Show as csv 182 | 183 | In this case value as expanded as float (with 2 decimal) 184 | 185 | ```sh 186 | kubectl-view-allocations -o csv 187 | Date,Kind,resource,node,pod,Requested,%Requested,Limit,%Limit,Allocatable,Free 188 | 2020-08-19T19:12:48.326605746+00:00,resource,cpu,,,59.94,22%,106.10,39%,272.00,165.90 189 | 2020-08-19T19:12:48.326605746+00:00,node,cpu,node-gpu1,,2.31,19%,4.47,37%,12.00,7.53 190 | 2020-08-19T19:12:48.326605746+00:00,pod,cpu,node-gpu1,yyy-b8bd56fbd-5x8vq,1.00,,2.00,,, 191 | 2020-08-19T19:12:48.326605746+00:00,pod,cpu,node-gpu1,kube-flannel-ds-amd64-7dz9z,0.10,,0.10,,, 192 | 2020-08-19T19:12:48.326605746+00:00,pod,cpu,node-gpu1,node-exporter-gpu-b4w7s,0.11,,0.22,,, 193 | 2020-08-19T19:12:48.326605746+00:00,pod,cpu,node-gpu1,xxx-backend-7d84544458-46qnh,1.00,,2.00,,, 194 | 2020-08-19T19:12:48.326605746+00:00,pod,cpu,node-gpu1,weave-scope-agent-bbdnz,0.10,,0.15,,, 195 | 2020-08-19T19:12:48.326605746+00:00,node,cpu,node-gpu2,,0.31,1%,0.47,2%,24.00,23.53 196 | 2020-08-19T19:12:48.326605746+00:00,pod,cpu,node-gpu2,kube-flannel-ds-amd64-b5b4v,0.10,,0.10,,, 197 | 2020-08-19T19:12:48.326605746+00:00,pod,cpu,node-gpu2,node-exporter-gpu-796jz,0.11,,0.22,,, 198 | 2020-08-19T19:12:48.326605746+00:00,pod,cpu,node-gpu2,weave-scope-agent-8rhnd,0.10,,0.15,,, 199 | 2020-08-19T19:12:48.326605746+00:00,node,cpu,node-gpu3,,3.41,11%,6.67,21%,32.00,25.33 200 | ... 201 | ``` 202 | 203 | It can be combined with "group-by" options. 204 | 205 | ```sh 206 | kubectl-view-allocations -g resource -o csv 207 | Date,Kind,resource,Requested,%Requested,Limit,%Limit,Allocatable,Free 208 | 2020-08-19T19:11:49.630864028+00:00,resource,cpu,59.94,22%,106.10,39%,272.00,165.90 209 | 2020-08-19T19:11:49.630864028+00:00,resource,ephemeral-storage,0.00,0%,0.00,0%,34462898618662.00,34462898618662.00 210 | 2020-08-19T19:11:49.630864028+00:00,resource,hugepages-1Gi,0.00,,0.00,,, 211 | 2020-08-19T19:11:49.630864028+00:00,resource,hugepages-2Mi,0.00,,0.00,,, 212 | 2020-08-19T19:11:49.630864028+00:00,resource,memory,69063409664.00,10%,224684670976.00,31%,722318667776.00,497633996800.00 213 | 2020-08-19T19:11:49.630864028+00:00,resource,nvidia.com/gpu,3.00,27%,3.00,27%,11.00,8.00 214 | 2020-08-19T19:11:49.630864028+00:00,resource,pods,0.00,0%,0.00,0%,1540.00,1540.00 215 | ``` 216 | 217 | ## Alternatives & Similars 218 | 219 | - see the discussion [Need simple kubectl command to see cluster resource usage · Issue #17512 · kubernetes/kubernetes](https://github.com/kubernetes/kubernetes/issues/17512) 220 | - For CPU & Memory only 221 | - [ahmetb/kubectl-node_resource: Query node allocations/utilization in kubectl](https://github.com/ahmetb/kubectl-node_resource) 222 | - [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), 223 | - [hjacobs/kube-resource-report: Report Kubernetes cluster and pod resource requests vs usage and generate static HTML](https://github.com/hjacobs/kube-resource-report) 224 | - [etopeter/kubectl-view-utilization: kubectl plugin to show cluster CPU and Memory requests utilization](https://github.com/etopeter/kubectl-view-utilization) 225 | - For CPU & Memory utilization only 226 | - `kubectl top pods` 227 | - [LeastAuthority/kubetop: A top(1)-like tool for Kubernetes.](https://github.com/LeastAuthority/kubetop) 228 | -------------------------------------------------------------------------------- /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() < 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 | 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 | Some(self.cmp(other)) 185 | } 186 | } 187 | 188 | impl Ord for Qty { 189 | //TODO optimize accuracy with big number 190 | fn cmp(&self, other: &Self) -> Ordering { 191 | let v1 = self.value; // f64::from(self); 192 | let v2 = other.value; // f64::from(other); 193 | v1.partial_cmp(&v2).unwrap() // i64 should always be comparable (no NaNs or anything crazy like that) 194 | } 195 | } 196 | 197 | pub fn select_scale_for_add(v1: &Qty, v2: &Qty) -> Scale { 198 | if v2.value == 0 { 199 | v1.scale.clone() 200 | } else if v1.value == 0 { 201 | v2.scale.clone() 202 | } else { 203 | v1.scale.min(&v2.scale) 204 | } 205 | } 206 | 207 | impl std::ops::Add for Qty { 208 | type Output = Qty; 209 | fn add(self, other: Self) -> Qty { 210 | &self + &other 211 | } 212 | } 213 | 214 | impl std::ops::Add for &Qty { 215 | type Output = Qty; 216 | fn add(self, other: Self) -> Qty { 217 | Qty { 218 | value: self.value + other.value, 219 | scale: select_scale_for_add(self, other), 220 | } 221 | } 222 | } 223 | 224 | impl<'b> std::ops::AddAssign<&'b Qty> for Qty { 225 | fn add_assign(&mut self, other: &'b Self) { 226 | *self = Qty { 227 | value: self.value + other.value, 228 | scale: select_scale_for_add(self, other), 229 | } 230 | } 231 | } 232 | 233 | impl std::ops::Sub for Qty { 234 | type Output = Qty; 235 | fn sub(self, other: Self) -> Qty { 236 | &self - &other 237 | } 238 | } 239 | 240 | impl std::ops::Sub for &Qty { 241 | type Output = Qty; 242 | fn sub(self, other: Self) -> Qty { 243 | Qty { 244 | value: self.value - other.value, 245 | scale: select_scale_for_add(self, other), 246 | } 247 | } 248 | } 249 | 250 | impl<'b> std::ops::SubAssign<&'b Qty> for Qty { 251 | fn sub_assign(&mut self, other: &'b Self) { 252 | *self = Qty { 253 | value: self.value - other.value, 254 | scale: select_scale_for_add(self, other), 255 | }; 256 | } 257 | } 258 | 259 | #[cfg(test)] 260 | mod tests { 261 | use super::*; 262 | use pretty_assertions::assert_eq; 263 | 264 | macro_rules! assert_is_close { 265 | ($x:expr, $y:expr, $range:expr) => { 266 | assert!($x >= ($y - $range)); 267 | assert!($x <= ($y + $range)); 268 | }; 269 | } 270 | 271 | #[test] 272 | fn test_to_base() -> Result<(), Box> { 273 | assert_is_close!( 274 | f64::from(&Qty::from_str("1k")?), 275 | f64::from(&Qty::from_str("1000000m")?), 276 | 0.01 277 | ); 278 | assert_eq!( 279 | Qty::from_str("1Ki")?, 280 | Qty { 281 | value: 1024000, 282 | scale: Scale { 283 | label: "Ki", 284 | base: 2, 285 | pow: 10, 286 | }, 287 | } 288 | ); 289 | Ok(()) 290 | } 291 | 292 | #[test] 293 | fn expectation_ok_for_adjust_scale() -> Result<(), Box> { 294 | let cases = vec![ 295 | ("1k", "1.0k"), 296 | ("10k", "10.0k"), 297 | ("100k", "100.0k"), 298 | ("999k", "999.0k"), 299 | ("1000k", "1.0M"), 300 | ("1999k", "2.0M"), //TODO 1.9M should be better ? 301 | ("1Ki", "1.0Ki"), 302 | ("10Ki", "10.0Ki"), 303 | ("100Ki", "100.0Ki"), 304 | ("1000Ki", "1000.0Ki"), 305 | ("1024Ki", "1.0Mi"), 306 | ("25641877504", "25.6G"), 307 | ("1770653738944", "1.8T"), 308 | ("1000m", "1.0"), 309 | ("100m", "100.0m"), 310 | ("1m", "1.0m"), 311 | ]; 312 | for (input, expected) in cases { 313 | assert_eq!( 314 | format!("{}", &Qty::from_str(input)?.adjust_scale()), 315 | expected.to_string() 316 | ); 317 | } 318 | Ok(()) 319 | } 320 | 321 | #[test] 322 | fn test_display() -> Result<(), Box> { 323 | let cases = vec![ 324 | ("1k", "1.0k"), 325 | ("10k", "10.0k"), 326 | ("100k", "100.0k"), 327 | ("999k", "999.0k"), 328 | ("1000k", "1000.0k"), 329 | ("1999k", "1999.0k"), 330 | ("1Ki", "1.0Ki"), 331 | ("10Ki", "10.0Ki"), 332 | ("100Ki", "100.0Ki"), 333 | ("1000Ki", "1000.0Ki"), 334 | ("1024Ki", "1024.0Ki"), 335 | ("25641877504", "25641877504.0"), 336 | ("1000m", "1000.0m"), 337 | ("100m", "100.0m"), 338 | ("1m", "1.0m"), 339 | ("1000000n", "1000000.0n"), 340 | // lowest precision is m, under 1m value is trunked 341 | ("1u", "0.0u"), 342 | ("1μ", "0.0μ"), 343 | ("1n", "0.0n"), 344 | ("999999n", "0.0n"), 345 | ]; 346 | for input in cases { 347 | assert_eq!(format!("{}", &Qty::from_str(input.0)?), input.1.to_string()); 348 | assert_eq!(format!("{}", &Qty::from_str(input.1)?), input.1.to_string()); 349 | } 350 | Ok(()) 351 | } 352 | 353 | #[test] 354 | fn test_f64_from_scale() -> Result<(), Box> { 355 | assert_is_close!(f64::from(&Scale::from_str("m")?), 0.001, 0.00001); 356 | Ok(()) 357 | } 358 | 359 | #[test] 360 | fn test_f64_from_qty() -> Result<(), Box> { 361 | assert_is_close!(f64::from(&Qty::from_str("20m")?), 0.020, 0.00001); 362 | assert_is_close!(f64::from(&Qty::from_str("300m")?), 0.300, 0.00001); 363 | assert_is_close!(f64::from(&Qty::from_str("1000m")?), 1.000, 0.00001); 364 | assert_is_close!(f64::from(&Qty::from_str("+1000m")?), 1.000, 0.00001); 365 | assert_is_close!(f64::from(&Qty::from_str("-1000m")?), -1.000, 0.00001); 366 | assert_is_close!( 367 | f64::from(&Qty::from_str("3145728e3")?), 368 | 3145728000.000, 369 | 0.00001 370 | ); 371 | Ok(()) 372 | } 373 | 374 | #[test] 375 | fn test_add() -> Result<(), Box> { 376 | assert_eq!( 377 | (Qty::from_str("1")? 378 | + Qty::from_str("300m")? 379 | + Qty::from_str("300m")? 380 | + Qty::from_str("300m")? 381 | + Qty::from_str("300m")?), 382 | Qty::from_str("2200m")? 383 | ); 384 | assert_eq!( 385 | Qty::default() + Qty::from_str("300m")?, 386 | Qty::from_str("300m")? 387 | ); 388 | assert_eq!( 389 | Qty::default() + Qty::from_str("16Gi")?, 390 | Qty::from_str("16Gi")? 391 | ); 392 | assert_eq!( 393 | Qty::from_str("20m")? + Qty::from_str("300m")?, 394 | Qty::from_str("320m")? 395 | ); 396 | assert_eq!( 397 | &(Qty::from_str("1k")? + Qty::from_str("300m")?), 398 | &Qty::from_str("1000300m")? 399 | ); 400 | assert_eq!( 401 | &(Qty::from_str("1Ki")? + Qty::from_str("1Ki")?), 402 | &Qty::from_str("2Ki")? 403 | ); 404 | assert_eq!( 405 | &(Qty::from_str("1Ki")? + Qty::from_str("1k")?), 406 | &Qty { 407 | value: 2024000, 408 | scale: Scale { 409 | label: "k", 410 | base: 10, 411 | pow: 3, 412 | }, 413 | } 414 | ); 415 | Ok(()) 416 | } 417 | } 418 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | 4 | ## Version x.y.z-dev 5 | 6 | 7 | ## Version 0.23.0 8 | 9 | ### Changed 10 | - ⬆️ Bump tokio from 1.46.1 to 1.47.0 11 | - ⬆️ Bump serde_json from 1.0.140 to 1.0.141 12 | - ⬆️ Bump clap from 4.5.40 to 4.5.41 13 | - ⬆️ Bump tokio from 1.45.1 to 1.46.1 14 | 15 | 16 | ## Version 0.22.1 17 | 18 | 19 | ## Version 0.22.0 20 | 21 | ### Added 22 | - ✨ allow a list of namespaces to be passed 23 | 24 | ### Changed 25 | - ⬆️ Bump clap from 4.5.39 to 4.5.40 26 | 27 | ### Miscellaneous 28 | - 🚀 (cargo-release) version 0.22.0 29 | 30 | 31 | ## Version 0.21.2 32 | 33 | ### Changed 34 | - ⬆️ Bump color-eyre from 0.6.4 to 0.6.5 35 | - ⬆️ Bump tokio from 1.45.0 to 1.45.1 36 | - ⬆️ Bump clap from 4.5.38 to 4.5.39 37 | 38 | ### Miscellaneous 39 | - 🚀 (cargo-release) version 0.21.2 40 | 41 | 42 | ## Version 0.21.1 43 | 44 | ### Miscellaneous 45 | - 🚀 (cargo-release) version 0.21.1 46 | 47 | 48 | ## Version 0.21.0 49 | 50 | ### Changed 51 | - ⬆️ upgrade to rust 1.86 52 | - ⬆️ Bump chrono from 0.4.40 to 0.4.41 53 | - ⬆️ Bump clap from 4.5.36 to 4.5.37 54 | - ⬆️ Bump anyhow from 1.0.97 to 1.0.98 55 | - ⬆️ Bump clap from 4.5.35 to 4.5.36 56 | - ⬆️ Bump tokio from 1.44.1 to 1.44.2 57 | - ⬆️ Bump clap from 4.5.34 to 4.5.35 58 | - ⬆️ Bump clap from 4.5.32 to 4.5.34 (#257) 59 | - ⬆️ Bump mozilla-actions/sccache-action from 0.0.8 to 0.0.9 (#256) 60 | 61 | ### Miscellaneous 62 | - 🚀 (cargo-release) version 0.21.0 63 | 64 | 65 | ## Version 0.20.5 66 | 67 | ### Changed 68 | - ⬆️ Bump mozilla-actions/sccache-action from 0.0.7 to 0.0.8 69 | 70 | ### Miscellaneous 71 | - 🚀 (cargo-release) version 0.20.5 72 | - 🚀 (cargo-release) version 0.20.4 73 | 74 | 75 | ## Version 0.20.3 76 | 77 | ### Miscellaneous 78 | - 🚀 (cargo-release) version 0.20.3 79 | 80 | 81 | ## Version 0.20.2 82 | 83 | ### Changed 84 | - ⬆️ Update itertools requirement from 0.13 to 0.14 85 | - ⬆️ Bump mozilla-actions/sccache-action from 0.0.6 to 0.0.7 86 | - ⬆️ Update kube requirement from 0.96 to 0.97 87 | - ⬆️ Update thiserror requirement from 1.0 to 2.0 88 | - ⬆️ Bump rajatjindal/krew-release-bot from 0.0.46 to 0.0.47 89 | 90 | ### Miscellaneous 91 | - 🚀 (cargo-release) version 0.20.2 92 | 93 | 94 | ## Version 0.20.1 95 | 96 | ### Miscellaneous 97 | - 🚀 (cargo-release) version 0.20.1 98 | 99 | 100 | ## Version 0.20.0 101 | 102 | ### Changed 103 | - ⬆️ Update itertools requirement from 0.12 to 0.13 104 | 105 | ### Miscellaneous 106 | - 🚀 (cargo-release) version 0.20.0 107 | 108 | 109 | ## Version 0.19.2 110 | 111 | ### Changed 112 | - ⬆️ Bump actions/cache from 3 to 4 113 | 114 | ### Miscellaneous 115 | - 🚀 (cargo-release) version 0.19.2 116 | - 🚀 (cargo-release) version 0.19.1 117 | - 🚀 (cargo-release) version 0.19.0 118 | 119 | 120 | ## Version 0.18.1 121 | 122 | ### Miscellaneous 123 | - 🚀 (cargo-release) version 0.18.1 124 | 125 | 126 | ## Version 0.18.0 127 | 128 | ### Changed 129 | - ⬆️ Bump actions/checkout from 3 to 4 130 | 131 | ### Miscellaneous 132 | - 🚀 (cargo-release) version 0.18.0 133 | 134 | 135 | ## Version 0.17.2 136 | 137 | ### Miscellaneous 138 | - 🚀 (cargo-release) version 0.17.2 139 | 140 | 141 | ## Version 0.17.1 142 | 143 | ### Changed 144 | - ⬆️ Update itertools requirement from 0.10 to 0.11 145 | 146 | ### Miscellaneous 147 | - 🚀 (cargo-release) version 0.17.1 148 | - 🚀 (cargo-release) version 0.17.0 149 | 150 | 151 | ## Version 0.16.3 152 | 153 | ### Miscellaneous 154 | - 🚀 (cargo-release) version 0.16.3 155 | 156 | 157 | ## Version 0.16.2 158 | 159 | ### Miscellaneous 160 | - 🚀 (cargo-release) version 0.16.2 161 | 162 | 163 | ## Version 0.16.1 164 | 165 | ### Miscellaneous 166 | - 🚀 (cargo-release) version 0.16.1 167 | 168 | 169 | ## Version 0.16.0 170 | 171 | ### Changed 172 | - ⬆️ Update prettytable-rs requirement from 0.9 to 0.10 173 | - ⬆️ Bump actions/cache from 3.0.11 to 3.2.5 174 | - ⬆️ Bump actions/cache from 3.0.9 to 3.0.11 175 | - ⬆️ Bump actions/cache from 3.0.8 to 3.0.9 176 | - ⬆️ Bump actions/cache from 3.0.7 to 3.0.8 177 | 178 | ### Miscellaneous 179 | - 🚀 (cargo-release) version 0.16.0 180 | - 🚧 (cargo-release) start next development iteration 0.15.2-dev 181 | 182 | 183 | ## Version 0.15.1 184 | 185 | ### Changed 186 | - ⬆️ Bump actions/cache from 3.0.5 to 3.0.7 187 | - ⬆️ Update prettytable-rs requirement from 0.8 to 0.9 188 | 189 | ### Fixed 190 | - 🐛 parse unit `u` and `μ` as micro and truncate to 0 191 | 192 | ### Miscellaneous 193 | - 🚀 (cargo-release) version 0.15.1 194 | - 🚧 (cargo-release) start next development iteration 0.15.1-dev 195 | 196 | 197 | ## Version 0.15.0 198 | 199 | ### Changed 200 | - 🚨 fix few warning 201 | - ⬆️ Update clap requirement from 3.0 to 3.2 202 | - ⬆️ Update k8s-openapi requirement from 0.14.0 to 0.15.0 203 | - ⬆️ Update kube requirement from 0.70.0 to 0.74.0 204 | - ⬆️ Bump actions/cache from 3.0.3 to 3.0.5 205 | - ⬆️ Bump actions/cache from 3.0.1 to 3.0.3 206 | - ⬆️ Update kube requirement from 0.69.1 to 0.70.0 207 | - ⬆️ Bump actions/cache from 2 to 3.0.1 208 | - ⬆️ Bump actions/checkout from 2.4.0 to 3 209 | 210 | ### Fixed 211 | - 🐛 fix stackoverflow 212 | 213 | ### Miscellaneous 214 | - 🚀 (cargo-release) version 0.15.0 215 | - 🚧 (cargo-release) start next development iteration 0.14.9-dev 216 | 217 | 218 | ## Version 0.14.8 219 | 220 | ### Miscellaneous 221 | - 🚀 (cargo-release) version 0.14.8 222 | - 📦 add missing LICENSE file into archive 223 | - 🚧 (cargo-release) start next development iteration 0.14.8-dev 224 | 225 | 226 | ## Version 0.14.7 227 | 228 | ### Added 229 | - 👷 refactor the ci/cd 230 | 231 | ### Changed 232 | - ⬆️ Update kube requirement from 0.67.0 to 0.69.1 233 | 234 | ### Removed 235 | - 🔇 hide warn log added (for debug) by kube-client 236 | 237 | ### Miscellaneous 238 | - 🚀 (cargo-release) version 0.14.7 239 | - 🚧 set as pre-release when not on a version tag 240 | - pencil update sample in README 241 | 242 | 243 | ## Version 0.14.7-dev 244 | 245 | ### Added 246 | - 👷 allow to trigger release-flow without tag 247 | - ➕ switch from structop to clap 3 248 | 249 | ### Changed 250 | - ⬆️ Bump actions/checkout from 2.3.4 to 2.4.0 251 | - 🚨 clippy suggestion 252 | - ⬆️ upgrade dependencies 253 | 254 | ### Miscellaneous 255 | - 🚧 (cargo-release) start next development iteration 0.14.7-dev 256 | 257 | 258 | ## Version 0.14.6 259 | 260 | ### Changed 261 | - ⬆️ Update kube requirement from 0.59 to 0.60 262 | 263 | ### Miscellaneous 264 | - 🚀 (cargo-release) version 0.14.6 265 | - 🚧 (cargo-release) start next development iteration 0.14.6-dev 266 | 267 | 268 | ## Version 0.14.5 269 | 270 | ### Added 271 | - 👷 add cache and a integration test with k3d 272 | 273 | ### Changed 274 | - 📌 be more specific about anyhow version 275 | - 🍱 add a sample to test api<pod>.list 276 | - ⬆️ bump kube to 0.59 from 0.57 (and k8s-openapi) 277 | 278 | ### Fixed 279 | - 🐛 prettytable was badly integrated as feature 280 | 281 | ### Miscellaneous 282 | - 🚀 (cargo-release) version 0.14.5 283 | - 🚀 (cargo-release) version 0.14.4 284 | - 🚧 (cargo-release) start next development iteration 0.14.4-dev 285 | 286 | 287 | ## Version 0.14.3 288 | 289 | ### Miscellaneous 290 | - 🚀 (cargo-release) version 0.14.3 291 | - 🚧 (cargo-release) start next development iteration 0.14.3-dev 292 | 293 | 294 | ## Version 0.14.2 295 | 296 | ### Changed 297 | - 🎨 format Cargo.toml 298 | 299 | ### Fixed 300 | - ✏️ fixing typo in level 301 | 302 | ### Miscellaneous 303 | - 🚀 (cargo-release) version 0.14.2 304 | - 🚧 (cargo-release) start next development iteration 0.14.2-dev 305 | 306 | 307 | ## Version 0.14.1 308 | 309 | ### Changed 310 | - ♻️ make library more flexible (#137) 311 | - ⬆️ Bump kube from 0.55.0 to 0.56.0 312 | - 🚨 fix warning (missing part to move behind feature flag 313 | - ♻️ moving some dependencies behind feature flags 314 | - ⬆️ no longer enforce patch level for dependencies 315 | - 🚨 apply clippy suggestion 316 | - ⬆️ Bump tokio from 1.6.0 to 1.6.1 317 | 318 | ### Fixed 319 | - 🐛 error report the wrong qualifier 320 | 321 | ### Miscellaneous 322 | - 🚀 (cargo-release) version 0.14.1 323 | - pencil add instruction to use as lib 324 | - 🙈 no longer commit Cargo.lock to thevois interference when used as lib 325 | - ⚗ try to not force k8s-openapi feature in lib mode 326 | - 🚧 (cargo-release) start next development iteration 0.14.1-dev 327 | 328 | 329 | ## Version 0.14.0 330 | 331 | ### Added 332 | - ✅ fix doc test 333 | - ✨ split the code to allow to use the crate as a lib 334 | 335 | ### Changed 336 | - ⬆️ kube explicit list of features 337 | - 🎨 format 338 | - 🏗 move from anyhow Error to explicit Error enum (use thiserror) 339 | - 🏗 move from log to tracing 340 | - ⬆️ Bump tokio from 1.5.0 to 1.6.0 341 | - ⬆️ Bump serde from 1.0.125 to 1.0.126 342 | - ⬆️ Bump kube from 0.52.0 to 0.53.0 343 | - ⬆️ Bump openssl from 0.10.33 to 0.10.34 344 | - ⬆️ Bump tokio from 1.4.0 to 1.5.0 345 | 346 | ### Breaking changes 347 | - 💥 restore original behavior only try to retrieve utilisation if explicitly requested 348 | - 💥 try to retrieve utilisation by default 349 | 350 | ### Miscellaneous 351 | - 🚀 (cargo-release) version 0.14.0 352 | - pencil add Similars project 353 | - pencil update doc about reporting of utilization" 354 | - pencil complete the list of Alternatives 355 | - 🚧 (cargo-release) start next development iteration 0.13.1-dev 356 | 357 | 358 | ## Version 0.13.0 359 | 360 | ### Added 361 | - ✨ add `--utilization` to retrieve current utilization of resource (only for cpu & memory) for metrics-server 362 | 363 | ### Changed 364 | - 💄 display "__" instead of "0.0" or blank in cell when the data is not available or defined 365 | - ♻️ rename "resource usage" into resource qualifier 366 | - ⬆️ Bump kube from 0.51.0 to 0.52.0 367 | - ⬆️ Bump anyhow from 1.0.39 to 1.0.40 368 | - ⬆️ Bump tokio from 1.3.0 to 1.4.0 369 | - ⬆️ Bump anyhow from 1.0.38 to 1.0.39 370 | - ⬆️ Bump openssl from 0.10.32 to 0.10.33 371 | - ⬆️ Bump tokio from 1.2.0 to 1.3.0 372 | - ⬆️ Bump kube from 0.50.1 to 0.51.0 373 | - ⬆️ Bump serde_json from 1.0.62 to 1.0.64 374 | 375 | ### Fixed 376 | - 🐛 utilization is at least `1m` if defined 377 | 378 | ### Miscellaneous 379 | - 🚀 (cargo-release) version 0.13.0 380 | - 💡 when metrics API server request failed, include the same error message than for "kubectl top node" to ease resolution for user 381 | - 🚧 (cargo-release) start next development iteration 0.12.2-dev 382 | 383 | 384 | ## Version 0.12.1 385 | 386 | ### Fixed 387 | - 🐛 fix regression on authentication via oidc 388 | 389 | ### Miscellaneous 390 | - 🚀 (cargo-release) version 0.12.1 391 | - 🚧 (cargo-release) start next development iteration 0.12.1-dev 392 | 393 | 394 | ## Version 0.12.0 395 | 396 | ### Changed 397 | - ⬆️ enable features from kube to be iso with previous version (able to work with oauth, gke,...) 398 | - ⬆️ Bump env_logger from 0.8.2 to 0.8.3 399 | - ⬆️ Bump kube from 0.48.0 to 0.50.0 400 | - ⬆️ Bump tokio from 1.1.1 to 1.2.0 401 | - ⬆️ Bump serde_json from 1.0.61 to 1.0.62 402 | - ⬆️ Bump tokio from 1.1.0 to 1.1.1 403 | 404 | ### Miscellaneous 405 | - 🚀 (cargo-release) version 0.12.0 406 | - pencil add instruction to install via krew 407 | - 🚧 (cargo-release) start next development iteration 0.11.1-dev 408 | 409 | 410 | ## Version 0.11.0 411 | 412 | ### Added 413 | - ✨ add `--context` cli flag to specify the kubernetes context to use 414 | 415 | ### Changed 416 | - ⬆️ upgrade kube from 0.47 to 0.48 417 | - ⬆️ Bump tokio from 1.0.2 to 1.1.0 418 | - ⬆️ Bump tokio from 1.0.1 to 1.0.2 419 | 420 | ### Miscellaneous 421 | - 🚀 (cargo-release) version 0.11.0 422 | - 🚧 (cargo-release) start next development iteration 0.10.1-dev 423 | 424 | 425 | ## Version 0.10.0 426 | 427 | ### Changed 428 | - ⬆️ cargo update 429 | - ⬆️ Bump kube from 0.46.0 to 0.47.0 430 | - ⬆️ Bump anyhow from 1.0.37 to 1.0.38 431 | - ⬆️ Bump anyhow from 1.0.36 to 1.0.37 432 | - ⬆️ Bump kube from 0.45.0 to 0.46.0 433 | - ⬆️ Bump serde_json from 1.0.60 to 1.0.61 434 | - ⬆️ upgrade k8s-openapi to be align with kube 435 | - ⬆️ Bump kube from 0.43.0 to 0.45.0 436 | - ⬆️ Bump openssl from 0.10.30 to 0.10.32 437 | - ⬆️ Bump itertools from 0.9.0 to 0.10.0 438 | - ⬆️ Bump anyhow from 1.0.35 to 1.0.36 439 | - ⬆️ Bump anyhow from 1.0.34 to 1.0.35 440 | - ⬆️ Bump structopt from 0.3.20 to 0.3.21 441 | - ⬆️ Bump serde_json from 1.0.59 to 1.0.60 442 | - ⬆️ Bump env_logger from 0.8.1 to 0.8.2 443 | - ⬆️ Bump tokio from 0.2.23 to 0.3.5 444 | - ⬆️ Bump tokio from 0.2.22 to 0.2.23 445 | - ⬆️ Bump actions/checkout from v1 to v2.3.4 446 | 447 | ### Miscellaneous 448 | - 🚀 (cargo-release) version 0.10.0 449 | - pencil update README 450 | - 🚧 (cargo-release) start next development iteration 0.9.3-dev 451 | 452 | 453 | ## Version 0.9.2 454 | 455 | ### Changed 456 | - ⬆️ upgrade config of dependabot from version 1 to 2 457 | - ⬆️ Bump anyhow from 1.0.33 to 1.0.34 458 | 459 | ### Miscellaneous 460 | - 🚀 (cargo-release) version 0.9.2 461 | - 🚧 (cargo-release) start next development iteration 0.9.2-dev 462 | 463 | 464 | ## Version 0.9.1 465 | 466 | ### Changed 467 | - 👽 update configuration of cargo-release 468 | - 💄 every qty are displayed with adjusted scale (use json ouput for full value) 469 | - 🎨 format code 470 | - ⬆️ Bump env_logger from 0.7.1 to 0.8.1 471 | - ⬆️ Bump serde_json from 1.0.58 to 1.0.59 472 | - ⬆️ Bump structopt from 0.3.19 to 0.3.20 473 | - ⬆️ Bump anyhow from 1.0.32 to 1.0.33 474 | - ⬆️ Bump structopt from 0.3.18 to 0.3.19 475 | - ⬆️ Bump kube from 0.42.0 to 0.43.0 476 | - ⬆️ Bump structopt from 0.3.17 to 0.3.18 477 | - ⬆️ Bump serde_json from 1.0.57 to 1.0.58 478 | - ⬆️ Bump chrono from 0.4.15 to 0.4.19 479 | - ⬆️ Bump kube from 0.40.0 to 0.42.0 480 | - ⬆️ Bump structopt from 0.3.16 to 0.3.17 481 | 482 | ### Miscellaneous 483 | - 🚀 (cargo-release) version 0.9.1 484 | - 🚀 (cargo-release) version 0.9.0 485 | - 🚧 (cargo-release) start next development iteration 0.8.3-dev 486 | 487 | 488 | ## Version 0.8.2 489 | 490 | ### Changed 491 | - 🚸 csv output export a date and the kind of the current line 492 | 493 | ### Miscellaneous 494 | - 🚀 (cargo-release) version 0.8.2 495 | - 🚧 (cargo-release) start next development iteration 0.8.2-dev 496 | 497 | 498 | ## Version 0.8.1 499 | 500 | ### Added 501 | - ✨ display output as csv 502 | 503 | ### Changed 504 | - 👽 update configuration of cargo-release 505 | - ⬆️ Bump kube from 0.39.0 to 0.40.0 506 | - ⬆️ Bump structopt from 0.3.15 to 0.3.16 507 | - ⬆️ Bump kube from 0.38.0 to 0.39.0 508 | - ⬆️ Bump serde_json from 1.0.56 to 1.0.57 509 | - ⬆️ Bump anyhow from 1.0.31 to 1.0.32 510 | - ⬆️ Bump kube from 0.37.0 to 0.38.0 511 | - ⬆️ Bump kube from 0.36.0 to 0.37.0 512 | - ⬆️ Bump tokio from 0.2.21 to 0.2.22 513 | - ⬆️ Bump kube from 0.35.1 to 0.36.0 514 | - ⬆️ Bump serde_json from 1.0.55 to 1.0.56 515 | - ⬆️ Bump openssl from 0.10.29 to 0.10.30 516 | - ⬆️ Bump kube from 0.35.0 to 0.35.1 517 | - ⬆️ Bump structopt from 0.3.14 to 0.3.15 518 | 519 | ### Miscellaneous 520 | - 🚀 (cargo-release) version 0.8.1 521 | - 🚀 (cargo-release) version 0.8.0 522 | - 🚧 (cargo-release) start next development iteration 0.7.4-dev 523 | 524 | 525 | ## Version 0.7.3 526 | 527 | ### Changed 528 | - ⬆️ Bump kube from 0.34.0 to 0.35.0 529 | - ⬆️ Bump serde_json from 1.0.54 to 1.0.55 530 | - ⬆️ Bump serde_json from 1.0.53 to 1.0.54 531 | - ⬆️ bump k8s-openapi to be sync with kube 532 | - ⬆️ Bump kube from 0.33.0 to 0.34.0 533 | - ⬆️ Bump anyhow from 1.0.30 to 1.0.31 534 | - ⬆️ Bump tokio from 0.2.20 to 0.2.21 535 | - ⬆️ Bump anyhow from 1.0.28 to 1.0.30 536 | - ⬆️ Bump serde_json from 1.0.52 to 1.0.53 537 | - ⬆️ Bump tokio from 0.2.19 to 0.2.20 538 | - ⬆️ Bump serde_json from 1.0.51 to 1.0.52 539 | - ⬆️ Bump kube from 0.32.1 to 0.33.0 540 | - ⬆️ Bump tokio from 0.2.18 to 0.2.19 541 | - ⬆️ Bump structopt from 0.3.13 to 0.3.14 542 | - ⬆️ Bump kube from 0.32.0 to 0.32.1 543 | - 🚨 remove useless variable binding 544 | - ⬆️ update dependencies 545 | - 👽 adapt to follow change in kube 0.32 api 546 | - ⬆️ Update kube requirement from 0.30.0 to 0.32.0 547 | - ⬆️ Update kube requirement from 0.29.0 to 0.30.0 548 | - 🚨 fix warning reported by clippy 549 | - ⬆️ Bump structopt from 0.2.11 to 0.2.13 550 | - ⬆️ Bump openssl from 0.10.27 to 0.10.28 551 | - ⬆️ Bump structopt from 0.3.9 to 0.3.11 552 | - ⬆️ Bump serde_json from 1.0.45 to 1.0.48 553 | - ⬆️ Bump kube from 0.28.0 to 0.29.0 554 | - 👽 update usage of kube & open-api 555 | - ⬆️ Update kube requirement from 0.27.0 to 0.28.0 556 | - ⬆️ Update itertools requirement from 0.8.2 to 0.9.0 557 | - ⬆️ Update kube requirement from 0.26.0 to 0.27.0 558 | - ⬆️ Update kube requirement from 0.25.0 to 0.26.0 559 | 560 | ### Miscellaneous 561 | - 🚀 (cargo-release) version 0.7.3 562 | - 🙈 do not ignore Cargo.lock for executable 563 | - 🚧 (cargo-release) start next development iteration 0.7.3-dev 564 | 565 | 566 | ## Version 0.7.2 567 | 568 | ### Changed 569 | - ⬆️ Update kube requirement from 0.24.0 to 0.25.0 570 | 571 | ### Breaking changes 572 | - 💥 display quantity with precision of 1 digit 573 | 574 | ### Fixed 575 | - 🐛 fix unit T & P (wrong power of 10) 576 | 577 | ### Miscellaneous 578 | - 🚀 (cargo-release) version 0.7.2 579 | - 🚧 (cargo-release) start next development iteration 0.7.2-dev 580 | - 🚀 (cargo-release) version 0.7.1 581 | 582 | 583 | ## Version 0.7.1 584 | 585 | ### Breaking changes 586 | - 💥 display quantity with precision of 1 digit 587 | 588 | ### Fixed 589 | - 🐛 fix unit T & P (wrong power of 10) 590 | 591 | ### Miscellaneous 592 | - 🚀 (cargo-release) version 0.7.1 593 | - 🚧 (cargo-release) start next development iteration 0.7.1-dev 594 | 595 | 596 | ## Version 0.7.0 597 | 598 | ### Changed 599 | - ⬆️ upgrade other dependencies because there are related: kube-rs, tokio. 600 | - ⬆️ Update k8s-openapi requirement from 0.6.0 to 0.7.1 601 | - ⚡️ (test) cache the ubuntu + curl docker image to avoid re-install of curl at each run of test_first_run.sh 602 | 603 | ### Fixed 604 | - 🐛 shebang is ignored when script call explicitly with `sh <script>` or `curl <script> | sh` because `sh` is explicitly called. The getLatest.sh failed with posix shell like dash (sh on ubuntu). 605 | 606 | ### Miscellaneous 607 | - 🚀 (cargo-release) version 0.7.0 608 | - 🔊 add info into getLastest.sh 609 | - 🚧 (cargo-release) start next development iteration 0.6.6-dev 610 | 611 | 612 | ## Version 0.6.5 613 | 614 | ### Fixed 615 | - 🐧 try to see if can be built on ubuntu 16.04 (a user request it) 616 | 617 | ### Miscellaneous 618 | - 🚀 (cargo-release) version 0.6.5 619 | - 🚧 (cargo-release) start next development iteration 0.6.5-dev 620 | 621 | 622 | ## Version 0.6.4 623 | 624 | ### Fixed 625 | - 🐛 quantity should support scientific notation with 'e', 'E', '+' and '-' as part of the number #13 626 | 627 | ### Miscellaneous 628 | - 🚀 (cargo-release) version 0.6.4 629 | - 🙈 gitignore macOS 630 | - 🚀 (cargo-release) version 0.6.3 631 | - 🔊 add more context information on some Error 632 | - 🚧 (cargo-release) start next development iteration 0.6.3-dev 633 | 634 | 635 | ## Version 0.6.2 636 | 637 | ### Changed 638 | - ⬆️ bump kube, tokio, anyhow, structopt 639 | 640 | ### Miscellaneous 641 | - 🚀 (cargo-release) version 0.6.2 642 | - 🚧 (cargo-release) start next development iteration 0.6.2-dev 643 | 644 | 645 | ## Version 0.6.1 646 | 647 | ### Changed 648 | - ⬆️ Update kube requirement from 0.20.0 to 0.22.1 649 | 650 | ### Fixed 651 | - 🚑 hack/workaround for gke/eks access 652 | 653 | ### Miscellaneous 654 | - 🚀 (cargo-release) version 0.6.1 655 | - 🚧 (cargo-release) start next development iteration 0.6.1-dev 656 | 657 | 658 | ## Version 0.6.0 659 | 660 | ### Changed 661 | - 🎨 clean 662 | - ⬆️ bump itertools to 0.3.5 from 0.3.3 663 | - ⬆️ bump itertools to 0.8.2 from 0.8.1 664 | - ⬆️ bump serde_json to 1.0.41 from 1.0.41 665 | - ⬆️ bump anyhow to 1.0.23 from 1.0.20 666 | - ⬆️ Update kube requirement from 0.19.0 to 0.20.0 667 | 668 | ### Fixed 669 | - 🐛 in addition use the scale of non-zero value 670 | - 🐛 fix the display and adjust_scale of Qty (cause by the refactor of internal) 671 | - 🐛 fix addition of qty 672 | - 💚 use latest v1 of rust-cargo-make 673 | 674 | ### Miscellaneous 675 | - 🚀 (cargo-release) version 0.6.0 676 | - ⚗ disable adjust_scale in the display because it round value maybe to aggressively 677 | - 🚧 (cargo-release) start next development iteration 0.5.2-dev 678 | 679 | 680 | ## Version 0.5.1 681 | 682 | ### Miscellaneous 683 | - 🚀 (cargo-release) version 0.5.1 684 | - pencil (README) add description of column 685 | - 📦 add LICENSE.txt into distribuable archive 686 | - 🚧 (cargo-release) start next development iteration 0.5.1-dev 687 | 688 | 689 | ## Version 0.5.0 690 | 691 | ### Changed 692 | - ⬆️ upgrade kube-rs to 0.19.0 + move from failure to anyhow 693 | - ⬆️ Update kube requirement from 0.17.1 to 0.18.1 694 | 695 | ### Fixed 696 | - 🐛 fix url to retreive the latest version 697 | 698 | ### Miscellaneous 699 | - 🚀 (cargo-release) version 0.5.0 700 | - poop introduce a workaround to have valid (non-expired) token and config to access the cluster 701 | - ⚗ try to compile staticly openssl 702 | - 🚧 (cargo-release) start next development iteration 0.4.2-dev 703 | 704 | 705 | ## Version 0.4.1 706 | 707 | ### Changed 708 | - ⬆️ Update kube requirement from 0.8.0 to 0.8.1 709 | - ⬆️ Update kube requirement from 0.17.0 to 0.17.1 710 | 711 | ### Fixed 712 | - 💚 switch to macOS-latest (macOS-10.14 is deprecated and broke the build) 713 | 714 | ### Miscellaneous 715 | - 🚀 (cargo-release) version 0.4.1 716 | - pencil (README) update general description. 717 | - 🚧 (cargo-release) start next development iteration 0.4.1-dev 718 | 719 | 720 | ## Version 0.4.0 721 | 722 | ### Fixed 723 | - 🐛 do not include non-running pod into resource consumer 724 | 725 | ### Miscellaneous 726 | - 🚀 (cargo-release) version 0.4.0 727 | - 🚧 (cargo-release) start next development iteration 0.3.1-dev 728 | 729 | 730 | ## Version 0.3.0 731 | 732 | ### Added 733 | - ✨ add cli options to configure usages'group by 734 | 735 | ### Changed 736 | - 🚨 apply some clippy suggestions + refactor 737 | - 🎨 format code 738 | 739 | ### Miscellaneous 740 | - 🚀 (cargo-release) version 0.3.0 741 | - pencil add sample 742 | - pencil (README) add instruction to install from github's release 743 | - 🚧 (cargo-release) start next development iteration 0.2.2-dev 744 | 745 | 746 | ## Version 0.2.1 747 | 748 | ### Fixed 749 | - ✏️ remove typo 750 | - ✏️ README use the official badge for github action 751 | 752 | ### Miscellaneous 753 | - 🚀 (cargo-release) version 0.2.1 754 | - pencil add badge for download 755 | - 🚧 (cargo-release) start next development iteration 0.2.1-dev 756 | 757 | 758 | ## Version 0.2.0 759 | 760 | ### Added 761 | - ✨ cli add filter by namespace 762 | - ✨ filter resources by name 763 | - ✨ add cli 764 | - 👷 enable dependabot 765 | - 👷 move from azure pipelines to github's action 766 | 767 | ### Changed 768 | - ⬆️ update env_logger to 0.7.1 from 0.7.0 769 | - 💄 hide zero(s) or display them with color 770 | - ⬆️ Update kube requirement from 0.16.1 to 0.17.0 771 | - 💄 display in 3 level: kind-node-pod 772 | - 💄 display per node resource usage 773 | - ⬆️ Update k8s-openapi requirement from 0.5.1 to 0.6.0 774 | - ⬆️ upgrade dependencies: kube, env_logger, failure, serde_jon 775 | 776 | ### Removed 777 | - 🔥 finish to remove tabwriter reference 778 | 779 | ### Fixed 780 | - 🐛 disable windows support 781 | - 🐛 fix adjust_scale to work when input.scale.base is 0 782 | 783 | ### Miscellaneous 784 | - 🚀 (cargo-release) version 0.2.0 785 | - pencil update README 786 | - 📦 prepare release of cli 787 | 788 | 789 | ## Version 0.1.0-dev 790 | 791 | ### Added 792 | - 🎉 first working prototype to show utilization 793 | - 🎉 init 794 | 795 | ### Changed 796 | - ⬆️ bump kube to 0.16.1 797 | - ♻️ remade qty 798 | - 🎨 extract code to function 799 | - 🚚 rename project from `kubectl-show-resources` to `kubectl-view-allocations` 800 | 801 | ### Fixed 802 | - 🐛 fix bugs in human_format 803 | 804 | ### Miscellaneous 805 | - ⚗ rewrite Qty to use f64 and human_format (fork) 806 | 807 | _Generated by [gitmoji-changelog (rust version)](https://github.com/fabienjuif/gitmoji-changelog-rust)_ -------------------------------------------------------------------------------- /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 futures::future::try_join_all; 10 | use itertools::Itertools; 11 | use k8s_openapi::api::core::v1::{Node, Pod}; 12 | use kube::api::{Api, ListParams, ObjectList}; 13 | #[cfg(feature = "prettytable")] 14 | use prettytable::{Cell, Row, Table, format, row}; 15 | use qty::Qty; 16 | use std::str::FromStr; 17 | use std::{collections::BTreeMap, path::PathBuf}; 18 | use tracing::{info, instrument, warn}; 19 | 20 | #[derive(thiserror::Error, Debug)] 21 | pub enum Error { 22 | #[error("Failed to run '{cmd}'")] 23 | CmdError { 24 | cmd: String, 25 | output: Option, 26 | source: Option, 27 | }, 28 | 29 | #[error("Failed to read Qty of location {location:?} / {qualifier:?} {kind}={input}")] 30 | ResourceQtyParseError { 31 | location: Location, 32 | qualifier: ResourceQualifier, 33 | kind: String, 34 | input: String, 35 | source: qty::Error, 36 | }, 37 | 38 | #[error("Failed to process Qty")] 39 | QtyError { 40 | #[from] 41 | source: qty::Error, 42 | }, 43 | 44 | #[error("Failed to {context}")] 45 | KubeError { 46 | context: String, 47 | source: kube::Error, 48 | }, 49 | 50 | #[error("Failed to {context}")] 51 | KubeConfigError { 52 | context: String, 53 | source: kube::config::KubeconfigError, 54 | }, 55 | 56 | #[error("Failed to {context}")] 57 | KubeInferConfigError { 58 | context: String, 59 | source: kube::config::InferConfigError, 60 | }, 61 | } 62 | 63 | #[derive(Debug, Clone, Default)] 64 | pub struct Location { 65 | pub node_name: String, 66 | pub namespace: Option, 67 | pub pod_name: Option, 68 | } 69 | 70 | #[derive(Debug, Clone)] 71 | pub struct Resource { 72 | pub kind: String, 73 | pub quantity: Qty, 74 | pub location: Location, 75 | pub qualifier: ResourceQualifier, 76 | } 77 | 78 | #[derive(Debug, Clone)] 79 | pub enum ResourceQualifier { 80 | Limit, 81 | Requested, 82 | Allocatable, 83 | Utilization, 84 | // HACK special qualifier, used to show zero/undef cpu & memory 85 | Present, 86 | } 87 | 88 | #[derive(Debug, Clone, Default)] 89 | pub struct QtyByQualifier { 90 | pub limit: Option, 91 | pub requested: Option, 92 | pub allocatable: Option, 93 | pub utilization: Option, 94 | pub present: Option, 95 | } 96 | 97 | fn add(lhs: Option, rhs: &Qty) -> Option { 98 | lhs.map(|l| &l + rhs).or_else(|| Some(rhs.clone())) 99 | } 100 | 101 | impl QtyByQualifier { 102 | pub fn calc_free(&self, used_mode: UsedMode) -> Option { 103 | let total_used = match used_mode { 104 | UsedMode::max_request_limit => { 105 | std::cmp::max(self.limit.as_ref(), self.requested.as_ref()) 106 | } 107 | UsedMode::only_request => self.requested.as_ref(), 108 | }; 109 | self.allocatable 110 | .as_ref() 111 | .zip(total_used) 112 | .map(|(allocatable, total_used)| { 113 | if allocatable > total_used { 114 | allocatable - total_used 115 | } else { 116 | Qty::default() 117 | } 118 | }) 119 | } 120 | } 121 | 122 | pub fn sum_by_qualifier(rsrcs: &[&Resource]) -> Option { 123 | if !rsrcs.is_empty() { 124 | let kind = rsrcs 125 | .first() 126 | .expect("group contains at least 1 element") 127 | .kind 128 | .clone(); 129 | 130 | if rsrcs.iter().all(|i| i.kind == kind) { 131 | let sum = rsrcs.iter().fold(QtyByQualifier::default(), |mut acc, v| { 132 | match &v.qualifier { 133 | ResourceQualifier::Limit => acc.limit = add(acc.limit, &v.quantity), 134 | ResourceQualifier::Requested => acc.requested = add(acc.requested, &v.quantity), 135 | ResourceQualifier::Allocatable => { 136 | acc.allocatable = add(acc.allocatable, &v.quantity) 137 | } 138 | ResourceQualifier::Utilization => { 139 | acc.utilization = add(acc.utilization, &v.quantity) 140 | } 141 | ResourceQualifier::Present => acc.present = add(acc.present, &v.quantity), 142 | }; 143 | acc 144 | }); 145 | Some(sum) 146 | } else { 147 | None 148 | } 149 | } else { 150 | None 151 | } 152 | } 153 | 154 | pub fn make_qualifiers( 155 | rsrcs: &[Resource], 156 | group_by: &[GroupBy], 157 | resource_names: &[String], 158 | ) -> Vec<(Vec, Option)> { 159 | let group_by_fct = group_by.iter().map(GroupBy::to_fct).collect::>(); 160 | let mut out = make_group_x_qualifier( 161 | &(rsrcs 162 | .iter() 163 | .filter(|a| accept_resource(&a.kind, resource_names)) 164 | .collect::>()), 165 | &[], 166 | &group_by_fct, 167 | 0, 168 | ); 169 | out.sort_by_key(|i| i.0.clone()); 170 | out 171 | } 172 | 173 | fn make_group_x_qualifier( 174 | rsrcs: &[&Resource], 175 | prefix: &[String], 176 | group_by_fct: &[fn(&Resource) -> Option], 177 | group_by_depth: usize, 178 | ) -> Vec<(Vec, Option)> { 179 | // Note: The `&` is significant here, `GroupBy` is iterable 180 | // only by reference. You can also call `.into_iter()` explicitly. 181 | let mut out = vec![]; 182 | if let Some(group_by) = group_by_fct.get(group_by_depth) { 183 | for (key, group) in rsrcs 184 | .iter() 185 | .filter_map(|e| group_by(e).map(|k| (k, *e))) 186 | .into_group_map() 187 | { 188 | let mut key_full = prefix.to_vec(); 189 | key_full.push(key); 190 | let children = 191 | make_group_x_qualifier(&group, &key_full, group_by_fct, group_by_depth + 1); 192 | out.push((key_full, sum_by_qualifier(&group))); 193 | out.extend(children); 194 | } 195 | } 196 | // let kg = &rsrcs.into_iter().group_by(|v| v.kind); 197 | // kg.into_iter().map(|(key, group)| ).collect() 198 | out 199 | } 200 | 201 | fn accept_resource(name: &str, resource_filter: &[String]) -> bool { 202 | resource_filter.is_empty() || resource_filter.iter().any(|x| name.contains(x)) 203 | } 204 | 205 | #[instrument(skip(client, resources))] 206 | pub async fn collect_from_nodes( 207 | client: kube::Client, 208 | resources: &mut Vec, 209 | selector: &Option, 210 | ) -> Result, Error> { 211 | let api_nodes: Api = Api::all(client); 212 | let mut lp = ListParams::default(); 213 | if let Some(labels) = &selector { 214 | lp = lp.labels(labels); 215 | } 216 | let nodes = api_nodes 217 | .list(&lp) 218 | .await 219 | .map_err(|source| Error::KubeError { 220 | context: "list nodes".to_string(), 221 | source, 222 | })? 223 | .items; 224 | let node_names = nodes 225 | .iter() 226 | .filter_map(|node| node.metadata.name.clone()) 227 | .collect(); 228 | extract_allocatable_from_nodes(nodes, resources).await?; 229 | Ok(node_names) 230 | } 231 | 232 | #[instrument(skip(node_list, resources))] 233 | pub async fn extract_allocatable_from_nodes( 234 | node_list: Vec, 235 | resources: &mut Vec, 236 | ) -> Result<(), Error> { 237 | for node in node_list { 238 | let location = Location { 239 | node_name: node.metadata.name.unwrap_or_default(), 240 | ..Location::default() 241 | }; 242 | if let Some(als) = node.status.and_then(|v| v.allocatable) { 243 | // add_resource(resources, &location, ResourceUsage::Allocatable, &als)? 244 | for (kind, value) in als.iter() { 245 | let quantity = 246 | Qty::from_str(&(value).0).map_err(|source| Error::ResourceQtyParseError { 247 | location: location.clone(), 248 | qualifier: ResourceQualifier::Allocatable, 249 | kind: kind.to_string(), 250 | input: value.0.to_string(), 251 | source, 252 | })?; 253 | resources.push(Resource { 254 | kind: kind.clone(), 255 | qualifier: ResourceQualifier::Allocatable, 256 | quantity, 257 | location: location.clone(), 258 | }); 259 | } 260 | } 261 | } 262 | Ok(()) 263 | } 264 | 265 | /* 266 | 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. 267 | 268 | There are five possible phase values: 269 | 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. 270 | 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. 271 | Succeeded: All containers in the pod have terminated in success, and will not be restarted. 272 | 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. 273 | 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. 274 | 275 | More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-phase 276 | */ 277 | 278 | pub fn is_scheduled(pod: &Pod) -> bool { 279 | pod.status 280 | .as_ref() 281 | .and_then(|ps| { 282 | ps.phase.as_ref().and_then(|phase| { 283 | match &phase[..] { 284 | "Succeeded" | "Failed" => Some(false), 285 | "Running" => Some(true), 286 | "Unknown" => None, // this is the case when a node is down (kubelet is not responding) 287 | "Pending" => ps.conditions.as_ref().map(|o| { 288 | o.iter() 289 | .any(|c| c.type_ == "PodScheduled" && c.status == "True") 290 | }), 291 | &_ => None, // should not happen 292 | } 293 | }) 294 | }) 295 | .unwrap_or(false) 296 | } 297 | 298 | #[allow(clippy::result_large_err)] 299 | fn push_resources( 300 | resources: &mut Vec, 301 | location: &Location, 302 | qualifier: ResourceQualifier, 303 | resource_list: &BTreeMap, 304 | ) -> Result<(), Error> { 305 | for (key, quantity) in resource_list.iter() { 306 | resources.push(Resource { 307 | kind: key.clone(), 308 | qualifier: qualifier.clone(), 309 | quantity: quantity.clone(), 310 | location: location.clone(), 311 | }); 312 | } 313 | // add a "pods" resource as well 314 | resources.push(Resource { 315 | kind: "pods".to_string(), 316 | qualifier, 317 | quantity: Qty::from_str("1")?, 318 | location: location.clone(), 319 | }); 320 | Ok(()) 321 | } 322 | 323 | #[allow(clippy::result_large_err)] 324 | fn process_resources( 325 | effective_resources: &mut BTreeMap, 326 | resource_list: &BTreeMap, 327 | op: F, 328 | ) -> Result<(), Error> 329 | where 330 | F: Fn(Qty, Qty) -> Qty, 331 | { 332 | for (key, value) in resource_list.iter() { 333 | let quantity = Qty::from_str(&(value).0)?; 334 | if let Some(current_quantity) = effective_resources.get_mut(key) { 335 | *current_quantity = op(current_quantity.clone(), quantity).clone(); 336 | } else { 337 | effective_resources.insert(key.clone(), quantity.clone()); 338 | } 339 | } 340 | Ok(()) 341 | } 342 | 343 | #[instrument(skip(client, resources))] 344 | pub async fn collect_from_pods( 345 | client: kube::Client, 346 | resources: &mut Vec, 347 | namespace: &[String], 348 | selected_node_names: &[String], 349 | ) -> Result<(), Error> { 350 | let mut apis: Vec> = vec![]; 351 | if namespace.is_empty() { 352 | apis.push(Api::all(client)) 353 | } else { 354 | for ns in namespace { 355 | apis.push(Api::namespaced(client.clone(), ns)) 356 | } 357 | } 358 | 359 | // Call `list` concurrently on every apis 360 | let pods: Vec = try_join_all( 361 | apis.iter() 362 | .map(|api| async { api.list(&ListParams::default()).await }), 363 | ) 364 | .await 365 | .map_err(|source| Error::KubeError { 366 | context: "list pods".to_string(), 367 | source, 368 | })? 369 | .into_iter() 370 | .flat_map(|list| list.items) 371 | .collect(); 372 | 373 | extract_allocatable_from_pods(pods, resources, selected_node_names).await?; 374 | Ok(()) 375 | } 376 | 377 | #[instrument(skip(pod_list, resources))] 378 | pub async fn extract_allocatable_from_pods( 379 | pod_list: Vec, 380 | resources: &mut Vec, 381 | selected_node_names: &[String], 382 | ) -> Result<(), Error> { 383 | for pod in pod_list.into_iter().filter(is_scheduled) { 384 | let spec = pod.spec.as_ref(); 385 | let node_name = spec.and_then(|s| s.node_name.clone()).unwrap_or_default(); 386 | if !selected_node_names.contains(&node_name) { 387 | continue; 388 | } 389 | let metadata = &pod.metadata; 390 | let location = Location { 391 | node_name: node_name.clone(), 392 | namespace: metadata.namespace.clone(), 393 | pod_name: metadata.name.clone(), 394 | }; 395 | // compute the effective resource qualifier 396 | // see https://kubernetes.io/docs/concepts/workloads/pods/init-containers/#resources 397 | let mut resource_requests: BTreeMap = BTreeMap::new(); 398 | let mut resource_limits: BTreeMap = BTreeMap::new(); 399 | // handle regular containers 400 | let containers = spec.map(|s| s.containers.clone()).unwrap_or_default(); 401 | for container in containers.into_iter() { 402 | if let Some(requirements) = container.resources { 403 | if let Some(r) = requirements.requests { 404 | process_resources(&mut resource_requests, &r, std::ops::Add::add)?; 405 | } 406 | if let Some(r) = requirements.limits { 407 | process_resources(&mut resource_limits, &r, std::ops::Add::add)?; 408 | } 409 | } 410 | } 411 | // handle initContainers 412 | let init_containers = spec 413 | .and_then(|s| s.init_containers.clone()) 414 | .unwrap_or_default(); 415 | for container in init_containers.into_iter() { 416 | if let Some(requirements) = container.resources { 417 | if let Some(r) = requirements.requests { 418 | process_resources(&mut resource_requests, &r, std::cmp::max)?; 419 | } 420 | if let Some(r) = requirements.limits { 421 | process_resources(&mut resource_limits, &r, std::cmp::max)?; 422 | } 423 | } 424 | } 425 | // handler overhead (add to both requests and limits) 426 | if let Some(ref overhead) = spec.and_then(|s| s.overhead.clone()) { 427 | process_resources(&mut resource_requests, overhead, std::ops::Add::add)?; 428 | process_resources(&mut resource_limits, overhead, std::ops::Add::add)?; 429 | } 430 | // push these onto resources 431 | push_resources( 432 | resources, 433 | &location, 434 | ResourceQualifier::Requested, 435 | &resource_requests, 436 | )?; 437 | push_resources( 438 | resources, 439 | &location, 440 | ResourceQualifier::Limit, 441 | &resource_limits, 442 | )?; 443 | // HACK add zero/None cpu & memory, to allow show-zero to display them 444 | resources.push(Resource { 445 | kind: "cpu".to_string(), 446 | qualifier: ResourceQualifier::Present, 447 | quantity: Qty::zero(), 448 | location: location.clone(), 449 | }); 450 | resources.push(Resource { 451 | kind: "memory".to_string(), 452 | qualifier: ResourceQualifier::Present, 453 | quantity: Qty::zero(), 454 | location: location.clone(), 455 | }); 456 | } 457 | Ok(()) 458 | } 459 | 460 | pub fn extract_locations( 461 | resources: &[Resource], 462 | ) -> std::collections::HashMap<(String, String), Location> { 463 | resources 464 | .iter() 465 | .filter_map(|resource| { 466 | let loc = &resource.location; 467 | loc.pod_name.as_ref().map(|n| { 468 | ( 469 | (loc.namespace.clone().unwrap_or_default(), n.to_owned()), 470 | loc.clone(), 471 | ) 472 | }) 473 | }) 474 | .collect() 475 | } 476 | 477 | //TODO need location of pods (aka node because its not part of metrics) 478 | //TODO filter to only retreive info from node's selector 479 | #[instrument(skip(client, resources))] 480 | pub async fn collect_from_metrics( 481 | client: kube::Client, 482 | resources: &mut Vec, 483 | ) -> Result<(), Error> { 484 | let api_pod_metrics: Api = Api::all(client); 485 | let pod_metrics = api_pod_metrics 486 | .list(&ListParams::default()) 487 | .await 488 | .map_err(|source| Error::KubeError { 489 | context: "list podmetrics, maybe Metrics API not available".to_string(), 490 | source, 491 | })?; 492 | 493 | extract_utilizations_from_pod_metrics(pod_metrics, resources).await?; 494 | Ok(()) 495 | } 496 | 497 | #[instrument(skip(pod_metrics, resources))] 498 | pub async fn extract_utilizations_from_pod_metrics( 499 | pod_metrics: ObjectList, 500 | resources: &mut Vec, 501 | ) -> Result<(), Error> { 502 | let cpu_kind = "cpu"; 503 | let memory_kind = "memory"; 504 | let locations = extract_locations(resources); 505 | for pod_metric in pod_metrics.items { 506 | let metadata = &pod_metric.metadata; 507 | let key = ( 508 | metadata.namespace.clone().unwrap_or_default(), 509 | metadata.name.clone().unwrap_or_default(), 510 | ); 511 | let location = locations.get(&key).cloned().unwrap_or_else(|| Location { 512 | // node_name: node_name.clone(), 513 | namespace: metadata.namespace.clone(), 514 | pod_name: metadata.name.clone(), 515 | ..Location::default() 516 | }); 517 | let mut cpu_utilization = Qty::default(); 518 | let mut memory_utilization = Qty::default(); 519 | for container in pod_metric.containers.into_iter() { 520 | cpu_utilization += &Qty::from_str(&container.usage.cpu) 521 | .map_err(|source| Error::ResourceQtyParseError { 522 | location: location.clone(), 523 | qualifier: ResourceQualifier::Utilization, 524 | kind: cpu_kind.to_string(), 525 | input: container.usage.cpu.clone(), 526 | source, 527 | })? 528 | .max(Qty::lowest_positive()); 529 | memory_utilization += &Qty::from_str(&container.usage.memory) 530 | .map_err(|source| Error::ResourceQtyParseError { 531 | location: location.clone(), 532 | qualifier: ResourceQualifier::Utilization, 533 | kind: memory_kind.to_string(), 534 | input: container.usage.memory.clone(), 535 | source, 536 | })? 537 | .max(Qty::lowest_positive()); 538 | } 539 | resources.push(Resource { 540 | kind: cpu_kind.to_string(), 541 | qualifier: ResourceQualifier::Utilization, 542 | quantity: cpu_utilization, 543 | location: location.clone(), 544 | }); 545 | resources.push(Resource { 546 | kind: memory_kind.to_string(), 547 | qualifier: ResourceQualifier::Utilization, 548 | quantity: memory_utilization, 549 | location: location.clone(), 550 | }); 551 | } 552 | Ok(()) 553 | } 554 | 555 | #[derive(Debug, Eq, PartialEq, ValueEnum, Clone)] 556 | #[allow(non_camel_case_types)] 557 | pub enum GroupBy { 558 | resource, 559 | node, 560 | pod, 561 | namespace, 562 | } 563 | 564 | impl GroupBy { 565 | pub fn to_fct(&self) -> fn(&Resource) -> Option { 566 | match self { 567 | Self::resource => Self::extract_kind, 568 | Self::node => Self::extract_node_name, 569 | Self::pod => Self::extract_pod_name, 570 | Self::namespace => Self::extract_namespace, 571 | } 572 | } 573 | 574 | fn extract_kind(e: &Resource) -> Option { 575 | Some(e.kind.clone()) 576 | } 577 | 578 | fn extract_node_name(e: &Resource) -> Option { 579 | Some(e.location.node_name.to_string()).filter(|s| !s.is_empty()) 580 | } 581 | 582 | fn extract_pod_name(e: &Resource) -> Option { 583 | // We do not need to display "pods" resource types when grouping by pods 584 | if e.kind == "pods" { 585 | return None; 586 | } 587 | e.location.pod_name.clone() 588 | } 589 | 590 | fn extract_namespace(e: &Resource) -> Option { 591 | e.location.namespace.clone() 592 | } 593 | } 594 | 595 | impl std::fmt::Display for GroupBy { 596 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 597 | let s = match self { 598 | Self::resource => "resource", 599 | Self::node => "node", 600 | Self::pod => "pod", 601 | Self::namespace => "namespace", 602 | }; 603 | f.write_str(s) 604 | } 605 | } 606 | 607 | #[derive(Debug, Eq, PartialEq, ValueEnum, Clone, Copy, Default)] 608 | #[allow(non_camel_case_types)] 609 | pub enum Output { 610 | #[default] 611 | table, 612 | csv, 613 | } 614 | 615 | #[derive(Debug, Eq, PartialEq, ValueEnum, Clone, Copy, Default)] 616 | #[allow(non_camel_case_types)] 617 | pub enum UsedMode { 618 | #[default] 619 | max_request_limit, 620 | only_request, 621 | } 622 | 623 | #[derive(Parser, Debug)] 624 | #[command( 625 | version, about, 626 | after_help(env!("CARGO_PKG_HOMEPAGE")), 627 | propagate_version = true 628 | )] 629 | pub struct CliOpts { 630 | /// Path to the kubeconfig file to use for requests to kubernetes cluster 631 | #[arg(long, value_parser)] 632 | pub kubeconfig: Option, 633 | 634 | /// The name of the kubeconfig context to use 635 | #[arg(long, value_parser)] 636 | pub context: Option, 637 | 638 | /// Filter pods by namespace(s), by default pods in all namespaces are listed (comma separated list or multiple calls) 639 | #[arg(short, long, value_parser, value_delimiter= ',', num_args = 1..)] 640 | pub namespace: Vec, 641 | 642 | /// Show only nodes match this label selector 643 | #[arg(short = 'l', long, value_parser)] 644 | pub selector: Option, 645 | 646 | /// Force to retrieve utilization (for cpu and memory), requires 647 | /// having metrics-server https://github.com/kubernetes-sigs/metrics-server 648 | #[arg(short = 'u', long, value_parser)] 649 | pub utilization: bool, 650 | 651 | /// Show lines with zero requested AND zero limit AND zero allocatable, 652 | /// OR pods with unset requested AND limit for `cpu` and `memory` 653 | #[arg(short = 'z', long, value_parser)] 654 | pub show_zero: bool, 655 | 656 | /// The way to compute the `used` part for free (`allocatable - used`) 657 | #[arg( 658 | long, 659 | value_enum, 660 | ignore_case = true, 661 | default_value = "max-request-limit", 662 | value_parser 663 | )] 664 | pub used_mode: UsedMode, 665 | 666 | /// Pre-check access and refresh token on kubeconfig by running `kubectl cluster-info` 667 | #[arg(long, value_parser)] 668 | pub precheck: bool, 669 | 670 | /// Accept invalid certificates (dangerous) 671 | #[arg(long, value_parser)] 672 | pub accept_invalid_certs: bool, 673 | 674 | /// Filter resources shown by name(s), by default all resources are listed (comma separated list or multiple calls) 675 | #[arg(short, long, value_parser, value_delimiter= ',', num_args = 1..)] 676 | pub resource_name: Vec, 677 | 678 | /// Group information in a hierarchical manner; defaults to `-g resource,node,pod` (comma-separated list or multiple calls) 679 | #[arg(short, long, value_enum, ignore_case = true, value_parser, value_delimiter= ',', num_args = 1..)] 680 | pub group_by: Vec, 681 | 682 | /// Output format 683 | #[arg( 684 | short, 685 | long, 686 | value_enum, 687 | ignore_case = true, 688 | default_value = "table", 689 | value_parser 690 | )] 691 | pub output: Output, 692 | } 693 | 694 | pub async fn refresh_kube_config(cli_opts: &CliOpts) -> Result<(), Error> { 695 | //HACK force refresh token by calling "kubectl cluster-info before loading configuration" 696 | use std::process::Command; 697 | let mut cmd = Command::new("kubectl"); 698 | cmd.arg("cluster-info"); 699 | if let Some(ref kubeconfig) = cli_opts.kubeconfig { 700 | cmd.arg("--kubeconfig").arg(kubeconfig); 701 | } 702 | if let Some(ref context) = cli_opts.context { 703 | cmd.arg("--context").arg(context); 704 | } 705 | let output = cmd.output().map_err(|source| Error::CmdError { 706 | cmd: "kubectl cluster-info".to_owned(), 707 | output: None, 708 | source: Some(source), 709 | })?; 710 | if !output.status.success() { 711 | return Err(Error::CmdError { 712 | cmd: "kubectl cluster-info".to_owned(), 713 | output: Some(output), 714 | source: None, 715 | }); 716 | } 717 | Ok(()) 718 | } 719 | 720 | pub async fn new_client(cli_opts: &CliOpts) -> Result { 721 | if cli_opts.precheck { 722 | refresh_kube_config(cli_opts).await?; 723 | } 724 | let mut client_config = match (&cli_opts.kubeconfig, &cli_opts.context) { 725 | (Some(kubeconfig), context) => { 726 | let options = kube::config::KubeConfigOptions { 727 | context: context.clone(), 728 | ..Default::default() 729 | }; 730 | kube::Config::from_custom_kubeconfig( 731 | kube::config::Kubeconfig::read_from(std::path::Path::new(kubeconfig)).map_err( 732 | |source| Error::KubeConfigError { 733 | context: format!("read kubeconfig from {}", kubeconfig.to_string_lossy()), 734 | source, 735 | }, 736 | )?, 737 | &options, 738 | ) 739 | .await 740 | .map_err(|source| Error::KubeConfigError { 741 | context: "create the kube client config from custom kubeconfig".to_string(), 742 | source, 743 | })? 744 | } 745 | (None, Some(context)) => kube::Config::from_kubeconfig(&kube::config::KubeConfigOptions { 746 | context: Some(context.clone()), 747 | ..Default::default() 748 | }) 749 | .await 750 | .map_err(|source| Error::KubeConfigError { 751 | context: "create the kube client config".to_string(), 752 | source, 753 | })?, 754 | (None, None) => { 755 | kube::Config::infer() 756 | .await 757 | .map_err(|source| Error::KubeInferConfigError { 758 | context: "create the kube client config".to_string(), 759 | source, 760 | })? 761 | } 762 | }; 763 | info!(cluster_url = client_config.cluster_url.to_string().as_str()); 764 | client_config.accept_invalid_certs = 765 | client_config.accept_invalid_certs || cli_opts.accept_invalid_certs; 766 | kube::Client::try_from(client_config).map_err(|source| Error::KubeError { 767 | context: "create the kube client".to_string(), 768 | source, 769 | }) 770 | } 771 | 772 | #[instrument] 773 | pub async fn do_main(cli_opts: &CliOpts) -> Result<(), Error> { 774 | let client = new_client(cli_opts).await?; 775 | let mut resources: Vec = vec![]; 776 | let node_names = collect_from_nodes(client.clone(), &mut resources, &cli_opts.selector).await?; 777 | collect_from_pods( 778 | client.clone(), 779 | &mut resources, 780 | &cli_opts.namespace, 781 | &node_names, 782 | ) 783 | .await?; 784 | 785 | let show_utilization = if cli_opts.utilization { 786 | match collect_from_metrics(client.clone(), &mut resources).await { 787 | Ok(_) => true, 788 | Err(err) => { 789 | warn!(?err); 790 | false 791 | } 792 | } 793 | } else { 794 | false 795 | }; 796 | 797 | let res = make_qualifiers(&resources, &cli_opts.group_by, &cli_opts.resource_name); 798 | match &cli_opts.output { 799 | Output::table => display_with_prettytable( 800 | &res, 801 | !&cli_opts.show_zero, 802 | show_utilization, 803 | cli_opts.used_mode, 804 | ), 805 | Output::csv => display_as_csv( 806 | &res, 807 | &cli_opts.group_by, 808 | show_utilization, 809 | cli_opts.used_mode, 810 | ), 811 | } 812 | Ok(()) 813 | } 814 | 815 | pub fn display_as_csv( 816 | data: &[(Vec, Option)], 817 | group_by: &[GroupBy], 818 | show_utilization: bool, 819 | used_mode: UsedMode, 820 | ) { 821 | // print header 822 | println!( 823 | "Date,Kind,{}{},Requested,%Requested,Limit,%Limit,Allocatable,Free", 824 | group_by.iter().map(|x| x.to_string()).join(","), 825 | if show_utilization { 826 | ",Utilization,%Utilization" 827 | } else { 828 | "" 829 | } 830 | ); 831 | 832 | // print data 833 | let empty = "".to_string(); 834 | let datetime = Utc::now().to_rfc3339(); 835 | for (k, oqtys) in data { 836 | if let Some(qtys) = oqtys { 837 | let mut row = vec![ 838 | datetime.clone(), 839 | group_by 840 | .get(k.len() - 1) 841 | .map(|x| x.to_string()) 842 | .unwrap_or_else(|| empty.clone()), 843 | ]; 844 | for i in 0..group_by.len() { 845 | row.push(k.get(i).cloned().unwrap_or_else(|| empty.clone())); 846 | } 847 | 848 | if show_utilization { 849 | add_cells_for_cvs(&mut row, &qtys.utilization, &qtys.allocatable); 850 | } 851 | add_cells_for_cvs(&mut row, &qtys.requested, &qtys.allocatable); 852 | add_cells_for_cvs(&mut row, &qtys.limit, &qtys.allocatable); 853 | 854 | row.push( 855 | qtys.allocatable 856 | .as_ref() 857 | .map(|qty| format!("{:.2}", f64::from(qty))) 858 | .unwrap_or_else(|| empty.clone()), 859 | ); 860 | row.push( 861 | qtys.calc_free(used_mode) 862 | .as_ref() 863 | .map(|qty| format!("{:.2}", f64::from(qty))) 864 | .unwrap_or_else(|| empty.clone()), 865 | ); 866 | println!("{}", &row.join(",")); 867 | } 868 | } 869 | } 870 | 871 | fn add_cells_for_cvs(row: &mut Vec, oqty: &Option, o100: &Option) { 872 | match oqty { 873 | None => { 874 | row.push("".to_string()); 875 | row.push("".to_string()); 876 | } 877 | Some(qty) => { 878 | row.push(format!("{:.2}", f64::from(qty))); 879 | row.push(match o100 { 880 | None => "".to_string(), 881 | Some(q100) => format!("{:.0}%", qty.calc_percentage(q100)), 882 | }); 883 | } 884 | }; 885 | } 886 | 887 | #[cfg(not(feature = "prettytable"))] 888 | pub fn display_with_prettytable( 889 | _data: &[(Vec, Option)], 890 | _filter_full_zero: bool, 891 | _show_utilization: bool, 892 | _used_mode: UsedMode, 893 | ) { 894 | warn!("feature 'prettytable' not enabled"); 895 | } 896 | 897 | #[cfg(feature = "prettytable")] 898 | pub fn display_with_prettytable( 899 | data: &[(Vec, Option)], 900 | filter_full_zero: bool, 901 | show_utilization: bool, 902 | used_mode: UsedMode, 903 | ) { 904 | // Create the table 905 | let mut table = Table::new(); 906 | let format = format::FormatBuilder::new() 907 | // .column_separator('|') 908 | // .borders('|') 909 | // .separators(&[format::LinePosition::Top, 910 | // format::LinePosition::Bottom], 911 | // format::LineSeparator::new('-', '+', '+', '+')) 912 | .separators(&[], format::LineSeparator::new('-', '+', '+', '+')) 913 | .padding(1, 1) 914 | .build(); 915 | table.set_format(format); 916 | let mut row_titles = row![bl->"Resource", br->"Utilization", br->"Requested", br->"Limit", br->"Allocatable", br->"Free"]; 917 | if !show_utilization { 918 | row_titles.remove_cell(1); 919 | } 920 | table.set_titles(row_titles); 921 | let data2 = data 922 | .iter() 923 | .filter(|d| { 924 | !filter_full_zero 925 | || !d 926 | .1 927 | .as_ref() 928 | .map(|x| { 929 | x.utilization.is_none() 930 | && is_empty(&x.requested) 931 | && is_empty(&x.limit) 932 | && is_empty(&x.allocatable) 933 | }) 934 | .unwrap_or(false) 935 | }) 936 | .collect::>(); 937 | let prefixes = tree::provide_prefix(&data2, |parent, item| parent.0.len() + 1 == item.0.len()); 938 | 939 | for ((k, oqtys), prefix) in data2.iter().zip(prefixes.iter()) { 940 | let column0 = format!( 941 | "{} {}", 942 | prefix, 943 | k.last().map(|x| x.as_str()).unwrap_or("???") 944 | ); 945 | if let Some(qtys) = oqtys { 946 | let style = if qtys.requested > qtys.limit 947 | || qtys.utilization > qtys.limit 948 | || is_empty(&qtys.requested) 949 | || is_empty(&qtys.limit) 950 | { 951 | "rFy" 952 | } else { 953 | "rFg" 954 | }; 955 | let mut row = Row::new(vec![ 956 | Cell::new(&column0), 957 | make_cell_for_prettytable(&qtys.utilization, &qtys.allocatable).style_spec(style), 958 | make_cell_for_prettytable(&qtys.requested, &qtys.allocatable).style_spec(style), 959 | make_cell_for_prettytable(&qtys.limit, &qtys.allocatable).style_spec(style), 960 | make_cell_for_prettytable(&qtys.allocatable, &None).style_spec(style), 961 | make_cell_for_prettytable(&qtys.calc_free(used_mode), &None).style_spec(style), 962 | ]); 963 | if !show_utilization { 964 | row.remove_cell(1); 965 | } 966 | table.add_row(row); 967 | } else { 968 | table.add_row(Row::new(vec![Cell::new(&column0)])); 969 | } 970 | } 971 | 972 | // Print the table to stdout 973 | table.printstd(); 974 | } 975 | 976 | #[cfg(feature = "prettytable")] 977 | fn is_empty(oqty: &Option) -> bool { 978 | match oqty { 979 | Some(qty) => qty.is_zero(), 980 | None => true, 981 | } 982 | } 983 | 984 | #[cfg(feature = "prettytable")] 985 | fn make_cell_for_prettytable(oqty: &Option, o100: &Option) -> Cell { 986 | let txt = match oqty { 987 | None => "__".to_string(), 988 | Some(qty) => match o100 { 989 | None => format!("{}", qty.adjust_scale()), 990 | Some(q100) => format!("({:.0}%) {}", qty.calc_percentage(q100), qty.adjust_scale()), 991 | }, 992 | }; 993 | Cell::new(&txt) 994 | } 995 | 996 | #[cfg(test)] 997 | mod tests { 998 | use super::*; 999 | 1000 | #[test] 1001 | fn test_accept_resource() { 1002 | assert!(accept_resource("cpu", &[])); 1003 | assert!(accept_resource("cpu", &["c".to_string()])); 1004 | assert!(accept_resource("cpu", &["cpu".to_string()])); 1005 | assert!(!accept_resource("cpu", &["cpu3".to_string()])); 1006 | assert!(accept_resource("gpu", &["gpu".to_string()])); 1007 | assert!(accept_resource("nvidia.com/gpu", &["gpu".to_string()])); 1008 | } 1009 | } 1010 | -------------------------------------------------------------------------------- /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.1" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" 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_system_properties" 44 | version = "0.1.5" 45 | source = "registry+https://github.com/rust-lang/crates.io-index" 46 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 47 | dependencies = [ 48 | "libc", 49 | ] 50 | 51 | [[package]] 52 | name = "anstream" 53 | version = "0.6.20" 54 | source = "registry+https://github.com/rust-lang/crates.io-index" 55 | checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" 56 | dependencies = [ 57 | "anstyle", 58 | "anstyle-parse", 59 | "anstyle-query", 60 | "anstyle-wincon", 61 | "colorchoice", 62 | "is_terminal_polyfill", 63 | "utf8parse", 64 | ] 65 | 66 | [[package]] 67 | name = "anstyle" 68 | version = "1.0.11" 69 | source = "registry+https://github.com/rust-lang/crates.io-index" 70 | checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" 71 | 72 | [[package]] 73 | name = "anstyle-parse" 74 | version = "0.2.7" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" 77 | dependencies = [ 78 | "utf8parse", 79 | ] 80 | 81 | [[package]] 82 | name = "anstyle-query" 83 | version = "1.1.4" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" 86 | dependencies = [ 87 | "windows-sys 0.60.2", 88 | ] 89 | 90 | [[package]] 91 | name = "anstyle-wincon" 92 | version = "3.0.10" 93 | source = "registry+https://github.com/rust-lang/crates.io-index" 94 | checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" 95 | dependencies = [ 96 | "anstyle", 97 | "once_cell_polyfill", 98 | "windows-sys 0.60.2", 99 | ] 100 | 101 | [[package]] 102 | name = "anyhow" 103 | version = "1.0.100" 104 | source = "registry+https://github.com/rust-lang/crates.io-index" 105 | checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" 106 | 107 | [[package]] 108 | name = "async-compression" 109 | version = "0.4.30" 110 | source = "registry+https://github.com/rust-lang/crates.io-index" 111 | checksum = "977eb15ea9efd848bb8a4a1a2500347ed7f0bf794edf0dc3ddcf439f43d36b23" 112 | dependencies = [ 113 | "compression-codecs", 114 | "compression-core", 115 | "futures-core", 116 | "pin-project-lite", 117 | "tokio", 118 | ] 119 | 120 | [[package]] 121 | name = "atomic-waker" 122 | version = "1.1.2" 123 | source = "registry+https://github.com/rust-lang/crates.io-index" 124 | checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" 125 | 126 | [[package]] 127 | name = "autocfg" 128 | version = "1.5.0" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" 131 | 132 | [[package]] 133 | name = "backtrace" 134 | version = "0.3.75" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" 137 | dependencies = [ 138 | "addr2line", 139 | "cfg-if", 140 | "libc", 141 | "miniz_oxide", 142 | "object", 143 | "rustc-demangle", 144 | "windows-targets 0.52.6", 145 | ] 146 | 147 | [[package]] 148 | name = "base64" 149 | version = "0.22.1" 150 | source = "registry+https://github.com/rust-lang/crates.io-index" 151 | checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" 152 | 153 | [[package]] 154 | name = "bitflags" 155 | version = "2.9.4" 156 | source = "registry+https://github.com/rust-lang/crates.io-index" 157 | checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" 158 | 159 | [[package]] 160 | name = "block-buffer" 161 | version = "0.10.4" 162 | source = "registry+https://github.com/rust-lang/crates.io-index" 163 | checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" 164 | dependencies = [ 165 | "generic-array", 166 | ] 167 | 168 | [[package]] 169 | name = "bumpalo" 170 | version = "3.19.0" 171 | source = "registry+https://github.com/rust-lang/crates.io-index" 172 | checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" 173 | 174 | [[package]] 175 | name = "bytes" 176 | version = "1.10.1" 177 | source = "registry+https://github.com/rust-lang/crates.io-index" 178 | checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" 179 | 180 | [[package]] 181 | name = "cc" 182 | version = "1.2.37" 183 | source = "registry+https://github.com/rust-lang/crates.io-index" 184 | checksum = "65193589c6404eb80b450d618eaf9a2cafaaafd57ecce47370519ef674a7bd44" 185 | dependencies = [ 186 | "find-msvc-tools", 187 | "shlex", 188 | ] 189 | 190 | [[package]] 191 | name = "cfg-if" 192 | version = "1.0.3" 193 | source = "registry+https://github.com/rust-lang/crates.io-index" 194 | checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" 195 | 196 | [[package]] 197 | name = "chrono" 198 | version = "0.4.42" 199 | source = "registry+https://github.com/rust-lang/crates.io-index" 200 | checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" 201 | dependencies = [ 202 | "iana-time-zone", 203 | "js-sys", 204 | "num-traits", 205 | "serde", 206 | "wasm-bindgen", 207 | "windows-link 0.2.0", 208 | ] 209 | 210 | [[package]] 211 | name = "clap" 212 | version = "4.5.53" 213 | source = "registry+https://github.com/rust-lang/crates.io-index" 214 | checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" 215 | dependencies = [ 216 | "clap_builder", 217 | "clap_derive", 218 | ] 219 | 220 | [[package]] 221 | name = "clap_builder" 222 | version = "4.5.53" 223 | source = "registry+https://github.com/rust-lang/crates.io-index" 224 | checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" 225 | dependencies = [ 226 | "anstream", 227 | "anstyle", 228 | "clap_lex", 229 | "strsim", 230 | ] 231 | 232 | [[package]] 233 | name = "clap_derive" 234 | version = "4.5.49" 235 | source = "registry+https://github.com/rust-lang/crates.io-index" 236 | checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" 237 | dependencies = [ 238 | "heck", 239 | "proc-macro2", 240 | "quote", 241 | "syn", 242 | ] 243 | 244 | [[package]] 245 | name = "clap_lex" 246 | version = "0.7.5" 247 | source = "registry+https://github.com/rust-lang/crates.io-index" 248 | checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" 249 | 250 | [[package]] 251 | name = "color-eyre" 252 | version = "0.6.5" 253 | source = "registry+https://github.com/rust-lang/crates.io-index" 254 | checksum = "e5920befb47832a6d61ee3a3a846565cfa39b331331e68a3b1d1116630f2f26d" 255 | dependencies = [ 256 | "backtrace", 257 | "color-spantrace", 258 | "eyre", 259 | "indenter", 260 | "once_cell", 261 | "owo-colors", 262 | "tracing-error", 263 | ] 264 | 265 | [[package]] 266 | name = "color-spantrace" 267 | version = "0.3.0" 268 | source = "registry+https://github.com/rust-lang/crates.io-index" 269 | checksum = "b8b88ea9df13354b55bc7234ebcce36e6ef896aca2e42a15de9e10edce01b427" 270 | dependencies = [ 271 | "once_cell", 272 | "owo-colors", 273 | "tracing-core", 274 | "tracing-error", 275 | ] 276 | 277 | [[package]] 278 | name = "colorchoice" 279 | version = "1.0.4" 280 | source = "registry+https://github.com/rust-lang/crates.io-index" 281 | checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" 282 | 283 | [[package]] 284 | name = "compression-codecs" 285 | version = "0.4.30" 286 | source = "registry+https://github.com/rust-lang/crates.io-index" 287 | checksum = "485abf41ac0c8047c07c87c72c8fb3eb5197f6e9d7ded615dfd1a00ae00a0f64" 288 | dependencies = [ 289 | "compression-core", 290 | "flate2", 291 | "memchr", 292 | ] 293 | 294 | [[package]] 295 | name = "compression-core" 296 | version = "0.4.29" 297 | source = "registry+https://github.com/rust-lang/crates.io-index" 298 | checksum = "e47641d3deaf41fb1538ac1f54735925e275eaf3bf4d55c81b137fba797e5cbb" 299 | 300 | [[package]] 301 | name = "core-foundation" 302 | version = "0.10.1" 303 | source = "registry+https://github.com/rust-lang/crates.io-index" 304 | checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" 305 | dependencies = [ 306 | "core-foundation-sys", 307 | "libc", 308 | ] 309 | 310 | [[package]] 311 | name = "core-foundation-sys" 312 | version = "0.8.7" 313 | source = "registry+https://github.com/rust-lang/crates.io-index" 314 | checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 315 | 316 | [[package]] 317 | name = "cpufeatures" 318 | version = "0.2.17" 319 | source = "registry+https://github.com/rust-lang/crates.io-index" 320 | checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" 321 | dependencies = [ 322 | "libc", 323 | ] 324 | 325 | [[package]] 326 | name = "crc32fast" 327 | version = "1.5.0" 328 | source = "registry+https://github.com/rust-lang/crates.io-index" 329 | checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" 330 | dependencies = [ 331 | "cfg-if", 332 | ] 333 | 334 | [[package]] 335 | name = "crypto-common" 336 | version = "0.1.6" 337 | source = "registry+https://github.com/rust-lang/crates.io-index" 338 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" 339 | dependencies = [ 340 | "generic-array", 341 | "typenum", 342 | ] 343 | 344 | [[package]] 345 | name = "data-encoding" 346 | version = "2.9.0" 347 | source = "registry+https://github.com/rust-lang/crates.io-index" 348 | checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" 349 | 350 | [[package]] 351 | name = "deranged" 352 | version = "0.5.3" 353 | source = "registry+https://github.com/rust-lang/crates.io-index" 354 | checksum = "d630bccd429a5bb5a64b5e94f693bfc48c9f8566418fda4c494cc94f911f87cc" 355 | dependencies = [ 356 | "powerfmt", 357 | ] 358 | 359 | [[package]] 360 | name = "derive_more" 361 | version = "2.0.1" 362 | source = "registry+https://github.com/rust-lang/crates.io-index" 363 | checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" 364 | dependencies = [ 365 | "derive_more-impl", 366 | ] 367 | 368 | [[package]] 369 | name = "derive_more-impl" 370 | version = "2.0.1" 371 | source = "registry+https://github.com/rust-lang/crates.io-index" 372 | checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" 373 | dependencies = [ 374 | "proc-macro2", 375 | "quote", 376 | "syn", 377 | ] 378 | 379 | [[package]] 380 | name = "diff" 381 | version = "0.1.13" 382 | source = "registry+https://github.com/rust-lang/crates.io-index" 383 | checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" 384 | 385 | [[package]] 386 | name = "digest" 387 | version = "0.10.7" 388 | source = "registry+https://github.com/rust-lang/crates.io-index" 389 | checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" 390 | dependencies = [ 391 | "block-buffer", 392 | "crypto-common", 393 | ] 394 | 395 | [[package]] 396 | name = "dirs-next" 397 | version = "2.0.0" 398 | source = "registry+https://github.com/rust-lang/crates.io-index" 399 | checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" 400 | dependencies = [ 401 | "cfg-if", 402 | "dirs-sys-next", 403 | ] 404 | 405 | [[package]] 406 | name = "dirs-sys-next" 407 | version = "0.1.2" 408 | source = "registry+https://github.com/rust-lang/crates.io-index" 409 | checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" 410 | dependencies = [ 411 | "libc", 412 | "redox_users", 413 | "winapi", 414 | ] 415 | 416 | [[package]] 417 | name = "displaydoc" 418 | version = "0.2.5" 419 | source = "registry+https://github.com/rust-lang/crates.io-index" 420 | checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" 421 | dependencies = [ 422 | "proc-macro2", 423 | "quote", 424 | "syn", 425 | ] 426 | 427 | [[package]] 428 | name = "either" 429 | version = "1.15.0" 430 | source = "registry+https://github.com/rust-lang/crates.io-index" 431 | checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" 432 | 433 | [[package]] 434 | name = "encode_unicode" 435 | version = "1.0.0" 436 | source = "registry+https://github.com/rust-lang/crates.io-index" 437 | checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" 438 | 439 | [[package]] 440 | name = "equivalent" 441 | version = "1.0.2" 442 | source = "registry+https://github.com/rust-lang/crates.io-index" 443 | checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" 444 | 445 | [[package]] 446 | name = "eyre" 447 | version = "0.6.12" 448 | source = "registry+https://github.com/rust-lang/crates.io-index" 449 | checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" 450 | dependencies = [ 451 | "indenter", 452 | "once_cell", 453 | ] 454 | 455 | [[package]] 456 | name = "find-msvc-tools" 457 | version = "0.1.1" 458 | source = "registry+https://github.com/rust-lang/crates.io-index" 459 | checksum = "7fd99930f64d146689264c637b5af2f0233a933bef0d8570e2526bf9e083192d" 460 | 461 | [[package]] 462 | name = "flate2" 463 | version = "1.1.2" 464 | source = "registry+https://github.com/rust-lang/crates.io-index" 465 | checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" 466 | dependencies = [ 467 | "crc32fast", 468 | "miniz_oxide", 469 | ] 470 | 471 | [[package]] 472 | name = "fnv" 473 | version = "1.0.7" 474 | source = "registry+https://github.com/rust-lang/crates.io-index" 475 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 476 | 477 | [[package]] 478 | name = "form_urlencoded" 479 | version = "1.2.2" 480 | source = "registry+https://github.com/rust-lang/crates.io-index" 481 | checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" 482 | dependencies = [ 483 | "percent-encoding", 484 | ] 485 | 486 | [[package]] 487 | name = "futures" 488 | version = "0.3.31" 489 | source = "registry+https://github.com/rust-lang/crates.io-index" 490 | checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" 491 | dependencies = [ 492 | "futures-channel", 493 | "futures-core", 494 | "futures-executor", 495 | "futures-io", 496 | "futures-sink", 497 | "futures-task", 498 | "futures-util", 499 | ] 500 | 501 | [[package]] 502 | name = "futures-channel" 503 | version = "0.3.31" 504 | source = "registry+https://github.com/rust-lang/crates.io-index" 505 | checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" 506 | dependencies = [ 507 | "futures-core", 508 | "futures-sink", 509 | ] 510 | 511 | [[package]] 512 | name = "futures-core" 513 | version = "0.3.31" 514 | source = "registry+https://github.com/rust-lang/crates.io-index" 515 | checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" 516 | 517 | [[package]] 518 | name = "futures-executor" 519 | version = "0.3.31" 520 | source = "registry+https://github.com/rust-lang/crates.io-index" 521 | checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" 522 | dependencies = [ 523 | "futures-core", 524 | "futures-task", 525 | "futures-util", 526 | ] 527 | 528 | [[package]] 529 | name = "futures-io" 530 | version = "0.3.31" 531 | source = "registry+https://github.com/rust-lang/crates.io-index" 532 | checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" 533 | 534 | [[package]] 535 | name = "futures-macro" 536 | version = "0.3.31" 537 | source = "registry+https://github.com/rust-lang/crates.io-index" 538 | checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" 539 | dependencies = [ 540 | "proc-macro2", 541 | "quote", 542 | "syn", 543 | ] 544 | 545 | [[package]] 546 | name = "futures-sink" 547 | version = "0.3.31" 548 | source = "registry+https://github.com/rust-lang/crates.io-index" 549 | checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" 550 | 551 | [[package]] 552 | name = "futures-task" 553 | version = "0.3.31" 554 | source = "registry+https://github.com/rust-lang/crates.io-index" 555 | checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" 556 | 557 | [[package]] 558 | name = "futures-util" 559 | version = "0.3.31" 560 | source = "registry+https://github.com/rust-lang/crates.io-index" 561 | checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" 562 | dependencies = [ 563 | "futures-channel", 564 | "futures-core", 565 | "futures-io", 566 | "futures-macro", 567 | "futures-sink", 568 | "futures-task", 569 | "memchr", 570 | "pin-project-lite", 571 | "pin-utils", 572 | "slab", 573 | ] 574 | 575 | [[package]] 576 | name = "generic-array" 577 | version = "0.14.7" 578 | source = "registry+https://github.com/rust-lang/crates.io-index" 579 | checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" 580 | dependencies = [ 581 | "typenum", 582 | "version_check", 583 | ] 584 | 585 | [[package]] 586 | name = "gethostname" 587 | version = "0.2.3" 588 | source = "registry+https://github.com/rust-lang/crates.io-index" 589 | checksum = "c1ebd34e35c46e00bb73e81363248d627782724609fe1b6396f553f68fe3862e" 590 | dependencies = [ 591 | "libc", 592 | "winapi", 593 | ] 594 | 595 | [[package]] 596 | name = "getrandom" 597 | version = "0.2.16" 598 | source = "registry+https://github.com/rust-lang/crates.io-index" 599 | checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" 600 | dependencies = [ 601 | "cfg-if", 602 | "libc", 603 | "wasi 0.11.1+wasi-snapshot-preview1", 604 | ] 605 | 606 | [[package]] 607 | name = "getrandom" 608 | version = "0.3.3" 609 | source = "registry+https://github.com/rust-lang/crates.io-index" 610 | checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" 611 | dependencies = [ 612 | "cfg-if", 613 | "libc", 614 | "r-efi", 615 | "wasi 0.14.7+wasi-0.2.4", 616 | ] 617 | 618 | [[package]] 619 | name = "gimli" 620 | version = "0.31.1" 621 | source = "registry+https://github.com/rust-lang/crates.io-index" 622 | checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" 623 | 624 | [[package]] 625 | name = "hashbrown" 626 | version = "0.15.5" 627 | source = "registry+https://github.com/rust-lang/crates.io-index" 628 | checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" 629 | 630 | [[package]] 631 | name = "heck" 632 | version = "0.5.0" 633 | source = "registry+https://github.com/rust-lang/crates.io-index" 634 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 635 | 636 | [[package]] 637 | name = "hermit-abi" 638 | version = "0.5.2" 639 | source = "registry+https://github.com/rust-lang/crates.io-index" 640 | checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" 641 | 642 | [[package]] 643 | name = "home" 644 | version = "0.5.11" 645 | source = "registry+https://github.com/rust-lang/crates.io-index" 646 | checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" 647 | dependencies = [ 648 | "windows-sys 0.59.0", 649 | ] 650 | 651 | [[package]] 652 | name = "http" 653 | version = "1.3.1" 654 | source = "registry+https://github.com/rust-lang/crates.io-index" 655 | checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" 656 | dependencies = [ 657 | "bytes", 658 | "fnv", 659 | "itoa", 660 | ] 661 | 662 | [[package]] 663 | name = "http-body" 664 | version = "1.0.1" 665 | source = "registry+https://github.com/rust-lang/crates.io-index" 666 | checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" 667 | dependencies = [ 668 | "bytes", 669 | "http", 670 | ] 671 | 672 | [[package]] 673 | name = "http-body-util" 674 | version = "0.1.3" 675 | source = "registry+https://github.com/rust-lang/crates.io-index" 676 | checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" 677 | dependencies = [ 678 | "bytes", 679 | "futures-core", 680 | "http", 681 | "http-body", 682 | "pin-project-lite", 683 | ] 684 | 685 | [[package]] 686 | name = "httparse" 687 | version = "1.10.1" 688 | source = "registry+https://github.com/rust-lang/crates.io-index" 689 | checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" 690 | 691 | [[package]] 692 | name = "hyper" 693 | version = "1.7.0" 694 | source = "registry+https://github.com/rust-lang/crates.io-index" 695 | checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" 696 | dependencies = [ 697 | "atomic-waker", 698 | "bytes", 699 | "futures-channel", 700 | "futures-core", 701 | "http", 702 | "http-body", 703 | "httparse", 704 | "itoa", 705 | "pin-project-lite", 706 | "pin-utils", 707 | "smallvec", 708 | "tokio", 709 | "want", 710 | ] 711 | 712 | [[package]] 713 | name = "hyper-rustls" 714 | version = "0.27.7" 715 | source = "registry+https://github.com/rust-lang/crates.io-index" 716 | checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" 717 | dependencies = [ 718 | "http", 719 | "hyper", 720 | "hyper-util", 721 | "log", 722 | "rustls", 723 | "rustls-native-certs", 724 | "rustls-pki-types", 725 | "tokio", 726 | "tokio-rustls", 727 | "tower-service", 728 | "webpki-roots", 729 | ] 730 | 731 | [[package]] 732 | name = "hyper-timeout" 733 | version = "0.5.2" 734 | source = "registry+https://github.com/rust-lang/crates.io-index" 735 | checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" 736 | dependencies = [ 737 | "hyper", 738 | "hyper-util", 739 | "pin-project-lite", 740 | "tokio", 741 | "tower-service", 742 | ] 743 | 744 | [[package]] 745 | name = "hyper-util" 746 | version = "0.1.17" 747 | source = "registry+https://github.com/rust-lang/crates.io-index" 748 | checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" 749 | dependencies = [ 750 | "base64", 751 | "bytes", 752 | "futures-channel", 753 | "futures-core", 754 | "futures-util", 755 | "http", 756 | "http-body", 757 | "hyper", 758 | "ipnet", 759 | "libc", 760 | "percent-encoding", 761 | "pin-project-lite", 762 | "socket2", 763 | "tokio", 764 | "tower-service", 765 | "tracing", 766 | ] 767 | 768 | [[package]] 769 | name = "iana-time-zone" 770 | version = "0.1.64" 771 | source = "registry+https://github.com/rust-lang/crates.io-index" 772 | checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" 773 | dependencies = [ 774 | "android_system_properties", 775 | "core-foundation-sys", 776 | "iana-time-zone-haiku", 777 | "js-sys", 778 | "log", 779 | "wasm-bindgen", 780 | "windows-core", 781 | ] 782 | 783 | [[package]] 784 | name = "iana-time-zone-haiku" 785 | version = "0.1.2" 786 | source = "registry+https://github.com/rust-lang/crates.io-index" 787 | checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 788 | dependencies = [ 789 | "cc", 790 | ] 791 | 792 | [[package]] 793 | name = "icu_collections" 794 | version = "2.0.0" 795 | source = "registry+https://github.com/rust-lang/crates.io-index" 796 | checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" 797 | dependencies = [ 798 | "displaydoc", 799 | "potential_utf", 800 | "yoke", 801 | "zerofrom", 802 | "zerovec", 803 | ] 804 | 805 | [[package]] 806 | name = "icu_locale_core" 807 | version = "2.0.0" 808 | source = "registry+https://github.com/rust-lang/crates.io-index" 809 | checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" 810 | dependencies = [ 811 | "displaydoc", 812 | "litemap", 813 | "tinystr", 814 | "writeable", 815 | "zerovec", 816 | ] 817 | 818 | [[package]] 819 | name = "icu_normalizer" 820 | version = "2.0.0" 821 | source = "registry+https://github.com/rust-lang/crates.io-index" 822 | checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" 823 | dependencies = [ 824 | "displaydoc", 825 | "icu_collections", 826 | "icu_normalizer_data", 827 | "icu_properties", 828 | "icu_provider", 829 | "smallvec", 830 | "zerovec", 831 | ] 832 | 833 | [[package]] 834 | name = "icu_normalizer_data" 835 | version = "2.0.0" 836 | source = "registry+https://github.com/rust-lang/crates.io-index" 837 | checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" 838 | 839 | [[package]] 840 | name = "icu_properties" 841 | version = "2.0.1" 842 | source = "registry+https://github.com/rust-lang/crates.io-index" 843 | checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" 844 | dependencies = [ 845 | "displaydoc", 846 | "icu_collections", 847 | "icu_locale_core", 848 | "icu_properties_data", 849 | "icu_provider", 850 | "potential_utf", 851 | "zerotrie", 852 | "zerovec", 853 | ] 854 | 855 | [[package]] 856 | name = "icu_properties_data" 857 | version = "2.0.1" 858 | source = "registry+https://github.com/rust-lang/crates.io-index" 859 | checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" 860 | 861 | [[package]] 862 | name = "icu_provider" 863 | version = "2.0.0" 864 | source = "registry+https://github.com/rust-lang/crates.io-index" 865 | checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" 866 | dependencies = [ 867 | "displaydoc", 868 | "icu_locale_core", 869 | "stable_deref_trait", 870 | "tinystr", 871 | "writeable", 872 | "yoke", 873 | "zerofrom", 874 | "zerotrie", 875 | "zerovec", 876 | ] 877 | 878 | [[package]] 879 | name = "idna" 880 | version = "1.1.0" 881 | source = "registry+https://github.com/rust-lang/crates.io-index" 882 | checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" 883 | dependencies = [ 884 | "idna_adapter", 885 | "smallvec", 886 | "utf8_iter", 887 | ] 888 | 889 | [[package]] 890 | name = "idna_adapter" 891 | version = "1.2.1" 892 | source = "registry+https://github.com/rust-lang/crates.io-index" 893 | checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" 894 | dependencies = [ 895 | "icu_normalizer", 896 | "icu_properties", 897 | ] 898 | 899 | [[package]] 900 | name = "indenter" 901 | version = "0.3.4" 902 | source = "registry+https://github.com/rust-lang/crates.io-index" 903 | checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5" 904 | 905 | [[package]] 906 | name = "indexmap" 907 | version = "2.11.3" 908 | source = "registry+https://github.com/rust-lang/crates.io-index" 909 | checksum = "92119844f513ffa41556430369ab02c295a3578af21cf945caa3e9e0c2481ac3" 910 | dependencies = [ 911 | "equivalent", 912 | "hashbrown", 913 | ] 914 | 915 | [[package]] 916 | name = "ipnet" 917 | version = "2.11.0" 918 | source = "registry+https://github.com/rust-lang/crates.io-index" 919 | checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" 920 | 921 | [[package]] 922 | name = "is-terminal" 923 | version = "0.4.16" 924 | source = "registry+https://github.com/rust-lang/crates.io-index" 925 | checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" 926 | dependencies = [ 927 | "hermit-abi", 928 | "libc", 929 | "windows-sys 0.59.0", 930 | ] 931 | 932 | [[package]] 933 | name = "is_terminal_polyfill" 934 | version = "1.70.1" 935 | source = "registry+https://github.com/rust-lang/crates.io-index" 936 | checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" 937 | 938 | [[package]] 939 | name = "itertools" 940 | version = "0.14.0" 941 | source = "registry+https://github.com/rust-lang/crates.io-index" 942 | checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" 943 | dependencies = [ 944 | "either", 945 | ] 946 | 947 | [[package]] 948 | name = "itoa" 949 | version = "1.0.15" 950 | source = "registry+https://github.com/rust-lang/crates.io-index" 951 | checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 952 | 953 | [[package]] 954 | name = "js-sys" 955 | version = "0.3.80" 956 | source = "registry+https://github.com/rust-lang/crates.io-index" 957 | checksum = "852f13bec5eba4ba9afbeb93fd7c13fe56147f055939ae21c43a29a0ecb2702e" 958 | dependencies = [ 959 | "once_cell", 960 | "wasm-bindgen", 961 | ] 962 | 963 | [[package]] 964 | name = "jsonpath-rust" 965 | version = "0.7.5" 966 | source = "registry+https://github.com/rust-lang/crates.io-index" 967 | checksum = "0c00ae348f9f8fd2d09f82a98ca381c60df9e0820d8d79fce43e649b4dc3128b" 968 | dependencies = [ 969 | "pest", 970 | "pest_derive", 971 | "regex", 972 | "serde_json", 973 | "thiserror 2.0.17", 974 | ] 975 | 976 | [[package]] 977 | name = "k8s-openapi" 978 | version = "0.26.0" 979 | source = "registry+https://github.com/rust-lang/crates.io-index" 980 | checksum = "d13f06d5326a915becaffabdfab75051b8cdc260c2a5c06c0e90226ede89a692" 981 | dependencies = [ 982 | "base64", 983 | "chrono", 984 | "serde", 985 | "serde_json", 986 | ] 987 | 988 | [[package]] 989 | name = "kube" 990 | version = "2.0.1" 991 | source = "registry+https://github.com/rust-lang/crates.io-index" 992 | checksum = "48e7bb0b6a46502cc20e4575b6ff401af45cfea150b34ba272a3410b78aa014e" 993 | dependencies = [ 994 | "k8s-openapi", 995 | "kube-client", 996 | "kube-core", 997 | ] 998 | 999 | [[package]] 1000 | name = "kube-client" 1001 | version = "2.0.1" 1002 | source = "registry+https://github.com/rust-lang/crates.io-index" 1003 | checksum = "4987d57a184d2b5294fdad3d7fc7f278899469d21a4da39a8f6ca16426567a36" 1004 | dependencies = [ 1005 | "base64", 1006 | "bytes", 1007 | "chrono", 1008 | "either", 1009 | "form_urlencoded", 1010 | "futures", 1011 | "home", 1012 | "http", 1013 | "http-body", 1014 | "http-body-util", 1015 | "hyper", 1016 | "hyper-rustls", 1017 | "hyper-timeout", 1018 | "hyper-util", 1019 | "jsonpath-rust", 1020 | "k8s-openapi", 1021 | "kube-core", 1022 | "pem", 1023 | "rustls", 1024 | "secrecy", 1025 | "serde", 1026 | "serde_json", 1027 | "serde_yaml", 1028 | "tame-oauth", 1029 | "thiserror 2.0.17", 1030 | "tokio", 1031 | "tokio-util", 1032 | "tower", 1033 | "tower-http", 1034 | "tracing", 1035 | ] 1036 | 1037 | [[package]] 1038 | name = "kube-core" 1039 | version = "2.0.1" 1040 | source = "registry+https://github.com/rust-lang/crates.io-index" 1041 | checksum = "914bbb770e7bb721a06e3538c0edd2babed46447d128f7c21caa68747060ee73" 1042 | dependencies = [ 1043 | "chrono", 1044 | "derive_more", 1045 | "form_urlencoded", 1046 | "http", 1047 | "k8s-openapi", 1048 | "serde", 1049 | "serde-value", 1050 | "serde_json", 1051 | "thiserror 2.0.17", 1052 | ] 1053 | 1054 | [[package]] 1055 | name = "kubectl-view-allocations" 1056 | version = "1.0.0" 1057 | dependencies = [ 1058 | "anyhow", 1059 | "chrono", 1060 | "clap", 1061 | "color-eyre", 1062 | "futures", 1063 | "itertools", 1064 | "k8s-openapi", 1065 | "kube", 1066 | "pretty_assertions", 1067 | "prettytable-rs", 1068 | "serde", 1069 | "serde_json", 1070 | "thiserror 2.0.17", 1071 | "tokio", 1072 | "tracing", 1073 | "tracing-bunyan-formatter", 1074 | "tracing-error", 1075 | "tracing-subscriber", 1076 | ] 1077 | 1078 | [[package]] 1079 | name = "lazy_static" 1080 | version = "1.5.0" 1081 | source = "registry+https://github.com/rust-lang/crates.io-index" 1082 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 1083 | 1084 | [[package]] 1085 | name = "libc" 1086 | version = "0.2.175" 1087 | source = "registry+https://github.com/rust-lang/crates.io-index" 1088 | checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" 1089 | 1090 | [[package]] 1091 | name = "libredox" 1092 | version = "0.1.10" 1093 | source = "registry+https://github.com/rust-lang/crates.io-index" 1094 | checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" 1095 | dependencies = [ 1096 | "bitflags", 1097 | "libc", 1098 | ] 1099 | 1100 | [[package]] 1101 | name = "litemap" 1102 | version = "0.8.0" 1103 | source = "registry+https://github.com/rust-lang/crates.io-index" 1104 | checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" 1105 | 1106 | [[package]] 1107 | name = "lock_api" 1108 | version = "0.4.13" 1109 | source = "registry+https://github.com/rust-lang/crates.io-index" 1110 | checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" 1111 | dependencies = [ 1112 | "autocfg", 1113 | "scopeguard", 1114 | ] 1115 | 1116 | [[package]] 1117 | name = "log" 1118 | version = "0.4.28" 1119 | source = "registry+https://github.com/rust-lang/crates.io-index" 1120 | checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" 1121 | 1122 | [[package]] 1123 | name = "matchers" 1124 | version = "0.2.0" 1125 | source = "registry+https://github.com/rust-lang/crates.io-index" 1126 | checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" 1127 | dependencies = [ 1128 | "regex-automata", 1129 | ] 1130 | 1131 | [[package]] 1132 | name = "memchr" 1133 | version = "2.7.5" 1134 | source = "registry+https://github.com/rust-lang/crates.io-index" 1135 | checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" 1136 | 1137 | [[package]] 1138 | name = "mime" 1139 | version = "0.3.17" 1140 | source = "registry+https://github.com/rust-lang/crates.io-index" 1141 | checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" 1142 | 1143 | [[package]] 1144 | name = "miniz_oxide" 1145 | version = "0.8.9" 1146 | source = "registry+https://github.com/rust-lang/crates.io-index" 1147 | checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" 1148 | dependencies = [ 1149 | "adler2", 1150 | ] 1151 | 1152 | [[package]] 1153 | name = "mio" 1154 | version = "1.0.4" 1155 | source = "registry+https://github.com/rust-lang/crates.io-index" 1156 | checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" 1157 | dependencies = [ 1158 | "libc", 1159 | "wasi 0.11.1+wasi-snapshot-preview1", 1160 | "windows-sys 0.59.0", 1161 | ] 1162 | 1163 | [[package]] 1164 | name = "num-conv" 1165 | version = "0.1.0" 1166 | source = "registry+https://github.com/rust-lang/crates.io-index" 1167 | checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" 1168 | 1169 | [[package]] 1170 | name = "num-traits" 1171 | version = "0.2.19" 1172 | source = "registry+https://github.com/rust-lang/crates.io-index" 1173 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 1174 | dependencies = [ 1175 | "autocfg", 1176 | ] 1177 | 1178 | [[package]] 1179 | name = "object" 1180 | version = "0.36.7" 1181 | source = "registry+https://github.com/rust-lang/crates.io-index" 1182 | checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" 1183 | dependencies = [ 1184 | "memchr", 1185 | ] 1186 | 1187 | [[package]] 1188 | name = "once_cell" 1189 | version = "1.21.3" 1190 | source = "registry+https://github.com/rust-lang/crates.io-index" 1191 | checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 1192 | 1193 | [[package]] 1194 | name = "once_cell_polyfill" 1195 | version = "1.70.1" 1196 | source = "registry+https://github.com/rust-lang/crates.io-index" 1197 | checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" 1198 | 1199 | [[package]] 1200 | name = "openssl-probe" 1201 | version = "0.1.6" 1202 | source = "registry+https://github.com/rust-lang/crates.io-index" 1203 | checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" 1204 | 1205 | [[package]] 1206 | name = "ordered-float" 1207 | version = "2.10.1" 1208 | source = "registry+https://github.com/rust-lang/crates.io-index" 1209 | checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" 1210 | dependencies = [ 1211 | "num-traits", 1212 | ] 1213 | 1214 | [[package]] 1215 | name = "owo-colors" 1216 | version = "4.2.2" 1217 | source = "registry+https://github.com/rust-lang/crates.io-index" 1218 | checksum = "48dd4f4a2c8405440fd0462561f0e5806bd0f77e86f51c761481bdd4018b545e" 1219 | 1220 | [[package]] 1221 | name = "parking_lot" 1222 | version = "0.12.4" 1223 | source = "registry+https://github.com/rust-lang/crates.io-index" 1224 | checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" 1225 | dependencies = [ 1226 | "lock_api", 1227 | "parking_lot_core", 1228 | ] 1229 | 1230 | [[package]] 1231 | name = "parking_lot_core" 1232 | version = "0.9.11" 1233 | source = "registry+https://github.com/rust-lang/crates.io-index" 1234 | checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" 1235 | dependencies = [ 1236 | "cfg-if", 1237 | "libc", 1238 | "redox_syscall", 1239 | "smallvec", 1240 | "windows-targets 0.52.6", 1241 | ] 1242 | 1243 | [[package]] 1244 | name = "pem" 1245 | version = "3.0.5" 1246 | source = "registry+https://github.com/rust-lang/crates.io-index" 1247 | checksum = "38af38e8470ac9dee3ce1bae1af9c1671fffc44ddfd8bd1d0a3445bf349a8ef3" 1248 | dependencies = [ 1249 | "base64", 1250 | "serde", 1251 | ] 1252 | 1253 | [[package]] 1254 | name = "percent-encoding" 1255 | version = "2.3.2" 1256 | source = "registry+https://github.com/rust-lang/crates.io-index" 1257 | checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" 1258 | 1259 | [[package]] 1260 | name = "pest" 1261 | version = "2.8.2" 1262 | source = "registry+https://github.com/rust-lang/crates.io-index" 1263 | checksum = "21e0a3a33733faeaf8651dfee72dd0f388f0c8e5ad496a3478fa5a922f49cfa8" 1264 | dependencies = [ 1265 | "memchr", 1266 | "thiserror 2.0.17", 1267 | "ucd-trie", 1268 | ] 1269 | 1270 | [[package]] 1271 | name = "pest_derive" 1272 | version = "2.8.2" 1273 | source = "registry+https://github.com/rust-lang/crates.io-index" 1274 | checksum = "bc58706f770acb1dbd0973e6530a3cff4746fb721207feb3a8a6064cd0b6c663" 1275 | dependencies = [ 1276 | "pest", 1277 | "pest_generator", 1278 | ] 1279 | 1280 | [[package]] 1281 | name = "pest_generator" 1282 | version = "2.8.2" 1283 | source = "registry+https://github.com/rust-lang/crates.io-index" 1284 | checksum = "6d4f36811dfe07f7b8573462465d5cb8965fffc2e71ae377a33aecf14c2c9a2f" 1285 | dependencies = [ 1286 | "pest", 1287 | "pest_meta", 1288 | "proc-macro2", 1289 | "quote", 1290 | "syn", 1291 | ] 1292 | 1293 | [[package]] 1294 | name = "pest_meta" 1295 | version = "2.8.2" 1296 | source = "registry+https://github.com/rust-lang/crates.io-index" 1297 | checksum = "42919b05089acbd0a5dcd5405fb304d17d1053847b81163d09c4ad18ce8e8420" 1298 | dependencies = [ 1299 | "pest", 1300 | "sha2", 1301 | ] 1302 | 1303 | [[package]] 1304 | name = "pin-project-lite" 1305 | version = "0.2.16" 1306 | source = "registry+https://github.com/rust-lang/crates.io-index" 1307 | checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" 1308 | 1309 | [[package]] 1310 | name = "pin-utils" 1311 | version = "0.1.0" 1312 | source = "registry+https://github.com/rust-lang/crates.io-index" 1313 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 1314 | 1315 | [[package]] 1316 | name = "potential_utf" 1317 | version = "0.1.3" 1318 | source = "registry+https://github.com/rust-lang/crates.io-index" 1319 | checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" 1320 | dependencies = [ 1321 | "zerovec", 1322 | ] 1323 | 1324 | [[package]] 1325 | name = "powerfmt" 1326 | version = "0.2.0" 1327 | source = "registry+https://github.com/rust-lang/crates.io-index" 1328 | checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" 1329 | 1330 | [[package]] 1331 | name = "pretty_assertions" 1332 | version = "1.4.1" 1333 | source = "registry+https://github.com/rust-lang/crates.io-index" 1334 | checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" 1335 | dependencies = [ 1336 | "diff", 1337 | "yansi", 1338 | ] 1339 | 1340 | [[package]] 1341 | name = "prettytable-rs" 1342 | version = "0.10.0" 1343 | source = "registry+https://github.com/rust-lang/crates.io-index" 1344 | checksum = "eea25e07510aa6ab6547308ebe3c036016d162b8da920dbb079e3ba8acf3d95a" 1345 | dependencies = [ 1346 | "encode_unicode", 1347 | "is-terminal", 1348 | "lazy_static", 1349 | "term", 1350 | "unicode-width", 1351 | ] 1352 | 1353 | [[package]] 1354 | name = "proc-macro2" 1355 | version = "1.0.101" 1356 | source = "registry+https://github.com/rust-lang/crates.io-index" 1357 | checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" 1358 | dependencies = [ 1359 | "unicode-ident", 1360 | ] 1361 | 1362 | [[package]] 1363 | name = "quote" 1364 | version = "1.0.40" 1365 | source = "registry+https://github.com/rust-lang/crates.io-index" 1366 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 1367 | dependencies = [ 1368 | "proc-macro2", 1369 | ] 1370 | 1371 | [[package]] 1372 | name = "r-efi" 1373 | version = "5.3.0" 1374 | source = "registry+https://github.com/rust-lang/crates.io-index" 1375 | checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" 1376 | 1377 | [[package]] 1378 | name = "redox_syscall" 1379 | version = "0.5.17" 1380 | source = "registry+https://github.com/rust-lang/crates.io-index" 1381 | checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" 1382 | dependencies = [ 1383 | "bitflags", 1384 | ] 1385 | 1386 | [[package]] 1387 | name = "redox_users" 1388 | version = "0.4.6" 1389 | source = "registry+https://github.com/rust-lang/crates.io-index" 1390 | checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" 1391 | dependencies = [ 1392 | "getrandom 0.2.16", 1393 | "libredox", 1394 | "thiserror 1.0.69", 1395 | ] 1396 | 1397 | [[package]] 1398 | name = "regex" 1399 | version = "1.11.2" 1400 | source = "registry+https://github.com/rust-lang/crates.io-index" 1401 | checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" 1402 | dependencies = [ 1403 | "aho-corasick", 1404 | "memchr", 1405 | "regex-automata", 1406 | "regex-syntax", 1407 | ] 1408 | 1409 | [[package]] 1410 | name = "regex-automata" 1411 | version = "0.4.10" 1412 | source = "registry+https://github.com/rust-lang/crates.io-index" 1413 | checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" 1414 | dependencies = [ 1415 | "aho-corasick", 1416 | "memchr", 1417 | "regex-syntax", 1418 | ] 1419 | 1420 | [[package]] 1421 | name = "regex-syntax" 1422 | version = "0.8.6" 1423 | source = "registry+https://github.com/rust-lang/crates.io-index" 1424 | checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" 1425 | 1426 | [[package]] 1427 | name = "ring" 1428 | version = "0.17.14" 1429 | source = "registry+https://github.com/rust-lang/crates.io-index" 1430 | checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" 1431 | dependencies = [ 1432 | "cc", 1433 | "cfg-if", 1434 | "getrandom 0.2.16", 1435 | "libc", 1436 | "untrusted", 1437 | "windows-sys 0.52.0", 1438 | ] 1439 | 1440 | [[package]] 1441 | name = "rustc-demangle" 1442 | version = "0.1.26" 1443 | source = "registry+https://github.com/rust-lang/crates.io-index" 1444 | checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" 1445 | 1446 | [[package]] 1447 | name = "rustls" 1448 | version = "0.23.31" 1449 | source = "registry+https://github.com/rust-lang/crates.io-index" 1450 | checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" 1451 | dependencies = [ 1452 | "log", 1453 | "once_cell", 1454 | "ring", 1455 | "rustls-pki-types", 1456 | "rustls-webpki", 1457 | "subtle", 1458 | "zeroize", 1459 | ] 1460 | 1461 | [[package]] 1462 | name = "rustls-native-certs" 1463 | version = "0.8.1" 1464 | source = "registry+https://github.com/rust-lang/crates.io-index" 1465 | checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" 1466 | dependencies = [ 1467 | "openssl-probe", 1468 | "rustls-pki-types", 1469 | "schannel", 1470 | "security-framework", 1471 | ] 1472 | 1473 | [[package]] 1474 | name = "rustls-pki-types" 1475 | version = "1.12.0" 1476 | source = "registry+https://github.com/rust-lang/crates.io-index" 1477 | checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" 1478 | dependencies = [ 1479 | "zeroize", 1480 | ] 1481 | 1482 | [[package]] 1483 | name = "rustls-webpki" 1484 | version = "0.103.6" 1485 | source = "registry+https://github.com/rust-lang/crates.io-index" 1486 | checksum = "8572f3c2cb9934231157b45499fc41e1f58c589fdfb81a844ba873265e80f8eb" 1487 | dependencies = [ 1488 | "ring", 1489 | "rustls-pki-types", 1490 | "untrusted", 1491 | ] 1492 | 1493 | [[package]] 1494 | name = "rustversion" 1495 | version = "1.0.22" 1496 | source = "registry+https://github.com/rust-lang/crates.io-index" 1497 | checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" 1498 | 1499 | [[package]] 1500 | name = "ryu" 1501 | version = "1.0.20" 1502 | source = "registry+https://github.com/rust-lang/crates.io-index" 1503 | checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" 1504 | 1505 | [[package]] 1506 | name = "schannel" 1507 | version = "0.1.28" 1508 | source = "registry+https://github.com/rust-lang/crates.io-index" 1509 | checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" 1510 | dependencies = [ 1511 | "windows-sys 0.61.0", 1512 | ] 1513 | 1514 | [[package]] 1515 | name = "scopeguard" 1516 | version = "1.2.0" 1517 | source = "registry+https://github.com/rust-lang/crates.io-index" 1518 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 1519 | 1520 | [[package]] 1521 | name = "secrecy" 1522 | version = "0.10.3" 1523 | source = "registry+https://github.com/rust-lang/crates.io-index" 1524 | checksum = "e891af845473308773346dc847b2c23ee78fe442e0472ac50e22a18a93d3ae5a" 1525 | dependencies = [ 1526 | "zeroize", 1527 | ] 1528 | 1529 | [[package]] 1530 | name = "security-framework" 1531 | version = "3.4.0" 1532 | source = "registry+https://github.com/rust-lang/crates.io-index" 1533 | checksum = "60b369d18893388b345804dc0007963c99b7d665ae71d275812d828c6f089640" 1534 | dependencies = [ 1535 | "bitflags", 1536 | "core-foundation", 1537 | "core-foundation-sys", 1538 | "libc", 1539 | "security-framework-sys", 1540 | ] 1541 | 1542 | [[package]] 1543 | name = "security-framework-sys" 1544 | version = "2.15.0" 1545 | source = "registry+https://github.com/rust-lang/crates.io-index" 1546 | checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" 1547 | dependencies = [ 1548 | "core-foundation-sys", 1549 | "libc", 1550 | ] 1551 | 1552 | [[package]] 1553 | name = "serde" 1554 | version = "1.0.228" 1555 | source = "registry+https://github.com/rust-lang/crates.io-index" 1556 | checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" 1557 | dependencies = [ 1558 | "serde_core", 1559 | "serde_derive", 1560 | ] 1561 | 1562 | [[package]] 1563 | name = "serde-value" 1564 | version = "0.7.0" 1565 | source = "registry+https://github.com/rust-lang/crates.io-index" 1566 | checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" 1567 | dependencies = [ 1568 | "ordered-float", 1569 | "serde", 1570 | ] 1571 | 1572 | [[package]] 1573 | name = "serde_core" 1574 | version = "1.0.228" 1575 | source = "registry+https://github.com/rust-lang/crates.io-index" 1576 | checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" 1577 | dependencies = [ 1578 | "serde_derive", 1579 | ] 1580 | 1581 | [[package]] 1582 | name = "serde_derive" 1583 | version = "1.0.228" 1584 | source = "registry+https://github.com/rust-lang/crates.io-index" 1585 | checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" 1586 | dependencies = [ 1587 | "proc-macro2", 1588 | "quote", 1589 | "syn", 1590 | ] 1591 | 1592 | [[package]] 1593 | name = "serde_json" 1594 | version = "1.0.145" 1595 | source = "registry+https://github.com/rust-lang/crates.io-index" 1596 | checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" 1597 | dependencies = [ 1598 | "itoa", 1599 | "memchr", 1600 | "ryu", 1601 | "serde", 1602 | "serde_core", 1603 | ] 1604 | 1605 | [[package]] 1606 | name = "serde_yaml" 1607 | version = "0.9.34+deprecated" 1608 | source = "registry+https://github.com/rust-lang/crates.io-index" 1609 | checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" 1610 | dependencies = [ 1611 | "indexmap", 1612 | "itoa", 1613 | "ryu", 1614 | "serde", 1615 | "unsafe-libyaml", 1616 | ] 1617 | 1618 | [[package]] 1619 | name = "sha2" 1620 | version = "0.10.9" 1621 | source = "registry+https://github.com/rust-lang/crates.io-index" 1622 | checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" 1623 | dependencies = [ 1624 | "cfg-if", 1625 | "cpufeatures", 1626 | "digest", 1627 | ] 1628 | 1629 | [[package]] 1630 | name = "sharded-slab" 1631 | version = "0.1.7" 1632 | source = "registry+https://github.com/rust-lang/crates.io-index" 1633 | checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" 1634 | dependencies = [ 1635 | "lazy_static", 1636 | ] 1637 | 1638 | [[package]] 1639 | name = "shlex" 1640 | version = "1.3.0" 1641 | source = "registry+https://github.com/rust-lang/crates.io-index" 1642 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 1643 | 1644 | [[package]] 1645 | name = "signal-hook-registry" 1646 | version = "1.4.6" 1647 | source = "registry+https://github.com/rust-lang/crates.io-index" 1648 | checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" 1649 | dependencies = [ 1650 | "libc", 1651 | ] 1652 | 1653 | [[package]] 1654 | name = "slab" 1655 | version = "0.4.11" 1656 | source = "registry+https://github.com/rust-lang/crates.io-index" 1657 | checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" 1658 | 1659 | [[package]] 1660 | name = "smallvec" 1661 | version = "1.15.1" 1662 | source = "registry+https://github.com/rust-lang/crates.io-index" 1663 | checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" 1664 | 1665 | [[package]] 1666 | name = "socket2" 1667 | version = "0.6.0" 1668 | source = "registry+https://github.com/rust-lang/crates.io-index" 1669 | checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" 1670 | dependencies = [ 1671 | "libc", 1672 | "windows-sys 0.59.0", 1673 | ] 1674 | 1675 | [[package]] 1676 | name = "stable_deref_trait" 1677 | version = "1.2.0" 1678 | source = "registry+https://github.com/rust-lang/crates.io-index" 1679 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 1680 | 1681 | [[package]] 1682 | name = "static_assertions" 1683 | version = "1.1.0" 1684 | source = "registry+https://github.com/rust-lang/crates.io-index" 1685 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 1686 | 1687 | [[package]] 1688 | name = "strsim" 1689 | version = "0.11.1" 1690 | source = "registry+https://github.com/rust-lang/crates.io-index" 1691 | checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 1692 | 1693 | [[package]] 1694 | name = "subtle" 1695 | version = "2.6.1" 1696 | source = "registry+https://github.com/rust-lang/crates.io-index" 1697 | checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" 1698 | 1699 | [[package]] 1700 | name = "syn" 1701 | version = "2.0.106" 1702 | source = "registry+https://github.com/rust-lang/crates.io-index" 1703 | checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" 1704 | dependencies = [ 1705 | "proc-macro2", 1706 | "quote", 1707 | "unicode-ident", 1708 | ] 1709 | 1710 | [[package]] 1711 | name = "sync_wrapper" 1712 | version = "1.0.2" 1713 | source = "registry+https://github.com/rust-lang/crates.io-index" 1714 | checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" 1715 | 1716 | [[package]] 1717 | name = "synstructure" 1718 | version = "0.13.2" 1719 | source = "registry+https://github.com/rust-lang/crates.io-index" 1720 | checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" 1721 | dependencies = [ 1722 | "proc-macro2", 1723 | "quote", 1724 | "syn", 1725 | ] 1726 | 1727 | [[package]] 1728 | name = "tame-oauth" 1729 | version = "0.10.0" 1730 | source = "registry+https://github.com/rust-lang/crates.io-index" 1731 | checksum = "c206bbecfbc0aea8bf35f57bf34e8be060d2cf7efe3937f8d0bdfdd4205ed771" 1732 | dependencies = [ 1733 | "data-encoding", 1734 | "http", 1735 | "ring", 1736 | "serde", 1737 | "serde_json", 1738 | "twox-hash", 1739 | "url", 1740 | ] 1741 | 1742 | [[package]] 1743 | name = "term" 1744 | version = "0.7.0" 1745 | source = "registry+https://github.com/rust-lang/crates.io-index" 1746 | checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" 1747 | dependencies = [ 1748 | "dirs-next", 1749 | "rustversion", 1750 | "winapi", 1751 | ] 1752 | 1753 | [[package]] 1754 | name = "thiserror" 1755 | version = "1.0.69" 1756 | source = "registry+https://github.com/rust-lang/crates.io-index" 1757 | checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" 1758 | dependencies = [ 1759 | "thiserror-impl 1.0.69", 1760 | ] 1761 | 1762 | [[package]] 1763 | name = "thiserror" 1764 | version = "2.0.17" 1765 | source = "registry+https://github.com/rust-lang/crates.io-index" 1766 | checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" 1767 | dependencies = [ 1768 | "thiserror-impl 2.0.17", 1769 | ] 1770 | 1771 | [[package]] 1772 | name = "thiserror-impl" 1773 | version = "1.0.69" 1774 | source = "registry+https://github.com/rust-lang/crates.io-index" 1775 | checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" 1776 | dependencies = [ 1777 | "proc-macro2", 1778 | "quote", 1779 | "syn", 1780 | ] 1781 | 1782 | [[package]] 1783 | name = "thiserror-impl" 1784 | version = "2.0.17" 1785 | source = "registry+https://github.com/rust-lang/crates.io-index" 1786 | checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" 1787 | dependencies = [ 1788 | "proc-macro2", 1789 | "quote", 1790 | "syn", 1791 | ] 1792 | 1793 | [[package]] 1794 | name = "thread_local" 1795 | version = "1.1.9" 1796 | source = "registry+https://github.com/rust-lang/crates.io-index" 1797 | checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" 1798 | dependencies = [ 1799 | "cfg-if", 1800 | ] 1801 | 1802 | [[package]] 1803 | name = "time" 1804 | version = "0.3.43" 1805 | source = "registry+https://github.com/rust-lang/crates.io-index" 1806 | checksum = "83bde6f1ec10e72d583d91623c939f623002284ef622b87de38cfd546cbf2031" 1807 | dependencies = [ 1808 | "deranged", 1809 | "num-conv", 1810 | "powerfmt", 1811 | "serde", 1812 | "time-core", 1813 | "time-macros", 1814 | ] 1815 | 1816 | [[package]] 1817 | name = "time-core" 1818 | version = "0.1.6" 1819 | source = "registry+https://github.com/rust-lang/crates.io-index" 1820 | checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" 1821 | 1822 | [[package]] 1823 | name = "time-macros" 1824 | version = "0.2.24" 1825 | source = "registry+https://github.com/rust-lang/crates.io-index" 1826 | checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" 1827 | dependencies = [ 1828 | "num-conv", 1829 | "time-core", 1830 | ] 1831 | 1832 | [[package]] 1833 | name = "tinystr" 1834 | version = "0.8.1" 1835 | source = "registry+https://github.com/rust-lang/crates.io-index" 1836 | checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" 1837 | dependencies = [ 1838 | "displaydoc", 1839 | "zerovec", 1840 | ] 1841 | 1842 | [[package]] 1843 | name = "tokio" 1844 | version = "1.48.0" 1845 | source = "registry+https://github.com/rust-lang/crates.io-index" 1846 | checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" 1847 | dependencies = [ 1848 | "bytes", 1849 | "libc", 1850 | "mio", 1851 | "parking_lot", 1852 | "pin-project-lite", 1853 | "signal-hook-registry", 1854 | "socket2", 1855 | "tokio-macros", 1856 | "windows-sys 0.61.0", 1857 | ] 1858 | 1859 | [[package]] 1860 | name = "tokio-macros" 1861 | version = "2.6.0" 1862 | source = "registry+https://github.com/rust-lang/crates.io-index" 1863 | checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" 1864 | dependencies = [ 1865 | "proc-macro2", 1866 | "quote", 1867 | "syn", 1868 | ] 1869 | 1870 | [[package]] 1871 | name = "tokio-rustls" 1872 | version = "0.26.3" 1873 | source = "registry+https://github.com/rust-lang/crates.io-index" 1874 | checksum = "05f63835928ca123f1bef57abbcd23bb2ba0ac9ae1235f1e65bda0d06e7786bd" 1875 | dependencies = [ 1876 | "rustls", 1877 | "tokio", 1878 | ] 1879 | 1880 | [[package]] 1881 | name = "tokio-util" 1882 | version = "0.7.16" 1883 | source = "registry+https://github.com/rust-lang/crates.io-index" 1884 | checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" 1885 | dependencies = [ 1886 | "bytes", 1887 | "futures-core", 1888 | "futures-sink", 1889 | "pin-project-lite", 1890 | "tokio", 1891 | ] 1892 | 1893 | [[package]] 1894 | name = "tower" 1895 | version = "0.5.2" 1896 | source = "registry+https://github.com/rust-lang/crates.io-index" 1897 | checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" 1898 | dependencies = [ 1899 | "futures-core", 1900 | "futures-util", 1901 | "pin-project-lite", 1902 | "sync_wrapper", 1903 | "tokio", 1904 | "tokio-util", 1905 | "tower-layer", 1906 | "tower-service", 1907 | "tracing", 1908 | ] 1909 | 1910 | [[package]] 1911 | name = "tower-http" 1912 | version = "0.6.6" 1913 | source = "registry+https://github.com/rust-lang/crates.io-index" 1914 | checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" 1915 | dependencies = [ 1916 | "async-compression", 1917 | "base64", 1918 | "bitflags", 1919 | "bytes", 1920 | "futures-core", 1921 | "http", 1922 | "http-body", 1923 | "http-body-util", 1924 | "mime", 1925 | "pin-project-lite", 1926 | "tokio", 1927 | "tokio-util", 1928 | "tower-layer", 1929 | "tower-service", 1930 | "tracing", 1931 | ] 1932 | 1933 | [[package]] 1934 | name = "tower-layer" 1935 | version = "0.3.3" 1936 | source = "registry+https://github.com/rust-lang/crates.io-index" 1937 | checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" 1938 | 1939 | [[package]] 1940 | name = "tower-service" 1941 | version = "0.3.3" 1942 | source = "registry+https://github.com/rust-lang/crates.io-index" 1943 | checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" 1944 | 1945 | [[package]] 1946 | name = "tracing" 1947 | version = "0.1.43" 1948 | source = "registry+https://github.com/rust-lang/crates.io-index" 1949 | checksum = "2d15d90a0b5c19378952d479dc858407149d7bb45a14de0142f6c534b16fc647" 1950 | dependencies = [ 1951 | "log", 1952 | "pin-project-lite", 1953 | "tracing-attributes", 1954 | "tracing-core", 1955 | ] 1956 | 1957 | [[package]] 1958 | name = "tracing-attributes" 1959 | version = "0.1.31" 1960 | source = "registry+https://github.com/rust-lang/crates.io-index" 1961 | checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" 1962 | dependencies = [ 1963 | "proc-macro2", 1964 | "quote", 1965 | "syn", 1966 | ] 1967 | 1968 | [[package]] 1969 | name = "tracing-bunyan-formatter" 1970 | version = "0.3.10" 1971 | source = "registry+https://github.com/rust-lang/crates.io-index" 1972 | checksum = "2d637245a0d8774bd48df6482e086c59a8b5348a910c3b0579354045a9d82411" 1973 | dependencies = [ 1974 | "ahash", 1975 | "gethostname", 1976 | "log", 1977 | "serde", 1978 | "serde_json", 1979 | "time", 1980 | "tracing", 1981 | "tracing-core", 1982 | "tracing-log", 1983 | "tracing-subscriber", 1984 | ] 1985 | 1986 | [[package]] 1987 | name = "tracing-core" 1988 | version = "0.1.35" 1989 | source = "registry+https://github.com/rust-lang/crates.io-index" 1990 | checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c" 1991 | dependencies = [ 1992 | "once_cell", 1993 | "valuable", 1994 | ] 1995 | 1996 | [[package]] 1997 | name = "tracing-error" 1998 | version = "0.2.1" 1999 | source = "registry+https://github.com/rust-lang/crates.io-index" 2000 | checksum = "8b1581020d7a273442f5b45074a6a57d5757ad0a47dac0e9f0bd57b81936f3db" 2001 | dependencies = [ 2002 | "tracing", 2003 | "tracing-subscriber", 2004 | ] 2005 | 2006 | [[package]] 2007 | name = "tracing-log" 2008 | version = "0.1.4" 2009 | source = "registry+https://github.com/rust-lang/crates.io-index" 2010 | checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2" 2011 | dependencies = [ 2012 | "log", 2013 | "once_cell", 2014 | "tracing-core", 2015 | ] 2016 | 2017 | [[package]] 2018 | name = "tracing-subscriber" 2019 | version = "0.3.22" 2020 | source = "registry+https://github.com/rust-lang/crates.io-index" 2021 | checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" 2022 | dependencies = [ 2023 | "matchers", 2024 | "once_cell", 2025 | "regex-automata", 2026 | "sharded-slab", 2027 | "thread_local", 2028 | "tracing", 2029 | "tracing-core", 2030 | ] 2031 | 2032 | [[package]] 2033 | name = "try-lock" 2034 | version = "0.2.5" 2035 | source = "registry+https://github.com/rust-lang/crates.io-index" 2036 | checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" 2037 | 2038 | [[package]] 2039 | name = "twox-hash" 2040 | version = "1.6.3" 2041 | source = "registry+https://github.com/rust-lang/crates.io-index" 2042 | checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" 2043 | dependencies = [ 2044 | "cfg-if", 2045 | "static_assertions", 2046 | ] 2047 | 2048 | [[package]] 2049 | name = "typenum" 2050 | version = "1.18.0" 2051 | source = "registry+https://github.com/rust-lang/crates.io-index" 2052 | checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" 2053 | 2054 | [[package]] 2055 | name = "ucd-trie" 2056 | version = "0.1.7" 2057 | source = "registry+https://github.com/rust-lang/crates.io-index" 2058 | checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" 2059 | 2060 | [[package]] 2061 | name = "unicode-ident" 2062 | version = "1.0.19" 2063 | source = "registry+https://github.com/rust-lang/crates.io-index" 2064 | checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" 2065 | 2066 | [[package]] 2067 | name = "unicode-width" 2068 | version = "0.1.14" 2069 | source = "registry+https://github.com/rust-lang/crates.io-index" 2070 | checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" 2071 | 2072 | [[package]] 2073 | name = "unsafe-libyaml" 2074 | version = "0.2.11" 2075 | source = "registry+https://github.com/rust-lang/crates.io-index" 2076 | checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" 2077 | 2078 | [[package]] 2079 | name = "untrusted" 2080 | version = "0.9.0" 2081 | source = "registry+https://github.com/rust-lang/crates.io-index" 2082 | checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" 2083 | 2084 | [[package]] 2085 | name = "url" 2086 | version = "2.5.7" 2087 | source = "registry+https://github.com/rust-lang/crates.io-index" 2088 | checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" 2089 | dependencies = [ 2090 | "form_urlencoded", 2091 | "idna", 2092 | "percent-encoding", 2093 | "serde", 2094 | ] 2095 | 2096 | [[package]] 2097 | name = "utf8_iter" 2098 | version = "1.0.4" 2099 | source = "registry+https://github.com/rust-lang/crates.io-index" 2100 | checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" 2101 | 2102 | [[package]] 2103 | name = "utf8parse" 2104 | version = "0.2.2" 2105 | source = "registry+https://github.com/rust-lang/crates.io-index" 2106 | checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 2107 | 2108 | [[package]] 2109 | name = "valuable" 2110 | version = "0.1.1" 2111 | source = "registry+https://github.com/rust-lang/crates.io-index" 2112 | checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" 2113 | 2114 | [[package]] 2115 | name = "version_check" 2116 | version = "0.9.5" 2117 | source = "registry+https://github.com/rust-lang/crates.io-index" 2118 | checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 2119 | 2120 | [[package]] 2121 | name = "want" 2122 | version = "0.3.1" 2123 | source = "registry+https://github.com/rust-lang/crates.io-index" 2124 | checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" 2125 | dependencies = [ 2126 | "try-lock", 2127 | ] 2128 | 2129 | [[package]] 2130 | name = "wasi" 2131 | version = "0.11.1+wasi-snapshot-preview1" 2132 | source = "registry+https://github.com/rust-lang/crates.io-index" 2133 | checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" 2134 | 2135 | [[package]] 2136 | name = "wasi" 2137 | version = "0.14.7+wasi-0.2.4" 2138 | source = "registry+https://github.com/rust-lang/crates.io-index" 2139 | checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" 2140 | dependencies = [ 2141 | "wasip2", 2142 | ] 2143 | 2144 | [[package]] 2145 | name = "wasip2" 2146 | version = "1.0.1+wasi-0.2.4" 2147 | source = "registry+https://github.com/rust-lang/crates.io-index" 2148 | checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" 2149 | dependencies = [ 2150 | "wit-bindgen", 2151 | ] 2152 | 2153 | [[package]] 2154 | name = "wasm-bindgen" 2155 | version = "0.2.103" 2156 | source = "registry+https://github.com/rust-lang/crates.io-index" 2157 | checksum = "ab10a69fbd0a177f5f649ad4d8d3305499c42bab9aef2f7ff592d0ec8f833819" 2158 | dependencies = [ 2159 | "cfg-if", 2160 | "once_cell", 2161 | "rustversion", 2162 | "wasm-bindgen-macro", 2163 | "wasm-bindgen-shared", 2164 | ] 2165 | 2166 | [[package]] 2167 | name = "wasm-bindgen-backend" 2168 | version = "0.2.103" 2169 | source = "registry+https://github.com/rust-lang/crates.io-index" 2170 | checksum = "0bb702423545a6007bbc368fde243ba47ca275e549c8a28617f56f6ba53b1d1c" 2171 | dependencies = [ 2172 | "bumpalo", 2173 | "log", 2174 | "proc-macro2", 2175 | "quote", 2176 | "syn", 2177 | "wasm-bindgen-shared", 2178 | ] 2179 | 2180 | [[package]] 2181 | name = "wasm-bindgen-macro" 2182 | version = "0.2.103" 2183 | source = "registry+https://github.com/rust-lang/crates.io-index" 2184 | checksum = "fc65f4f411d91494355917b605e1480033152658d71f722a90647f56a70c88a0" 2185 | dependencies = [ 2186 | "quote", 2187 | "wasm-bindgen-macro-support", 2188 | ] 2189 | 2190 | [[package]] 2191 | name = "wasm-bindgen-macro-support" 2192 | version = "0.2.103" 2193 | source = "registry+https://github.com/rust-lang/crates.io-index" 2194 | checksum = "ffc003a991398a8ee604a401e194b6b3a39677b3173d6e74495eb51b82e99a32" 2195 | dependencies = [ 2196 | "proc-macro2", 2197 | "quote", 2198 | "syn", 2199 | "wasm-bindgen-backend", 2200 | "wasm-bindgen-shared", 2201 | ] 2202 | 2203 | [[package]] 2204 | name = "wasm-bindgen-shared" 2205 | version = "0.2.103" 2206 | source = "registry+https://github.com/rust-lang/crates.io-index" 2207 | checksum = "293c37f4efa430ca14db3721dfbe48d8c33308096bd44d80ebaa775ab71ba1cf" 2208 | dependencies = [ 2209 | "unicode-ident", 2210 | ] 2211 | 2212 | [[package]] 2213 | name = "webpki-roots" 2214 | version = "1.0.2" 2215 | source = "registry+https://github.com/rust-lang/crates.io-index" 2216 | checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" 2217 | dependencies = [ 2218 | "rustls-pki-types", 2219 | ] 2220 | 2221 | [[package]] 2222 | name = "winapi" 2223 | version = "0.3.9" 2224 | source = "registry+https://github.com/rust-lang/crates.io-index" 2225 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 2226 | dependencies = [ 2227 | "winapi-i686-pc-windows-gnu", 2228 | "winapi-x86_64-pc-windows-gnu", 2229 | ] 2230 | 2231 | [[package]] 2232 | name = "winapi-i686-pc-windows-gnu" 2233 | version = "0.4.0" 2234 | source = "registry+https://github.com/rust-lang/crates.io-index" 2235 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 2236 | 2237 | [[package]] 2238 | name = "winapi-x86_64-pc-windows-gnu" 2239 | version = "0.4.0" 2240 | source = "registry+https://github.com/rust-lang/crates.io-index" 2241 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 2242 | 2243 | [[package]] 2244 | name = "windows-core" 2245 | version = "0.62.0" 2246 | source = "registry+https://github.com/rust-lang/crates.io-index" 2247 | checksum = "57fe7168f7de578d2d8a05b07fd61870d2e73b4020e9f49aa00da8471723497c" 2248 | dependencies = [ 2249 | "windows-implement", 2250 | "windows-interface", 2251 | "windows-link 0.2.0", 2252 | "windows-result", 2253 | "windows-strings", 2254 | ] 2255 | 2256 | [[package]] 2257 | name = "windows-implement" 2258 | version = "0.60.0" 2259 | source = "registry+https://github.com/rust-lang/crates.io-index" 2260 | checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" 2261 | dependencies = [ 2262 | "proc-macro2", 2263 | "quote", 2264 | "syn", 2265 | ] 2266 | 2267 | [[package]] 2268 | name = "windows-interface" 2269 | version = "0.59.1" 2270 | source = "registry+https://github.com/rust-lang/crates.io-index" 2271 | checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" 2272 | dependencies = [ 2273 | "proc-macro2", 2274 | "quote", 2275 | "syn", 2276 | ] 2277 | 2278 | [[package]] 2279 | name = "windows-link" 2280 | version = "0.1.3" 2281 | source = "registry+https://github.com/rust-lang/crates.io-index" 2282 | checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" 2283 | 2284 | [[package]] 2285 | name = "windows-link" 2286 | version = "0.2.0" 2287 | source = "registry+https://github.com/rust-lang/crates.io-index" 2288 | checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" 2289 | 2290 | [[package]] 2291 | name = "windows-result" 2292 | version = "0.4.0" 2293 | source = "registry+https://github.com/rust-lang/crates.io-index" 2294 | checksum = "7084dcc306f89883455a206237404d3eaf961e5bd7e0f312f7c91f57eb44167f" 2295 | dependencies = [ 2296 | "windows-link 0.2.0", 2297 | ] 2298 | 2299 | [[package]] 2300 | name = "windows-strings" 2301 | version = "0.5.0" 2302 | source = "registry+https://github.com/rust-lang/crates.io-index" 2303 | checksum = "7218c655a553b0bed4426cf54b20d7ba363ef543b52d515b3e48d7fd55318dda" 2304 | dependencies = [ 2305 | "windows-link 0.2.0", 2306 | ] 2307 | 2308 | [[package]] 2309 | name = "windows-sys" 2310 | version = "0.52.0" 2311 | source = "registry+https://github.com/rust-lang/crates.io-index" 2312 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 2313 | dependencies = [ 2314 | "windows-targets 0.52.6", 2315 | ] 2316 | 2317 | [[package]] 2318 | name = "windows-sys" 2319 | version = "0.59.0" 2320 | source = "registry+https://github.com/rust-lang/crates.io-index" 2321 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 2322 | dependencies = [ 2323 | "windows-targets 0.52.6", 2324 | ] 2325 | 2326 | [[package]] 2327 | name = "windows-sys" 2328 | version = "0.60.2" 2329 | source = "registry+https://github.com/rust-lang/crates.io-index" 2330 | checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" 2331 | dependencies = [ 2332 | "windows-targets 0.53.3", 2333 | ] 2334 | 2335 | [[package]] 2336 | name = "windows-sys" 2337 | version = "0.61.0" 2338 | source = "registry+https://github.com/rust-lang/crates.io-index" 2339 | checksum = "e201184e40b2ede64bc2ea34968b28e33622acdbbf37104f0e4a33f7abe657aa" 2340 | dependencies = [ 2341 | "windows-link 0.2.0", 2342 | ] 2343 | 2344 | [[package]] 2345 | name = "windows-targets" 2346 | version = "0.52.6" 2347 | source = "registry+https://github.com/rust-lang/crates.io-index" 2348 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 2349 | dependencies = [ 2350 | "windows_aarch64_gnullvm 0.52.6", 2351 | "windows_aarch64_msvc 0.52.6", 2352 | "windows_i686_gnu 0.52.6", 2353 | "windows_i686_gnullvm 0.52.6", 2354 | "windows_i686_msvc 0.52.6", 2355 | "windows_x86_64_gnu 0.52.6", 2356 | "windows_x86_64_gnullvm 0.52.6", 2357 | "windows_x86_64_msvc 0.52.6", 2358 | ] 2359 | 2360 | [[package]] 2361 | name = "windows-targets" 2362 | version = "0.53.3" 2363 | source = "registry+https://github.com/rust-lang/crates.io-index" 2364 | checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" 2365 | dependencies = [ 2366 | "windows-link 0.1.3", 2367 | "windows_aarch64_gnullvm 0.53.0", 2368 | "windows_aarch64_msvc 0.53.0", 2369 | "windows_i686_gnu 0.53.0", 2370 | "windows_i686_gnullvm 0.53.0", 2371 | "windows_i686_msvc 0.53.0", 2372 | "windows_x86_64_gnu 0.53.0", 2373 | "windows_x86_64_gnullvm 0.53.0", 2374 | "windows_x86_64_msvc 0.53.0", 2375 | ] 2376 | 2377 | [[package]] 2378 | name = "windows_aarch64_gnullvm" 2379 | version = "0.52.6" 2380 | source = "registry+https://github.com/rust-lang/crates.io-index" 2381 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 2382 | 2383 | [[package]] 2384 | name = "windows_aarch64_gnullvm" 2385 | version = "0.53.0" 2386 | source = "registry+https://github.com/rust-lang/crates.io-index" 2387 | checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" 2388 | 2389 | [[package]] 2390 | name = "windows_aarch64_msvc" 2391 | version = "0.52.6" 2392 | source = "registry+https://github.com/rust-lang/crates.io-index" 2393 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 2394 | 2395 | [[package]] 2396 | name = "windows_aarch64_msvc" 2397 | version = "0.53.0" 2398 | source = "registry+https://github.com/rust-lang/crates.io-index" 2399 | checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" 2400 | 2401 | [[package]] 2402 | name = "windows_i686_gnu" 2403 | version = "0.52.6" 2404 | source = "registry+https://github.com/rust-lang/crates.io-index" 2405 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 2406 | 2407 | [[package]] 2408 | name = "windows_i686_gnu" 2409 | version = "0.53.0" 2410 | source = "registry+https://github.com/rust-lang/crates.io-index" 2411 | checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" 2412 | 2413 | [[package]] 2414 | name = "windows_i686_gnullvm" 2415 | version = "0.52.6" 2416 | source = "registry+https://github.com/rust-lang/crates.io-index" 2417 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 2418 | 2419 | [[package]] 2420 | name = "windows_i686_gnullvm" 2421 | version = "0.53.0" 2422 | source = "registry+https://github.com/rust-lang/crates.io-index" 2423 | checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" 2424 | 2425 | [[package]] 2426 | name = "windows_i686_msvc" 2427 | version = "0.52.6" 2428 | source = "registry+https://github.com/rust-lang/crates.io-index" 2429 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 2430 | 2431 | [[package]] 2432 | name = "windows_i686_msvc" 2433 | version = "0.53.0" 2434 | source = "registry+https://github.com/rust-lang/crates.io-index" 2435 | checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" 2436 | 2437 | [[package]] 2438 | name = "windows_x86_64_gnu" 2439 | version = "0.52.6" 2440 | source = "registry+https://github.com/rust-lang/crates.io-index" 2441 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 2442 | 2443 | [[package]] 2444 | name = "windows_x86_64_gnu" 2445 | version = "0.53.0" 2446 | source = "registry+https://github.com/rust-lang/crates.io-index" 2447 | checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" 2448 | 2449 | [[package]] 2450 | name = "windows_x86_64_gnullvm" 2451 | version = "0.52.6" 2452 | source = "registry+https://github.com/rust-lang/crates.io-index" 2453 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 2454 | 2455 | [[package]] 2456 | name = "windows_x86_64_gnullvm" 2457 | version = "0.53.0" 2458 | source = "registry+https://github.com/rust-lang/crates.io-index" 2459 | checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" 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 = "windows_x86_64_msvc" 2469 | version = "0.53.0" 2470 | source = "registry+https://github.com/rust-lang/crates.io-index" 2471 | checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" 2472 | 2473 | [[package]] 2474 | name = "wit-bindgen" 2475 | version = "0.46.0" 2476 | source = "registry+https://github.com/rust-lang/crates.io-index" 2477 | checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" 2478 | 2479 | [[package]] 2480 | name = "writeable" 2481 | version = "0.6.1" 2482 | source = "registry+https://github.com/rust-lang/crates.io-index" 2483 | checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" 2484 | 2485 | [[package]] 2486 | name = "yansi" 2487 | version = "1.0.1" 2488 | source = "registry+https://github.com/rust-lang/crates.io-index" 2489 | checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" 2490 | 2491 | [[package]] 2492 | name = "yoke" 2493 | version = "0.8.0" 2494 | source = "registry+https://github.com/rust-lang/crates.io-index" 2495 | checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" 2496 | dependencies = [ 2497 | "serde", 2498 | "stable_deref_trait", 2499 | "yoke-derive", 2500 | "zerofrom", 2501 | ] 2502 | 2503 | [[package]] 2504 | name = "yoke-derive" 2505 | version = "0.8.0" 2506 | source = "registry+https://github.com/rust-lang/crates.io-index" 2507 | checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" 2508 | dependencies = [ 2509 | "proc-macro2", 2510 | "quote", 2511 | "syn", 2512 | "synstructure", 2513 | ] 2514 | 2515 | [[package]] 2516 | name = "zerocopy" 2517 | version = "0.8.27" 2518 | source = "registry+https://github.com/rust-lang/crates.io-index" 2519 | checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" 2520 | dependencies = [ 2521 | "zerocopy-derive", 2522 | ] 2523 | 2524 | [[package]] 2525 | name = "zerocopy-derive" 2526 | version = "0.8.27" 2527 | source = "registry+https://github.com/rust-lang/crates.io-index" 2528 | checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" 2529 | dependencies = [ 2530 | "proc-macro2", 2531 | "quote", 2532 | "syn", 2533 | ] 2534 | 2535 | [[package]] 2536 | name = "zerofrom" 2537 | version = "0.1.6" 2538 | source = "registry+https://github.com/rust-lang/crates.io-index" 2539 | checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" 2540 | dependencies = [ 2541 | "zerofrom-derive", 2542 | ] 2543 | 2544 | [[package]] 2545 | name = "zerofrom-derive" 2546 | version = "0.1.6" 2547 | source = "registry+https://github.com/rust-lang/crates.io-index" 2548 | checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" 2549 | dependencies = [ 2550 | "proc-macro2", 2551 | "quote", 2552 | "syn", 2553 | "synstructure", 2554 | ] 2555 | 2556 | [[package]] 2557 | name = "zeroize" 2558 | version = "1.8.1" 2559 | source = "registry+https://github.com/rust-lang/crates.io-index" 2560 | checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" 2561 | 2562 | [[package]] 2563 | name = "zerotrie" 2564 | version = "0.2.2" 2565 | source = "registry+https://github.com/rust-lang/crates.io-index" 2566 | checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" 2567 | dependencies = [ 2568 | "displaydoc", 2569 | "yoke", 2570 | "zerofrom", 2571 | ] 2572 | 2573 | [[package]] 2574 | name = "zerovec" 2575 | version = "0.11.4" 2576 | source = "registry+https://github.com/rust-lang/crates.io-index" 2577 | checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" 2578 | dependencies = [ 2579 | "yoke", 2580 | "zerofrom", 2581 | "zerovec-derive", 2582 | ] 2583 | 2584 | [[package]] 2585 | name = "zerovec-derive" 2586 | version = "0.11.1" 2587 | source = "registry+https://github.com/rust-lang/crates.io-index" 2588 | checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" 2589 | dependencies = [ 2590 | "proc-macro2", 2591 | "quote", 2592 | "syn", 2593 | ] 2594 | --------------------------------------------------------------------------------