From a7dcee739459b613d927216a8c04a94195d9c54a Mon Sep 17 00:00:00 2001 From: Jens Reimann Date: Fri, 3 Apr 2020 15:46:05 +0200 Subject: [PATCH] initial import of "hat" --- .github/workflows/hat.yaml | 49 + hat/.gitignore | 14 + hat/Cargo.lock | 2202 ++++++++++++++++++++++++++++++++++++ hat/Cargo.toml | 49 + hat/README.md | 167 +++ hat/hat.wxi | 10 + hat/hat.wxs | 52 + hat/src/args.rs | 72 ++ hat/src/client.rs | 30 + hat/src/context.rs | 594 ++++++++++ hat/src/credentials.rs | 494 ++++++++ hat/src/devices.rs | 383 +++++++ hat/src/error.rs | 187 +++ hat/src/hash.rs | 149 +++ hat/src/help.rs | 21 + hat/src/main.rs | 474 ++++++++ hat/src/output.rs | 34 + hat/src/overrides.rs | 66 ++ hat/src/resource.rs | 318 ++++++ hat/src/tenant.rs | 249 ++++ hat/src/utils.rs | 28 + 21 files changed, 5642 insertions(+) create mode 100644 .github/workflows/hat.yaml create mode 100644 hat/.gitignore create mode 100644 hat/Cargo.lock create mode 100644 hat/Cargo.toml create mode 100644 hat/README.md create mode 100644 hat/hat.wxi create mode 100644 hat/hat.wxs create mode 100644 hat/src/args.rs create mode 100644 hat/src/client.rs create mode 100644 hat/src/context.rs create mode 100644 hat/src/credentials.rs create mode 100644 hat/src/devices.rs create mode 100644 hat/src/error.rs create mode 100644 hat/src/hash.rs create mode 100644 hat/src/help.rs create mode 100644 hat/src/main.rs create mode 100644 hat/src/output.rs create mode 100644 hat/src/overrides.rs create mode 100644 hat/src/resource.rs create mode 100644 hat/src/tenant.rs create mode 100644 hat/src/utils.rs diff --git a/.github/workflows/hat.yaml b/.github/workflows/hat.yaml new file mode 100644 index 00000000..1f727ca6 --- /dev/null +++ b/.github/workflows/hat.yaml @@ -0,0 +1,49 @@ +name: Hono Admin Tool + +on: + push: + branches: ["master"] + paths: + - 'hat/**' + pull_request: + branches: ["master"] + paths: + - 'hat/**' + +env: + CARGO_TERM_COLOR: always + +jobs: + + build: + + runs-on: ${{ matrix.os }} + + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + + steps: + + - uses: actions/checkout@v2 + + - uses: actions/cache@v2 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + + - name: Build + run: cargo build --release --verbose + + - name: Run tests + run: cargo test --release --verbose + + - uses: actions/upload-artifact@v2 + with: + name: hat-${{ matrix.os }} + path: | + target/release/hat + target/release/hat.exe diff --git a/hat/.gitignore b/hat/.gitignore new file mode 100644 index 00000000..f541a880 --- /dev/null +++ b/hat/.gitignore @@ -0,0 +1,14 @@ +# Rust + +target/ + +# Eclipse + +.project +.settings/ + +# IntelliJ + +*.iml +.idea/* + diff --git a/hat/Cargo.lock b/hat/Cargo.lock new file mode 100644 index 00000000..354b3eeb --- /dev/null +++ b/hat/Cargo.lock @@ -0,0 +1,2202 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "adler32" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "aho-corasick" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "anyhow" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "arrayref" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "arrayvec" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "async-compression" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", + "flate2 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-project-lite 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "hermit-abi 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "autocfg" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "autocfg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "backtrace" +version = "0.3.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "backtrace-sys" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "base64" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "base64" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bcrypt" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "blowfish 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "blake2b_simd" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arrayref 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "constant_time_eq 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "block-cipher-trait" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "blowfish" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bumpalo" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "byteorder" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bytes" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "c2-chacha" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cc" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "chrono" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "clap" +version = "2.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "term_size 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "colored_json" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ansi_term 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", + "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "core-foundation" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "core-foundation" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "core-foundation-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "core-foundation-sys" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "core-foundation-sys" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "crc32fast" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-utils" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ct-logs" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "sct 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "dirs" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_users 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "dirs" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "dirs-sys 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "dirs-sys" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_users 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "dtoa" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "either" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "encoding_rs" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "failure" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace 0.3.44 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "failure_derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "flate2" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "miniz_oxide 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fnv" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "futures" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures-channel 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-executor 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-io 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-sink 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-task 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-util 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "futures-channel" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-sink 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "futures-core" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "futures-executor" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-task 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-util 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "futures-io" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "futures-macro" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "futures-sink" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "futures-task" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "futures-timer" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "futures-util" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures-channel 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-io 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-macro 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-sink 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-task 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-nested 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "generic-array" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "getrandom" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "h2" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", + "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-sink 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-util 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "indexmap 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-util 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hat" +version = "0.7.2" +dependencies = [ + "ansi_term 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", + "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bcrypt 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", + "colored_json 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "k8s-openapi 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "kube 0.28.1 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "reqwest 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_yaml 0.8.11 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "simplelog 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", + "url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "heck" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hermit-abi" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "http" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", + "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "http-body" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "httparse" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "hyper" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-channel 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-util 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "h2 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "http-body 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-project 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", + "tower-service 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "want 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hyper-rustls" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", + "ct-logs 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-util 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.13.3 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "rustls 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustls-native-certs 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-rustls 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", + "webpki 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hyper-tls" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.13.3 (registry+https://github.com/rust-lang/crates.io-index)", + "native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-tls 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "idna" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-normalization 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "indexmap" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "iovec" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "itoa" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "js-sys" +version = "0.3.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "wasm-bindgen 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "k8s-openapi" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "serde-value 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", + "url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "kube" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "Inflector 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)", + "base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", + "dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-timer 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-util 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "k8s-openapi 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "reqwest 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)", + "rustls 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_yaml 0.8.11 (registry+https://github.com/rust-lang/crates.io-index)", + "thiserror 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libc" +version = "0.2.66" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "linked-hash-map" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "log" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "matches" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "memchr" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "mime_guess" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "mime 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", + "unicase 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "miniz_oxide" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "mio" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "miow" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "native-tls" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl 0.10.28 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.54 (registry+https://github.com/rust-lang/crates.io-index)", + "schannel 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "net2" +version = "0.2.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "nom" +version = "4.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-integer" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-traits" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num_cpus" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "hermit-abi 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "openssl" +version = "0.10.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.54 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "openssl-probe" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "openssl-sys" +version = "0.9.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", + "vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ordered-float" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "pin-project" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "pin-project-internal 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pin-project-internal" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pin-project-lite" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "pin-utils" +version = "0.1.0-alpha.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "pkg-config" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "ppv-lite86" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "proc-macro-hack" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "proc-macro-nested" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "proc-macro2" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quote" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_chacha" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_chacha" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_hc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_isaac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_jitter" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_os" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_pcg" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_xorshift" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "redox_syscall" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "redox_users" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "rust-argon2 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "aho-corasick 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex-syntax" +version = "0.6.14" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "remove_dir_all" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "reqwest" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "async-compression 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", + "encoding_rs 0.8.22 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-util 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "http-body 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.13.3 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper-rustls 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper-tls 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "js-sys 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "mime 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", + "mime_guess 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-project-lite 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "rustls 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_urlencoded 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-rustls 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-tls 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-futures 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "web-sys 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)", + "webpki-roots 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winreg 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ring" +version = "0.16.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "untrusted 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "web-sys 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rust-argon2" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "blake2b_simd 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", + "constant_time_eq 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rustls" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "ring 0.16.11 (registry+https://github.com/rust-lang/crates.io-index)", + "sct 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "webpki 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rustls" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "ring 0.16.11 (registry+https://github.com/rust-lang/crates.io-index)", + "sct 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "webpki 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rustls-native-certs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rustls 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", + "schannel 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rustversion" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ryu" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "schannel" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "sct" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ring 0.16.11 (registry+https://github.com/rust-lang/crates.io-index)", + "untrusted 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "security-framework" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "security-framework" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework-sys 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "security-framework-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "security-framework-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "core-foundation-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde-value" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ordered-float 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_derive" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_json" +version = "1.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_urlencoded" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "dtoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_yaml" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "dtoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "sha2" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "simplelog" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "term 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "slab" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "smallvec" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "sourcefile" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "syn" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "synstructure" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tempfile" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "term" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "dirs 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "term_size" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "term_size 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "thiserror" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "thiserror-impl 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "thread_local" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "time" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "time" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "rustversion 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "time-macros 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "time-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", + "time-macros-impl 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "time-macros-impl" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", + "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-project-lite 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-macros 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-macros" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-rustls" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "rustls 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", + "webpki 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-tls" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-util" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-sink 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-project-lite 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tower-service" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "try-lock" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "typenum" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-segmentation" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-width" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "untrusted" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "url" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "vcpkg" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "vec_map" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "version_check" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "version_check" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "wasm-bindgen" +version = "0.2.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-macro 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bumpalo 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-shared 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "js-sys 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "web-sys 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-macro-support 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-backend 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-shared 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.58" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "wasm-bindgen-webidl" +version = "0.2.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "anyhow 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", + "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-backend 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "weedle 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "web-sys" +version = "0.3.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "anyhow 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", + "js-sys 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)", + "sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-webidl 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "webpki" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ring 0.16.11 (registry+https://github.com/rust-lang/crates.io-index)", + "untrusted 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "webpki-roots" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "webpki 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "weedle" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winreg" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "yaml-rust" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[metadata] +"checksum Inflector 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)" = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" +"checksum adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2" +"checksum aho-corasick 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)" = "d5e63fd144e18ba274ae7095c0197a870a7b9468abc801dd62f190d80817d2ec" +"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +"checksum ansi_term 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +"checksum anyhow 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)" = "7825f6833612eb2414095684fcf6c635becf3ce97fe48cf6421321e93bfbd53c" +"checksum arrayref 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" +"checksum arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" +"checksum async-compression 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9d37ca0ddff0c8afe8307cd4cc3636c19f0fa09ecfc642344b1597d08a19d1a2" +"checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +"checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" +"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" +"checksum backtrace 0.3.44 (registry+https://github.com/rust-lang/crates.io-index)" = "e4036b9bf40f3cf16aba72a3d65e8a520fc4bafcdc7079aea8f848c58c5b5536" +"checksum backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6575f128516de27e3ce99689419835fce9643a9b215a14d2b5b685be018491" +"checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" +"checksum base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" +"checksum bcrypt 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b4fd6a91ff640809cfab4ea74312a892238a7bbae53adbf717b71122deb0c85" +"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +"checksum blake2b_simd 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a" +"checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +"checksum block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774" +"checksum block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +"checksum blowfish 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6aeb80d00f2688459b8542068abd974cfb101e7a82182414a99b5026c0d85cc3" +"checksum bumpalo 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1f359dc14ff8911330a51ef78022d376f25ed00248912803b58f00cb1c27f742" +"checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" +"checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" +"checksum bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "130aac562c0dd69c56b3b1cc8ffd2e17be31d0b6c25b61c96b76231aa23e39e1" +"checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb" +"checksum cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)" = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd" +"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +"checksum chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "31850b4a4d6bae316f7a09e691c944c28299298837edc0a03f755618c23cbc01" +"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" +"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +"checksum colored_json 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1fd32eb54d016e203b7c2600e3a7802c75843a92e38ccc4869aefeca21771a64" +"checksum constant_time_eq 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" +"checksum core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d" +"checksum core-foundation 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171" +"checksum core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" +"checksum core-foundation-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" +"checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" +"checksum crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce446db02cdc3165b94ae73111e570793400d0794e46125cc4056c81cbb039f4" +"checksum ct-logs 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4d3686f5fa27dbc1d76c751300376e167c5a43387f44bb451fd1c24776e49113" +"checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +"checksum dirs 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901" +"checksum dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" +"checksum dirs-sys 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b" +"checksum dtoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4358a9e11b9a09cf52383b451b49a169e8d797b68aa02301ff586d70d9661ea3" +"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" +"checksum encoding_rs 0.8.22 (registry+https://github.com/rust-lang/crates.io-index)" = "cd8d03faa7fe0c1431609dfad7bbe827af30f82e1e2ae6f7ee4fca6bd764bc28" +"checksum failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f8273f13c977665c5db7eb2b99ae520952fe5ac831ae4cd09d80c4c7042b5ed9" +"checksum failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0bc225b78e0391e4b8683440bf2e63c2deeeb2ce5189eab46e2b68c6d3725d08" +"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" +"checksum flate2 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6bd6d6f4752952feb71363cffc9ebac9411b75b87c6ab6058c40c8900cf43c0f" +"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" +"checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +"checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" +"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" +"checksum futures 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5c329ae8753502fb44ae4fc2b622fa2a94652c41e795143765ba0927f92ab780" +"checksum futures-channel 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f0c77d04ce8edd9cb903932b608268b3fffec4163dc053b3b402bf47eac1f1a8" +"checksum futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f25592f769825e89b92358db00d26f965761e094951ac44d3663ef25b7ac464a" +"checksum futures-executor 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f674f3e1bcb15b37284a90cedf55afdba482ab061c407a9c0ebbd0f3109741ba" +"checksum futures-io 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a638959aa96152c7a4cddf50fcb1e3fede0583b27157c26e67d6f99904090dc6" +"checksum futures-macro 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "9a5081aa3de1f7542a794a397cde100ed903b0630152d0973479018fd85423a7" +"checksum futures-sink 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3466821b4bc114d95b087b850a724c6f83115e929bc88f1fa98a3304a944c8a6" +"checksum futures-task 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7b0a34e53cf6cdcd0178aa573aed466b646eb3db769570841fda0c7ede375a27" +"checksum futures-timer 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" +"checksum futures-util 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "22766cf25d64306bedf0384da004d05c9974ab104fcc4528f1236181c18004c5" +"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" +"checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" +"checksum h2 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9d5c295d1c0c68e4e42003d75f908f5e16a1edd1cbe0b0d02e4dc2006a384f47" +"checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" +"checksum hermit-abi 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "e2c55f143919fbc0bc77e427fe2d74cf23786d7c1875666f2fde3ac3c659bb67" +"checksum http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b708cc7f06493459026f53b9a61a7a121a5d1ec6238dee58ea4941132b30156b" +"checksum http-body 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b" +"checksum httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" +"checksum hyper 0.13.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e7b15203263d1faa615f9337d79c1d37959439dc46c2b4faab33286fadc2a1c5" +"checksum hyper-rustls 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac965ea399ec3a25ac7d13b8affd4b8f39325cca00858ddf5eb29b79e6b14b08" +"checksum hyper-tls 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3adcd308402b9553630734e9c36b77a7e48b3821251ca2493e8cd596763aafaa" +"checksum idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" +"checksum indexmap 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "076f042c5b7b98f31d205f1249267e12a6518c1481e9dae9764af19b707d2292" +"checksum iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +"checksum itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" +"checksum js-sys 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)" = "7889c7c36282151f6bf465be4700359318aef36baa951462382eae49e9577cf9" +"checksum k8s-openapi 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "96affb18356fc998baa692c2185d198095ffdf6e6fff3bc9efb4927952f86851" +"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +"checksum kube 0.28.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d128769b413d409e02a7cf65e7c78ba12939d31c817b87809f6f335a667b4d63" +"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +"checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" +"checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" +"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" +"checksum memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" +"checksum mime 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +"checksum mime_guess 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1a0ed03949aef72dbdf3116a383d7b38b4768e6f960528cd6a6044aa9ed68599" +"checksum miniz_oxide 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "aa679ff6578b1cddee93d7e82e263b94a575e0bfced07284eb0c037c1d2416a5" +"checksum mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)" = "302dec22bcf6bae6dfb69c647187f4b4d0fb6f535521f7bc022430ce8e12008f" +"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" +"checksum native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4b2df1a4c22fd44a62147fd8f13dd0f95c9d8ca7b2610299b2a2f9cf8964274e" +"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" +"checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" +"checksum num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" +"checksum num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" +"checksum num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6" +"checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" +"checksum openssl 0.10.28 (registry+https://github.com/rust-lang/crates.io-index)" = "973293749822d7dd6370d6da1e523b0d1db19f06c459134c658b2a4261378b52" +"checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" +"checksum openssl-sys 0.9.54 (registry+https://github.com/rust-lang/crates.io-index)" = "1024c0a59774200a555087a6da3f253a9095a5f344e353b212ac4c8b8e450986" +"checksum ordered-float 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "18869315e81473c951eb56ad5558bbc56978562d3ecfb87abb7a1e944cea4518" +"checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +"checksum pin-project 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7804a463a8d9572f13453c516a5faea534a2403d7ced2f0c7e100eeff072772c" +"checksum pin-project-internal 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "385322a45f2ecf3410c68d2a549a4a2685e8051d0f278e39743ff4e451cb9b3f" +"checksum pin-project-lite 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "237844750cfbb86f67afe27eee600dfbbcb6188d734139b534cbfbf4f96792ae" +"checksum pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587" +"checksum pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" +"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" +"checksum proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)" = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5" +"checksum proc-macro-nested 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "369a6ed065f249a159e06c45752c780bda2fb53c995718f9e484d08daa9eb42e" +"checksum proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3acb317c6ff86a4e579dfa00fc5e6cca91ecbb4e7eb2df0468805b674eb88548" +"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" +"checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" +"checksum rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +"checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" +"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" +"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +"checksum rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" +"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +"checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" +"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +"checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" +"checksum rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" +"checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" +"checksum rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" +"checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" +"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" +"checksum redox_users 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "09b23093265f8d200fa7b4c2c76297f47e681c655f6f1285a8780d6a022f7431" +"checksum regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "322cf97724bea3ee221b78fe25ac9c46114ebb51747ad5babd51a2fc6a8235a8" +"checksum regex-syntax 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)" = "b28dfe3fe9badec5dbf0a79a9cccad2cfc2ab5484bdb3e44cbd1ae8b3ba2be06" +"checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" +"checksum reqwest 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)" = "02b81e49ddec5109a9dcfc5f2a317ff53377c915e9ae9d4f2fb50914b85614e2" +"checksum ring 0.16.11 (registry+https://github.com/rust-lang/crates.io-index)" = "741ba1704ae21999c00942f9f5944f801e977f54302af346b596287599ad1862" +"checksum rust-argon2 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2bc8af4bda8e1ff4932523b94d3dd20ee30a87232323eda55903ffd71d2fb017" +"checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" +"checksum rustls 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b25a18b1bf7387f0145e7f8324e700805aade3842dd3db2e74e4cdeb4677c09e" +"checksum rustls 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0d4a31f5d68413404705d6982529b0e11a9aacd4839d1d6222ee3b8cb4015e1" +"checksum rustls-native-certs 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a75ffeb84a6bd9d014713119542ce415db3a3e4748f0bfce1e1416cd224a23a5" +"checksum rustversion 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b3bba175698996010c4f6dce5e7f173b6eb781fce25d2cfc45e27091ce0b79f6" +"checksum ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8" +"checksum schannel 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "507a9e6e8ffe0a4e0ebb9a10293e62fdf7657c06f1b8bb07a8fcf697d2abf295" +"checksum sct 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e3042af939fca8c3453b7af0f1c66e533a15a86169e39de2657310ade8f98d3c" +"checksum security-framework 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8ef2429d7cefe5fd28bd1d2ed41c944547d4ff84776f5935b456da44593a16df" +"checksum security-framework 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "97bbedbe81904398b6ebb054b3e912f99d55807125790f3198ac990d98def5b0" +"checksum security-framework-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e31493fc37615debb8c5090a7aeb4a9730bc61e77ab10b9af59f1a202284f895" +"checksum security-framework-sys 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "06fd2f23e31ef68dd2328cc383bd493142e46107a3a0e24f7d734e3f3b80fe4c" +"checksum serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449" +"checksum serde-value 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5a65a7291a8a568adcae4c10a677ebcedbc6c9cec91c054dee2ce40b0e3290eb" +"checksum serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64" +"checksum serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)" = "9371ade75d4c2d6cb154141b9752cf3781ec9c05e0e5cf35060e1e70ee7b9c25" +"checksum serde_urlencoded 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9ec5d77e2d4c73717816afac02670d5c4f534ea95ed430442cad02e7a6e32c97" +"checksum serde_yaml 0.8.11 (registry+https://github.com/rust-lang/crates.io-index)" = "691b17f19fc1ec9d94ec0b5864859290dff279dbd7b03f017afda54eb36c3c35" +"checksum sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "27044adfd2e1f077f649f59deb9490d3941d674002f7d062870a60ebe9bd47a0" +"checksum simplelog 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e95345f185d5adeb8ec93459d2dc99654e294cc6ccf5b75414d8ea262de9a13" +"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" +"checksum smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5c2fb2ec9bcd216a5b0d0ccf31ab17b5ed1d627960edff65bbe95d3ce221cefc" +"checksum sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4bf77cb82ba8453b42b6ae1d692e4cdc92f9a47beaf89a847c8be83f4e328ad3" +"checksum spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +"checksum syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "af6f3550d8dff9ef7dc34d384ac6f107e5d31c8f57d9f28e0081503f547ac8f5" +"checksum synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" +"checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" +"checksum term 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "edd106a334b7657c10b7c540a0106114feadeb4dc314513e97df481d5d966f42" +"checksum term_size 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9e5b9a66db815dcfd2da92db471106457082577c3c278d4138ab3e3b4e189327" +"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +"checksum thiserror 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "ee14bf8e6767ab4c687c9e8bc003879e042a96fd67a3ba5934eadb6536bef4db" +"checksum thiserror-impl 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "a7b51e1fbc44b5a0840be594fbc0f960be09050f2617e61e6aa43bef97cd3ef4" +"checksum thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" +"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" +"checksum time 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "3043ac959c44dccc548a57417876c8fe241502aed69d880efc91166c02717a93" +"checksum time-macros 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9ae9b6e9f095bc105e183e3cd493d72579be3181ad4004fceb01adbe9eecab2d" +"checksum time-macros-impl 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e987cfe0537f575b5fc99909de6185f6c19c3ad8889e2275e686a873d0869ba1" +"checksum tokio 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "0fa5e81d6bc4e67fe889d5783bd2a128ab2e0cfa487e0be16b6a8d177b101616" +"checksum tokio-macros 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389" +"checksum tokio-rustls 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4adb8b3e5f86b707f1b54e7c15b6de52617a823608ccda98a15d3a24222f265a" +"checksum tokio-tls 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7bde02a3a5291395f59b06ec6945a3077602fac2b07eeeaf0dee2122f3619828" +"checksum tokio-util 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "571da51182ec208780505a32528fc5512a8fe1443ab960b3f2f3ef093cd16930" +"checksum tower-service 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860" +"checksum try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382" +"checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" +"checksum unicase 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" +"checksum unicode-normalization 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4" +"checksum unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" +"checksum unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" +"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +"checksum untrusted 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60369ef7a31de49bcb3f6ca728d4ba7300d9a1658f94c727d4cab8c8d9f4aece" +"checksum url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb" +"checksum vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168" +"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" +"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" +"checksum version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce" +"checksum want 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +"checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" +"checksum wasm-bindgen 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "5205e9afdf42282b192e2310a5b463a6d1c1d774e30dc3c791ac37ab42d2616c" +"checksum wasm-bindgen-backend 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "11cdb95816290b525b32587d76419facd99662a07e59d3cdb560488a819d9a45" +"checksum wasm-bindgen-futures 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8bbdd49e3e28b40dec6a9ba8d17798245ce32b019513a845369c641b275135d9" +"checksum wasm-bindgen-macro 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "574094772ce6921576fb6f2e3f7497b8a76273b6db092be18fc48a082de09dc3" +"checksum wasm-bindgen-macro-support 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "e85031354f25eaebe78bb7db1c3d86140312a911a106b2e29f9cc440ce3e7668" +"checksum wasm-bindgen-shared 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "f5e7e61fc929f4c0dddb748b102ebf9f632e2b8d739f2016542b4de2965a9601" +"checksum wasm-bindgen-webidl 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "ef012a0d93fc0432df126a8eaf547b2dce25a8ce9212e1d3cbeef5c11157975d" +"checksum web-sys 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)" = "aaf97caf6aa8c2b1dac90faf0db529d9d63c93846cca4911856f78a83cebf53b" +"checksum webpki 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f1f50e1972865d6b1adb54167d1c8ed48606004c2c9d0ea5f1eeb34d95e863ef" +"checksum webpki-roots 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)" = "91cd5736df7f12a964a5067a12c62fa38e1bd8080aff1f80bc29be7c80d19ab4" +"checksum weedle 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3bb43f70885151e629e2a19ce9e50bd730fd436cfd4b666894c9ce4de9141164" +"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" +"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" +"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +"checksum winreg 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b2986deb581c4fe11b621998a5e53361efe6b48a151178d0cd9eeffa4dc6acc9" +"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" +"checksum yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "65923dd1784f44da1d2c3dbbc5e822045628c590ba72123e1c73d3c230c4434d" diff --git a/hat/Cargo.toml b/hat/Cargo.toml new file mode 100644 index 00000000..0d652140 --- /dev/null +++ b/hat/Cargo.toml @@ -0,0 +1,49 @@ +[package] +name = "hat" +version = "0.7.2" +authors = ["Jens Reimann "] +edition = "2018" + +[dependencies] +clap = { version = "2.33", features = ["suggestions", "wrap_help", "color"] } +log = "0.4" +simplelog = "0.5" + +serde = { version = "1.0", features = ["derive"] } +serde_yaml = "0.8" +serde_json = "1.0" + +failure_derive = "0.1" +failure = "0.1" + +url = "2.1.1" +percent-encoding = "2.1" + +dirs = "2" + +tokio = { version = "0.2", features = [ "macros", "rt-threaded" ] } +reqwest = { version = "0.10.4", features = [ "gzip", "json" ] } + +http = "0.2" + +base64 = "0.10" + +rand = "0.6" + +sha2 = "0.8" +bcrypt = "0.5" + +colored_json = "2.1" +ansi_term = "0.12" + +futures = "0.3.4" + +[dependencies.kube] +version = "0.28.1" +default-features = false +features = [ "rustls-tls" ] + +[dev-dependencies.k8s-openapi] +version = "0.7.1" +default-features = false +features = ["v1_17"] diff --git a/hat/README.md b/hat/README.md new file mode 100644 index 00000000..4e668bbc --- /dev/null +++ b/hat/README.md @@ -0,0 +1,167 @@ +# HAT – Hono Admin Tool [![GitHub release](https://img.shields.io/github/release/ctron/hat.svg)](https://github.com/ctron/hat/releases) + +Getting help: + + hat --help + +## Global switches + +Temporarily use a different context (with `-c` or `--context`): + + hat -c context2 device create … + +Or override a tenant (with `-t` or `--tenant`): + + hat -t my.tenant device get … + +## Managing contexts + +Create a new context: + + hat context create foo https://device-registry.hono.my + +Create a new context with credentials: + + hat context create foo https://device-registry.hono.my --username --password + +Create a new context, using local Kubernetes token: + + hat context create foo https://device-registry.hono.my --use-kubernetes + +Update an existing context: + + hat context update foo --url https://device-registry.hono.my + hat context update foo --username --password + hat context update foo --use-kubernetes + +Delete an existing context: + + hat context delete foo + +Switch to an existing context: + + hat context switch foo + +List existing contexts: + + hat context list + +### Default tenant + +It is possible to set a *default tenant*, which is used on all calls when using +this context. + +Set a default tenant when creating a context: + + hat context create foo https://device-registry.hono.my --tenant + +Or update later: + + hat context update foo https://device-registry.hono.my --tenant + +It is possible to override the default tenant with `-t` or `--tenant`: + + hat device create -t my-tenant 4711 + +Or by setting the environment variable `HAT_TENANT`: + + HAT_TENANT=foo hat device create 4711 + +## Tenants + +Creating a new tenant: + + hat tenant create my-tenant + +Creating a new tenant with payload: + + hat tenant create my-tenant '{"enabled": false}' + +Getting a tenant: + + hat tenant get my-tenant + +Deleting a tenant: + + hat tenant delete my-tenant + +Enable/Disable a tenant: + + hat tenant enable my-tenant + hat tenant disable my-tenant + +## Device registrations + +Register a new device: + + hat device create 4711 + +Register a new device with payload: + + hat device create 4711 '{…}' + +Inspect the device: + + hat device get 4711 + +Enable/Disable a device: + + hat device enable 4711 + hat device disable 4711 + +### Set via + +You can also set the "via" attribute directly: + + hat device set-via 4711 my-gw + +### Set defaults entry + +Set a defaults entry using: + + hat device set-defaults 4711 key value + +The value will be converted into a JSON value. If it cannot +be parsed, it will be stored as a string (depending on the +shell you are using, you might need different quotation marks): + + hat device set-defaults 4711 key true # Booolean: true + hat device set-defaults 4711 key '"true"' # String: true + hat device set-defaults 4711 key 123 # Number: 123 + hat device set-defaults 4711 key '"123"' # String: 123 + hat device set-defaults 4711 key foobar # String: foobar + hat device set-defaults 4711 key '{"foo":123}' # Object: {"foo":123} + +Delete an entry by omitting the value: + + hat device set-defaults 4711 key value + +## Credentials + +Replace credentials: + + hat creds set device1 '[]' + +Clear all credentials: + + hat creds set device1 + +Add a password: + + hat creds add-password device1 sensor1 password + +Set password as only password: + + hat creds set-password device1 sensor1 password + +Set password with pre-hashed password: + + hat creds set-password device1 sensor1 password --hash sha-512 + +Set PSK: + + hat creds set-psk device1 sensor1 PSK + +Enable X509: + + hat creds enable-x509 device1 sensor1 diff --git a/hat/hat.wxi b/hat/hat.wxi new file mode 100644 index 00000000..53d2d836 --- /dev/null +++ b/hat/hat.wxi @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/hat/hat.wxs b/hat/hat.wxs new file mode 100644 index 00000000..9031f895 --- /dev/null +++ b/hat/hat.wxs @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/hat/src/args.rs b/hat/src/args.rs new file mode 100644 index 00000000..2a0eae33 --- /dev/null +++ b/hat/src/args.rs @@ -0,0 +1,72 @@ +/******************************************************************************* + * Copyright (c) 2019, 2020 Red Hat Inc + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +use crate::utils::Either; + +pub fn flag_arg(name: &str, matches: &clap::ArgMatches) -> Option { + matches.is_present(name).either( + Some(matches.value_of(name).map_or(true, map_switch_value)), + None, + ) +} + +pub fn map_switch_value(value: &str) -> bool { + match value { + "true" | "yes" | "on" => true, + _ => false, + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn setup<'a, 'b>() -> clap::App<'a, 'b> { + clap::App::new("test").arg( + clap::Arg::with_name("k") + .short("k") + .min_values(0) + .max_values(1) + .takes_value(true), + ) + } + + #[test] + fn test_flag_arg_1() { + let app = setup(); + let m = app.get_matches_from(vec!["test"]); + assert_eq!(flag_arg("k", &m), None); + } + + #[test] + fn test_flag_arg_2() { + let app = setup(); + let m = app.get_matches_from(vec!["test", "-k"]); + assert_eq!(flag_arg("k", &m), Some(true)); + } + + #[test] + fn test_flag_arg_3() { + let app = setup(); + let m = app.get_matches_from(vec!["test", "-k=false"]); + assert_eq!(flag_arg("k", &m), Some(false)); + } + + #[test] + fn test_flag_arg_4() { + let app = setup(); + + let m = app.get_matches_from(vec!["test", "-k=true"]); + assert_eq!(flag_arg("k", &m), Some(true)); + } +} diff --git a/hat/src/client.rs b/hat/src/client.rs new file mode 100644 index 00000000..b9de59f9 --- /dev/null +++ b/hat/src/client.rs @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2020 Red Hat Inc + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +use crate::context::Context; +use crate::error; +use crate::overrides::Overrides; + +type Result = std::result::Result; + +pub struct Client { + pub client: reqwest::Client, +} + +impl Client { + pub async fn new(context: &Context, overrides: &Overrides) -> Result { + let client = context.create_client(overrides).await?; + + Ok(Client { client }) + } +} diff --git a/hat/src/context.rs b/hat/src/context.rs new file mode 100644 index 00000000..28a4753d --- /dev/null +++ b/hat/src/context.rs @@ -0,0 +1,594 @@ +/******************************************************************************* + * Copyright (c) 2018, 2019 Red Hat Inc + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +use std::fs::File; +use std::result::Result; + +#[cfg(unix)] +use std::os::unix::fs::PermissionsExt; + +use url::Url; + +use crate::help::help; +use clap::{App, ArgMatches}; + +use std::str::Utf8Error; + +use std::io::prelude::*; +use std::path::*; + +use crate::error; +use crate::error::ErrorKind; + +use crate::Overrides; + +use crate::args::flag_arg; +use crate::resource::Tracer; +use crate::utils::Either; +use ansi_term::Style; +use colored_json::*; +use kube::config::ConfigOptions; +use percent_encoding::{percent_decode, utf8_percent_encode, NON_ALPHANUMERIC}; +use serde; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug)] +pub struct Context { + url: String, + username: Option, + password: Option, + token: Option, + #[serde(default)] + use_kubernetes: bool, + kubernetes_context: Option, + kubernetes_cluster: Option, + #[serde(default)] + insecure: bool, + default_tenant: Option, +} + +impl Context { + pub fn to_url(&self, overrides: &Overrides) -> Result { + let url = overrides.url().unwrap_or(&self.url); + Url::parse(url) + } + + pub fn username(&self) -> &Option { + &self.username + } + + pub fn password(&self) -> &Option { + &self.password + } + + pub fn token(&self) -> &Option { + &self.token + } + + pub fn use_kubernetes(&self) -> bool { + self.use_kubernetes + } + + pub fn kubernetes_cluster(&self) -> Option<&String> { + return self.kubernetes_cluster.as_ref(); + } + + pub fn kubernetes_context(&self) -> Option<&String> { + return self.kubernetes_context.as_ref(); + } + + #[allow(dead_code)] + pub fn insecure(&self) -> bool { + self.insecure + } + + #[allow(dead_code)] + pub fn default_tenant(&self) -> &Option { + &self.default_tenant + } + + pub fn make_tenant(&self, overrides: &Overrides) -> Result { + overrides.tenant().clone() + .map(|s|s.to_string()) + .or_else(|| self.default_tenant.clone()) + .ok_or_else(|| error::Error::from(ErrorKind::GenericError("No tenant specified. Either set a default tenant for the context or use the argument --tenant to provide one.".into()))) + } + + fn apply_common_config( + &self, + client_builder: reqwest::ClientBuilder, + overrides: &Overrides, + ) -> reqwest::ClientBuilder { + let client_builder = if overrides.insecure().unwrap_or(self.insecure) { + client_builder.danger_accept_invalid_certs(true) + } else { + client_builder + }; + + client_builder + } + + pub async fn create_client( + &self, + overrides: &Overrides, + ) -> Result { + let builder = if overrides.use_kubernetes().unwrap_or(self.use_kubernetes) { + let cluster = overrides.kubernetes_cluster().or(self.kubernetes_cluster()); + let context = overrides.kubernetes_context().or(self.kubernetes_context()); + let options = ConfigOptions { + context: context.cloned(), + cluster: cluster.cloned(), + ..Default::default() + }; + let result = kube::config::create_client_builder(options).await?; + result.0 + } else { + reqwest::Client::builder() + }; + + Ok(self + .apply_common_config(builder, overrides) + .build()? + .trace()) + } +} + +pub fn context(app: &mut App, matches: &ArgMatches) -> Result<(), error::Error> { + match matches.subcommand() { + ("create", Some(cmd_matches)) => context_create( + cmd_matches.value_of("context-name").unwrap(), + cmd_matches + .value_of("context-url") + .or_else(|| cmd_matches.value_of("url")) + .unwrap(), + cmd_matches.value_of("username"), + cmd_matches.value_of("password"), + cmd_matches.value_of("token"), + flag_arg("use-kubernetes", cmd_matches).unwrap_or(false), + cmd_matches.value_of("kubernetes-cluster"), + cmd_matches.value_of("kubernetes-context"), + flag_arg("insecure", cmd_matches).unwrap_or(false), + cmd_matches.value_of("tenant"), + ), + ("update", Some(cmd_matches)) => context_update( + cmd_matches.value_of("context-name"), + cmd_matches.value_of("url"), + cmd_matches.value_of("username"), + cmd_matches.value_of("password"), + cmd_matches.value_of("token"), + flag_arg("use-kubernetes", cmd_matches), + cmd_matches.value_of("kubernetes-cluster"), + cmd_matches.value_of("kubernetes-context"), + flag_arg("insecure", cmd_matches), + cmd_matches.value_of("tenant"), + ), + ("switch", Some(cmd_matches)) => { + context_switch(cmd_matches.value_of("context-name").unwrap()) + } + ("delete", Some(cmd_matches)) => { + context_delete(cmd_matches.value_of("context-name").unwrap()) + } + ("list", Some(_)) => context_list(), + ("show", Some(cmd_matches)) => context_show( + cmd_matches + .value_of("context-name") + .or_else(|| cmd_matches.value_of("context")), + ), + ("current", Some(_)) => context_current(), + _ => help(app), + } +} + +fn context_encode_file_name(context: S) -> String +where + S: Into, +{ + utf8_percent_encode(&context.into(), NON_ALPHANUMERIC).collect() +} + +fn context_decode_file_name(name: &str) -> Result { + let iter = percent_decode(name.as_bytes()); + + Ok(iter.decode_utf8()?.to_string()) +} + +fn context_config_dir() -> Result { + let dir = dirs::config_dir().expect("Unable to evaluate user's configuration directory"); + + Ok(dir.join("hat")) +} + +fn context_contexts_dir() -> Result { + context_config_dir().map(|path| path.join("contexts")) +} + +fn context_file_path(context: S) -> Result +where + S: AsRef, +{ + let context = context.as_ref().to_string(); + let name = context.trim(); + + if name.is_empty() { + return Err(ErrorKind::ContextNameError(context).into()); + } + + context_contexts_dir().map(|path| path.join(context_encode_file_name(context))) +} + +/// Load the provided context. +fn context_load(context: S) -> Result +where + S: AsRef, +{ + let file = File::open(context_file_path(context.as_ref())?); + + match file { + Ok(file) => Ok(serde_yaml::from_reader(file)?), + Err(err) => match err.kind() { + std::io::ErrorKind::NotFound => { + Err(ErrorKind::ContextUnknownError(context.as_ref().to_string()).into()) + } + _ => Err(err.into()), + }, + } +} + +/// Read the current selected context. +fn context_get_current() -> Result, error::Error> { + let path = context_config_dir().map(|path| path.join("current"))?; + + if !path.exists() { + return Ok(None); + } + + let mut current = String::new(); + + File::open(path)?.read_to_string(&mut current)?; + + Ok(Some(current)) +} + +/// Loads the provided, or default context. +fn context_load_or_current(name: Option) -> Result { + context_load_or_fail(name.or(context_get_current()?)) +} + +/// Loads the current (or overridde) context. +pub fn context_load_current(overrides: Option<&Overrides>) -> Result { + context_load_or_current(overrides.and_then(Overrides::context)) +} + +/// Tests if the name contains a valid context name. +/// [`None`] is never a valid name. +fn context_names_valid(name: Option) -> Result { + name.ok_or_else(|| { + ErrorKind::GenericError( + "No context selected. Create a first context or select an existing one.".to_string(), + ) + .into() + }) +} + +/// Loads a context, if the name is valid. +fn context_load_or_fail(name: Option) -> Result { + context_names_valid(name).and_then(|current| context_load(current.as_str())) +} + +#[cfg(unix)] +fn limit_access(file: &mut File) -> Result<(), error::Error> { + let mut permissions = file.metadata()?.permissions(); + permissions.set_mode(0o600); + file.set_permissions(permissions)?; + + Ok(()) +} + +#[cfg(not(unix))] +fn limit_access(_file: &mut File) -> Result<(), error::Error> { + Ok(()) +} + +fn context_store(context_name: &str, context: Context) -> Result<(), error::Error> { + let path = context_file_path(context_name)?; + + std::fs::create_dir_all(path.parent().unwrap())?; + + let mut file = File::create(path)?; + + limit_access(&mut file)?; + + file.write_all(serde_yaml::to_string(&context)?.as_bytes())?; + + Ok(()) +} + +fn context_validate_url(url: &str) -> Result<(), error::Error> { + Url::parse(&url)?; + Ok(()) +} + +fn context_switch(context: &str) -> Result<(), error::Error> { + context_load(context)?; + + let path = context_config_dir().map(|path| path.join("current"))?; + + File::create(path)?.write_all(context.trim().as_bytes())?; + + println!("Switched to context: {}", context); + + Ok(()) +} + +fn context_create( + context: &str, + url: &str, + username: Option<&str>, + password: Option<&str>, + token: Option<&str>, + use_kubernetes: bool, + kubernetes_cluster: Option<&str>, + kubernetes_context: Option<&str>, + insecure: bool, + default_tenant: Option<&str>, +) -> Result<(), error::Error> { + if context_file_path(context)?.exists() { + return Err(ErrorKind::ContextExistsError(context.to_string()).into()); + } + + context_validate_url(url)?; + + let ctx = Context { + url: url.into(), + username: username.map(Into::into), + password: password.map(Into::into), + token: token.map(Into::into), + use_kubernetes, + kubernetes_context: kubernetes_context.map(Into::into), + kubernetes_cluster: kubernetes_cluster.map(Into::into), + + insecure, + default_tenant: default_tenant.map(Into::into), + }; + + context_store(context, ctx)?; + + println!("Created new context: {} for {}", context, url); + context_switch(context)?; + + Ok(()) +} + +fn context_update( + context: Option<&str>, + url: Option<&str>, + username: Option<&str>, + password: Option<&str>, + token: Option<&str>, + use_kubernetes: Option, + kubernetes_cluster: Option<&str>, + kubernetes_context: Option<&str>, + insecure: Option, + default_tenant: Option<&str>, +) -> Result<(), error::Error> { + let context = match context { + Some(c) => { + println!("Updating context: '{}':", c); + Some(c.to_string()) + } + None => { + let ctx = context_get_current()?; + if let Some(ref c) = ctx { + println!("Updating current context: '{}':", c); + } + ctx + } + }; + let context = context_names_valid(context)?; + + let mut ctx = context_load(context.as_str())?; + + if url.is_some() { + context_validate_url(url.unwrap())?; + ctx.url = url.unwrap().into(); + println!("\tSetting URL to: {}", ctx.url); + } + + if let Some(k) = use_kubernetes { + ctx.use_kubernetes = k; + println!( + "\t{}using local Kubernetes config", + ctx.use_kubernetes.either("", "NOT ") + ); + } + + if let Some(i) = insecure { + ctx.insecure = i; + println!( + "\t{}validating TLS certificate and hostname", + ctx.insecure.either("NOT ", "") + ); + } + + if let Some(u) = username { + if u.is_empty() { + ctx.username = None; + } else { + ctx.username = Some(u.into()); + } + + println!("\tSetting username to: {}", u); + } + + if let Some(p) = password { + if p.is_empty() { + ctx.password = None; + } else { + ctx.password = Some(p.into()); + } + + println!("\tSetting password"); + } + + if let Some(p) = token { + if p.is_empty() { + ctx.token = None; + } else { + ctx.token = Some(p.into()); + } + + println!("\tSetting token"); + } + + if let Some(t) = default_tenant { + if t.is_empty() { + ctx.default_tenant = None; + } else { + ctx.default_tenant = Some(t.into()); + } + + println!("\tSetting default tenant to: {}", t); + } + + if let Some(p) = kubernetes_cluster { + if p.is_empty() { + ctx.kubernetes_cluster = None; + } else { + ctx.kubernetes_cluster = Some(p.into()); + } + + println!("\tSetting Kubernetes cluster to: {}", p); + } + + if let Some(p) = kubernetes_context { + if p.is_empty() { + ctx.kubernetes_context = None; + } else { + ctx.kubernetes_context = Some(p.into()); + } + + println!("\tSetting Kubernetes context to: {}", p); + } + + context_store(&context, ctx)?; + + Ok(()) +} + +fn context_delete(context: &str) -> Result<(), error::Error> { + // delete context file + + let path = context_file_path(context)?; + if path.exists() { + std::fs::remove_file(path)?; + + // delete "current" marker + + let current = context_get_current()?; + if current == Some(context.to_string()) { + let cp = context_config_dir().map(|path| path.join("current"))?; + std::fs::remove_file(cp)?; + } + } else { + println!("Nothing to delete"); + return Ok(()); + } + + // success + + println!("Deleted context: {}", context); + + Ok(()) +} + +fn context_show(context: Option<&str>) -> Result<(), error::Error> { + let context = context + .map(|s| Ok(s.to_string())) + .or_else(|| context_get_current().transpose()) + .transpose() + .and_then(|name| context_names_valid(name))?; + + let ctx = context_load(&context)?; + + println!(" Current context: {}", context); + println!(" URL: {}", ctx.url); + + println!( + " Username: {}", + ctx.username.unwrap_or_else(|| String::from("")) + ); + println!( + " Password: {}", + ctx.password.and(Some("***")).unwrap_or("") + ); + println!( + " Token: {}", + ctx.token.unwrap_or_else(|| String::from("")) + ); + println!( + " Use Kubernetes: {}", + ctx.use_kubernetes.either("yes", "no") + ); + if ctx.use_kubernetes { + println!( + "Kubernetes cluster: {}", + ctx.kubernetes_cluster + .unwrap_or_else(|| String::from("")) + ); + println!( + "Kubernetes context: {}", + ctx.kubernetes_context + .unwrap_or_else(|| String::from("")) + ); + } + println!(" Insecure TLS: {}", ctx.insecure.either("yes", "no")); + println!( + " Default tenant: {}", + ctx.default_tenant.unwrap_or_else(|| String::from("")) + ); + + Ok(()) +} + +fn context_list() -> Result<(), error::Error> { + let path = context_contexts_dir()?; + + if !path.exists() { + println!("No known contexts"); + return Ok(()); + } + + let current = context_get_current()?; + + let active = Style::new().bold().fg(Color::Green); + + for entry in path.read_dir()? { + let name = context_decode_file_name(entry?.file_name().to_str().unwrap())?; + + if current == Some(name.clone()) && ColorMode::Auto(Output::StdOut).use_color() { + println!("{} *", active.paint(name)); + } else { + println!("{}", name); + } + } + + Ok(()) +} + +fn context_current() -> Result<(), error::Error> { + let current = context_get_current()?; + if let Some(current) = current { + println!("{}", current); + } + Ok(()) +} diff --git a/hat/src/credentials.rs b/hat/src/credentials.rs new file mode 100644 index 00000000..d03ca62b --- /dev/null +++ b/hat/src/credentials.rs @@ -0,0 +1,494 @@ +/******************************************************************************* + * Copyright (c) 2018 Red Hat Inc + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +use clap::value_t; +use clap::{App, ArgMatches}; + +use log::{debug, info}; + +use crate::help::help; + +use crate::context::Context; + +use http::header::*; +use http::method::Method; +use http::status::StatusCode; + +use crate::error; +use crate::error::ErrorKind::*; + +use crate::hash::HashFunction; + +use crate::resource::{ + resource_err_bad_request, resource_get, resource_modify, resource_url, AuthExt, Tracer, +}; + +use serde_json::value::{Map, Value}; + +use crate::client::Client; +use crate::overrides::Overrides; +use failure::Fail; +use futures::executor::block_on; + +type Result = std::result::Result; + +static RESOURCE_NAME: &str = "credentials"; +static TYPE_HASHED_PASSWORD: &str = "hashed-password"; +static TYPE_PSK: &str = "psk"; +static TYPE_X509: &str = "x509-cert"; + +pub async fn credentials( + app: &mut App<'_, '_>, + matches: &ArgMatches<'_>, + overrides: &Overrides, + context: &Context, +) -> Result<()> { + let client = Client::new(context, overrides).await?; + + match matches.subcommand() { + ("set", Some(cmd_matches)) => { + credentials_set( + context, + overrides, + cmd_matches.value_of("device").unwrap(), + cmd_matches.value_of("payload"), + ) + .await? + } + ("get", Some(cmd_matches)) => { + credentials_get(context, overrides, cmd_matches.value_of("device").unwrap()).await? + } + ("add-password", Some(cmd_matches)) => { + credentials_add_password( + &client, + context, + overrides, + cmd_matches.value_of("device").unwrap(), + cmd_matches.value_of("auth-id").unwrap(), + cmd_matches.value_of("password").unwrap(), + &value_t!(cmd_matches.value_of("hash-function"), HashFunction).unwrap(), + false, + ) + .await? + } + ("set-password", Some(cmd_matches)) => { + credentials_add_password( + &client, + context, + overrides, + cmd_matches.value_of("device").unwrap(), + cmd_matches.value_of("auth-id").unwrap(), + cmd_matches.value_of("password").unwrap(), + &value_t!(cmd_matches.value_of("hash-function"), HashFunction).unwrap(), + true, + ) + .await? + } + ("add-psk", Some(cmd_matches)) => { + credentials_add_psk( + &client, + context, + overrides, + cmd_matches.value_of("device").unwrap(), + cmd_matches.value_of("auth-id").unwrap(), + cmd_matches.value_of("psk").unwrap(), + false, + ) + .await? + } + ("set-psk", Some(cmd_matches)) => { + credentials_add_psk( + &client, + context, + overrides, + cmd_matches.value_of("device").unwrap(), + cmd_matches.value_of("auth-id").unwrap(), + cmd_matches.value_of("psk").unwrap(), + true, + ) + .await? + } + ("enable-x509", Some(cmd_matches)) => { + credentials_enable_x509( + &client, + context, + overrides, + cmd_matches.value_of("device").unwrap(), + cmd_matches.value_of("auth-id").unwrap(), + true, + ) + .await? + } + ("disable-x509", Some(cmd_matches)) => { + credentials_enable_x509( + &client, + context, + overrides, + cmd_matches.value_of("device").unwrap(), + cmd_matches.value_of("auth-id").unwrap(), + false, + ) + .await? + } + ("delete", Some(cmd_matches)) => { + let expected_type_name = cmd_matches.value_of("type").unwrap(); + let expected_auth_id = cmd_matches.value_of("auth-id").unwrap(); + + info!( + "Deleting credentials from device - type: {}, auth_id: {}", + expected_type_name, expected_auth_id + ); + + credentials_delete( + &client, + context, + overrides, + cmd_matches.value_of("device").unwrap(), + |_, type_name, auth_id| { + let result = expected_type_name == type_name && expected_auth_id == auth_id; + debug!( + "Testing - {}/{} == {}/{} => {}", + expected_type_name, expected_auth_id, type_name, auth_id, result + ); + result + }, + ) + .await? + } + ("delete-all", Some(cmd_matches)) => { + credentials_delete( + &client, + context, + overrides, + cmd_matches.value_of("device").unwrap(), + |_, _, _| true, + ) + .await? + } + _ => help(app)?, + }; + + Ok(()) +} + +async fn credentials_set( + context: &Context, + overrides: &Overrides, + device: &str, + payload: Option<&str>, +) -> Result<()> { + let tenant = context.make_tenant(overrides)?; + let url = resource_url( + context, + overrides, + RESOURCE_NAME, + &[&tenant, &device.into()], + )?; + + let payload = match payload { + Some(_) => serde_json::from_str(payload.unwrap())?, + _ => Vec::::new(), + }; + + let client = context.create_client(overrides).await?; + + client + .request(Method::PUT, url) + .apply_auth(context)? + .header(CONTENT_TYPE, "application/json") + .json(&payload) + .trace() + .send() + .await + .trace() + .map_err(error::Error::from) + .and_then(|response| match response.status() { + StatusCode::NO_CONTENT => Ok(response), + StatusCode::NOT_FOUND => Err(NotFound(device.to_string()).into()), + StatusCode::BAD_REQUEST => block_on(resource_err_bad_request(response)), + _ => Err(UnexpectedResult(response.status()).into()), + })?; + + println!("Updated device secrets: {}", device); + + Ok(()) +} + +async fn credentials_get(context: &Context, overrides: &Overrides, device: &str) -> Result<()> { + let tenant = context.make_tenant(overrides)?; + let url = resource_url( + context, + overrides, + RESOURCE_NAME, + &[&tenant, &device.into()], + )?; + + resource_get(&context, overrides, &url, "Credentials").await +} + +fn credentials_url(context: &Context, overrides: &Overrides, device: &str) -> Result { + resource_url( + context, + overrides, + RESOURCE_NAME, + &[&context.make_tenant(overrides)?, &device.into()], + ) +} + +async fn credentials_delete( + client: &Client, + context: &Context, + overrides: &Overrides, + device: &str, + predicate: F, +) -> Result<()> +where + F: Fn(&Map, &String, &String) -> bool, +{ + let mut count: usize = 0; + let mut diff: usize = 0; + + credentials_modify(client, &context, overrides, device, |payload| { + count = payload.len(); + payload.retain(|cred| match cred { + Value::Object(o) => match (o.get("type"), o.get("auth-id")) { + (Some(Value::String(t)), Some(Value::String(a))) => !predicate(o, t, a), + _ => true, + }, + _ => true, + }); + diff = count - payload.len(); + Ok(()) + }) + .await?; + + println!("Deleted {} elements, {} remain.", diff, count); + + Ok(()) +} + +async fn credentials_modify( + client: &Client, + context: &Context, + overrides: &Overrides, + device: &str, + modifier: F, +) -> Result<()> +where + F: FnMut(&mut Vec) -> Result<()>, +{ + let url = credentials_url(context, overrides, device)?; + + resource_modify(client, &context, &url, &url, device, modifier).await?; + + Ok(()) +} + +async fn credentials_add_psk( + client: &Client, + context: &Context, + overrides: &Overrides, + device: &str, + auth_id: &str, + psk: &str, + clear: bool, +) -> Result<()> { + let new_secret = new_psk_secret(psk)?; + + cred_add_or_insert( + client, + context, + overrides, + clear, + TYPE_PSK, + device, + auth_id, + Some(new_secret), + ) + .await?; + + if clear { + println!("PSK set for {}/{}", device, auth_id); + } else { + println!("PSK added to {}/{}", device, auth_id); + } + + Ok(()) +} + +async fn credentials_add_password( + client: &Client, + context: &Context, + overrides: &Overrides, + device: &str, + auth_id: &str, + password: &str, + hash_function: &HashFunction, + clear: bool, +) -> Result<()> { + let new_secret = new_password_secret(password, hash_function)?; + + cred_add_or_insert( + client, + context, + overrides, + clear, + TYPE_HASHED_PASSWORD, + device, + auth_id, + Some(new_secret), + ) + .await?; + + if clear { + println!("Password set for {}/{}", device, auth_id); + } else { + println!("Password added to {}/{}", device, auth_id); + } + + Ok(()) +} + +async fn credentials_enable_x509( + client: &Client, + context: &Context, + overrides: &Overrides, + device: &str, + auth_id: &str, + enable: bool, +) -> Result<()> { + let new_secret = match enable { + true => Some(new_x509()), + false => None, + }; + + cred_add_or_insert( + client, context, overrides, true, TYPE_X509, device, auth_id, new_secret, + ) + .await?; + + if enable { + println!("X509 enabled for {}/{}", device, auth_id); + } else { + println!("X509 disabled for {}/{}", device, auth_id); + } + + Ok(()) +} + +async fn cred_add_or_insert( + client: &Client, + context: &Context, + overrides: &Overrides, + clear: bool, + type_name: &str, + device: &str, + auth_id: &str, + new_secret: Option, +) -> Result<()> { + credentials_modify(client, &context, overrides, device, |payload| { + let cred = payload + .iter_mut() + .flat_map(|c| cred_for_type_and_auth(type_name, auth_id, c)) + .nth(0); + + let cred = match cred { + Some(c) => c, + None => { + let cred = new_credential(type_name, auth_id); + payload.push(cred); + payload.last_mut().unwrap().as_object_mut().unwrap() + } + }; + + let new_secret = new_secret.clone(); + + if clear { + cred.remove("secrets"); + } + + if let Some(secret) = new_secret { + if let Some(Value::Array(s)) = cred.get_mut("secrets") { + s.push(secret); + } else { + cred.insert("secrets".into(), vec![secret].into()); + } + } + + // return success + + Ok(()) + }) + .await +} + +fn cred_for_type_and_auth<'a, 'b, 'c>( + type_name: &'b str, + auth_id: &'c str, + cred: &'a mut Value, +) -> Option<&'a mut Map> { + match cred { + Value::Object(o) => match (o.get("type"), o.get("auth-id")) { + (Some(Value::String(t)), Some(Value::String(a))) if t == type_name && a == auth_id => { + Some(o) + } + _ => None, + }, + _ => None, + } +} + +fn new_credential(type_name: &str, auth_id: &str) -> Value { + let mut new_pair = Map::new(); + + new_pair.insert("auth-id".into(), auth_id.into()); + new_pair.insert("type".into(), type_name.into()); + + Value::Object(new_pair) +} + +/// Create a new secrets entry, based on `hashed-password` +fn new_password_secret(plain_password: &str, hash_function: &HashFunction) -> Result { + let mut new_pair = Map::new(); + + // put to result + + hash_function.insert(&mut new_pair, &plain_password)?; + + // return as value + + Ok(Value::Object(new_pair)) +} + +/// Create a new secrets entry, based on `psk` +fn new_psk_secret>(psk: S) -> Result { + let mut new_pair = Map::new(); + + // validate it is base64 encoded + + let psk = psk.into(); + base64::decode(psk.as_bytes()) + .map_err(|err| err.context(GenericError("Key must be base64 encoded".into())))?; + + // put to result + + new_pair.insert("key".into(), psk.into()); + + // return as value + + Ok(Value::Object(new_pair)) +} + +fn new_x509() -> Value { + Value::Object(Map::new()) +} diff --git a/hat/src/devices.rs b/hat/src/devices.rs new file mode 100644 index 00000000..54222309 --- /dev/null +++ b/hat/src/devices.rs @@ -0,0 +1,383 @@ +/******************************************************************************* + * Copyright (c) 2018, 2019 Red Hat Inc + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +use clap::{App, ArgMatches}; + +use crate::context::Context; +use crate::help::help; + +use serde_json::value::Value::Object; +use serde_json::value::{Map, Value}; + +use http::header::CONTENT_TYPE; +use http::method::Method; +use http::status::StatusCode; + +use crate::error; +use crate::error::ErrorKind::*; + +use crate::utils::Either; + +use crate::client::Client; +use crate::overrides::Overrides; +use crate::resource::Tracer; +use crate::resource::{ + resource_append_path, resource_delete, resource_err_bad_request, resource_get, + resource_id_from_location, resource_modify, resource_url, AuthExt, +}; +use futures::executor::block_on; + +type Result = std::result::Result; +const RESOURCE_NAME: &str = "devices"; +const RESOURCE_LABEL: &str = "Device"; +const PROP_ENABLED: &str = "enabled"; +const PROP_VIA: &str = "via"; +const PROP_DEFAULTS: &str = "defaults"; + +pub async fn registration( + app: &mut App<'_, '_>, + matches: &ArgMatches<'_>, + overrides: &Overrides, + context: &Context, +) -> Result<()> { + let client = Client::new(context, overrides).await?; + + match matches.subcommand() { + ("create", Some(cmd_matches)) => { + registration_create( + context, + overrides, + cmd_matches.value_of("device"), + cmd_matches.value_of("payload"), + ) + .await? + } + ("update", Some(cmd_matches)) => { + registration_update( + context, + overrides, + cmd_matches.value_of("device").unwrap(), + cmd_matches.value_of("payload"), + ) + .await? + } + ("get", Some(cmd_matches)) => { + registration_get(context, overrides, cmd_matches.value_of("device").unwrap()).await? + } + ("delete", Some(cmd_matches)) => { + registration_delete(context, overrides, cmd_matches.value_of("device").unwrap()).await? + } + ("enable", Some(cmd_matches)) => { + registration_enable( + context, + overrides, + cmd_matches.value_of("device").unwrap(), + true, + ) + .await? + } + ("disable", Some(cmd_matches)) => { + registration_enable( + context, + overrides, + cmd_matches.value_of("device").unwrap(), + false, + ) + .await? + } + ("set-via", Some(cmd_matches)) => { + registration_via( + &client, + context, + overrides, + cmd_matches.value_of("device").unwrap(), + cmd_matches.values_of("via"), + ) + .await? + } + ("set-default", Some(cmd_matches)) => { + registration_set_default( + &client, + context, + overrides, + cmd_matches.value_of("device").unwrap(), + cmd_matches.value_of("defaults-name").unwrap(), + cmd_matches.value_of("defaults-value"), + ) + .await? + } + _ => help(app)?, + }; + + Ok(()) +} + +async fn registration_create( + context: &Context, + overrides: &Overrides, + device: Option<&str>, + payload: Option<&str>, +) -> Result<()> { + let tenant = context.make_tenant(overrides)?; + let url = resource_url(context, overrides, RESOURCE_NAME, &[&tenant])?; + + let url = resource_append_path(url, device)?; + + let payload = match payload { + Some(_) => serde_json::from_str(payload.unwrap())?, + _ => serde_json::value::Map::new(), + }; + + let client = context.create_client(overrides).await?; + + let device = client + .request(Method::POST, url) + .apply_auth(context)? + .header(CONTENT_TYPE, "application/json") + .json(&payload) + .trace() + .send() + .await + .trace() + .map_err(error::Error::from) + .and_then(|response| match response.status() { + StatusCode::CREATED => Ok(response), + StatusCode::CONFLICT => Err(AlreadyExists(device.unwrap().to_string()).into()), + StatusCode::BAD_REQUEST => block_on(resource_err_bad_request(response)), + _ => Err(UnexpectedResult(response.status()).into()), + }) + .and_then(resource_id_from_location)?; + + println!("Registered device {} for tenant {}", device, tenant); + + Ok(()) +} + +async fn registration_update( + context: &Context, + overrides: &Overrides, + device: &str, + payload: Option<&str>, +) -> Result<()> { + let tenant = context.make_tenant(overrides)?; + let url = resource_url( + context, + overrides, + RESOURCE_NAME, + &[&tenant, &device.into()], + )?; + + let payload = match payload { + Some(_) => serde_json::from_str(payload.unwrap())?, + _ => serde_json::value::Map::new(), + }; + + let client = context.create_client(overrides).await?; + + client + .request(Method::PUT, url) + .apply_auth(context)? + .header(CONTENT_TYPE, "application/json") + .json(&payload) + .trace() + .send() + .await + .trace() + .map_err(error::Error::from) + .and_then(|response| match response.status() { + StatusCode::NO_CONTENT => Ok(response), + StatusCode::NOT_FOUND => Err(NotFound(device.to_string()).into()), + StatusCode::BAD_REQUEST => block_on(resource_err_bad_request(response)), + _ => Err(UnexpectedResult(response.status()).into()), + })?; + + println!("Updated device device {} for tenant {}", device, tenant); + + Ok(()) +} + +async fn registration_delete(context: &Context, overrides: &Overrides, device: &str) -> Result<()> { + let url = resource_url( + context, + overrides, + RESOURCE_NAME, + &[&context.make_tenant(overrides)?, &device.into()], + )?; + resource_delete(&context, overrides, &url, RESOURCE_LABEL, &device).await +} + +async fn registration_get(context: &Context, overrides: &Overrides, device: &str) -> Result<()> { + let url = resource_url( + context, + overrides, + RESOURCE_NAME, + &[&context.make_tenant(overrides)?, &device.into()], + )?; + resource_get(&context, overrides, &url, RESOURCE_LABEL).await +} + +async fn registration_enable( + context: &Context, + overrides: &Overrides, + device: &str, + status: bool, +) -> Result<()> { + let client = Client::new(context, overrides).await?; + + let url = resource_url( + context, + overrides, + RESOURCE_NAME, + &[&context.make_tenant(overrides)?, &device.into()], + )?; + + resource_modify( + &client, + &context, + &url, + &url, + RESOURCE_LABEL, + |reg: &mut Map| { + reg.insert(PROP_ENABLED.into(), serde_json::value::Value::Bool(status)); + Ok(()) + }, + ) + .await?; + + println!( + "Registration for device {} {}", + device, + status.either("enabled", "disabled") + ); + + Ok(()) +} + +fn registration_url(context: &Context, overrides: &Overrides, device: S) -> Result +where + S: Into, +{ + resource_url( + context, + overrides, + RESOURCE_NAME, + &[&context.make_tenant(overrides)?, &device.into()], + ) +} + +async fn registration_set_default( + client: &Client, + context: &Context, + overrides: &Overrides, + device: &str, + name: &str, + payload: Option<&str>, +) -> Result<()> { + let payload: Option = match payload { + Some(p) => Some(serde_json::from_str(p).unwrap_or_else(|_| Value::String(p.into()))), + _ => None, + }; + + let url = registration_url(context, overrides, device)?; + + resource_modify( + client, + &context, + &url, + &url, + RESOURCE_LABEL, + |reg: &mut Map| { + match &payload { + None => match reg.get_mut(PROP_DEFAULTS.into()) { + Some(Object(ref mut defaults)) => { + // remove from defaults map + defaults.remove(name); + } + _ => {} + }, + Some(payload) => match reg.get_mut(PROP_DEFAULTS.into()) { + Some(Object(ref mut defaults)) => { + // add to defaults map + defaults.insert(name.into(), payload.clone()); + } + _ => { + // defaults is either not present, or not an object + let mut defaults = Map::new(); + defaults.insert(name.into(), payload.clone()); + reg.insert(PROP_DEFAULTS.into(), Object(defaults)); + } + }, + }; + + Ok(()) + }, + ) + .await?; + + match payload { + None => println!("Cleared default value {} for device {}", name, device), + Some(ref v) => { + println!( + "Set default value {} for device {} to {:#?}", + name, device, v + ); + } + } + + Ok(()) +} + +async fn registration_via( + client: &Client, + context: &Context, + overrides: &Overrides, + device: &str, + via: Option>, +) -> Result<()> { + let url = registration_url(context, overrides, device)?; + + resource_modify( + client, + &context, + &url, + &url, + RESOURCE_LABEL, + |reg: &mut Map| { + match via { + None => { + reg.remove(PROP_VIA.into()); + } + Some(ref v) => { + let json = serde_json::value::to_value::>(v.clone().collect())?; + reg.insert(PROP_VIA.into(), json); + } + }; + + Ok(()) + }, + ) + .await?; + + match via { + None => println!("Gateways cleared for device {}", device), + Some(v) => { + println!( + "Gateway(s) set for device {} {:#?}", + device, + v.collect::>() + ); + } + } + + Ok(()) +} diff --git a/hat/src/error.rs b/hat/src/error.rs new file mode 100644 index 00000000..8989a43d --- /dev/null +++ b/hat/src/error.rs @@ -0,0 +1,187 @@ +/******************************************************************************* + * Copyright (c) 2018 Red Hat Inc + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +use failure::{Backtrace, Context, Fail}; +use std::fmt::{self, Display}; + +#[derive(Debug)] +pub struct Error { + inner: Context, +} + +#[derive(Clone, Debug, Fail)] +pub enum ErrorKind { + #[fail(display = "{}", _0)] + GenericError(String), + + #[fail(display = "I/O error: {:?}", _0)] + Io(::std::io::ErrorKind), + + #[fail(display = "Command Line Error: {:?}", _0)] + CommandLine(::clap::ErrorKind), + + #[fail(display = "Request error: {}", _0)] + Request(String), + + #[fail(display = "Response error: {}", _0)] + Response(String), + + #[fail(display = "URL format error")] + UrlError, + + #[fail(display = "JSON format error: {:?}", _0)] + JsonError(::serde_json::error::Category), + + #[fail(display = "YAML format error")] + YamlError, + + #[fail(display = "Invalid UTF-8 string")] + Utf8Error, + + #[fail(display = "Kubernetes client error")] + KubeError, + + // context errors + #[fail(display = "Context '{}' already exists", _0)] + ContextExistsError(String), + #[fail(display = "Unknown context '{}'", _0)] + ContextUnknownError(String), + #[fail(display = "Invalid context name: {}", _0)] + ContextNameError(String), + + // remote errors + #[fail(display = "Resource not found: {}", _0)] + NotFound(String), + + #[fail(display = "Resource already exists: {}", _0)] + AlreadyExists(String), + + #[fail(display = "Malformed request: {}", _0)] + MalformedRequest(String), + + #[fail(display = "Unexpected return code: {}", _0)] + UnexpectedResult(http::StatusCode), +} + +impl Fail for Error { + fn cause(&self) -> Option<&dyn Fail> { + self.inner.cause() + } + + fn backtrace(&self) -> Option<&Backtrace> { + self.inner.backtrace() + } +} + +impl Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Display::fmt(&self.inner, f) + } +} + +#[allow(dead_code)] +impl Error { + pub fn kind(&self) -> ErrorKind { + self.inner.get_context().clone() + } +} + +impl From for Error { + fn from(kind: ErrorKind) -> Error { + Error { + inner: Context::new(kind), + } + } +} + +impl From> for Error { + fn from(inner: Context) -> Error { + Error { inner } + } +} + +impl From for Error { + fn from(err: reqwest::Error) -> Error { + let msg = format!("{}", err); + err.context(ErrorKind::Request(msg)).into() + } +} + +impl From for Error { + fn from(err: http::header::ToStrError) -> Error { + let msg = err.to_string(); + err.context(ErrorKind::Request(msg)).into() + } +} + +impl From for Error { + fn from(_err: url::ParseError) -> Error { + ErrorKind::UrlError.into() + } +} + +impl From for Error { + fn from(err: serde_json::Error) -> Error { + let cat = err.classify(); + err.context(ErrorKind::JsonError(cat)).into() + } +} + +impl From for Error { + fn from(err: serde_yaml::Error) -> Error { + err.context(ErrorKind::YamlError).into() + } +} + +impl From for Error { + fn from(err: std::io::Error) -> Error { + let kind = err.kind(); + err.context(ErrorKind::Io(kind)).into() + } +} + +impl From for Error { + fn from(err: clap::Error) -> Error { + let kind = err.kind; + err.context(ErrorKind::CommandLine(kind)).into() + } +} + +impl From for Error { + fn from(err: std::str::Utf8Error) -> Error { + err.context(ErrorKind::Utf8Error).into() + } +} + +impl From for Error { + fn from(err: std::string::FromUtf8Error) -> Error { + err.context(ErrorKind::Utf8Error).into() + } +} + +impl From for Error { + fn from(err: bcrypt::BcryptError) -> Error { + err.context(ErrorKind::GenericError( + "Failed to generate BCrypt hash".into(), + )) + .into() + } +} + +impl From for Error { + fn from(err: kube::Error) -> Error { + Error { + inner: err.context(ErrorKind::KubeError), + } + } +} diff --git a/hat/src/hash.rs b/hat/src/hash.rs new file mode 100644 index 00000000..9fc55a2d --- /dev/null +++ b/hat/src/hash.rs @@ -0,0 +1,149 @@ +/******************************************************************************* + * Copyright (c) 2018 Red Hat Inc + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +use sha2::Digest; +use sha2::{Sha256, Sha512}; + +use rand::rngs::EntropyRng; +use rand::RngCore; + +use std::fmt; + +use serde_json::value::{Map, Value}; + +pub enum HashFunction { + Plain, + Sha256, + Sha512, + Bcrypt(u8), +} + +use crate::error; +type Result = std::result::Result; + +impl std::str::FromStr for HashFunction { + type Err = &'static str; + + fn from_str(s: &str) -> std::result::Result { + match s { + "plain" => Ok(HashFunction::Plain), + "sha-256" => Ok(HashFunction::Sha256), + "sha-512" => Ok(HashFunction::Sha512), + "bcrypt" => Ok(HashFunction::Bcrypt(10)), + _ => HashFunction::from_bcrypt(s).unwrap_or_else(|| Err("Unknown hash function")), + } + } +} + +impl fmt::Display for HashFunction { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + HashFunction::Plain => write!(f, "plain"), + HashFunction::Sha256 => write!(f, "sha-256"), + HashFunction::Sha512 => write!(f, "sha-512"), + HashFunction::Bcrypt(i) => write!(f, "bcrypt:{}", i), + } + } +} + +fn do_hash(salt: &[u8], password: &str) -> (String, Option) { + let mut md = D::default(); + md.input(salt); + md.input(password); + + let dig = md.result(); + + (base64::encode(&dig), Some(base64::encode(&salt))) +} + +fn do_bcrypt(password: &str, iterations: u8) -> Result<(String, Option)> { + let mut hash = bcrypt::hash(password, u32::from(iterations))?; + + hash.replace_range(1..3, "2a"); + + Ok((hash, None)) +} + +fn gen_salt(size: usize) -> Vec { + let mut rnd = EntropyRng::new(); + let mut salt = vec![0; size]; + + rnd.fill_bytes(&mut salt); + salt +} + +impl HashFunction { + pub fn name(&self) -> &str { + match self { + HashFunction::Plain => "plain", + HashFunction::Sha256 => "sha-256", + HashFunction::Sha512 => "sha-512", + HashFunction::Bcrypt(_) => "bcrypt", // we omit the iterations here + } + } + + fn from_bcrypt(s: &str) -> Option> { + let v: Vec<&str> = s.splitn(2, ':').collect(); + + match (v.get(0), v.get(1)) { + (Some(t), None) if *t == "bcrypt" => Some(Ok(HashFunction::Bcrypt(10))), + (Some(t), Some(i)) if *t == "bcrypt" => { + let iter = i.parse::(); + + Some( + iter.map(HashFunction::Bcrypt) + .map_err(|_| "Failed to parse number of iterations"), + ) + } + _ => None, + } + } + + fn insert_hash( + &self, + new_pair: &mut Map, + password: &str, + ) -> Result<()> { + new_pair.insert("hash-function".into(), self.name().into()); + let r = do_hash::(gen_salt(16).as_slice(), password); + new_pair.insert("pwd-hash".into(), r.0.into()); + if let Some(salt) = r.1 { + new_pair.insert("salt".into(), salt.into()); + } + Ok(()) + } + + fn insert_bcrypt( + &self, + new_pair: &mut Map, + password: &str, + i: u8, + ) -> Result<()> { + new_pair.insert("hash-function".into(), self.name().into()); + let r = do_bcrypt(password, i)?; + new_pair.insert("pwd-hash".into(), r.0.into()); + Ok(()) + } + + pub fn insert(&self, new_pair: &mut Map, password: &str) -> Result<()> { + match self { + HashFunction::Plain => { + new_pair.insert("pwd-plain".into(), password.into()); + Ok(()) + } + HashFunction::Sha256 => self.insert_hash::(new_pair, password), + HashFunction::Sha512 => self.insert_hash::(new_pair, password), + HashFunction::Bcrypt(i) => self.insert_bcrypt(new_pair, password, *i), + } + } +} diff --git a/hat/src/help.rs b/hat/src/help.rs new file mode 100644 index 00000000..a22694e1 --- /dev/null +++ b/hat/src/help.rs @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2018 Red Hat Inc + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +use crate::error; +use clap::App; + +pub fn help(app: &mut App) -> Result<(), error::Error> { + app.print_help()?; + println!(); + Ok(()) +} diff --git a/hat/src/main.rs b/hat/src/main.rs new file mode 100644 index 00000000..837d40d0 --- /dev/null +++ b/hat/src/main.rs @@ -0,0 +1,474 @@ +/******************************************************************************* + * Copyright (c) 2018, 2020 Red Hat Inc + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +use clap::crate_version; +use clap::{App, AppSettings, Arg, SubCommand}; +use failure::Fail; +use overrides::Overrides; +use simplelog::{Config, LevelFilter, TermLogger}; +use std::result::Result; + +use log::debug; + +mod args; +mod client; +mod context; +mod credentials; +mod devices; +mod error; +mod hash; +mod help; +mod output; +mod overrides; +mod resource; +mod tenant; +mod utils; + +fn app() -> App<'static, 'static> { + // globals + + let args_global_verbose = Arg::with_name("verbose") + .help("Be more verbose, repeat to increase verbosity") + .global(true) + .short("v") + .long("verbose") + .multiple(true); + + // context + + let args_ctx = Arg::with_name("context-name") + .help("Name of the context") + .number_of_values(1) + .required(true); + let args_ctx_url = Arg::with_name("context-url") + .help("URL of the registry") + .number_of_values(1) + .overrides_with("url") + .required(true); + let args_ctx_username = Arg::with_name("username") + .help("Username for accessing the device registry") + .long("username") + .short("u") + .number_of_values(1); + let args_ctx_password = Arg::with_name("password") + .help("Password for accessing the device registry") + .long("password") + .short("p") + .number_of_values(1); + let args_ctx_token = Arg::with_name("token") + .help("Bearer token for accessing the device registry") + .long("token") + .number_of_values(1); + + // tenant + + let args_tenant_name = Arg::with_name("tenant_name") + .help("Tenant name") + .required(true); + let args_tenant_name_optional = Arg::with_name("tenant_name") + .help("Tenant name") + .required(false); + let args_tenant_payload = Arg::with_name("payload").help("Tenant payload"); + + // device + + let args_device = Arg::with_name("device") + .help("ID of the device") + .required(true); + let args_device_optional = Arg::with_name("device") + .help("ID of the device") + .required(false); + let args_device_payload = Arg::with_name("payload").help("Device payload"); + let args_device_via = Arg::with_name("via") + .help("Gateways of the device") + .empty_values(false) + .multiple(true) + .takes_value(true) + .required(false); + let args_device_defaults_key = Arg::with_name("defaults-name") + .help("Name of the defaults entry") + .number_of_values(1) + .required(true); + let args_device_json_value = Arg::with_name("defaults-value") + .help("Value for the defaults entry") + .min_values(0) + .max_values(1) + .required(false); + + // credentials + + let args_credentials_auth_id = Arg::with_name("auth-id") + .help("Device Authentication ID") + .required(true); + let args_credentials_type = Arg::with_name("type") + .help("Device Authentication Type") + .required(true); + let args_credentials_payload = + Arg::with_name("payload").help("Credentials payload in JSON format"); + let args_credentials_hash_function = Arg::with_name("hash-function") + .short("h") + .long("hash") + .takes_value(true) + .help("Password hash function [possible values: plain, sha-256, sha-512, bcrypt<:iterations>]") + .default_value("plain"); + let args_credentials_password = Arg::with_name("password") + .required(true) + .help("The plaintext password"); + let args_credentials_psk = Arg::with_name("psk") + .required(true) + .help("The PSK value, base64 encoded"); + + // overrides + + let args_override_url = Arg::with_name("url") + .help("Set the URL to use") + .global(true) + .short("U") + .long("url") + .env("HAT_URL") + .number_of_values(1); + + let args_override_tenant = Arg::with_name("tenant") + .help("Set the tenant to use") + .global(true) + .short("t") + .long("tenant") + .env("HAT_TENANT") + .number_of_values(1); + + let args_override_context = Arg::with_name("context") + .help("The context to use") + .global(true) + .short("c") + .long("context") + .env("HAT_CONTEXT") + .number_of_values(1); + + let args_override_kubernetes = Arg::with_name("use-kubernetes") + .help("Whether to use the Kubernetes credentials") + .global(true) + .long("use-kubernetes") + .short("k") + .possible_values(&["true", "false"]) + .min_values(0) + .max_values(1); + + let args_override_insecure = Arg::with_name("insecure") + .help("Ignore TLS certificate and hostname (INSECURE!)") + .global(true) + .long("insecure") + .possible_values(&["true", "false"]) + .min_values(0) + .max_values(1); + + let args_override_kubernetes_cluster = Arg::with_name("kubernetes-cluster") + .global(true) + .help("Kubernetes cluster to select from local configuration") + .long("kubernetes-cluster") + .min_values(0) + .max_values(1); + + let args_override_kubernetes_context = Arg::with_name("kubernetes-context") + .global(true) + .help("Kubernetes context to select from local configuration") + .long("kubernetes-context") + .min_values(0) + .max_values(1); + + let args_overrides = [ + args_override_context, + args_override_kubernetes, + args_override_tenant, + args_override_url, + args_override_insecure, + args_override_kubernetes_cluster, + args_override_kubernetes_context, + ]; + + // main app + + App::new("Hono Admin Tool") + .version(crate_version!()) + .bin_name("hat") + .author("Jens Reimann ") + .about("Work with an Eclipse Hono instance") + .global_setting(AppSettings::VersionlessSubcommands) + .arg(args_global_verbose) + .args(&args_overrides) + .subcommand( + SubCommand::with_name("context") + .about("Work with contexts") + .setting(AppSettings::SubcommandRequiredElseHelp) + .subcommand( + SubCommand::with_name("create") + .about("Create a new context") + .arg(args_ctx.clone()) + .arg(args_ctx_url.clone()) + .arg(args_ctx_username.clone()) + .arg(args_ctx_password.clone()) + .arg(args_ctx_token.clone()), + ) + .subcommand( + SubCommand::with_name("update") + .about("Update an existing context") + .arg(args_ctx.clone().required(false)) + .arg(args_ctx_username.clone()) + .arg(args_ctx_password.clone()) + .arg(args_ctx_token.clone()), + ) + .subcommand( + SubCommand::with_name("delete") + .about("Delete a context") + .arg(args_ctx.clone()), + ) + .subcommand(SubCommand::with_name("list").about("List existing contexts")) + .subcommand( + SubCommand::with_name("current").about("Print current selected context"), + ) + .subcommand( + SubCommand::with_name("switch") + .about("Switch to existing context") + .arg(args_ctx.clone()), + ) + .subcommand( + SubCommand::with_name("show") + .about("Show current context information") + .arg(args_ctx.clone().required(false)), + ), + ) + .subcommand( + SubCommand::with_name("tenant") + .about("Work with tenants") + .setting(AppSettings::SubcommandRequiredElseHelp) + .subcommand( + SubCommand::with_name("create") + .about("Create a new tenant") + .arg(args_tenant_name_optional.clone()) + .arg(args_tenant_payload.clone()), + ) + .subcommand( + SubCommand::with_name("update") + .about("Update an existing tenant") + .arg(args_tenant_name.clone()) + .arg(args_tenant_payload.clone()), + ) + .subcommand( + SubCommand::with_name("get") + .about("Get tenant information") + .arg(args_tenant_name.clone()), + ) + .subcommand( + SubCommand::with_name("delete") + .about("Delete an existing tenant") + .arg(args_tenant_name.clone()), + ) + .subcommand( + SubCommand::with_name("enable") + .about("Enable an existing tenant") + .arg(args_tenant_name.clone()), + ) + .subcommand( + SubCommand::with_name("disable") + .about("Disable an existing tenant") + .arg(args_tenant_name.clone()), + ), + ) + .subcommand( + SubCommand::with_name("device") + .about("Work with devices") + .setting(AppSettings::SubcommandRequiredElseHelp) + .subcommand( + SubCommand::with_name("create") + .about("Register a new device") + .arg(args_device_optional.clone()) + .arg(args_device_payload.clone()), + ) + .subcommand( + SubCommand::with_name("get") + .about("Get a device") + .arg(args_device.clone()), + ) + .subcommand( + SubCommand::with_name("update") + .about("Update an existing device registration") + .arg(args_device.clone()) + .arg(args_device_payload.clone()), + ) + .subcommand( + SubCommand::with_name("set-via") + .about("Set the gateway configuration") + .arg(args_device.clone()) + .arg(args_device_via.clone()), + ) + .subcommand( + SubCommand::with_name("set-default") + .alias("set-defaults") + .about("Set or remove defaults entry") + .arg(args_device.clone()) + .arg(args_device_defaults_key.clone()) + .arg(args_device_json_value.clone()), + ) + .subcommand( + SubCommand::with_name("delete") + .about("Delete a device registration") + .arg(args_device.clone()), + ) + .subcommand( + SubCommand::with_name("enable") + .about("Enable a device registration") + .arg(args_device.clone()), + ) + .subcommand( + SubCommand::with_name("disable") + .about("Disable a device registration") + .arg(args_device.clone()), + ), + ) + .subcommand( + SubCommand::with_name("cred") + .aliases(&["creds", "auth", "credentials"]) + .about("Work with device credentials") + .setting(AppSettings::SubcommandRequiredElseHelp) + .subcommand( + SubCommand::with_name("set") + .about("Set all credentials for device") + .arg(args_device.clone()) + .arg(args_credentials_payload.clone()), + ) + .subcommand( + SubCommand::with_name("get") + .about("Get all credentials for an existing device") + .arg(args_device.clone()), + ) + .subcommand( + SubCommand::with_name("add-password") + .about("Add password secret") + .arg(args_device.clone()) + .arg(args_credentials_auth_id.clone()) + .arg(args_credentials_hash_function.clone()) + .arg(args_credentials_password.clone()), + ) + .subcommand( + SubCommand::with_name("set-password") + .about("Set password as the only secret") + .arg(args_device.clone()) + .arg(args_credentials_auth_id.clone()) + .arg(args_credentials_hash_function.clone()) + .arg(args_credentials_password.clone()), + ) + .subcommand( + SubCommand::with_name("add-psk") + .about("Add PSK secret") + .arg(args_device.clone()) + .arg(args_credentials_auth_id.clone()) + .arg(args_credentials_hash_function.clone()) + .arg(args_credentials_psk.clone()), + ) + .subcommand( + SubCommand::with_name("set-psk") + .about("Set PSK as the only secret") + .arg(args_device.clone()) + .arg(args_credentials_auth_id.clone()) + .arg(args_credentials_psk.clone()), + ) + .subcommand( + SubCommand::with_name("enable-x509") + .about("Enable device for X509") + .arg(args_device.clone()) + .arg(args_credentials_auth_id.clone()), + ) + .subcommand( + SubCommand::with_name("disable-x509") + .about("Disable device for X509") + .arg(args_device.clone()) + .arg(args_credentials_auth_id.clone()), + ) + .subcommand( + SubCommand::with_name("delete") + .about("Delete a credential set from a device") + .arg(args_device.clone()) + .arg(args_credentials_type.clone()) + .arg(args_credentials_auth_id.clone()), + ) + .subcommand( + SubCommand::with_name("delete-all") + .about("Delete all credentials for a device") + .arg(args_device.clone()), + ), + ) +} + +async fn run() -> Result<(), failure::Error> { + let mut app = app(); + let matches = app.clone().get_matches(); + + let level_filter = match matches.occurrences_of("verbose") { + 0 => LevelFilter::Warn, + 1 => LevelFilter::Info, + 2 => LevelFilter::Debug, + _ => LevelFilter::Trace, + }; + + let cfg = Config::default(); + TermLogger::init(level_filter, cfg).unwrap(); + + debug!("Args: {:#?}", matches); + + // fill overrides + let overrides = Overrides::from(&matches); + + let (cmd_name, cmd) = matches.subcommand(); + + // process non-network commands + + if cmd_name == "context" { + context::context(&mut app, cmd.unwrap())?; + return Ok(()); + } + + // process remote commands + + let context = context::context_load_current(Some(&overrides))?; + + match cmd_name { + "tenant" => tenant::tenant(&mut app, cmd.unwrap(), &overrides, &context).await?, + "device" => devices::registration(&mut app, cmd.unwrap(), &overrides, &context).await?, + "cred" => credentials::credentials(&mut app, cmd.unwrap(), &overrides, &context).await?, + _ => help::help(&mut app)?, + }; + + Ok(()) +} + +fn hat_exit(err: failure::Error) -> ! { + for cause in Fail::iter_chain(err.as_fail()) + .collect::>() + .iter() + .rev() + { + println!("{}: {}", cause.name().unwrap_or("Error"), cause); + } + + std::process::exit(1) +} + +#[tokio::main(core_threads = 1)] +async fn main() { + #[cfg(windows)] + let _enabled = colored_json::enable_ansi_support(); + + match run().await { + Err(e) => hat_exit(e), + Ok(()) => {} + } +} diff --git a/hat/src/output.rs b/hat/src/output.rs new file mode 100644 index 00000000..77ea30c2 --- /dev/null +++ b/hat/src/output.rs @@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (c) 2018 Red Hat Inc + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +use colored_json::write_colored_json; + +use serde_json::value::Value; + +use crate::error; +use std::io::stdout; +use std::io::Write; + +pub fn display_json_value(value: &Value) -> std::result::Result<(), error::Error> { + let mut out = stdout(); + + { + let mut out = out.lock(); + write_colored_json(value, &mut out)?; + out.write_all("\n".as_bytes())?; + } + + out.flush()?; + + Ok(()) +} diff --git a/hat/src/overrides.rs b/hat/src/overrides.rs new file mode 100644 index 00000000..ba99ebec --- /dev/null +++ b/hat/src/overrides.rs @@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright (c) 2018 Red Hat Inc + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +use crate::args::flag_arg; + +pub struct Overrides { + context: Option, + url: Option, + tenant: Option, + use_kubernetes: Option, + kubernetes_context: Option, + kubernetes_cluster: Option, + insecure: Option, +} + +impl Overrides { + pub fn context(&self) -> Option { + self.context.clone() + } + pub fn url(&self) -> Option<&String> { + self.url.as_ref() + } + pub fn tenant(&self) -> Option { + self.tenant.clone() + } + pub fn use_kubernetes(&self) -> Option { + self.use_kubernetes + } + pub fn kubernetes_cluster(&self) -> Option<&String> { + self.kubernetes_cluster.as_ref() + } + pub fn kubernetes_context(&self) -> Option<&String> { + self.kubernetes_context.as_ref() + } + pub fn insecure(&self) -> Option { + self.insecure + } +} + +impl<'a> From<&'a clap::ArgMatches<'a>> for Overrides { + fn from(matches: &'a clap::ArgMatches) -> Self { + Overrides { + context: matches.value_of("context").map(ToString::to_string), + url: matches.value_of("url").map(ToString::to_string), + tenant: matches.value_of("tenant").map(ToString::to_string), + use_kubernetes: flag_arg("use-kubernetes", matches), + kubernetes_cluster: matches + .value_of("kubernetes-cluster") + .map(ToString::to_string), + kubernetes_context: matches + .value_of("kubernetes-context") + .map(ToString::to_string), + insecure: flag_arg("insecure", matches), + } + } +} diff --git a/hat/src/resource.rs b/hat/src/resource.rs new file mode 100644 index 00000000..3c0cfa61 --- /dev/null +++ b/hat/src/resource.rs @@ -0,0 +1,318 @@ +/******************************************************************************* + * Copyright (c) 2018, 2019 Red Hat Inc + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +use log::info; + +use url; +use url::Url; + +use std::collections::HashMap; + +use http::header::{CONTENT_TYPE, ETAG, IF_MATCH, LOCATION}; +use http::Method; +use http::StatusCode; + +use crate::error::ErrorKind::{MalformedRequest, NotFound, Response, UnexpectedResult}; + +use crate::context::Context; +use crate::error; + +use crate::client::Client; +use crate::output::display_json_value; +use crate::overrides::Overrides; +use futures::executor::block_on; +use serde::de::DeserializeOwned; +use serde::Serialize; + +type Result = std::result::Result; + +pub trait AuthExt +where + Self: Sized, +{ + fn apply_auth(self, context: &Context) -> Result; +} + +impl AuthExt for reqwest::RequestBuilder { + fn apply_auth(self, context: &Context) -> Result { + if context.use_kubernetes() { + // we already got configured, do nothing in addition + Ok(self) + } else if let Some(token) = context.token() { + Ok(self.bearer_auth(token)) + } else if let Some(user) = context.username() { + Ok(self.basic_auth(user, context.password().clone())) + } else { + Ok(self) + } + } +} + +pub trait IfMatch { + fn if_match(self, value: Option<&http::header::HeaderValue>) -> Self; +} + +impl IfMatch for reqwest::RequestBuilder { + fn if_match(self, value: Option<&http::header::HeaderValue>) -> Self { + if let Some(etag) = value { + self.header(IF_MATCH, etag.clone()) + } else { + self + } + } +} + +pub trait Tracer { + fn trace(self) -> Self; +} + +impl Tracer for reqwest::RequestBuilder { + fn trace(self) -> Self { + info!("{:#?}", self); + self + } +} + +impl Tracer for reqwest::Client { + fn trace(self) -> Self { + info!("{:#?}", self); + self + } +} + +impl Tracer for std::result::Result { + fn trace(self) -> Self { + info!("{:#?}", self); + self + } +} + +pub fn resource_url( + context: &Context, + overrides: &Overrides, + resource: &str, + segments: S, +) -> Result +where + S: IntoIterator, + S::Item: AsRef, +{ + resource_url_query(context, overrides, resource, segments, None) +} + +pub fn resource_append_path(url: url::Url, segments: S) -> Result +where + S: IntoIterator, + S::Item: AsRef, +{ + let mut url = url.clone(); + { + let mut path = url + .path_segments_mut() + .map_err(|_| error::ErrorKind::UrlError)?; + + path.extend(segments); + } + + Ok(url) +} + +pub fn resource_url_query( + context: &Context, + overrides: &Overrides, + resource: &str, + segments: S, + query: Option<&HashMap>, +) -> Result +where + S: IntoIterator, + S::Item: AsRef, +{ + let url = context.to_url(overrides)?; + let url = resource_append_path(url, Some(resource))?; + let mut url = resource_append_path(url, segments)?; + + if let Some(q) = query { + let mut query = url.query_pairs_mut(); + for (name, value) in q { + query.append_pair(name, value); + } + } + + Ok(url) +} + +pub async fn resource_delete( + context: &Context, + overrides: &Overrides, + url: &url::Url, + resource_type: &str, + resource_name: &str, +) -> Result<()> { + let client = context.create_client(overrides).await?; + + client + .request(Method::DELETE, url.clone()) + .apply_auth(context)? + .trace() + .send() + .await + .trace() + .map_err(error::Error::from) + .and_then(|response| match response.status() { + StatusCode::NO_CONTENT => Ok(response), + StatusCode::NOT_FOUND => Ok(response), + _ => Err(UnexpectedResult(response.status()).into()), + })?; + + println!("{} deleted: {}", resource_type, resource_name); + + Ok(()) +} + +pub async fn resource_get( + context: &Context, + overrides: &Overrides, + url: &url::Url, + resource_type: &str, +) -> Result<()> { + let client = context.create_client(overrides).await?; + + let result: serde_json::value::Value = client + .request(Method::GET, url.clone()) + .apply_auth(context)? + .trace() + .send() + .await + .trace() + .map_err(error::Error::from) + .and_then(|response| match response.status() { + StatusCode::OK => Ok(response), + StatusCode::NOT_FOUND => Err(NotFound(resource_type.to_string()).into()), + _ => Err(UnexpectedResult(response.status()).into()), + })? + .json() + .await?; + + display_json_value(&result)?; + + Ok(()) +} + +pub async fn resource_modify_with_create( + client: &Client, + context: &Context, + read_url: &Url, + update_url: &Url, + resource_name: &str, + creator: C, + mut modifier: F, +) -> Result +where + F: FnMut(&mut T) -> Result<()>, + C: Fn() -> Result, + T: Serialize + DeserializeOwned + std::fmt::Debug, +{ + // get + + let response = client + .client + .request(Method::GET, read_url.clone()) + .apply_auth(context)? + .trace() + .send() + .await + .trace() + .map_err(error::Error::from)?; + + // retrieve ETag header + let etag = &response.headers().get(ETAG).map(|o| o.clone()); + + let mut payload: T = match response.status() { + StatusCode::OK => response.json().await.map_err(error::Error::from), + StatusCode::NOT_FOUND => creator(), + _ => Err(UnexpectedResult(response.status()).into()), + }?; + + info!("GET Payload: {:#?}", payload); + + // call consumer + + modifier(&mut payload)?; + + info!("PUT Payload: {:#?}", payload); + + // update + + client + .client + .request(Method::PUT, update_url.clone()) + .apply_auth(context)? + .header(CONTENT_TYPE, "application/json") + .if_match(etag.as_ref()) + .json(&payload) + .trace() + .send() + .await + .trace() + .map_err(error::Error::from) + .and_then(|response| match response.status() { + StatusCode::NO_CONTENT => Ok(response), + StatusCode::NOT_FOUND => Err(NotFound(resource_name.into()).into()), + StatusCode::BAD_REQUEST => block_on(resource_err_bad_request(response)), + _ => Err(UnexpectedResult(response.status()).into()), + }) +} + +pub async fn resource_err_bad_request(response: reqwest::Response) -> Result { + Err(MalformedRequest(response.text().await.unwrap_or_else(|_| "".into())).into()) +} + +pub async fn resource_modify( + client: &Client, + context: &Context, + read_url: &Url, + update_url: &Url, + resource_name: &str, + modifier: F, +) -> Result +where + F: FnMut(&mut T) -> Result<()>, + T: Serialize + DeserializeOwned + std::fmt::Debug, +{ + resource_modify_with_create( + client, + context, + read_url, + update_url, + resource_name, + || Err(NotFound(resource_name.into()).into()), + modifier, + ) + .await +} + +pub fn resource_id_from_location(response: reqwest::Response) -> Result { + let loc = response.headers().get(LOCATION); + + if let Some(s) = loc { + let id: String = s.to_str()?.into(); + + let s = id.split('/').last(); + + s.map(|s| s.into()) + .ok_or_else(|| Response(String::from("Missing ID element in 'Location' header")).into()) + } else { + Err(Response(String::from("Missing 'Location' header in response")).into()) + } +} diff --git a/hat/src/tenant.rs b/hat/src/tenant.rs new file mode 100644 index 00000000..25542c1d --- /dev/null +++ b/hat/src/tenant.rs @@ -0,0 +1,249 @@ +/******************************************************************************* + * Copyright (c) 2018 Red Hat Inc + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +use clap::{App, ArgMatches}; + +use crate::context::Context; +use crate::help::help; + +use http::header::*; +use http::method::Method; +use http::status::StatusCode; + +use serde_json::value::*; + +use crate::error; +use crate::error::ErrorKind::*; + +use crate::resource::{ + resource_delete, resource_err_bad_request, resource_get, resource_id_from_location, + resource_modify, resource_url, AuthExt, +}; + +use crate::client::Client; +use crate::overrides::Overrides; +use crate::resource::Tracer; +use futures::executor::block_on; + +type Result = std::result::Result; + +static KEY_ENABLED: &str = "enabled"; +static RESOURCE_NAME: &str = "devices"; + +pub async fn tenant( + app: &mut App<'_, '_>, + matches: &ArgMatches<'_>, + overrides: &Overrides, + context: &Context, +) -> Result<()> { + let client = Client::new(context, overrides).await?; + + match matches.subcommand() { + ("create", Some(cmd_matches)) => { + tenant_create( + context, + overrides, + cmd_matches.value_of("tenant_name"), + cmd_matches.value_of("payload"), + ) + .await? + } + ("update", Some(cmd_matches)) => { + tenant_update( + context, + overrides, + cmd_matches.value_of("tenant_name").unwrap(), + cmd_matches.value_of("payload"), + ) + .await? + } + ("get", Some(cmd_matches)) => { + tenant_get( + context, + overrides, + cmd_matches.value_of("tenant_name").unwrap(), + ) + .await? + } + ("delete", Some(cmd_matches)) => { + tenant_delete( + context, + overrides, + cmd_matches.value_of("tenant_name").unwrap(), + ) + .await? + } + ("enable", Some(cmd_matches)) => { + tenant_enable( + &client, + context, + overrides, + cmd_matches.value_of("tenant_name").unwrap(), + ) + .await? + } + ("disable", Some(cmd_matches)) => { + tenant_disable( + &client, + context, + overrides, + cmd_matches.value_of("tenant_name").unwrap(), + ) + .await? + } + _ => help(app)?, + }; + + Ok(()) +} + +async fn tenant_create( + context: &Context, + overrides: &Overrides, + tenant: Option<&str>, + payload: Option<&str>, +) -> Result<()> { + let url = resource_url(context, overrides, RESOURCE_NAME, tenant)?; + + let payload = match payload { + Some(_) => serde_json::from_str(payload.unwrap())?, + _ => serde_json::value::Map::new(), + }; + + let client = context.create_client(overrides).await?; + + let tenant = client + .request(Method::POST, url) + .apply_auth(context)? + .header(CONTENT_TYPE, "application/json") + .json(&payload) + .trace() + .send() + .await + .trace() + .map_err(error::Error::from) + .and_then(|response| match response.status() { + StatusCode::CREATED => Ok(response), + StatusCode::CONFLICT => Err(AlreadyExists(tenant.unwrap().to_string()).into()), + StatusCode::BAD_REQUEST => block_on(resource_err_bad_request(response)), + _ => Err(UnexpectedResult(response.status()).into()), + }) + .and_then(resource_id_from_location)?; + + println!("Created tenant: {}", tenant); + + Ok(()) +} + +async fn tenant_update( + context: &Context, + overrides: &Overrides, + tenant: &str, + payload: Option<&str>, +) -> Result<()> { + let url = resource_url(context, overrides, RESOURCE_NAME, Some(tenant))?; + + let mut payload = match payload { + Some(_) => serde_json::from_str(payload.unwrap())?, + _ => serde_json::value::Map::new(), + }; + + payload.insert( + "tenant-id".to_string(), + serde_json::value::to_value(tenant)?, + ); + + let client = context.create_client(overrides).await?; + + client + .request(Method::PUT, url) + .apply_auth(context)? + .header(CONTENT_TYPE, "application/json") + .json(&payload) + .trace() + .send() + .await + .map_err(error::Error::from) + .and_then(|response| match response.status() { + StatusCode::NO_CONTENT => Ok(response), + StatusCode::NOT_FOUND => Err(NotFound(tenant.to_string()).into()), + StatusCode::BAD_REQUEST => block_on(resource_err_bad_request(response)), + _ => Err(UnexpectedResult(response.status()).into()), + })?; + + println!("Updated tenant: {}", tenant); + + Ok(()) +} + +async fn tenant_delete(context: &Context, overrides: &Overrides, tenant: &str) -> Result<()> { + let url = resource_url(context, overrides, RESOURCE_NAME, Some(tenant))?; + resource_delete(&context, overrides, &url, "Tenant", tenant).await +} + +async fn tenant_enable( + client: &Client, + context: &Context, + overrides: &Overrides, + tenant: &str, +) -> Result<()> { + let url = resource_url(context, overrides, RESOURCE_NAME, Some(tenant))?; + + resource_modify( + client, + &context, + &url, + &url, + tenant, + |payload: &mut Map| { + payload.insert(KEY_ENABLED.into(), Value::Bool(true)); + Ok(()) + }, + ) + .await?; + + println!("Tenant {} enabled", tenant); + + Ok(()) +} + +async fn tenant_disable( + client: &Client, + context: &Context, + overrides: &Overrides, + tenant: &str, +) -> Result<()> { + let url = resource_url(context, overrides, RESOURCE_NAME, Some(tenant))?; + + resource_modify( + client, + &context, + &url, + &url, + tenant, + |payload: &mut Map| { + payload.insert(KEY_ENABLED.into(), Value::Bool(false)); + Ok(()) + }, + ) + .await?; + + println!("Tenant {} disabled", tenant); + + Ok(()) +} + +async fn tenant_get(context: &Context, overrides: &Overrides, tenant: &str) -> Result<()> { + let url = resource_url(context, overrides, RESOURCE_NAME, Some(tenant))?; + resource_get(&context, overrides, &url, "Tenant").await +} diff --git a/hat/src/utils.rs b/hat/src/utils.rs new file mode 100644 index 00000000..315a7846 --- /dev/null +++ b/hat/src/utils.rs @@ -0,0 +1,28 @@ +/******************************************************************************* + * Copyright (c) 2018 Red Hat Inc + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +pub trait Either { + fn either(&self, this: T, that: T) -> T; +} + +impl Either for bool { + /// transforms the bool in a value of either the first (when true) or the second (when false) + /// parameter. + fn either(&self, when_true: T, when_false: T) -> T { + if *self { + when_true + } else { + when_false + } + } +}