diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 75bface..c9cc9fb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,50 +1,56 @@ name: Release Build +# Runs when a GitHub Release is published (you create the release for an existing tag, or +# create tag + release together in the UI). Assets are built and attached to that release. +# Optional: workflow_dispatch with a tag for manual builds (creates/updates release assets). on: - push: - tags: - - "v*.*.*" + release: + types: [published] workflow_dispatch: + inputs: + tag: + description: "Existing git tag to build (e.g. v1.0.0)" + required: true + type: string permissions: contents: write packages: write - issues: write - pull-requests: write env: CARGO_TERM_COLOR: always RUST_BACKTRACE: 1 jobs: - create-release: + metadata: runs-on: ubuntu-latest outputs: - upload_url: ${{ steps.create_release.outputs.upload_url }} - version: ${{ steps.version.outputs.version }} + tag: ${{ steps.meta.outputs.tag }} + version: ${{ steps.meta.outputs.version }} steps: - - name: Get version from tag - id: version - run: echo "version=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT - - - name: Create Release - id: create_release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: ${{ github.ref_name }} - release_name: Release ${{ github.ref_name }} - draft: false - prerelease: false + - id: meta + run: | + if [ "${{ github.event_name }}" = "release" ]; then + TAG="${{ github.event.release.tag_name }}" + else + TAG="${{ github.event.inputs.tag }}" + fi + if [[ ! "$TAG" =~ ^v ]]; then + echo "::error::Tag must start with v (got: $TAG)" + exit 1 + fi + echo "tag=$TAG" >> "$GITHUB_OUTPUT" + echo "version=${TAG#v}" >> "$GITHUB_OUTPUT" build-linux: - needs: create-release - runs-on: ubuntu-latest + needs: metadata + runs-on: blacksmith-2vcpu-ubuntu-2204 container: image: ubuntu:latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 + with: + ref: ${{ needs.metadata.outputs.tag }} - name: Install build dependencies run: | @@ -71,44 +77,31 @@ jobs: run: | cargo build --release --target x86_64-unknown-linux-gnu mkdir -p build/release - - # Copy the binary cp target/x86_64-unknown-linux-gnu/release/hardware_report build/release/hardware_report-linux-x86_64 - - # Strip the binary to reduce size strip build/release/hardware_report-linux-x86_64 - name: Create tarball run: | + VERSION="${{ needs.metadata.outputs.version }}" cd build/release - tar czf hardware_report-linux-x86_64-${{ needs.create-release.outputs.version }}.tar.gz hardware_report-linux-x86_64 - sha256sum hardware_report-linux-x86_64-${{ needs.create-release.outputs.version }}.tar.gz > hardware_report-linux-x86_64-${{ needs.create-release.outputs.version }}.tar.gz.sha256 + tar czf "hardware_report-linux-x86_64-${VERSION}.tar.gz" hardware_report-linux-x86_64 + sha256sum "hardware_report-linux-x86_64-${VERSION}.tar.gz" > "hardware_report-linux-x86_64-${VERSION}.tar.gz.sha256" - - name: Upload Release Asset - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ needs.create-release.outputs.upload_url }} - asset_path: build/release/hardware_report-linux-x86_64-${{ needs.create-release.outputs.version }}.tar.gz - asset_name: hardware_report-linux-x86_64-${{ needs.create-release.outputs.version }}.tar.gz - asset_content_type: application/gzip - - - name: Upload SHA256 checksum - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Upload build artifacts + uses: actions/upload-artifact@v4 with: - upload_url: ${{ needs.create-release.outputs.upload_url }} - asset_path: build/release/hardware_report-linux-x86_64-${{ needs.create-release.outputs.version }}.tar.gz.sha256 - asset_name: hardware_report-linux-x86_64-${{ needs.create-release.outputs.version }}.tar.gz.sha256 - asset_content_type: text/plain - - build-debian: - needs: create-release - runs-on: ubuntu-latest + name: linux-release + path: | + build/release/*.tar.gz + build/release/*.sha256 + + build-linux-arm64: + needs: metadata + runs-on: blacksmith-2vcpu-ubuntu-2204-arm steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 + with: + ref: ${{ needs.metadata.outputs.tag }} - name: Install build dependencies run: | @@ -116,10 +109,13 @@ jobs: sudo apt-get install -y \ build-essential \ curl \ + numactl \ + pciutils \ + ethtool \ + dmidecode \ + ipmitool \ pkg-config \ - libssl-dev \ - dpkg-dev \ - debhelper + libssl-dev - name: Install Rust toolchain uses: actions-rs/toolchain@v1 @@ -128,64 +124,54 @@ jobs: override: true - name: Build release binary - run: cargo build --release + run: | + cargo build --release + mkdir -p build/release + cp target/release/hardware_report build/release/hardware_report-linux-aarch64 + strip build/release/hardware_report-linux-aarch64 - - name: Create Debian package structure + - name: Create tarball run: | - VERSION=${{ needs.create-release.outputs.version }} - mkdir -p debian-pkg/hardware-report_${VERSION}_amd64/{DEBIAN,usr/bin,usr/share/doc/hardware-report} - - # Copy binary - cp target/release/hardware_report debian-pkg/hardware-report_${VERSION}_amd64/usr/bin/ - - # Create control file - cat > debian-pkg/hardware-report_${VERSION}_amd64/DEBIAN/control << EOF - Package: hardware-report - Version: ${VERSION} - Architecture: amd64 - Maintainer: Kenny Sheridan - Description: Hardware information collection tool - A tool for generating detailed hardware information reports from Linux servers, - outputting the data in TOML format for infrastructure standardization. - Depends: numactl, ipmitool, ethtool, util-linux, pciutils - Priority: optional - Section: utils - EOF - - # Create copyright file - cat > debian-pkg/hardware-report_${VERSION}_amd64/usr/share/doc/hardware-report/copyright << EOF - Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ - Upstream-Name: hardware_report - Source: https://github.com/sfcompute/hardware_report - - Files: * - Copyright: 2024 Kenny Sheridan - License: MIT - EOF - - # Build the package - dpkg-deb --build debian-pkg/hardware-report_${VERSION}_amd64 - mv debian-pkg/hardware-report_${VERSION}_amd64.deb . - - # Generate checksum - sha256sum hardware-report_${VERSION}_amd64.deb > hardware-report_${VERSION}_amd64.deb.sha256 - - - name: Upload Debian package - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + VERSION="${{ needs.metadata.outputs.version }}" + cd build/release + tar czf "hardware_report-linux-aarch64-${VERSION}.tar.gz" hardware_report-linux-aarch64 + sha256sum "hardware_report-linux-aarch64-${VERSION}.tar.gz" > "hardware_report-linux-aarch64-${VERSION}.tar.gz.sha256" + + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: linux-arm64-release + path: | + build/release/*.tar.gz + build/release/*.sha256 + + attach-assets: + needs: + - metadata + - build-linux + - build-linux-arm64 + runs-on: ubuntu-latest + steps: + - name: Download Linux x86_64 artifacts + uses: actions/download-artifact@v4 with: - upload_url: ${{ needs.create-release.outputs.upload_url }} - asset_path: hardware-report_${{ needs.create-release.outputs.version }}_amd64.deb - asset_name: hardware-report_${{ needs.create-release.outputs.version }}_amd64.deb - asset_content_type: application/vnd.debian.binary-package + name: linux-release + path: dist - - name: Upload Debian package checksum - uses: actions/upload-release-asset@v1 + - name: Download Linux arm64 artifacts + uses: actions/download-artifact@v4 + with: + name: linux-arm64-release + path: dist + + - name: Upload release assets + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ needs.metadata.outputs.tag }} + working_directory: dist + fail_on_unmatched_files: true + files: | + *.tar.gz + *.tar.gz.sha256 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ needs.create-release.outputs.upload_url }} - asset_path: hardware-report_${{ needs.create-release.outputs.version }}_amd64.deb.sha256 - asset_name: hardware-report_${{ needs.create-release.outputs.version }}_amd64.deb.sha256 - asset_content_type: text/plain diff --git a/Cargo.lock b/Cargo.lock index b5b464a..b1d731c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -500,21 +500,6 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - [[package]] name = "form_urlencoded" version = "1.2.1" @@ -757,28 +742,29 @@ dependencies = [ ] [[package]] -name = "hyper-timeout" -version = "0.4.1" +name = "hyper-rustls" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ + "futures-util", + "http", "hyper", - "pin-project-lite", + "rustls", "tokio", - "tokio-io-timeout", + "tokio-rustls", ] [[package]] -name = "hyper-tls" -version = "0.5.0" +name = "hyper-timeout" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" dependencies = [ - "bytes", "hyper", - "native-tls", + "pin-project-lite", "tokio", - "tokio-native-tls", + "tokio-io-timeout", ] [[package]] @@ -1070,23 +1056,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "native-tls" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" -dependencies = [ - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - [[package]] name = "normalize-line-endings" version = "0.3.0" @@ -1155,50 +1124,6 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" -[[package]] -name = "openssl" -version = "0.10.72" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fedfea7d58a1f73118430a55da6a286e7b044961736ce96a16a17068ea25e5da" -dependencies = [ - "bitflags 2.9.1", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "openssl-probe" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" - -[[package]] -name = "openssl-sys" -version = "0.9.108" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e145e1651e858e820e4860f7b9c5e169bc1d8ce1c86043be79fa7b7634821847" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - [[package]] name = "parking_lot" version = "0.12.3" @@ -1260,12 +1185,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -[[package]] -name = "pkg-config" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" - [[package]] name = "portable-atomic" version = "1.11.0" @@ -1498,15 +1417,15 @@ dependencies = [ "http", "http-body", "hyper", - "hyper-tls", + "hyper-rustls", "ipnet", "js-sys", "log", "mime", - "native-tls", "once_cell", "percent-encoding", "pin-project-lite", + "rustls", "rustls-pemfile", "serde", "serde_json", @@ -1514,15 +1433,30 @@ dependencies = [ "sync_wrapper", "system-configuration", "tokio", - "tokio-native-tls", + "tokio-rustls", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", + "webpki-roots", "winreg", ] +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.16", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + [[package]] name = "rustc-demangle" version = "0.1.24" @@ -1542,6 +1476,18 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "rustls" +version = "0.21.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +dependencies = [ + "log", + "ring", + "rustls-webpki", + "sct", +] + [[package]] name = "rustls-pemfile" version = "1.0.4" @@ -1551,6 +1497,16 @@ dependencies = [ "base64", ] +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.21" @@ -1572,15 +1528,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "schannel" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" -dependencies = [ - "windows-sys 0.59.0", -] - [[package]] name = "scopeguard" version = "1.2.0" @@ -1588,26 +1535,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] -name = "security-framework" -version = "2.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" -dependencies = [ - "bitflags 2.9.1", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.14.0" +name = "sct" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "core-foundation-sys", - "libc", + "ring", + "untrusted", ] [[package]] @@ -1923,12 +1857,12 @@ dependencies = [ ] [[package]] -name = "tokio-native-tls" -version = "0.3.1" +name = "tokio-rustls" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "native-tls", + "rustls", "tokio", ] @@ -2111,6 +2045,12 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "url" version = "2.5.4" @@ -2134,12 +2074,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - [[package]] name = "vec_map" version = "0.8.2" @@ -2267,6 +2201,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki-roots" +version = "0.25.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" + [[package]] name = "winapi" version = "0.3.9" diff --git a/Cargo.toml b/Cargo.toml index 021c8ea..39b15ca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ description = "A tool for generating hardware information reports" [dependencies] lazy_static = "1.4" tonic = "0.10" -reqwest = { version = "0.11", features = ["json"] } +reqwest = { version = "0.11", default-features = false, features = ["json", "rustls-tls"] } structopt = "0.3" sysinfo = "0.32.0" # For system information gathering serde = { version = "1.0", features = ["derive"] } diff --git a/README.md b/README.md index 2494a15..82c843f 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ sudo ./result/bin/hardware_report Output: `_hardware_report.toml` -**Pre-built .deb package:** Download from [GitHub Releases](https://github.com/sfcompute/hardware_report/releases) +**Pre-built binaries:** Linux x86_64 and aarch64 tarballs on [GitHub Releases](https://github.com/sfcompute/hardware_report/releases) **Other installation methods:** See [docs/INSTALLATION.md](docs/INSTALLATION.md) diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md index 5ed235a..3616730 100644 --- a/docs/DEPLOYMENT.md +++ b/docs/DEPLOYMENT.md @@ -8,41 +8,41 @@ ## Production Deployment -### Debian Package (Recommended) +### Pre-built binary (recommended) ```bash -# Download and install +# Download tarball (x86_64 example; use aarch64 asset names on ARM) curl -sL https://api.github.com/repos/sfcompute/hardware_report/releases/latest \ - | grep "browser_download_url.*\.deb" | cut -d '"' -f 4 | wget -qi - -sudo apt install -y ./hardware-report_*_amd64.deb + | grep "browser_download_url.*hardware_report-linux-x86_64.*\.tar\.gz" | cut -d '"' -f 4 | wget -qi - +tar xzf hardware_report-linux-x86_64-*.tar.gz + +# Install to PATH +sudo install -m 755 hardware_report-linux-x86_64 /usr/local/bin/hardware_report + +# Runtime tools (Debian/Ubuntu example) +sudo apt-get install -y numactl ipmitool ethtool pciutils -# Run sudo hardware_report ``` -### Multi-Server Deployment with Ansible +### Multi-server deployment with Ansible ```bash -# Deploy package to all servers -ansible servers -m copy -a "src=hardware-report_*_amd64.deb dest=/tmp/" -ansible servers -m apt -a "deb=/tmp/hardware-report_*_amd64.deb state=present" - -# Run on all servers -ansible servers -m shell -a "sudo hardware_report" --become +# After extracting the release tarball locally: +ansible servers -m copy -a "src=hardware_report-linux-x86_64 dest=/usr/local/bin/hardware_report mode=0755" +ansible servers -m apt -a "name=numactl,ipmitool,ethtool,pciutils state=present" --become +ansible servers -m shell -a "sudo /usr/local/bin/hardware_report" --become ``` -### Binary Deployment +### Binary deployment (scp) ```bash -# Download binary curl -sL https://api.github.com/repos/sfcompute/hardware_report/releases/latest \ - | grep "browser_download_url.*tar.gz" | cut -d '"' -f 4 | wget -qi - + | grep "browser_download_url.*hardware_report-linux-x86_64.*\.tar\.gz" | cut -d '"' -f 4 | wget -qi - tar xzf hardware_report-linux-x86_64-*.tar.gz -# Deploy to target -scp hardware_report-linux-x86_64 user@target:/usr/local/bin/hardware_report - -# Ensure runtime dependencies on target +scp hardware_report-linux-x86_64 user@target:/tmp/ +ssh user@target "sudo install -m 755 /tmp/hardware_report-linux-x86_64 /usr/local/bin/hardware_report" ssh user@target "sudo apt-get install -y numactl ipmitool ethtool pciutils" ``` diff --git a/docs/INSTALLATION.md b/docs/INSTALLATION.md index 4454055..b5107fb 100644 --- a/docs/INSTALLATION.md +++ b/docs/INSTALLATION.md @@ -34,22 +34,16 @@ cargo build --release Download from [GitHub Releases](https://github.com/sfcompute/hardware_report/releases): -**Debian/Ubuntu package:** +**Linux tarball** (pick the archive for your architecture — `x86_64` or `aarch64`): ```bash curl -sL https://api.github.com/repos/sfcompute/hardware_report/releases/latest \ - | grep "browser_download_url.*\.deb" | cut -d '"' -f 4 | wget -qi - -sudo apt install -y ./hardware-report_*_amd64.deb -sudo hardware_report -``` - -**Standalone binary:** -```bash -curl -sL https://api.github.com/repos/sfcompute/hardware_report/releases/latest \ - | grep "browser_download_url.*tar.gz" | cut -d '"' -f 4 | wget -qi - + | grep "browser_download_url.*hardware_report-linux-x86_64.*\.tar\.gz" | cut -d '"' -f 4 | wget -qi - tar xzf hardware_report-linux-x86_64-*.tar.gz sudo ./hardware_report-linux-x86_64 ``` +On ARM64, use `hardware_report-linux-aarch64` in the `grep` pattern and tarball / binary names instead. + ## Cargo Install ```bash diff --git a/examples/basic_usage.rs b/examples/basic_usage.rs index 60ee759..51fc4e6 100644 --- a/examples/basic_usage.rs +++ b/examples/basic_usage.rs @@ -47,6 +47,7 @@ async fn main() -> Result<(), Box> { post_data( server_info, labels, + None, "https://api.example.com/hardware", Some("your-auth-token"), None, diff --git a/src/adapters/secondary/publisher/http.rs b/src/adapters/secondary/publisher/http.rs index d294a8b..9219ef9 100644 --- a/src/adapters/secondary/publisher/http.rs +++ b/src/adapters/secondary/publisher/http.rs @@ -53,18 +53,26 @@ impl HttpDataPublisher { Self::new(Duration::from_secs(30), false) } - /// Create a payload with labels merged in + /// Create a payload with `system_id` and optional labels merged in fn create_payload(&self, report: &HardwareReport, config: &PublishConfig) -> serde_json::Value { let mut payload = serde_json::to_value(report).unwrap_or_else(|_| json!({})); - // Add labels if provided - if !config.labels.is_empty() { - if let Some(obj) = payload.as_object_mut() { + let system_id = config + .system_identifier + .as_deref() + .map(str::trim) + .filter(|s| !s.is_empty()) + .map(String::from) + .unwrap_or_else(|| report.summary.system_info.uuid.clone()); + + if let Some(obj) = payload.as_object_mut() { + if !config.labels.is_empty() { obj.insert( "labels".to_string(), serde_json::to_value(&config.labels).unwrap_or(json!({})), ); } + obj.insert("system_id".to_string(), json!(system_id)); } payload @@ -249,14 +257,33 @@ mod tests { auth_token: None, skip_tls_verify: false, labels, + system_identifier: None, }; let payload = publisher.create_payload(&report, &config); + assert_eq!(payload["system_id"], "test-uuid"); assert!(payload.get("labels").is_some()); assert_eq!(payload["labels"]["environment"], "test"); assert_eq!(payload["labels"]["datacenter"], "dc1"); } + #[tokio::test] + async fn test_create_payload_system_identifier_override() { + let publisher = HttpDataPublisher::with_defaults().unwrap(); + let report = create_test_report(); + + let config = PublishConfig { + endpoint: "http://example.com".to_string(), + auth_token: None, + skip_tls_verify: false, + labels: HashMap::new(), + system_identifier: Some("custom-machine-42".to_string()), + }; + + let payload = publisher.create_payload(&report, &config); + assert_eq!(payload["system_id"], "custom-machine-42"); + } + #[tokio::test] async fn test_empty_endpoint_error() { let publisher = HttpDataPublisher::with_defaults().unwrap(); @@ -266,6 +293,7 @@ mod tests { auth_token: None, skip_tls_verify: false, labels: HashMap::new(), + system_identifier: None, }; let result = publisher.publish(&report, &config).await; diff --git a/src/bin/hardware_report.rs b/src/bin/hardware_report.rs index c65856a..fe71bcb 100644 --- a/src/bin/hardware_report.rs +++ b/src/bin/hardware_report.rs @@ -67,6 +67,10 @@ struct Opt { #[structopt(long = "label", parse(try_from_str = parse_label))] labels: Vec<(String, String)>, + /// Override `system_id` in the POST JSON body (defaults to DMI system UUID); only valid with --post + #[structopt(long)] + system_identifier: Option, + /// Output file format (toml or json) #[structopt(long, default_value = "toml")] _file_format: FileFormat, @@ -97,6 +101,10 @@ fn parse_label(s: &str) -> Result<(String, String), String> { async fn main() -> Result<(), Box> { let opt = Opt::from_args(); + if opt.system_identifier.is_some() && !opt.post { + return Err("`--system-identifier` is only valid when posting (`--post`)".into()); + } + // Collect server information let server_info = ServerInfo::collect()?; @@ -366,6 +374,7 @@ async fn main() -> Result<(), Box> { post_data( server_info, labels, + opt.system_identifier.as_deref(), &opt.endpoint, opt.auth_token.as_deref(), opt.save_payload.as_deref(), diff --git a/src/domain/entities.rs b/src/domain/entities.rs index bd15e32..1fad2cc 100644 --- a/src/domain/entities.rs +++ b/src/domain/entities.rs @@ -744,4 +744,6 @@ pub struct PublishConfig { pub skip_tls_verify: bool, /// Additional labels/metadata pub labels: HashMap, + /// Override `system_id` in the HTTP JSON payload; if unset, uses `summary.system_info.uuid` + pub system_identifier: Option, } diff --git a/src/posting.rs b/src/posting.rs index 26922a3..cceb3e3 100644 --- a/src/posting.rs +++ b/src/posting.rs @@ -13,19 +13,31 @@ pub enum PostMethod { #[derive(Serialize)] pub struct PostPayload { pub labels: HashMap, + pub system_id: String, pub result: ServerInfo, } +fn resolve_system_id(data: &ServerInfo, system_identifier: Option<&str>) -> String { + system_identifier + .map(str::trim) + .filter(|s| !s.is_empty()) + .map(String::from) + .unwrap_or_else(|| data.summary.system_info.uuid.clone()) +} + pub async fn post_data( data: ServerInfo, labels: HashMap, + system_identifier: Option<&str>, endpoint: &str, auth_token: Option<&str>, write_payload_to: Option<&str>, skip_tls_verify: bool, ) -> Result<(), Box> { + let system_id = resolve_system_id(&data, system_identifier); let payload = PostPayload { labels, + system_id, result: data, };