├── .dockerignore ├── .github └── workflows │ └── test.yaml ├── .gitignore ├── .idea ├── .gitignore ├── modules.xml ├── qmlSettings.xml ├── tolerable.iml └── vcs.xml ├── Cargo.lock ├── Cargo.toml ├── Dockerfile ├── LICENSE.md ├── README.md ├── build.rs ├── docker └── tolerable.toml ├── justfile ├── kustomize ├── certificate.webhook.yaml ├── deployment.tolerable.yaml ├── deployment.ubuntu-test.yaml ├── issuer.selfsigned.yaml ├── kustomization.yaml ├── mutatingwebhook.yaml ├── ns.tolerable.yaml ├── service.tolerable.yaml ├── svcacct.tolerable.yaml └── tolerable.toml ├── src ├── consts.rs ├── main.rs ├── manifest.rs ├── metrics.rs ├── models.rs ├── mutation.rs └── tests │ ├── admission-review-not-pod.json │ ├── admission-review-pod-half-arm.json │ ├── admission-review-pod-match.json │ ├── admission-review-pod.json │ ├── mod.rs │ ├── test_bl.rs │ └── test_serde.rs ├── tolerable.toml └── tools └── target_arch.sh /.dockerignore: -------------------------------------------------------------------------------- 1 | target/**/deps 2 | target/**/build 3 | target/debug 4 | target/**/.fingerprint 5 | -------------------------------------------------------------------------------- /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | env: 2 | RUST_LOG: debug 3 | TOLERABLE_SUPPORTED_ARCHITECTURES: "arm64" 4 | on: 5 | push: {} 6 | pull_request: 7 | branches: [ "main" ] 8 | name: Run Tests 9 | jobs: 10 | test: 11 | name: tolerable 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | - uses: actions-rs/toolchain@v1 16 | with: 17 | toolchain: stable 18 | - uses: timheuer/base64-to-file@v1.2 19 | with: 20 | fileName: 'docker.io.toml' 21 | fileDir: './creds/' 22 | encodedString: ${{ secrets.DOCKER_IO_CREDS }} 23 | - uses: actions-rs/cargo@v1 24 | with: 25 | command: test 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /cfssl 3 | /creds 4 | .secrets 5 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/qmlSettings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13 | 14 | -------------------------------------------------------------------------------- /.idea/tolerable.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "actix-codec" 7 | version = "0.5.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "57a7559404a7f3573127aab53c08ce37a6c6a315c374a31070f3c91cd1b4a7fe" 10 | dependencies = [ 11 | "bitflags", 12 | "bytes", 13 | "futures-core", 14 | "futures-sink", 15 | "log", 16 | "memchr", 17 | "pin-project-lite", 18 | "tokio", 19 | "tokio-util", 20 | ] 21 | 22 | [[package]] 23 | name = "actix-http" 24 | version = "3.3.1" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | checksum = "c2079246596c18b4a33e274ae10c0e50613f4d32a4198e09c7b93771013fed74" 27 | dependencies = [ 28 | "actix-codec", 29 | "actix-rt", 30 | "actix-service", 31 | "actix-tls", 32 | "actix-utils", 33 | "ahash 0.8.3", 34 | "base64 0.21.0", 35 | "bitflags", 36 | "brotli", 37 | "bytes", 38 | "bytestring", 39 | "derive_more", 40 | "encoding_rs", 41 | "flate2", 42 | "futures-core", 43 | "h2", 44 | "http", 45 | "httparse", 46 | "httpdate", 47 | "itoa", 48 | "language-tags", 49 | "local-channel", 50 | "mime", 51 | "percent-encoding", 52 | "pin-project-lite", 53 | "rand", 54 | "sha1", 55 | "smallvec", 56 | "tokio", 57 | "tokio-util", 58 | "tracing", 59 | "zstd", 60 | ] 61 | 62 | [[package]] 63 | name = "actix-macros" 64 | version = "0.2.3" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | checksum = "465a6172cf69b960917811022d8f29bc0b7fa1398bc4f78b3c466673db1213b6" 67 | dependencies = [ 68 | "quote", 69 | "syn", 70 | ] 71 | 72 | [[package]] 73 | name = "actix-router" 74 | version = "0.5.1" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | checksum = "d66ff4d247d2b160861fa2866457e85706833527840e4133f8f49aa423a38799" 77 | dependencies = [ 78 | "bytestring", 79 | "http", 80 | "regex", 81 | "serde", 82 | "tracing", 83 | ] 84 | 85 | [[package]] 86 | name = "actix-rt" 87 | version = "2.8.0" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | checksum = "15265b6b8e2347670eb363c47fc8c75208b4a4994b27192f345fcbe707804f3e" 90 | dependencies = [ 91 | "futures-core", 92 | "tokio", 93 | ] 94 | 95 | [[package]] 96 | name = "actix-server" 97 | version = "2.2.0" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | checksum = "3e8613a75dd50cc45f473cee3c34d59ed677c0f7b44480ce3b8247d7dc519327" 100 | dependencies = [ 101 | "actix-rt", 102 | "actix-service", 103 | "actix-utils", 104 | "futures-core", 105 | "futures-util", 106 | "mio", 107 | "num_cpus", 108 | "socket2", 109 | "tokio", 110 | "tracing", 111 | ] 112 | 113 | [[package]] 114 | name = "actix-service" 115 | version = "2.0.2" 116 | source = "registry+https://github.com/rust-lang/crates.io-index" 117 | checksum = "3b894941f818cfdc7ccc4b9e60fa7e53b5042a2e8567270f9147d5591893373a" 118 | dependencies = [ 119 | "futures-core", 120 | "paste", 121 | "pin-project-lite", 122 | ] 123 | 124 | [[package]] 125 | name = "actix-tls" 126 | version = "3.0.3" 127 | source = "registry+https://github.com/rust-lang/crates.io-index" 128 | checksum = "9fde0cf292f7cdc7f070803cb9a0d45c018441321a78b1042ffbbb81ec333297" 129 | dependencies = [ 130 | "actix-codec", 131 | "actix-rt", 132 | "actix-service", 133 | "actix-utils", 134 | "futures-core", 135 | "http", 136 | "log", 137 | "pin-project-lite", 138 | "tokio-rustls", 139 | "tokio-util", 140 | "webpki-roots", 141 | ] 142 | 143 | [[package]] 144 | name = "actix-utils" 145 | version = "3.0.1" 146 | source = "registry+https://github.com/rust-lang/crates.io-index" 147 | checksum = "88a1dcdff1466e3c2488e1cb5c36a71822750ad43839937f85d2f4d9f8b705d8" 148 | dependencies = [ 149 | "local-waker", 150 | "pin-project-lite", 151 | ] 152 | 153 | [[package]] 154 | name = "actix-web" 155 | version = "4.3.1" 156 | source = "registry+https://github.com/rust-lang/crates.io-index" 157 | checksum = "cd3cb42f9566ab176e1ef0b8b3a896529062b4efc6be0123046095914c4c1c96" 158 | dependencies = [ 159 | "actix-codec", 160 | "actix-http", 161 | "actix-macros", 162 | "actix-router", 163 | "actix-rt", 164 | "actix-server", 165 | "actix-service", 166 | "actix-tls", 167 | "actix-utils", 168 | "actix-web-codegen", 169 | "ahash 0.7.6", 170 | "bytes", 171 | "bytestring", 172 | "cfg-if", 173 | "cookie", 174 | "derive_more", 175 | "encoding_rs", 176 | "futures-core", 177 | "futures-util", 178 | "http", 179 | "itoa", 180 | "language-tags", 181 | "log", 182 | "mime", 183 | "once_cell", 184 | "pin-project-lite", 185 | "regex", 186 | "serde", 187 | "serde_json", 188 | "serde_urlencoded", 189 | "smallvec", 190 | "socket2", 191 | "time", 192 | "url", 193 | ] 194 | 195 | [[package]] 196 | name = "actix-web-codegen" 197 | version = "4.2.0" 198 | source = "registry+https://github.com/rust-lang/crates.io-index" 199 | checksum = "2262160a7ae29e3415554a3f1fc04c764b1540c116aa524683208078b7a75bc9" 200 | dependencies = [ 201 | "actix-router", 202 | "proc-macro2", 203 | "quote", 204 | "syn", 205 | ] 206 | 207 | [[package]] 208 | name = "actix-web-prom" 209 | version = "0.6.0" 210 | source = "registry+https://github.com/rust-lang/crates.io-index" 211 | checksum = "9df3127d20a5d01c9fc9aceb969a38d31a6767e1b48a54d55a8f56c769a84923" 212 | dependencies = [ 213 | "actix-web", 214 | "futures-core", 215 | "pin-project-lite", 216 | "prometheus", 217 | ] 218 | 219 | [[package]] 220 | name = "adler" 221 | version = "1.0.2" 222 | source = "registry+https://github.com/rust-lang/crates.io-index" 223 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 224 | 225 | [[package]] 226 | name = "ahash" 227 | version = "0.7.6" 228 | source = "registry+https://github.com/rust-lang/crates.io-index" 229 | checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" 230 | dependencies = [ 231 | "getrandom", 232 | "once_cell", 233 | "version_check", 234 | ] 235 | 236 | [[package]] 237 | name = "ahash" 238 | version = "0.8.3" 239 | source = "registry+https://github.com/rust-lang/crates.io-index" 240 | checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" 241 | dependencies = [ 242 | "cfg-if", 243 | "getrandom", 244 | "once_cell", 245 | "version_check", 246 | ] 247 | 248 | [[package]] 249 | name = "aho-corasick" 250 | version = "0.7.20" 251 | source = "registry+https://github.com/rust-lang/crates.io-index" 252 | checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" 253 | dependencies = [ 254 | "memchr", 255 | ] 256 | 257 | [[package]] 258 | name = "alloc-no-stdlib" 259 | version = "2.0.4" 260 | source = "registry+https://github.com/rust-lang/crates.io-index" 261 | checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" 262 | 263 | [[package]] 264 | name = "alloc-stdlib" 265 | version = "0.2.2" 266 | source = "registry+https://github.com/rust-lang/crates.io-index" 267 | checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" 268 | dependencies = [ 269 | "alloc-no-stdlib", 270 | ] 271 | 272 | [[package]] 273 | name = "anyhow" 274 | version = "1.0.69" 275 | source = "registry+https://github.com/rust-lang/crates.io-index" 276 | checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" 277 | 278 | [[package]] 279 | name = "array_tool" 280 | version = "1.0.3" 281 | source = "registry+https://github.com/rust-lang/crates.io-index" 282 | checksum = "8f8cb5d814eb646a863c4f24978cff2880c4be96ad8cde2c0f0678732902e271" 283 | 284 | [[package]] 285 | name = "async-trait" 286 | version = "0.1.66" 287 | source = "registry+https://github.com/rust-lang/crates.io-index" 288 | checksum = "b84f9ebcc6c1f5b8cb160f6990096a5c127f423fcb6e1ccc46c370cbdfb75dfc" 289 | dependencies = [ 290 | "proc-macro2", 291 | "quote", 292 | "syn", 293 | ] 294 | 295 | [[package]] 296 | name = "async_once" 297 | version = "0.2.6" 298 | source = "registry+https://github.com/rust-lang/crates.io-index" 299 | checksum = "2ce4f10ea3abcd6617873bae9f91d1c5332b4a778bd9ce34d0cd517474c1de82" 300 | 301 | [[package]] 302 | name = "atty" 303 | version = "0.2.14" 304 | source = "registry+https://github.com/rust-lang/crates.io-index" 305 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 306 | dependencies = [ 307 | "hermit-abi 0.1.19", 308 | "libc", 309 | "winapi", 310 | ] 311 | 312 | [[package]] 313 | name = "autocfg" 314 | version = "1.1.0" 315 | source = "registry+https://github.com/rust-lang/crates.io-index" 316 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 317 | 318 | [[package]] 319 | name = "awc" 320 | version = "3.1.1" 321 | source = "registry+https://github.com/rust-lang/crates.io-index" 322 | checksum = "87ef547a81796eb2dfe9b345aba34c2e08391a0502493711395b36dd64052b69" 323 | dependencies = [ 324 | "actix-codec", 325 | "actix-http", 326 | "actix-rt", 327 | "actix-service", 328 | "actix-tls", 329 | "actix-utils", 330 | "ahash 0.7.6", 331 | "base64 0.21.0", 332 | "bytes", 333 | "cfg-if", 334 | "cookie", 335 | "derive_more", 336 | "futures-core", 337 | "futures-util", 338 | "h2", 339 | "http", 340 | "itoa", 341 | "log", 342 | "mime", 343 | "percent-encoding", 344 | "pin-project-lite", 345 | "rand", 346 | "rustls", 347 | "serde", 348 | "serde_json", 349 | "serde_urlencoded", 350 | "tokio", 351 | ] 352 | 353 | [[package]] 354 | name = "base64" 355 | version = "0.13.1" 356 | source = "registry+https://github.com/rust-lang/crates.io-index" 357 | checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" 358 | 359 | [[package]] 360 | name = "base64" 361 | version = "0.21.0" 362 | source = "registry+https://github.com/rust-lang/crates.io-index" 363 | checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" 364 | 365 | [[package]] 366 | name = "bitflags" 367 | version = "1.3.2" 368 | source = "registry+https://github.com/rust-lang/crates.io-index" 369 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 370 | 371 | [[package]] 372 | name = "block-buffer" 373 | version = "0.10.4" 374 | source = "registry+https://github.com/rust-lang/crates.io-index" 375 | checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" 376 | dependencies = [ 377 | "generic-array", 378 | ] 379 | 380 | [[package]] 381 | name = "brotli" 382 | version = "3.3.4" 383 | source = "registry+https://github.com/rust-lang/crates.io-index" 384 | checksum = "a1a0b1dbcc8ae29329621f8d4f0d835787c1c38bb1401979b49d13b0b305ff68" 385 | dependencies = [ 386 | "alloc-no-stdlib", 387 | "alloc-stdlib", 388 | "brotli-decompressor", 389 | ] 390 | 391 | [[package]] 392 | name = "brotli-decompressor" 393 | version = "2.3.4" 394 | source = "registry+https://github.com/rust-lang/crates.io-index" 395 | checksum = "4b6561fd3f895a11e8f72af2cb7d22e08366bebc2b6b57f7744c4bda27034744" 396 | dependencies = [ 397 | "alloc-no-stdlib", 398 | "alloc-stdlib", 399 | ] 400 | 401 | [[package]] 402 | name = "bumpalo" 403 | version = "3.12.0" 404 | source = "registry+https://github.com/rust-lang/crates.io-index" 405 | checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" 406 | 407 | [[package]] 408 | name = "bytes" 409 | version = "1.4.0" 410 | source = "registry+https://github.com/rust-lang/crates.io-index" 411 | checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" 412 | 413 | [[package]] 414 | name = "bytestring" 415 | version = "1.3.0" 416 | source = "registry+https://github.com/rust-lang/crates.io-index" 417 | checksum = "238e4886760d98c4f899360c834fa93e62cf7f721ac3c2da375cbdf4b8679aae" 418 | dependencies = [ 419 | "bytes", 420 | ] 421 | 422 | [[package]] 423 | name = "cached" 424 | version = "0.42.0" 425 | source = "registry+https://github.com/rust-lang/crates.io-index" 426 | checksum = "5e5877db5d1af7fae60d06b5db9430b68056a69b3582a0be8e3691e87654aeb6" 427 | dependencies = [ 428 | "async-trait", 429 | "async_once", 430 | "cached_proc_macro", 431 | "cached_proc_macro_types", 432 | "futures", 433 | "hashbrown 0.13.2", 434 | "instant", 435 | "lazy_static", 436 | "once_cell", 437 | "thiserror", 438 | "tokio", 439 | ] 440 | 441 | [[package]] 442 | name = "cached_proc_macro" 443 | version = "0.16.0" 444 | source = "registry+https://github.com/rust-lang/crates.io-index" 445 | checksum = "e10ca87c81aaa3a949dbbe2b5e6c2c45dbc94ba4897e45ea31ff9ec5087be3dc" 446 | dependencies = [ 447 | "cached_proc_macro_types", 448 | "darling", 449 | "proc-macro2", 450 | "quote", 451 | "syn", 452 | ] 453 | 454 | [[package]] 455 | name = "cached_proc_macro_types" 456 | version = "0.1.0" 457 | source = "registry+https://github.com/rust-lang/crates.io-index" 458 | checksum = "3a4f925191b4367301851c6d99b09890311d74b0d43f274c0b34c86d308a3663" 459 | 460 | [[package]] 461 | name = "cc" 462 | version = "1.0.79" 463 | source = "registry+https://github.com/rust-lang/crates.io-index" 464 | checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" 465 | dependencies = [ 466 | "jobserver", 467 | ] 468 | 469 | [[package]] 470 | name = "cfg-if" 471 | version = "1.0.0" 472 | source = "registry+https://github.com/rust-lang/crates.io-index" 473 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 474 | 475 | [[package]] 476 | name = "config" 477 | version = "0.13.3" 478 | source = "registry+https://github.com/rust-lang/crates.io-index" 479 | checksum = "d379af7f68bfc21714c6c7dea883544201741d2ce8274bb12fa54f89507f52a7" 480 | dependencies = [ 481 | "async-trait", 482 | "json5", 483 | "lazy_static", 484 | "nom", 485 | "pathdiff", 486 | "ron", 487 | "rust-ini", 488 | "serde", 489 | "serde_json", 490 | "toml", 491 | "yaml-rust", 492 | ] 493 | 494 | [[package]] 495 | name = "const_format" 496 | version = "0.2.30" 497 | source = "registry+https://github.com/rust-lang/crates.io-index" 498 | checksum = "7309d9b4d3d2c0641e018d449232f2e28f1b22933c137f157d3dbc14228b8c0e" 499 | dependencies = [ 500 | "const_format_proc_macros", 501 | ] 502 | 503 | [[package]] 504 | name = "const_format_proc_macros" 505 | version = "0.2.29" 506 | source = "registry+https://github.com/rust-lang/crates.io-index" 507 | checksum = "d897f47bf7270cf70d370f8f98c1abb6d2d4cf60a6845d30e05bfb90c6568650" 508 | dependencies = [ 509 | "proc-macro2", 510 | "quote", 511 | "unicode-xid", 512 | ] 513 | 514 | [[package]] 515 | name = "convert_case" 516 | version = "0.4.0" 517 | source = "registry+https://github.com/rust-lang/crates.io-index" 518 | checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" 519 | 520 | [[package]] 521 | name = "cookie" 522 | version = "0.16.2" 523 | source = "registry+https://github.com/rust-lang/crates.io-index" 524 | checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" 525 | dependencies = [ 526 | "percent-encoding", 527 | "time", 528 | "version_check", 529 | ] 530 | 531 | [[package]] 532 | name = "cpufeatures" 533 | version = "0.2.5" 534 | source = "registry+https://github.com/rust-lang/crates.io-index" 535 | checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" 536 | dependencies = [ 537 | "libc", 538 | ] 539 | 540 | [[package]] 541 | name = "crc32fast" 542 | version = "1.3.2" 543 | source = "registry+https://github.com/rust-lang/crates.io-index" 544 | checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" 545 | dependencies = [ 546 | "cfg-if", 547 | ] 548 | 549 | [[package]] 550 | name = "crypto-common" 551 | version = "0.1.6" 552 | source = "registry+https://github.com/rust-lang/crates.io-index" 553 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" 554 | dependencies = [ 555 | "generic-array", 556 | "typenum", 557 | ] 558 | 559 | [[package]] 560 | name = "ctor" 561 | version = "0.1.26" 562 | source = "registry+https://github.com/rust-lang/crates.io-index" 563 | checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" 564 | dependencies = [ 565 | "quote", 566 | "syn", 567 | ] 568 | 569 | [[package]] 570 | name = "darling" 571 | version = "0.14.3" 572 | source = "registry+https://github.com/rust-lang/crates.io-index" 573 | checksum = "c0808e1bd8671fb44a113a14e13497557533369847788fa2ae912b6ebfce9fa8" 574 | dependencies = [ 575 | "darling_core", 576 | "darling_macro", 577 | ] 578 | 579 | [[package]] 580 | name = "darling_core" 581 | version = "0.14.3" 582 | source = "registry+https://github.com/rust-lang/crates.io-index" 583 | checksum = "001d80444f28e193f30c2f293455da62dcf9a6b29918a4253152ae2b1de592cb" 584 | dependencies = [ 585 | "fnv", 586 | "ident_case", 587 | "proc-macro2", 588 | "quote", 589 | "strsim", 590 | "syn", 591 | ] 592 | 593 | [[package]] 594 | name = "darling_macro" 595 | version = "0.14.3" 596 | source = "registry+https://github.com/rust-lang/crates.io-index" 597 | checksum = "b36230598a2d5de7ec1c6f51f72d8a99a9208daff41de2084d06e3fd3ea56685" 598 | dependencies = [ 599 | "darling_core", 600 | "quote", 601 | "syn", 602 | ] 603 | 604 | [[package]] 605 | name = "derive_more" 606 | version = "0.99.17" 607 | source = "registry+https://github.com/rust-lang/crates.io-index" 608 | checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" 609 | dependencies = [ 610 | "convert_case", 611 | "proc-macro2", 612 | "quote", 613 | "rustc_version", 614 | "syn", 615 | ] 616 | 617 | [[package]] 618 | name = "digest" 619 | version = "0.10.6" 620 | source = "registry+https://github.com/rust-lang/crates.io-index" 621 | checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" 622 | dependencies = [ 623 | "block-buffer", 624 | "crypto-common", 625 | ] 626 | 627 | [[package]] 628 | name = "dlv-list" 629 | version = "0.3.0" 630 | source = "registry+https://github.com/rust-lang/crates.io-index" 631 | checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257" 632 | 633 | [[package]] 634 | name = "docker-image-reference" 635 | version = "0.1.0" 636 | source = "git+https://github.com/PeterGrace/docker-image-reference.git#d2f97edef269b20548664cf123057b90f0557d2e" 637 | dependencies = [ 638 | "anyhow", 639 | "const_format", 640 | "lazy_static", 641 | "log", 642 | "regex", 643 | ] 644 | 645 | [[package]] 646 | name = "encoding_rs" 647 | version = "0.8.32" 648 | source = "registry+https://github.com/rust-lang/crates.io-index" 649 | checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" 650 | dependencies = [ 651 | "cfg-if", 652 | ] 653 | 654 | [[package]] 655 | name = "env_logger" 656 | version = "0.7.1" 657 | source = "registry+https://github.com/rust-lang/crates.io-index" 658 | checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" 659 | dependencies = [ 660 | "atty", 661 | "humantime", 662 | "log", 663 | "regex", 664 | "termcolor", 665 | ] 666 | 667 | [[package]] 668 | name = "flate2" 669 | version = "1.0.25" 670 | source = "registry+https://github.com/rust-lang/crates.io-index" 671 | checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" 672 | dependencies = [ 673 | "crc32fast", 674 | "miniz_oxide", 675 | ] 676 | 677 | [[package]] 678 | name = "fnv" 679 | version = "1.0.7" 680 | source = "registry+https://github.com/rust-lang/crates.io-index" 681 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 682 | 683 | [[package]] 684 | name = "form_urlencoded" 685 | version = "1.1.0" 686 | source = "registry+https://github.com/rust-lang/crates.io-index" 687 | checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" 688 | dependencies = [ 689 | "percent-encoding", 690 | ] 691 | 692 | [[package]] 693 | name = "futures" 694 | version = "0.3.26" 695 | source = "registry+https://github.com/rust-lang/crates.io-index" 696 | checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84" 697 | dependencies = [ 698 | "futures-channel", 699 | "futures-core", 700 | "futures-io", 701 | "futures-sink", 702 | "futures-task", 703 | "futures-util", 704 | ] 705 | 706 | [[package]] 707 | name = "futures-channel" 708 | version = "0.3.26" 709 | source = "registry+https://github.com/rust-lang/crates.io-index" 710 | checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" 711 | dependencies = [ 712 | "futures-core", 713 | "futures-sink", 714 | ] 715 | 716 | [[package]] 717 | name = "futures-core" 718 | version = "0.3.26" 719 | source = "registry+https://github.com/rust-lang/crates.io-index" 720 | checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" 721 | 722 | [[package]] 723 | name = "futures-io" 724 | version = "0.3.26" 725 | source = "registry+https://github.com/rust-lang/crates.io-index" 726 | checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" 727 | 728 | [[package]] 729 | name = "futures-sink" 730 | version = "0.3.26" 731 | source = "registry+https://github.com/rust-lang/crates.io-index" 732 | checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" 733 | 734 | [[package]] 735 | name = "futures-task" 736 | version = "0.3.26" 737 | source = "registry+https://github.com/rust-lang/crates.io-index" 738 | checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" 739 | 740 | [[package]] 741 | name = "futures-util" 742 | version = "0.3.26" 743 | source = "registry+https://github.com/rust-lang/crates.io-index" 744 | checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" 745 | dependencies = [ 746 | "futures-core", 747 | "futures-sink", 748 | "futures-task", 749 | "pin-project-lite", 750 | "pin-utils", 751 | ] 752 | 753 | [[package]] 754 | name = "generic-array" 755 | version = "0.14.6" 756 | source = "registry+https://github.com/rust-lang/crates.io-index" 757 | checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" 758 | dependencies = [ 759 | "typenum", 760 | "version_check", 761 | ] 762 | 763 | [[package]] 764 | name = "getrandom" 765 | version = "0.2.8" 766 | source = "registry+https://github.com/rust-lang/crates.io-index" 767 | checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" 768 | dependencies = [ 769 | "cfg-if", 770 | "libc", 771 | "wasi", 772 | ] 773 | 774 | [[package]] 775 | name = "h2" 776 | version = "0.3.16" 777 | source = "registry+https://github.com/rust-lang/crates.io-index" 778 | checksum = "5be7b54589b581f624f566bf5d8eb2bab1db736c51528720b6bd36b96b55924d" 779 | dependencies = [ 780 | "bytes", 781 | "fnv", 782 | "futures-core", 783 | "futures-sink", 784 | "futures-util", 785 | "http", 786 | "indexmap", 787 | "slab", 788 | "tokio", 789 | "tokio-util", 790 | "tracing", 791 | ] 792 | 793 | [[package]] 794 | name = "hashbrown" 795 | version = "0.12.3" 796 | source = "registry+https://github.com/rust-lang/crates.io-index" 797 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 798 | dependencies = [ 799 | "ahash 0.7.6", 800 | ] 801 | 802 | [[package]] 803 | name = "hashbrown" 804 | version = "0.13.2" 805 | source = "registry+https://github.com/rust-lang/crates.io-index" 806 | checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" 807 | 808 | [[package]] 809 | name = "heck" 810 | version = "0.4.1" 811 | source = "registry+https://github.com/rust-lang/crates.io-index" 812 | checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" 813 | 814 | [[package]] 815 | name = "hermit-abi" 816 | version = "0.1.19" 817 | source = "registry+https://github.com/rust-lang/crates.io-index" 818 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 819 | dependencies = [ 820 | "libc", 821 | ] 822 | 823 | [[package]] 824 | name = "hermit-abi" 825 | version = "0.2.6" 826 | source = "registry+https://github.com/rust-lang/crates.io-index" 827 | checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" 828 | dependencies = [ 829 | "libc", 830 | ] 831 | 832 | [[package]] 833 | name = "http" 834 | version = "0.2.9" 835 | source = "registry+https://github.com/rust-lang/crates.io-index" 836 | checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" 837 | dependencies = [ 838 | "bytes", 839 | "fnv", 840 | "itoa", 841 | ] 842 | 843 | [[package]] 844 | name = "httparse" 845 | version = "1.8.0" 846 | source = "registry+https://github.com/rust-lang/crates.io-index" 847 | checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" 848 | 849 | [[package]] 850 | name = "httpdate" 851 | version = "1.0.2" 852 | source = "registry+https://github.com/rust-lang/crates.io-index" 853 | checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" 854 | 855 | [[package]] 856 | name = "humantime" 857 | version = "1.3.0" 858 | source = "registry+https://github.com/rust-lang/crates.io-index" 859 | checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" 860 | dependencies = [ 861 | "quick-error", 862 | ] 863 | 864 | [[package]] 865 | name = "ident_case" 866 | version = "1.0.1" 867 | source = "registry+https://github.com/rust-lang/crates.io-index" 868 | checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" 869 | 870 | [[package]] 871 | name = "idna" 872 | version = "0.3.0" 873 | source = "registry+https://github.com/rust-lang/crates.io-index" 874 | checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" 875 | dependencies = [ 876 | "unicode-bidi", 877 | "unicode-normalization", 878 | ] 879 | 880 | [[package]] 881 | name = "indexmap" 882 | version = "1.9.2" 883 | source = "registry+https://github.com/rust-lang/crates.io-index" 884 | checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" 885 | dependencies = [ 886 | "autocfg", 887 | "hashbrown 0.12.3", 888 | ] 889 | 890 | [[package]] 891 | name = "instant" 892 | version = "0.1.12" 893 | source = "registry+https://github.com/rust-lang/crates.io-index" 894 | checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" 895 | dependencies = [ 896 | "cfg-if", 897 | ] 898 | 899 | [[package]] 900 | name = "itoa" 901 | version = "1.0.6" 902 | source = "registry+https://github.com/rust-lang/crates.io-index" 903 | checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" 904 | 905 | [[package]] 906 | name = "jobserver" 907 | version = "0.1.26" 908 | source = "registry+https://github.com/rust-lang/crates.io-index" 909 | checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" 910 | dependencies = [ 911 | "libc", 912 | ] 913 | 914 | [[package]] 915 | name = "js-sys" 916 | version = "0.3.61" 917 | source = "registry+https://github.com/rust-lang/crates.io-index" 918 | checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" 919 | dependencies = [ 920 | "wasm-bindgen", 921 | ] 922 | 923 | [[package]] 924 | name = "json5" 925 | version = "0.4.1" 926 | source = "registry+https://github.com/rust-lang/crates.io-index" 927 | checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1" 928 | dependencies = [ 929 | "pest", 930 | "pest_derive", 931 | "serde", 932 | ] 933 | 934 | [[package]] 935 | name = "language-tags" 936 | version = "0.3.2" 937 | source = "registry+https://github.com/rust-lang/crates.io-index" 938 | checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" 939 | 940 | [[package]] 941 | name = "lazy_static" 942 | version = "1.4.0" 943 | source = "registry+https://github.com/rust-lang/crates.io-index" 944 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 945 | 946 | [[package]] 947 | name = "libc" 948 | version = "0.2.139" 949 | source = "registry+https://github.com/rust-lang/crates.io-index" 950 | checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" 951 | 952 | [[package]] 953 | name = "linked-hash-map" 954 | version = "0.5.6" 955 | source = "registry+https://github.com/rust-lang/crates.io-index" 956 | checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" 957 | 958 | [[package]] 959 | name = "local-channel" 960 | version = "0.1.3" 961 | source = "registry+https://github.com/rust-lang/crates.io-index" 962 | checksum = "7f303ec0e94c6c54447f84f3b0ef7af769858a9c4ef56ef2a986d3dcd4c3fc9c" 963 | dependencies = [ 964 | "futures-core", 965 | "futures-sink", 966 | "futures-util", 967 | "local-waker", 968 | ] 969 | 970 | [[package]] 971 | name = "local-waker" 972 | version = "0.1.3" 973 | source = "registry+https://github.com/rust-lang/crates.io-index" 974 | checksum = "e34f76eb3611940e0e7d53a9aaa4e6a3151f69541a282fd0dad5571420c53ff1" 975 | 976 | [[package]] 977 | name = "lock_api" 978 | version = "0.4.9" 979 | source = "registry+https://github.com/rust-lang/crates.io-index" 980 | checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" 981 | dependencies = [ 982 | "autocfg", 983 | "scopeguard", 984 | ] 985 | 986 | [[package]] 987 | name = "log" 988 | version = "0.4.17" 989 | source = "registry+https://github.com/rust-lang/crates.io-index" 990 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 991 | dependencies = [ 992 | "cfg-if", 993 | ] 994 | 995 | [[package]] 996 | name = "memchr" 997 | version = "2.5.0" 998 | source = "registry+https://github.com/rust-lang/crates.io-index" 999 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 1000 | 1001 | [[package]] 1002 | name = "mime" 1003 | version = "0.3.16" 1004 | source = "registry+https://github.com/rust-lang/crates.io-index" 1005 | checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" 1006 | 1007 | [[package]] 1008 | name = "minimal-lexical" 1009 | version = "0.2.1" 1010 | source = "registry+https://github.com/rust-lang/crates.io-index" 1011 | checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" 1012 | 1013 | [[package]] 1014 | name = "miniz_oxide" 1015 | version = "0.6.2" 1016 | source = "registry+https://github.com/rust-lang/crates.io-index" 1017 | checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" 1018 | dependencies = [ 1019 | "adler", 1020 | ] 1021 | 1022 | [[package]] 1023 | name = "mio" 1024 | version = "0.8.6" 1025 | source = "registry+https://github.com/rust-lang/crates.io-index" 1026 | checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" 1027 | dependencies = [ 1028 | "libc", 1029 | "log", 1030 | "wasi", 1031 | "windows-sys", 1032 | ] 1033 | 1034 | [[package]] 1035 | name = "nom" 1036 | version = "7.1.3" 1037 | source = "registry+https://github.com/rust-lang/crates.io-index" 1038 | checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" 1039 | dependencies = [ 1040 | "memchr", 1041 | "minimal-lexical", 1042 | ] 1043 | 1044 | [[package]] 1045 | name = "num_cpus" 1046 | version = "1.15.0" 1047 | source = "registry+https://github.com/rust-lang/crates.io-index" 1048 | checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" 1049 | dependencies = [ 1050 | "hermit-abi 0.2.6", 1051 | "libc", 1052 | ] 1053 | 1054 | [[package]] 1055 | name = "once_cell" 1056 | version = "1.17.1" 1057 | source = "registry+https://github.com/rust-lang/crates.io-index" 1058 | checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" 1059 | 1060 | [[package]] 1061 | name = "ordered-multimap" 1062 | version = "0.4.3" 1063 | source = "registry+https://github.com/rust-lang/crates.io-index" 1064 | checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a" 1065 | dependencies = [ 1066 | "dlv-list", 1067 | "hashbrown 0.12.3", 1068 | ] 1069 | 1070 | [[package]] 1071 | name = "parking_lot" 1072 | version = "0.12.1" 1073 | source = "registry+https://github.com/rust-lang/crates.io-index" 1074 | checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" 1075 | dependencies = [ 1076 | "lock_api", 1077 | "parking_lot_core", 1078 | ] 1079 | 1080 | [[package]] 1081 | name = "parking_lot_core" 1082 | version = "0.9.7" 1083 | source = "registry+https://github.com/rust-lang/crates.io-index" 1084 | checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" 1085 | dependencies = [ 1086 | "cfg-if", 1087 | "libc", 1088 | "redox_syscall", 1089 | "smallvec", 1090 | "windows-sys", 1091 | ] 1092 | 1093 | [[package]] 1094 | name = "paste" 1095 | version = "1.0.12" 1096 | source = "registry+https://github.com/rust-lang/crates.io-index" 1097 | checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" 1098 | 1099 | [[package]] 1100 | name = "pathdiff" 1101 | version = "0.2.1" 1102 | source = "registry+https://github.com/rust-lang/crates.io-index" 1103 | checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" 1104 | 1105 | [[package]] 1106 | name = "percent-encoding" 1107 | version = "2.2.0" 1108 | source = "registry+https://github.com/rust-lang/crates.io-index" 1109 | checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" 1110 | 1111 | [[package]] 1112 | name = "pest" 1113 | version = "2.5.6" 1114 | source = "registry+https://github.com/rust-lang/crates.io-index" 1115 | checksum = "8cbd939b234e95d72bc393d51788aec68aeeb5d51e748ca08ff3aad58cb722f7" 1116 | dependencies = [ 1117 | "thiserror", 1118 | "ucd-trie", 1119 | ] 1120 | 1121 | [[package]] 1122 | name = "pest_derive" 1123 | version = "2.5.6" 1124 | source = "registry+https://github.com/rust-lang/crates.io-index" 1125 | checksum = "a81186863f3d0a27340815be8f2078dd8050b14cd71913db9fbda795e5f707d7" 1126 | dependencies = [ 1127 | "pest", 1128 | "pest_generator", 1129 | ] 1130 | 1131 | [[package]] 1132 | name = "pest_generator" 1133 | version = "2.5.6" 1134 | source = "registry+https://github.com/rust-lang/crates.io-index" 1135 | checksum = "75a1ef20bf3193c15ac345acb32e26b3dc3223aff4d77ae4fc5359567683796b" 1136 | dependencies = [ 1137 | "pest", 1138 | "pest_meta", 1139 | "proc-macro2", 1140 | "quote", 1141 | "syn", 1142 | ] 1143 | 1144 | [[package]] 1145 | name = "pest_meta" 1146 | version = "2.5.6" 1147 | source = "registry+https://github.com/rust-lang/crates.io-index" 1148 | checksum = "5e3b284b1f13a20dc5ebc90aff59a51b8d7137c221131b52a7260c08cbc1cc80" 1149 | dependencies = [ 1150 | "once_cell", 1151 | "pest", 1152 | "sha2", 1153 | ] 1154 | 1155 | [[package]] 1156 | name = "pin-project-lite" 1157 | version = "0.2.9" 1158 | source = "registry+https://github.com/rust-lang/crates.io-index" 1159 | checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" 1160 | 1161 | [[package]] 1162 | name = "pin-utils" 1163 | version = "0.1.0" 1164 | source = "registry+https://github.com/rust-lang/crates.io-index" 1165 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 1166 | 1167 | [[package]] 1168 | name = "pkg-config" 1169 | version = "0.3.26" 1170 | source = "registry+https://github.com/rust-lang/crates.io-index" 1171 | checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" 1172 | 1173 | [[package]] 1174 | name = "ppv-lite86" 1175 | version = "0.2.17" 1176 | source = "registry+https://github.com/rust-lang/crates.io-index" 1177 | checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" 1178 | 1179 | [[package]] 1180 | name = "pretty_env_logger" 1181 | version = "0.4.0" 1182 | source = "registry+https://github.com/rust-lang/crates.io-index" 1183 | checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d" 1184 | dependencies = [ 1185 | "env_logger", 1186 | "log", 1187 | ] 1188 | 1189 | [[package]] 1190 | name = "proc-macro2" 1191 | version = "1.0.51" 1192 | source = "registry+https://github.com/rust-lang/crates.io-index" 1193 | checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" 1194 | dependencies = [ 1195 | "unicode-ident", 1196 | ] 1197 | 1198 | [[package]] 1199 | name = "prometheus" 1200 | version = "0.13.3" 1201 | source = "registry+https://github.com/rust-lang/crates.io-index" 1202 | checksum = "449811d15fbdf5ceb5c1144416066429cf82316e2ec8ce0c1f6f8a02e7bbcf8c" 1203 | dependencies = [ 1204 | "cfg-if", 1205 | "fnv", 1206 | "lazy_static", 1207 | "memchr", 1208 | "parking_lot", 1209 | "protobuf", 1210 | "thiserror", 1211 | ] 1212 | 1213 | [[package]] 1214 | name = "protobuf" 1215 | version = "2.28.0" 1216 | source = "registry+https://github.com/rust-lang/crates.io-index" 1217 | checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" 1218 | 1219 | [[package]] 1220 | name = "quick-error" 1221 | version = "1.2.3" 1222 | source = "registry+https://github.com/rust-lang/crates.io-index" 1223 | checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" 1224 | 1225 | [[package]] 1226 | name = "quote" 1227 | version = "1.0.23" 1228 | source = "registry+https://github.com/rust-lang/crates.io-index" 1229 | checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" 1230 | dependencies = [ 1231 | "proc-macro2", 1232 | ] 1233 | 1234 | [[package]] 1235 | name = "rand" 1236 | version = "0.8.5" 1237 | source = "registry+https://github.com/rust-lang/crates.io-index" 1238 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 1239 | dependencies = [ 1240 | "libc", 1241 | "rand_chacha", 1242 | "rand_core", 1243 | ] 1244 | 1245 | [[package]] 1246 | name = "rand_chacha" 1247 | version = "0.3.1" 1248 | source = "registry+https://github.com/rust-lang/crates.io-index" 1249 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 1250 | dependencies = [ 1251 | "ppv-lite86", 1252 | "rand_core", 1253 | ] 1254 | 1255 | [[package]] 1256 | name = "rand_core" 1257 | version = "0.6.4" 1258 | source = "registry+https://github.com/rust-lang/crates.io-index" 1259 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 1260 | dependencies = [ 1261 | "getrandom", 1262 | ] 1263 | 1264 | [[package]] 1265 | name = "redox_syscall" 1266 | version = "0.2.16" 1267 | source = "registry+https://github.com/rust-lang/crates.io-index" 1268 | checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" 1269 | dependencies = [ 1270 | "bitflags", 1271 | ] 1272 | 1273 | [[package]] 1274 | name = "regex" 1275 | version = "1.7.1" 1276 | source = "registry+https://github.com/rust-lang/crates.io-index" 1277 | checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" 1278 | dependencies = [ 1279 | "aho-corasick", 1280 | "memchr", 1281 | "regex-syntax", 1282 | ] 1283 | 1284 | [[package]] 1285 | name = "regex-syntax" 1286 | version = "0.6.28" 1287 | source = "registry+https://github.com/rust-lang/crates.io-index" 1288 | checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" 1289 | 1290 | [[package]] 1291 | name = "ring" 1292 | version = "0.16.20" 1293 | source = "registry+https://github.com/rust-lang/crates.io-index" 1294 | checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" 1295 | dependencies = [ 1296 | "cc", 1297 | "libc", 1298 | "once_cell", 1299 | "spin", 1300 | "untrusted", 1301 | "web-sys", 1302 | "winapi", 1303 | ] 1304 | 1305 | [[package]] 1306 | name = "ron" 1307 | version = "0.7.1" 1308 | source = "registry+https://github.com/rust-lang/crates.io-index" 1309 | checksum = "88073939a61e5b7680558e6be56b419e208420c2adb92be54921fa6b72283f1a" 1310 | dependencies = [ 1311 | "base64 0.13.1", 1312 | "bitflags", 1313 | "serde", 1314 | ] 1315 | 1316 | [[package]] 1317 | name = "rust-ini" 1318 | version = "0.18.0" 1319 | source = "registry+https://github.com/rust-lang/crates.io-index" 1320 | checksum = "f6d5f2436026b4f6e79dc829837d467cc7e9a55ee40e750d716713540715a2df" 1321 | dependencies = [ 1322 | "cfg-if", 1323 | "ordered-multimap", 1324 | ] 1325 | 1326 | [[package]] 1327 | name = "rustc_version" 1328 | version = "0.4.0" 1329 | source = "registry+https://github.com/rust-lang/crates.io-index" 1330 | checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" 1331 | dependencies = [ 1332 | "semver", 1333 | ] 1334 | 1335 | [[package]] 1336 | name = "rustls" 1337 | version = "0.20.8" 1338 | source = "registry+https://github.com/rust-lang/crates.io-index" 1339 | checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" 1340 | dependencies = [ 1341 | "log", 1342 | "ring", 1343 | "sct", 1344 | "webpki", 1345 | ] 1346 | 1347 | [[package]] 1348 | name = "rustls-pemfile" 1349 | version = "1.0.2" 1350 | source = "registry+https://github.com/rust-lang/crates.io-index" 1351 | checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" 1352 | dependencies = [ 1353 | "base64 0.21.0", 1354 | ] 1355 | 1356 | [[package]] 1357 | name = "rustversion" 1358 | version = "1.0.12" 1359 | source = "registry+https://github.com/rust-lang/crates.io-index" 1360 | checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" 1361 | 1362 | [[package]] 1363 | name = "ryu" 1364 | version = "1.0.13" 1365 | source = "registry+https://github.com/rust-lang/crates.io-index" 1366 | checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" 1367 | 1368 | [[package]] 1369 | name = "scopeguard" 1370 | version = "1.1.0" 1371 | source = "registry+https://github.com/rust-lang/crates.io-index" 1372 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 1373 | 1374 | [[package]] 1375 | name = "sct" 1376 | version = "0.7.0" 1377 | source = "registry+https://github.com/rust-lang/crates.io-index" 1378 | checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" 1379 | dependencies = [ 1380 | "ring", 1381 | "untrusted", 1382 | ] 1383 | 1384 | [[package]] 1385 | name = "semver" 1386 | version = "1.0.16" 1387 | source = "registry+https://github.com/rust-lang/crates.io-index" 1388 | checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" 1389 | 1390 | [[package]] 1391 | name = "serde" 1392 | version = "1.0.154" 1393 | source = "registry+https://github.com/rust-lang/crates.io-index" 1394 | checksum = "8cdd151213925e7f1ab45a9bbfb129316bd00799784b174b7cc7bcd16961c49e" 1395 | dependencies = [ 1396 | "serde_derive", 1397 | ] 1398 | 1399 | [[package]] 1400 | name = "serde_derive" 1401 | version = "1.0.154" 1402 | source = "registry+https://github.com/rust-lang/crates.io-index" 1403 | checksum = "4fc80d722935453bcafdc2c9a73cd6fac4dc1938f0346035d84bf99fa9e33217" 1404 | dependencies = [ 1405 | "proc-macro2", 1406 | "quote", 1407 | "syn", 1408 | ] 1409 | 1410 | [[package]] 1411 | name = "serde_json" 1412 | version = "1.0.94" 1413 | source = "registry+https://github.com/rust-lang/crates.io-index" 1414 | checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" 1415 | dependencies = [ 1416 | "itoa", 1417 | "ryu", 1418 | "serde", 1419 | ] 1420 | 1421 | [[package]] 1422 | name = "serde_urlencoded" 1423 | version = "0.7.1" 1424 | source = "registry+https://github.com/rust-lang/crates.io-index" 1425 | checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" 1426 | dependencies = [ 1427 | "form_urlencoded", 1428 | "itoa", 1429 | "ryu", 1430 | "serde", 1431 | ] 1432 | 1433 | [[package]] 1434 | name = "sha1" 1435 | version = "0.10.5" 1436 | source = "registry+https://github.com/rust-lang/crates.io-index" 1437 | checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" 1438 | dependencies = [ 1439 | "cfg-if", 1440 | "cpufeatures", 1441 | "digest", 1442 | ] 1443 | 1444 | [[package]] 1445 | name = "sha2" 1446 | version = "0.10.6" 1447 | source = "registry+https://github.com/rust-lang/crates.io-index" 1448 | checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" 1449 | dependencies = [ 1450 | "cfg-if", 1451 | "cpufeatures", 1452 | "digest", 1453 | ] 1454 | 1455 | [[package]] 1456 | name = "signal-hook-registry" 1457 | version = "1.4.1" 1458 | source = "registry+https://github.com/rust-lang/crates.io-index" 1459 | checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" 1460 | dependencies = [ 1461 | "libc", 1462 | ] 1463 | 1464 | [[package]] 1465 | name = "slab" 1466 | version = "0.4.8" 1467 | source = "registry+https://github.com/rust-lang/crates.io-index" 1468 | checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" 1469 | dependencies = [ 1470 | "autocfg", 1471 | ] 1472 | 1473 | [[package]] 1474 | name = "smallvec" 1475 | version = "1.10.0" 1476 | source = "registry+https://github.com/rust-lang/crates.io-index" 1477 | checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" 1478 | 1479 | [[package]] 1480 | name = "socket2" 1481 | version = "0.4.9" 1482 | source = "registry+https://github.com/rust-lang/crates.io-index" 1483 | checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" 1484 | dependencies = [ 1485 | "libc", 1486 | "winapi", 1487 | ] 1488 | 1489 | [[package]] 1490 | name = "spin" 1491 | version = "0.5.2" 1492 | source = "registry+https://github.com/rust-lang/crates.io-index" 1493 | checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" 1494 | 1495 | [[package]] 1496 | name = "strsim" 1497 | version = "0.10.0" 1498 | source = "registry+https://github.com/rust-lang/crates.io-index" 1499 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 1500 | 1501 | [[package]] 1502 | name = "strum" 1503 | version = "0.24.1" 1504 | source = "registry+https://github.com/rust-lang/crates.io-index" 1505 | checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" 1506 | 1507 | [[package]] 1508 | name = "strum_macros" 1509 | version = "0.24.3" 1510 | source = "registry+https://github.com/rust-lang/crates.io-index" 1511 | checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" 1512 | dependencies = [ 1513 | "heck", 1514 | "proc-macro2", 1515 | "quote", 1516 | "rustversion", 1517 | "syn", 1518 | ] 1519 | 1520 | [[package]] 1521 | name = "syn" 1522 | version = "1.0.109" 1523 | source = "registry+https://github.com/rust-lang/crates.io-index" 1524 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 1525 | dependencies = [ 1526 | "proc-macro2", 1527 | "quote", 1528 | "unicode-ident", 1529 | ] 1530 | 1531 | [[package]] 1532 | name = "termcolor" 1533 | version = "1.2.0" 1534 | source = "registry+https://github.com/rust-lang/crates.io-index" 1535 | checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" 1536 | dependencies = [ 1537 | "winapi-util", 1538 | ] 1539 | 1540 | [[package]] 1541 | name = "thiserror" 1542 | version = "1.0.39" 1543 | source = "registry+https://github.com/rust-lang/crates.io-index" 1544 | checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c" 1545 | dependencies = [ 1546 | "thiserror-impl", 1547 | ] 1548 | 1549 | [[package]] 1550 | name = "thiserror-impl" 1551 | version = "1.0.39" 1552 | source = "registry+https://github.com/rust-lang/crates.io-index" 1553 | checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e" 1554 | dependencies = [ 1555 | "proc-macro2", 1556 | "quote", 1557 | "syn", 1558 | ] 1559 | 1560 | [[package]] 1561 | name = "time" 1562 | version = "0.3.20" 1563 | source = "registry+https://github.com/rust-lang/crates.io-index" 1564 | checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" 1565 | dependencies = [ 1566 | "itoa", 1567 | "serde", 1568 | "time-core", 1569 | "time-macros", 1570 | ] 1571 | 1572 | [[package]] 1573 | name = "time-core" 1574 | version = "0.1.0" 1575 | source = "registry+https://github.com/rust-lang/crates.io-index" 1576 | checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" 1577 | 1578 | [[package]] 1579 | name = "time-macros" 1580 | version = "0.2.8" 1581 | source = "registry+https://github.com/rust-lang/crates.io-index" 1582 | checksum = "fd80a657e71da814b8e5d60d3374fc6d35045062245d80224748ae522dd76f36" 1583 | dependencies = [ 1584 | "time-core", 1585 | ] 1586 | 1587 | [[package]] 1588 | name = "tinyvec" 1589 | version = "1.6.0" 1590 | source = "registry+https://github.com/rust-lang/crates.io-index" 1591 | checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" 1592 | dependencies = [ 1593 | "tinyvec_macros", 1594 | ] 1595 | 1596 | [[package]] 1597 | name = "tinyvec_macros" 1598 | version = "0.1.1" 1599 | source = "registry+https://github.com/rust-lang/crates.io-index" 1600 | checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" 1601 | 1602 | [[package]] 1603 | name = "tokio" 1604 | version = "1.26.0" 1605 | source = "registry+https://github.com/rust-lang/crates.io-index" 1606 | checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64" 1607 | dependencies = [ 1608 | "autocfg", 1609 | "bytes", 1610 | "libc", 1611 | "memchr", 1612 | "mio", 1613 | "parking_lot", 1614 | "pin-project-lite", 1615 | "signal-hook-registry", 1616 | "socket2", 1617 | "tokio-macros", 1618 | "windows-sys", 1619 | ] 1620 | 1621 | [[package]] 1622 | name = "tokio-macros" 1623 | version = "1.8.2" 1624 | source = "registry+https://github.com/rust-lang/crates.io-index" 1625 | checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" 1626 | dependencies = [ 1627 | "proc-macro2", 1628 | "quote", 1629 | "syn", 1630 | ] 1631 | 1632 | [[package]] 1633 | name = "tokio-rustls" 1634 | version = "0.23.4" 1635 | source = "registry+https://github.com/rust-lang/crates.io-index" 1636 | checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" 1637 | dependencies = [ 1638 | "rustls", 1639 | "tokio", 1640 | "webpki", 1641 | ] 1642 | 1643 | [[package]] 1644 | name = "tokio-util" 1645 | version = "0.7.7" 1646 | source = "registry+https://github.com/rust-lang/crates.io-index" 1647 | checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" 1648 | dependencies = [ 1649 | "bytes", 1650 | "futures-core", 1651 | "futures-sink", 1652 | "pin-project-lite", 1653 | "tokio", 1654 | "tracing", 1655 | ] 1656 | 1657 | [[package]] 1658 | name = "tolerable" 1659 | version = "0.2.1" 1660 | dependencies = [ 1661 | "actix-web", 1662 | "actix-web-prom", 1663 | "anyhow", 1664 | "array_tool", 1665 | "awc", 1666 | "base64 0.21.0", 1667 | "cached", 1668 | "config", 1669 | "ctor", 1670 | "docker-image-reference", 1671 | "lazy_static", 1672 | "log", 1673 | "pretty_env_logger", 1674 | "prometheus", 1675 | "regex", 1676 | "rustls", 1677 | "rustls-pemfile", 1678 | "serde", 1679 | "serde_json", 1680 | "strum", 1681 | "strum_macros", 1682 | "thiserror", 1683 | ] 1684 | 1685 | [[package]] 1686 | name = "toml" 1687 | version = "0.5.11" 1688 | source = "registry+https://github.com/rust-lang/crates.io-index" 1689 | checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" 1690 | dependencies = [ 1691 | "serde", 1692 | ] 1693 | 1694 | [[package]] 1695 | name = "tracing" 1696 | version = "0.1.37" 1697 | source = "registry+https://github.com/rust-lang/crates.io-index" 1698 | checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" 1699 | dependencies = [ 1700 | "cfg-if", 1701 | "log", 1702 | "pin-project-lite", 1703 | "tracing-core", 1704 | ] 1705 | 1706 | [[package]] 1707 | name = "tracing-core" 1708 | version = "0.1.30" 1709 | source = "registry+https://github.com/rust-lang/crates.io-index" 1710 | checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" 1711 | dependencies = [ 1712 | "once_cell", 1713 | ] 1714 | 1715 | [[package]] 1716 | name = "typenum" 1717 | version = "1.16.0" 1718 | source = "registry+https://github.com/rust-lang/crates.io-index" 1719 | checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" 1720 | 1721 | [[package]] 1722 | name = "ucd-trie" 1723 | version = "0.1.5" 1724 | source = "registry+https://github.com/rust-lang/crates.io-index" 1725 | checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" 1726 | 1727 | [[package]] 1728 | name = "unicode-bidi" 1729 | version = "0.3.11" 1730 | source = "registry+https://github.com/rust-lang/crates.io-index" 1731 | checksum = "524b68aca1d05e03fdf03fcdce2c6c94b6daf6d16861ddaa7e4f2b6638a9052c" 1732 | 1733 | [[package]] 1734 | name = "unicode-ident" 1735 | version = "1.0.8" 1736 | source = "registry+https://github.com/rust-lang/crates.io-index" 1737 | checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" 1738 | 1739 | [[package]] 1740 | name = "unicode-normalization" 1741 | version = "0.1.22" 1742 | source = "registry+https://github.com/rust-lang/crates.io-index" 1743 | checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" 1744 | dependencies = [ 1745 | "tinyvec", 1746 | ] 1747 | 1748 | [[package]] 1749 | name = "unicode-xid" 1750 | version = "0.2.4" 1751 | source = "registry+https://github.com/rust-lang/crates.io-index" 1752 | checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" 1753 | 1754 | [[package]] 1755 | name = "untrusted" 1756 | version = "0.7.1" 1757 | source = "registry+https://github.com/rust-lang/crates.io-index" 1758 | checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" 1759 | 1760 | [[package]] 1761 | name = "url" 1762 | version = "2.3.1" 1763 | source = "registry+https://github.com/rust-lang/crates.io-index" 1764 | checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" 1765 | dependencies = [ 1766 | "form_urlencoded", 1767 | "idna", 1768 | "percent-encoding", 1769 | ] 1770 | 1771 | [[package]] 1772 | name = "version_check" 1773 | version = "0.9.4" 1774 | source = "registry+https://github.com/rust-lang/crates.io-index" 1775 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 1776 | 1777 | [[package]] 1778 | name = "wasi" 1779 | version = "0.11.0+wasi-snapshot-preview1" 1780 | source = "registry+https://github.com/rust-lang/crates.io-index" 1781 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1782 | 1783 | [[package]] 1784 | name = "wasm-bindgen" 1785 | version = "0.2.84" 1786 | source = "registry+https://github.com/rust-lang/crates.io-index" 1787 | checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" 1788 | dependencies = [ 1789 | "cfg-if", 1790 | "wasm-bindgen-macro", 1791 | ] 1792 | 1793 | [[package]] 1794 | name = "wasm-bindgen-backend" 1795 | version = "0.2.84" 1796 | source = "registry+https://github.com/rust-lang/crates.io-index" 1797 | checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" 1798 | dependencies = [ 1799 | "bumpalo", 1800 | "log", 1801 | "once_cell", 1802 | "proc-macro2", 1803 | "quote", 1804 | "syn", 1805 | "wasm-bindgen-shared", 1806 | ] 1807 | 1808 | [[package]] 1809 | name = "wasm-bindgen-macro" 1810 | version = "0.2.84" 1811 | source = "registry+https://github.com/rust-lang/crates.io-index" 1812 | checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" 1813 | dependencies = [ 1814 | "quote", 1815 | "wasm-bindgen-macro-support", 1816 | ] 1817 | 1818 | [[package]] 1819 | name = "wasm-bindgen-macro-support" 1820 | version = "0.2.84" 1821 | source = "registry+https://github.com/rust-lang/crates.io-index" 1822 | checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" 1823 | dependencies = [ 1824 | "proc-macro2", 1825 | "quote", 1826 | "syn", 1827 | "wasm-bindgen-backend", 1828 | "wasm-bindgen-shared", 1829 | ] 1830 | 1831 | [[package]] 1832 | name = "wasm-bindgen-shared" 1833 | version = "0.2.84" 1834 | source = "registry+https://github.com/rust-lang/crates.io-index" 1835 | checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" 1836 | 1837 | [[package]] 1838 | name = "web-sys" 1839 | version = "0.3.61" 1840 | source = "registry+https://github.com/rust-lang/crates.io-index" 1841 | checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" 1842 | dependencies = [ 1843 | "js-sys", 1844 | "wasm-bindgen", 1845 | ] 1846 | 1847 | [[package]] 1848 | name = "webpki" 1849 | version = "0.22.0" 1850 | source = "registry+https://github.com/rust-lang/crates.io-index" 1851 | checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" 1852 | dependencies = [ 1853 | "ring", 1854 | "untrusted", 1855 | ] 1856 | 1857 | [[package]] 1858 | name = "webpki-roots" 1859 | version = "0.22.6" 1860 | source = "registry+https://github.com/rust-lang/crates.io-index" 1861 | checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" 1862 | dependencies = [ 1863 | "webpki", 1864 | ] 1865 | 1866 | [[package]] 1867 | name = "winapi" 1868 | version = "0.3.9" 1869 | source = "registry+https://github.com/rust-lang/crates.io-index" 1870 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1871 | dependencies = [ 1872 | "winapi-i686-pc-windows-gnu", 1873 | "winapi-x86_64-pc-windows-gnu", 1874 | ] 1875 | 1876 | [[package]] 1877 | name = "winapi-i686-pc-windows-gnu" 1878 | version = "0.4.0" 1879 | source = "registry+https://github.com/rust-lang/crates.io-index" 1880 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1881 | 1882 | [[package]] 1883 | name = "winapi-util" 1884 | version = "0.1.5" 1885 | source = "registry+https://github.com/rust-lang/crates.io-index" 1886 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 1887 | dependencies = [ 1888 | "winapi", 1889 | ] 1890 | 1891 | [[package]] 1892 | name = "winapi-x86_64-pc-windows-gnu" 1893 | version = "0.4.0" 1894 | source = "registry+https://github.com/rust-lang/crates.io-index" 1895 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1896 | 1897 | [[package]] 1898 | name = "windows-sys" 1899 | version = "0.45.0" 1900 | source = "registry+https://github.com/rust-lang/crates.io-index" 1901 | checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" 1902 | dependencies = [ 1903 | "windows-targets", 1904 | ] 1905 | 1906 | [[package]] 1907 | name = "windows-targets" 1908 | version = "0.42.1" 1909 | source = "registry+https://github.com/rust-lang/crates.io-index" 1910 | checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" 1911 | dependencies = [ 1912 | "windows_aarch64_gnullvm", 1913 | "windows_aarch64_msvc", 1914 | "windows_i686_gnu", 1915 | "windows_i686_msvc", 1916 | "windows_x86_64_gnu", 1917 | "windows_x86_64_gnullvm", 1918 | "windows_x86_64_msvc", 1919 | ] 1920 | 1921 | [[package]] 1922 | name = "windows_aarch64_gnullvm" 1923 | version = "0.42.1" 1924 | source = "registry+https://github.com/rust-lang/crates.io-index" 1925 | checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" 1926 | 1927 | [[package]] 1928 | name = "windows_aarch64_msvc" 1929 | version = "0.42.1" 1930 | source = "registry+https://github.com/rust-lang/crates.io-index" 1931 | checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" 1932 | 1933 | [[package]] 1934 | name = "windows_i686_gnu" 1935 | version = "0.42.1" 1936 | source = "registry+https://github.com/rust-lang/crates.io-index" 1937 | checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" 1938 | 1939 | [[package]] 1940 | name = "windows_i686_msvc" 1941 | version = "0.42.1" 1942 | source = "registry+https://github.com/rust-lang/crates.io-index" 1943 | checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" 1944 | 1945 | [[package]] 1946 | name = "windows_x86_64_gnu" 1947 | version = "0.42.1" 1948 | source = "registry+https://github.com/rust-lang/crates.io-index" 1949 | checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" 1950 | 1951 | [[package]] 1952 | name = "windows_x86_64_gnullvm" 1953 | version = "0.42.1" 1954 | source = "registry+https://github.com/rust-lang/crates.io-index" 1955 | checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" 1956 | 1957 | [[package]] 1958 | name = "windows_x86_64_msvc" 1959 | version = "0.42.1" 1960 | source = "registry+https://github.com/rust-lang/crates.io-index" 1961 | checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" 1962 | 1963 | [[package]] 1964 | name = "yaml-rust" 1965 | version = "0.4.5" 1966 | source = "registry+https://github.com/rust-lang/crates.io-index" 1967 | checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" 1968 | dependencies = [ 1969 | "linked-hash-map", 1970 | ] 1971 | 1972 | [[package]] 1973 | name = "zstd" 1974 | version = "0.12.3+zstd.1.5.2" 1975 | source = "registry+https://github.com/rust-lang/crates.io-index" 1976 | checksum = "76eea132fb024e0e13fd9c2f5d5d595d8a967aa72382ac2f9d39fcc95afd0806" 1977 | dependencies = [ 1978 | "zstd-safe", 1979 | ] 1980 | 1981 | [[package]] 1982 | name = "zstd-safe" 1983 | version = "6.0.4+zstd.1.5.4" 1984 | source = "registry+https://github.com/rust-lang/crates.io-index" 1985 | checksum = "7afb4b54b8910cf5447638cb54bf4e8a65cbedd783af98b98c62ffe91f185543" 1986 | dependencies = [ 1987 | "libc", 1988 | "zstd-sys", 1989 | ] 1990 | 1991 | [[package]] 1992 | name = "zstd-sys" 1993 | version = "2.0.7+zstd.1.5.4" 1994 | source = "registry+https://github.com/rust-lang/crates.io-index" 1995 | checksum = "94509c3ba2fe55294d752b79842c530ccfab760192521df74a081a78d2b3c7f5" 1996 | dependencies = [ 1997 | "cc", 1998 | "libc", 1999 | "pkg-config", 2000 | ] 2001 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tolerable" 3 | version = "0.2.1" 4 | edition = "2021" 5 | repository = "https://github.com/PeterGrace/tolerable" 6 | description = "kubernetes webhook to set configurable cpu architecture tolerations" 7 | license = "MIT" 8 | 9 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 10 | [dev-dependencies] 11 | ctor = "0.1.26" 12 | [dependencies] 13 | anyhow = "1.0.69" 14 | pretty_env_logger = "0.4.0" 15 | log = "0.4.17" 16 | serde = {version = "1.0.154", features=["derive"]} 17 | serde_json = "1.0.94" 18 | prometheus = "0.13.3" 19 | lazy_static = "1.4.0" 20 | actix-web = {version="4.3.1", features=["rustls"]} 21 | actix-web-prom = "0.6.0" 22 | strum = "0.24.1" 23 | strum_macros = "0.24.3" 24 | config = {version ="0.13.3", features=["toml"]} 25 | base64 = "0.21.0" 26 | rustls = "0.20.8" 27 | rustls-pemfile = "1.0.2" 28 | awc = { version = "3.1.1", features=["rustls"]} 29 | regex = "1.7.1" 30 | docker-image-reference = { git = "https://github.com/PeterGrace/docker-image-reference.git", version = "0.1.0" } 31 | cached = {version = "0.42.0"} 32 | thiserror = "1.0.39" 33 | array_tool = "1.0.3" 34 | 35 | [profile.release] 36 | strip="debuginfo" 37 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM docker.io/ubuntu:20.04 2 | ARG TARGETARCH 3 | 4 | RUN mkdir -p /opt/tolerable 5 | WORKDIR /opt/tolerable 6 | COPY ./tools/target_arch.sh /opt/tolerable 7 | COPY docker/tolerable.toml /opt/tolerable/ 8 | RUN --mount=type=bind,target=/context \ 9 | cp /context/target/$(/opt/tolerable/target_arch.sh)/release/tolerable /opt/tolerable/tolerable 10 | CMD ["/opt/tolerable/tolerable"] 11 | EXPOSE 8443 12 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Peter Grace 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Build](https://img.shields.io/github/actions/workflow/status/petergrace/tolerable/test.yaml) 2 | ![Docker Image Size (latest by date)](https://img.shields.io/docker/image-size/petergrace/tolerable/latest) 3 | # tolerable 4 | ## Auto-configure tolerations for architecture-based taints 5 | This software program was written due to the author's frustration with running a mixed-architecture kubernetes cluster. When you are running both amd64 and arm64 processors in your cluster, there is not automatic method of detecting whether an image is built for arm64 or not. This results in Pods going into CrashLoopBackOff with a log line similar to "exec format error." 6 | 7 | `tolerable` is a Kubernetes Mutating Webhook which watches the creation of Pods in the cluster. When a request for a Pod is tendered, the Mutating Webhook receives a copy of the request. It then asks the registry that houses the image whether it has an arm64 version. If the registry responds affirmatively, it patches in a pre-configured toleration to the pod so it can schedule on the specified architecture's node(s). 8 | 9 | ## quickstart (ish) 10 | there is a kustomize/ folder that has a kustomization spec for deploying the service. It relies on cert-manager to create the certificates required to enable a MutatingWebhookConfiguration. If you don't have cert-manager, you'll need to generate these certs manually and patch the containing secret into your deployment. You'll need to also include credentials into the deployment for any registries you want to pull from that need auth (docker.io, ghcr, etc). See the creds file example below for more info. 11 | 12 | 13 | ## configs 14 | 15 | ### tolerable.toml 16 | For each of these settings in `tolerable.toml`, you can override the settings via environment variable, prefixed by TOLERABLE_. Example: `TOLERABLE_SUPPORTED_ARCHITECTURES=s390x,arm64` 17 | 18 | | value | what it is | 19 | | ----- | ---------- | 20 | | ssl_key_path | path to private key, pem format | 21 | | ssl_cert_path | path to cert, pem format | 22 | | registry_credential_path | specified path to individual credential files, toml format | 23 | | supported_architectures | array of valid architectures for the kubernetes cluster | 24 | | [tolerations] | a toml object definition that contains the toleration format for the kubernetes cluster | 25 | 26 | ### tolerations 27 | This will be specific to your deployment. In my case, I set a taint on my arm64 nodes of `kubernetes.io/arch=arm64:NoSchedule` which means only a toleration that matches that taint will be allowed to schedule. The toleration for this taint is here: 28 | ``` 29 | [tolerations] 30 | effect = "NoSchedule" 31 | operator = "Equal" 32 | key = "kubernetes.io/arch" 33 | ``` 34 | 35 | ### creds files (e.g., docker.io.toml) 36 | Each registry that you need to auth to should be specified in a toml file, named for the registry name of the registry with .toml suffixed, and the file should contain two keys, `user` and `secret`. 37 | Example: 38 | ``` 39 | $ cat creds/docker.io.toml 40 | user="foobar" 41 | secret="bazbat" 42 | ``` 43 | 44 | 45 | ## Anticipatory FAQs 46 | ### Why are the creds stored as individual files? 47 | So that one can mount the credentials individually as subpaths from secrets in kubernetes 48 | ### Can you make an image of tolerable for platform X? 49 | Yes, open an issue and I'll amend the build process to build an image for it 50 | 51 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | use std::process::Command; 2 | fn main() { 3 | // note: add error checking yourself. 4 | let output = Command::new("git") 5 | .args(&["rev-parse", "HEAD"]) 6 | .output() 7 | .unwrap(); 8 | let git_hash = String::from_utf8(output.stdout).unwrap(); 9 | println!("cargo:rustc-env=GIT_HASH={}", git_hash); 10 | } -------------------------------------------------------------------------------- /docker/tolerable.toml: -------------------------------------------------------------------------------- 1 | log_level = "DEBUG" 2 | -------------------------------------------------------------------------------- /justfile: -------------------------------------------------------------------------------- 1 | commit := `git rev-parse HEAD` 2 | shortcommit := `git rev-parse HEAD` 3 | transport := "docker://" 4 | registry := "docker.io" 5 | image := "petergrace/tolerable" 6 | tag := `git describe --tags` 7 | 8 | build: 9 | cross build --release --target aarch64-unknown-linux-gnu 10 | cross build --release --target x86_64-unknown-linux-gnu 11 | make-image: 12 | docker buildx build --no-cache --push --platform linux/amd64,linux/arm64/v8 \ 13 | -t {{registry}}/{{image}}:latest \ 14 | -t {{registry}}/{{image}}:{{shortcommit}} \ 15 | -t {{registry}}/{{image}}:{{commit}} \ 16 | -t {{registry}}/{{image}}:{{tag}} \ 17 | . 18 | 19 | release-patch: 20 | cargo release --no-publish --no-verify patch --execute 21 | release-minor: 22 | cargo release --no-publish --no-verify minor --execute 23 | release-major: 24 | cargo release --no-publish --no-verify minor --execute 25 | -------------------------------------------------------------------------------- /kustomize/certificate.webhook.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: cert-manager.io/v1 3 | kind: Certificate 4 | metadata: 5 | annotations: 6 | cert-manager.io/allow-direct-injection: "true" 7 | name: tolerable-webhook 8 | spec: 9 | secretName: tolerable-tls 10 | dnsNames: 11 | - tolerable-webhook.tolerable 12 | - tolerable-webhook.tolerable.svc 13 | - tolerable-webhook.tolerable.svc.cluster.local 14 | issuerRef: 15 | name: selfsigned 16 | -------------------------------------------------------------------------------- /kustomize/deployment.tolerable.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: tolerable-webhook 5 | spec: 6 | progressDeadlineSeconds: 600 7 | replicas: 1 8 | revisionHistoryLimit: 10 9 | selector: 10 | matchLabels: 11 | app.kubernetes.io/name: tolerable 12 | strategy: 13 | rollingUpdate: 14 | maxSurge: 25% 15 | maxUnavailable: 25% 16 | type: RollingUpdate 17 | template: 18 | metadata: 19 | creationTimestamp: null 20 | labels: 21 | app.kubernetes.io/name: tolerable 22 | spec: 23 | automountServiceAccountToken: false 24 | containers: 25 | - image: SET-IN-KUSTOMIZE/tolerable 26 | imagePullPolicy: Always 27 | livenessProbe: 28 | failureThreshold: 3 29 | httpGet: 30 | path: /health 31 | port: 8443 32 | scheme: HTTPS 33 | initialDelaySeconds: 300 34 | periodSeconds: 10 35 | successThreshold: 1 36 | timeoutSeconds: 1 37 | name: tolerable 38 | ports: 39 | - containerPort: 8443 40 | protocol: TCP 41 | readinessProbe: 42 | failureThreshold: 3 43 | httpGet: 44 | path: /health 45 | port: 8443 46 | scheme: HTTPS 47 | initialDelaySeconds: 1 48 | periodSeconds: 10 49 | successThreshold: 1 50 | timeoutSeconds: 1 51 | resources: {} 52 | terminationMessagePath: /dev/termination-log 53 | terminationMessagePolicy: File 54 | volumeMounts: 55 | - mountPath: /opt/tolerable/ssl 56 | name: tls-certs 57 | - mountPath: /opt/tolerable/tolerable.toml 58 | name: config 59 | subPath: tolerable.toml 60 | dnsPolicy: ClusterFirst 61 | restartPolicy: Always 62 | schedulerName: default-scheduler 63 | securityContext: 64 | fsGroup: 10000 65 | runAsUser: 10000 66 | terminationGracePeriodSeconds: 30 67 | volumes: 68 | - configMap: 69 | name: tolerable-config 70 | name: config 71 | - secret: 72 | secretName: tolerable-tls 73 | name: tls-certs 74 | -------------------------------------------------------------------------------- /kustomize/deployment.ubuntu-test.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: ubuntu-test 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app.kubernetes.io/name: ubuntu-test 10 | template: 11 | metadata: 12 | labels: 13 | app.kubernetes.io/name: ubuntu-test 14 | spec: 15 | containers: 16 | - image: docker.io/ubuntu 17 | imagePullPolicy: Always 18 | name: ubuntu-test 19 | command: 20 | - /bin/sleep 21 | - infinity 22 | -------------------------------------------------------------------------------- /kustomize/issuer.selfsigned.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: cert-manager.io/v1 3 | kind: Issuer 4 | metadata: 5 | name: selfsigned 6 | spec: 7 | selfSigned: {} 8 | -------------------------------------------------------------------------------- /kustomize/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: tolerable 4 | 5 | commonLabels: 6 | app.kubernetes.io/name: tolerable 7 | 8 | configMapGenerator: 9 | - files: 10 | - tolerable.toml 11 | name: tolerable-config 12 | 13 | images: 14 | - name: SET-IN-KUSTOMIZE/tolerable 15 | newName: docker.io/petergrace/tolerable 16 | newTag: latest 17 | 18 | 19 | resources: 20 | - ns.tolerable.yaml 21 | - certificate.webhook.yaml 22 | - issuer.selfsigned.yaml 23 | - service.tolerable.yaml 24 | - svcacct.tolerable.yaml 25 | - deployment.tolerable.yaml 26 | - mutatingwebhook.yaml 27 | -------------------------------------------------------------------------------- /kustomize/mutatingwebhook.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: admissionregistration.k8s.io/v1 3 | kind: MutatingWebhookConfiguration 4 | metadata: 5 | name: tolerable-configuration 6 | annotations: 7 | cert-manager.io/inject-ca-from: tolerable/tolerable-webhook 8 | webhooks: 9 | - clientConfig: 10 | caBundle: Cg== 11 | service: 12 | name: tolerable-webhook 13 | path: /mutate 14 | port: 8443 15 | namespace: "tolerable" 16 | sideEffects: None 17 | admissionReviewVersions: ["v1beta1"] 18 | failurePolicy: Ignore 19 | name: webhook.tolerable.dev 20 | rules: 21 | - apiGroups: 22 | - "" 23 | apiVersions: 24 | - "v1" 25 | operations: 26 | - CREATE 27 | - UPDATE 28 | resources: 29 | - pods 30 | scope: "Namespaced" 31 | -------------------------------------------------------------------------------- /kustomize/ns.tolerable.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: tolerable 5 | -------------------------------------------------------------------------------- /kustomize/service.tolerable.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: tolerable-webhook 5 | spec: 6 | internalTrafficPolicy: Cluster 7 | type: ClusterIP 8 | ports: 9 | - name: https 10 | port: 8443 11 | protocol: TCP 12 | targetPort: 8443 13 | -------------------------------------------------------------------------------- /kustomize/svcacct.tolerable.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: tolerable 5 | -------------------------------------------------------------------------------- /kustomize/tolerable.toml: -------------------------------------------------------------------------------- 1 | log_level = "INFO" 2 | ssl_key_path = "/opt/tolerable/ssl/tls.key" 3 | ssl_cert_path = "/opt/tolerable/ssl/tls.crt" 4 | registry_credential_path = "/opt/tolerable/creds" 5 | supported_architectures = [ "amd64", "arm64" ] 6 | [tolerations] 7 | effect = "NoSchedule" 8 | operator = "Equal" 9 | key = "kubernetes.io/arch" 10 | value= "arm64" 11 | -------------------------------------------------------------------------------- /src/consts.rs: -------------------------------------------------------------------------------- 1 | pub const APP_NAME: &str = "tolerable"; 2 | pub const DOCKER_IMAGE_REGEXP: &str = r#"^(?P[\w.\-_]+((?::\d+|)(?=/[a-z0-9._-]+/[a-z0-9._-]+))|)\(?:/|)(?P[a-z0-9.\-_]+(?:/[a-z0-9.\-_]+|))(:(?P[\w.\-_]{1,127})|)$"#; 3 | pub const TOKEN_AUTH_REGEXP: &str = r#"^Bearer realm="(?P.+)",service="(?P.+)",scope="(?P.+)"$"#; 4 | 5 | // amusingly enough, it seems that docker.io is the only registry allowed to have a different actual dns name, 6 | // and everyone just seems to go along with this. 7 | pub const ACTUAL_DOCKER_REGISTRY: &str = r#"registry-1.docker.io"#; 8 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | mod consts; 2 | mod metrics; 3 | mod models; 4 | mod mutation; 5 | mod tests; 6 | mod manifest; 7 | 8 | #[macro_use] 9 | extern crate log; 10 | 11 | #[macro_use] 12 | extern crate lazy_static; 13 | #[macro_use] 14 | extern crate prometheus; 15 | 16 | use actix_web::{middleware, web, App, HttpResponse, HttpServer}; 17 | 18 | use crate::metrics::{register_metrics, APPVER, STATIC_PROM}; 19 | use crate::mutation::mutate_handler; 20 | use config::{Config}; 21 | use rustls::ServerConfig; 22 | use rustls_pemfile; 23 | use std::env; 24 | use std::env::set_var; 25 | use std::fs; 26 | use std::io::BufReader; 27 | use std::sync::RwLock; 28 | 29 | lazy_static! { 30 | static ref SETTINGS: RwLock = RwLock::new({ 31 | let cfg_file = match std::env::var("CONFIG_FILE_PATH") { 32 | Ok(s) => s, 33 | Err(_e) => { "./tolerable.toml".to_string()} 34 | }; 35 | let settings = match Config::builder() 36 | .add_source(config::File::with_name(&cfg_file)) 37 | .add_source( 38 | config::Environment::with_prefix("TOLERABLE") 39 | .try_parsing(true) 40 | .list_separator(",") 41 | .with_list_parse_key("supported_architectures") 42 | ) 43 | .build() 44 | { 45 | Ok(s) => s, 46 | Err(e) => { 47 | panic!("{}", e); 48 | } 49 | }; 50 | settings 51 | }); 52 | } 53 | 54 | #[actix_web::main] 55 | async fn main() -> anyhow::Result<()> { 56 | // initialize logging 57 | match read_setting_string("log_level") { 58 | Ok(log_level) => set_var("RUST_LOG", log_level), 59 | Err(_e) => {} 60 | } 61 | 62 | pretty_env_logger::init(); 63 | 64 | // get prometheus metrics ready to rock 65 | register_metrics(); 66 | 67 | // measure our first metric, the appver 68 | let appdata = APPVER.with_label_values(&[env!("CARGO_PKG_VERSION"), env!("GIT_HASH")]); 69 | appdata.set(1 as f64); 70 | debug!("tolerable cargo:{}, githash:{}", env!("CARGO_PKG_VERSION"),env!("GIT_HASH")); 71 | 72 | let ssl_key_path = match read_setting_string("ssl_key_path") { 73 | Ok(s) => s, 74 | Err(e) => {panic!("{}",e);} 75 | }; 76 | let ssl_cert_path = match read_setting_string("ssl_cert_path") { 77 | Ok(s) => s, 78 | Err(e) => {panic!("{}",e);} 79 | }; 80 | let versions = rustls::ALL_VERSIONS.to_vec(); 81 | let suites = rustls::ALL_CIPHER_SUITES.to_vec(); 82 | let certs = load_certs(&ssl_cert_path); 83 | let privkey = load_private_key(&ssl_key_path); 84 | let rustls_config = ServerConfig::builder() 85 | .with_cipher_suites(&suites) 86 | .with_safe_default_kx_groups() 87 | .with_protocol_versions(&versions) 88 | .unwrap() 89 | .with_no_client_auth() 90 | .with_single_cert(certs, privkey) 91 | .unwrap(); 92 | // fire up server and lets go! 93 | HttpServer::new(move || { 94 | App::new() 95 | .wrap(middleware::Logger::default()) 96 | .wrap(STATIC_PROM.clone()) 97 | .service(mutate_handler) 98 | .route("/health", web::get().to(HttpResponse::Ok)) 99 | }) 100 | .bind_rustls(("0.0.0.0", 8443), rustls_config)? 101 | .run() 102 | .await?; 103 | Ok(()) 104 | } 105 | 106 | fn load_private_key(filename: &str) -> rustls::PrivateKey { 107 | let keyfile = fs::File::open(filename).expect("cannot open private key file"); 108 | let mut reader = BufReader::new(keyfile); 109 | 110 | loop { 111 | match rustls_pemfile::read_one(&mut reader).expect("cannot parse private key .pem file") { 112 | Some(rustls_pemfile::Item::RSAKey(key)) => return rustls::PrivateKey(key), 113 | Some(rustls_pemfile::Item::PKCS8Key(key)) => return rustls::PrivateKey(key), 114 | Some(rustls_pemfile::Item::ECKey(key)) => return rustls::PrivateKey(key), 115 | None => break, 116 | _ => {} 117 | } 118 | } 119 | 120 | panic!( 121 | "no keys found in {:?} (encrypted keys not supported)", 122 | filename 123 | ); 124 | } 125 | fn load_certs(filename: &str) -> Vec { 126 | let certfile = fs::File::open(filename).expect("cannot open certificate file"); 127 | let mut reader = BufReader::new(certfile); 128 | rustls_pemfile::certs(&mut reader) 129 | .unwrap() 130 | .iter() 131 | .map(|v| rustls::Certificate(v.clone())) 132 | .collect() 133 | } 134 | 135 | fn read_setting_string(key: &str) -> anyhow::Result { 136 | match SETTINGS.read().unwrap().get::(&key) { 137 | Ok(s) => Ok(s), 138 | Err(_e) => { 139 | Err(anyhow::anyhow!("unable to read config setting {}",key)) 140 | } 141 | } 142 | } -------------------------------------------------------------------------------- /src/manifest.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::path::Path; 3 | use anyhow::{bail}; 4 | use awc::{Client, ClientRequest, SendClientRequest}; 5 | use awc::error::JsonPayloadError; 6 | use config::Config; 7 | use regex::Regex; 8 | use docker_image_reference::Reference; 9 | use crate::read_setting_string; 10 | use cached::proc_macro::cached; 11 | use serde_json::Value; 12 | use crate::consts::*; 13 | 14 | 15 | lazy_static! { 16 | static ref DOCKER_RE: Regex = Regex::new(DOCKER_IMAGE_REGEXP).unwrap(); 17 | static ref TOKEN_AUTH_RE: Regex = Regex::new(TOKEN_AUTH_REGEXP).unwrap(); 18 | } 19 | 20 | #[cached] 21 | pub async fn validate_manifest(image: String) -> Option> { 22 | 23 | let mut manifest_ref: Reference = match Reference::from_str(&image){ 24 | Ok(m) => m, 25 | Err(e) => { 26 | warn!{"unable to match manifest from image string: {}", e}; 27 | return None; 28 | } 29 | }; 30 | // first attempt to hit the endpoint 31 | let mut registryport = "".to_string(); 32 | // we will start with the docker hub registry as its the assumed default. 33 | let mut registry = "docker.io"; 34 | let mut tag = ""; 35 | if manifest_ref.registry_name().is_some() { 36 | registry = manifest_ref.registry_name().unwrap(); 37 | } 38 | let cred = get_credentials_for_registry(registry.to_string()).await; 39 | if registry == "docker.io" { 40 | // see consts.rs for commentary 41 | registry = ACTUAL_DOCKER_REGISTRY; 42 | } 43 | if manifest_ref.registry_port().is_some() { 44 | registryport = format!("{}{}", registry, manifest_ref.registry_port().unwrap()) 45 | } else { 46 | registryport = format!("{}", registry); 47 | } 48 | if manifest_ref.tag().is_some() { 49 | tag = manifest_ref.tag().unwrap(); 50 | } else { 51 | tag = "latest"; 52 | } 53 | 54 | // Docker legacy image names can be specified without a repository name ('nginx' vs 'library/nginx') so 55 | // we will fix these here, as the backend for docker.io at least requires that in the api call. 56 | let mut image_name: String; 57 | if !manifest_ref.name().contains('/') { 58 | image_name = format!("library/{}", manifest_ref.name()); 59 | } else { 60 | image_name = manifest_ref.name().to_owned(); 61 | } 62 | 63 | let url = format!("https://{registryport}/v2/{}/manifests/{tag}", image_name); 64 | let token = get_jwt(url.clone(), cred).await; 65 | let client = Client::new(); 66 | let image_manifest_types = vec![ 67 | "application/vnd.oci.image.index.v1+json", 68 | "application/vnd.docker.distribution.manifest.list.v2+json" 69 | ]; 70 | let mut manifest_req :ClientRequest= client.get(&url).insert_header(("Accept", image_manifest_types.join(","))); 71 | if token.is_some() { 72 | manifest_req = manifest_req.bearer_auth(token.unwrap()); 73 | } 74 | debug!("MANIFEST REQ: {:#?}", manifest_req); 75 | let mut manifest_rs = match manifest_req.send().await { 76 | Ok(r) => r, 77 | Err(e) => { 78 | warn!("Unable to contact for manifest request: {e}"); 79 | return None; 80 | } 81 | }; 82 | 83 | 84 | // due to the fact that docker.io returns a mime type that actix-web doesn't like, we'll save the body to 85 | // a variable and attempt to convert it to json there. 86 | let rs_body = match manifest_rs.body().await { 87 | Ok(b) => b, 88 | Err(e) => { 89 | warn!("Unable to decode payload of manifest response: {e}"); 90 | return None; 91 | } 92 | }; 93 | 94 | let rs_json: Value = match serde_json::from_slice(rs_body.as_ref()) { 95 | Ok(m) => m, 96 | Err(e) => { 97 | warn!("Error decoding result from manifest request: {e}"); 98 | debug!("{:#?}", manifest_rs); 99 | debug!("{:#?}", manifest_rs.body().await); 100 | return None; 101 | } 102 | }; 103 | 104 | // depending on schemaVersion, we need to work a little differently. 105 | let jsondata: String = format!("{:#?}",rs_json); 106 | let schemaVersion: u64 = match rs_json.get("schemaVersion") { 107 | Some(v) => v.as_u64().unwrap(), 108 | None => { 109 | warn!("Response had no schemaVersion, so we can't deduce where a platform value would live."); 110 | return None; 111 | } 112 | }; 113 | return match schemaVersion { 114 | 1 => get_version_1_arches(&rs_json), 115 | 2 => get_version_2_arches(&rs_json), 116 | _ => { 117 | warn!("We received a value we did not expect for schemaVersion: {}", schemaVersion); 118 | None 119 | } 120 | } 121 | } 122 | 123 | pub fn get_version_1_arches(json: &Value) -> Option> { 124 | let mut arches:Vec = vec![]; 125 | match json.get("architecture") { 126 | Some(s) => arches.push(s.as_str().unwrap().to_string()), 127 | None => { 128 | warn!("Schema version 1, but no architecture specified"); 129 | return None 130 | } 131 | }; 132 | Some(arches) 133 | } 134 | 135 | pub fn get_version_2_arches(json: &Value) -> Option> { 136 | let manifests = match json.get("manifests") { 137 | Some(m) => m.as_array().unwrap(), 138 | None => { 139 | warn!("no manifests found: {:#?}", json); 140 | return None; 141 | } 142 | }; 143 | let mut arches = Vec::new(); 144 | for mani in manifests { 145 | let platform = match mani.get("platform") { 146 | Some(p) => p, 147 | None => { 148 | warn!("no platforms detected: {:#?}", mani); 149 | return None; 150 | } 151 | }; 152 | let arch = match platform.get("architecture") { 153 | Some(a) => a.as_str().unwrap(), 154 | None => { 155 | warn!("platform exists but no architecture found: {:#?}", platform); 156 | return None; 157 | } 158 | }; 159 | if !arches.contains(&arch.to_string()) { 160 | arches.push(arch.to_string()); 161 | } 162 | } 163 | debug!("v2 architectures discovered {:#?}", arches); 164 | Some(arches) 165 | } 166 | 167 | 168 | pub async fn get_jwt(url: String, credentials: Option) -> Option { 169 | let client = Client::new(); 170 | 171 | let mut rs = match client.get(&url).send().await { 172 | Ok(r) => r, 173 | Err(e) => { 174 | warn!("Error on initial request for token data"); 175 | return None; 176 | } 177 | }; 178 | 179 | let auth = match rs.headers().get("www-authenticate") { 180 | Some(val) => val.to_str().unwrap(), 181 | None => { 182 | info!("we tried to query for a jwt but did not get www-authenticate header."); 183 | return None; 184 | } 185 | }; 186 | let cap = TOKEN_AUTH_RE.captures(auth).unwrap(); 187 | let authurl = format!("{}?service={}&scope={}", 188 | cap.name("url").unwrap().as_str(), 189 | cap.name("service").unwrap().as_str(), 190 | cap.name("scope").unwrap().as_str()); 191 | let mut auth_req = client.get(authurl.clone()); 192 | if credentials.is_some() { 193 | auth_req = auth_req.basic_auth(credentials.clone().unwrap().user, credentials.clone().unwrap().secret); 194 | } 195 | debug!("AUTH REQ: {:#?}", auth_req); 196 | let mut auth_rs = match auth_req 197 | .send() 198 | .await { 199 | Ok(a) => a, 200 | Err(e) => { 201 | warn!("Couldn't get token from {}: {}", authurl, e); 202 | return None; 203 | } 204 | }; 205 | 206 | let body = match auth_rs.json::().await { 207 | Ok(a) => a, 208 | Err(e) => { 209 | warn!("Didn't deserialize body to json in response: {}", e); 210 | return None 211 | } 212 | }; 213 | match body.get("token") { 214 | Some(a) => Some(a.as_str().unwrap().parse().unwrap()), 215 | None => { 216 | warn!("Couldn't find token in response."); 217 | None 218 | } 219 | } 220 | } 221 | 222 | pub async fn get_credentials_for_registry(registry: String) -> Option { 223 | let cred_path = match read_setting_string("registry_credential_path") { 224 | Ok(path) => path, 225 | Err(e) => { 226 | warn!{"cred path not found in config (registry_credential_path)"} 227 | return None; 228 | } 229 | }; 230 | let cred_file = format!("{}/{}.toml",cred_path,registry); 231 | if !Path::new(&cred_file).exists() { 232 | warn!("credential file does not exist: {}", cred_file); 233 | return None; 234 | }; 235 | let cred: config::Config = match Config::builder() 236 | .add_source(config::File::with_name(&cred_file)) 237 | .build(){ 238 | Ok(config) => config, 239 | Err(e) => { 240 | warn!("path '{}' exists but config couldn't initialize", cred_file); 241 | return None; 242 | } 243 | }; 244 | 245 | let user: String = match cred.get::("user") { 246 | Ok(user) => user, 247 | Err(e) => { 248 | warn!("In '{}', config key 'user' does not exist: {}", cred_file, e); 249 | return None; 250 | } 251 | }; 252 | let secret: String = match cred.get::("secret") { 253 | Ok(secret) => secret, 254 | Err(e) => { 255 | warn!("In '{}', config key 'secret' does not exist: {}", cred_file, e); 256 | return None; 257 | } 258 | }; 259 | Some(RegistryCredential {user, secret}) 260 | } 261 | 262 | #[derive(Clone, Debug)] 263 | pub struct RegistryCredential { 264 | user: String, 265 | secret: String 266 | } 267 | -------------------------------------------------------------------------------- /src/metrics.rs: -------------------------------------------------------------------------------- 1 | use crate::consts::APP_NAME; 2 | use actix_web_prom::{PrometheusMetrics, PrometheusMetricsBuilder}; 3 | use prometheus::GaugeVec; 4 | lazy_static! { 5 | // setup prometheus 6 | pub static ref STATIC_PROM: PrometheusMetrics = PrometheusMetricsBuilder::new(APP_NAME) 7 | .endpoint("/metrics") 8 | .build() 9 | .unwrap(); 10 | pub static ref APPVER: GaugeVec = register_gauge_vec!( 11 | format!("{}_app_info",APP_NAME), 12 | "static app labels that potentially only change at restart", 13 | &["crate_version", "git_hash"] 14 | ) 15 | .unwrap(); 16 | } 17 | 18 | pub fn register_metrics() { 19 | STATIC_PROM 20 | .registry 21 | .register(Box::new(APPVER.clone())) 22 | .expect("couldn't register appver metric"); 23 | } 24 | -------------------------------------------------------------------------------- /src/models.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use serde_json::{Map, Value}; 3 | use strum_macros::EnumString; 4 | 5 | #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Default)] 6 | pub struct AdmissionReview { 7 | /// request is the incoming AdmissionRequest object 8 | #[serde(rename = "request", skip_serializing_if = "Option::is_none")] 9 | pub request: Option, 10 | /// response is the outgoing, mutated object. 11 | #[serde(rename = "response", skip_serializing_if = "Option::is_none")] 12 | pub response: Option, 13 | } 14 | #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Default)] 15 | pub struct AdmissionRequest { 16 | /// uid is an identifier for the individual request/response 17 | pub uid: String, 18 | /// kind is the kind of the object 19 | pub kind: GroupVersionKind, 20 | /// resource is the fully-qualified resource being requested 21 | pub resource: GroupVersionResource, 22 | #[serde(rename = "subResource", skip_serializing_if = "Option::is_none")] 23 | pub sub_resource: Option, 24 | /// request_kind is the fully-qualified type of the original API request 25 | #[serde(rename = "requestKind", skip_serializing_if = "Option::is_none")] 26 | pub request_kind: Option, 27 | /// request_sub_resource is the name of the subresource of the original API request 28 | #[serde(rename = "requestSubResource", skip_serializing_if = "Option::is_none")] 29 | pub request_sub_resource: Option, 30 | /// name is the name of the object as presented in the request. 31 | #[serde(rename = "name", skip_serializing_if = "Option::is_none")] 32 | pub name: Option, 33 | /// namespace is the namespace associated with the request, if any. 34 | #[serde(rename = "namespace", skip_serializing_if = "Option::is_none")] 35 | pub namespace: Option, 36 | /// operation is the operation being performed, which may be different than requested. 37 | /// For example, a PATCH might wind up resulting in a CREATE or UPDATE operation. 38 | pub operation: Operation, 39 | /// user_info is information about the requesting user 40 | #[serde(rename = "userInfo")] 41 | pub user_info: Map, 42 | /// object is the object from the incoming request. 43 | #[serde(rename = "object", skip_serializing_if = "Option::is_none")] 44 | pub object: Option>, 45 | /// old_object is the existing object, only populated during DELETE or UPDATE requests. 46 | #[serde(rename = "oldObject", skip_serializing_if = "Option::is_none")] 47 | pub old_object: Option>, 48 | /// dry_run indicates whether the modifications will be persisted for the object. default false. 49 | #[serde(rename = "dryRun", skip_serializing_if = "Option::is_none")] 50 | pub dry_run: Option, 51 | /// options is the operation options structure of the operation being performed. 52 | #[serde(rename = "options", skip_serializing_if = "Option::is_none")] 53 | pub options: Option>, 54 | } 55 | #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Default)] 56 | pub struct AdmissionResponse { 57 | /// uid is an identifier for the individual response 58 | pub uid: String, 59 | /// allowed indicates whether or not the admission request was permitted 60 | pub allowed: bool, 61 | /// status contains extra details into why an admission request was denied. 62 | #[serde(rename = "status", skip_serializing_if = "Option::is_none")] 63 | pub status: Option>, 64 | /// patch is the jsonpatch (RFC6902) for the object 65 | #[serde(rename = "patch", skip_serializing_if = "Option::is_none")] 66 | pub patch: Option, 67 | /// patch_type is the type of patch. Currently, only JSONPatch can be used. 68 | #[serde(rename = "patchType", skip_serializing_if = "Option::is_none")] 69 | pub patch_type: Option, 70 | /// audit_annotations is a structured key-value map set by the remote admission controller 71 | #[serde(rename = "auditAnnotations", skip_serializing_if = "Option::is_none")] 72 | pub audit_annotations: Option>, 73 | /// warnings is a list of warning messages to return to the api client 74 | #[serde(rename = "warnings", skip_serializing_if = "Option::is_none")] 75 | pub warnings: Option>, 76 | } 77 | 78 | #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Default)] 79 | pub struct GroupVersionKind { 80 | /// group is the api group of the kind 81 | pub group: String, 82 | /// version is the version value assigned to this kind 83 | pub version: String, 84 | /// kind is the kubernetes object-kind 85 | pub kind: String, 86 | } 87 | #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Default)] 88 | pub struct GroupVersionResource { 89 | /// group is the api group of the resource 90 | pub group: String, 91 | /// version is the version value assigned to this resource 92 | pub version: String, 93 | /// resource is the name of the resource 94 | pub resource: String, 95 | } 96 | #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, EnumString)] 97 | pub enum Operation { 98 | CREATE, 99 | UPDATE, 100 | DELETE, 101 | CONNECT, 102 | } 103 | 104 | impl Default for Operation { 105 | fn default() -> Self { 106 | Operation::UPDATE 107 | } 108 | } 109 | 110 | #[derive(Debug, PartialEq, Clone, EnumString, Serialize, Deserialize)] 111 | pub enum PatchType { 112 | #[serde(rename = "JSONPatch")] 113 | JSONPatch, 114 | } 115 | impl Default for PatchType { 116 | fn default() -> Self { 117 | PatchType::JSONPatch 118 | } 119 | } 120 | 121 | #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Default)] 122 | pub struct StatusResult { 123 | /// status is a string that describes the status 124 | #[serde(rename = "status", skip_serializing_if = "Option::is_none")] 125 | pub status: Option, 126 | /// message is a human-readable description of the operation failed 127 | #[serde(rename = "message", skip_serializing_if = "Option::is_none")] 128 | pub message: Option, 129 | /// reason is a machine-readable description of the failure scenario 130 | #[serde(rename = "reason", skip_serializing_if = "Option::is_none")] 131 | pub reason: Option, 132 | /// details is extended data associated with the reason 133 | #[serde(rename = "details", skip_serializing_if = "Option::is_none")] 134 | pub details: Option, 135 | /// code is the suggested http return code 136 | #[serde(rename = "code", skip_serializing_if = "Option::is_none")] 137 | pub code: Option, 138 | } 139 | -------------------------------------------------------------------------------- /src/mutation.rs: -------------------------------------------------------------------------------- 1 | use crate::models::{AdmissionResponse, AdmissionReview, GroupVersionKind, StatusResult}; 2 | use crate::SETTINGS; 3 | use actix_web::{post, web}; 4 | use base64::{engine::general_purpose, Engine as _}; 5 | use serde_json::{json, Value}; 6 | use std::collections::HashMap; 7 | use crate::manifest::validate_manifest; 8 | use array_tool::vec::Union; 9 | 10 | 11 | fn generate_error_response(uid: String, msg: &str) -> AdmissionReview { 12 | let message = StatusResult { 13 | code: Some(400), 14 | reason: Some(msg.to_string()), 15 | message: Some(msg.to_string()), 16 | details: None, 17 | status: Some(msg.to_string()), 18 | }; 19 | let messages = vec![message]; 20 | let mut response = AdmissionResponse::default(); 21 | response.uid = uid; 22 | response.allowed = false; 23 | response.status = Some(messages); 24 | let mut review = AdmissionReview::default(); 25 | review.response = Some(response); 26 | review 27 | } 28 | 29 | 30 | #[post("/mutate")] 31 | pub async fn mutate_handler( 32 | incoming_review: web::Json, 33 | ) -> web::Json { 34 | let req = incoming_review.request.clone().unwrap(); 35 | let object = req.object.clone().unwrap(); 36 | let mut patches: Vec = Vec::new(); 37 | // is this an actual kubernetes object or junk? 38 | let kind: GroupVersionKind = req.kind; 39 | 40 | // build response object 41 | let mut response = AdmissionResponse::default(); 42 | response.uid = req.uid.clone(); 43 | response.allowed = true; 44 | 45 | // figure out if we should mutate 46 | if kind.kind == "Pod" { 47 | // A pod either has a name or a generateName. 48 | match object.get("metadata").unwrap().get("name") { 49 | Some(name) => { info!("Considering pod {}",name) } 50 | None => { info!("Considering generateName {}", object.get("metadata").unwrap().get("generateName").unwrap()) } 51 | }; 52 | 53 | let spec = match object.get("spec") { 54 | Some(s) => s, 55 | None => { 56 | warn!("We think object is a pod, but it has no spec?"); 57 | return web::Json(generate_error_response(req.uid.clone(), "Pod has no spec?")); 58 | } 59 | }; 60 | let containers = match spec.get("containers") { 61 | Some(c) => c.as_array().unwrap(), 62 | None => { 63 | warn!("We think the object is a pod, but it has no containers?"); 64 | return web::Json(generate_error_response( 65 | req.uid.clone(), 66 | "PodSpec has no containers?", 67 | )); 68 | } 69 | }; 70 | 71 | let supported_architectures: Vec = match SETTINGS 72 | .read() 73 | .unwrap() 74 | .get::>("supported_architectures") { 75 | Ok(vs) => vs, 76 | Err(e) => { 77 | warn!("{e}: no supported architectures found (or error in config file?) -- letting pod through without patch"); 78 | let mut review = AdmissionReview::default(); 79 | review.response = Some(response); 80 | return web::Json(review); 81 | } 82 | }; 83 | let mut toleration_config: HashMap = match SETTINGS 84 | .read() 85 | .unwrap() 86 | .get::>("tolerations") 87 | { 88 | Ok(c) => c, 89 | Err(_) => { 90 | warn!("No toleration is specified in configfile, letting pod through without patch."); 91 | let mut review = AdmissionReview::default(); 92 | review.response = Some(response); 93 | return web::Json(review); 94 | } 95 | }; 96 | 97 | 98 | for architecture in supported_architectures { 99 | let container_count: u16 = containers.len() as u16; 100 | let mut match_count: u16 = 0; 101 | for container in containers { 102 | let obj: HashMap = serde_json::from_value(container.clone()).unwrap(); 103 | let image = obj.get("image").unwrap().as_str().unwrap(); 104 | 105 | let arches = match validate_manifest(image.to_string()).await { 106 | Some(a) => a, 107 | None => { 108 | warn!("Can't find architecture for image {}", image); 109 | vec![] 110 | } 111 | }; 112 | 113 | if arches.contains(&architecture) { 114 | info!("HIT: {image} has an image of type {architecture}"); 115 | match_count += 1; 116 | } else { 117 | info!("MISS: image {image} doesn't contain an {architecture} image"); 118 | }; 119 | 120 | } 121 | if match_count == container_count { 122 | let mut arch_toleration = toleration_config.clone(); 123 | arch_toleration.entry("value".to_string()) 124 | .and_modify(|val| *val = architecture.clone()).or_insert(architecture.clone()); 125 | // all of the containers of this pod match supported image list, lets add the toleration 126 | debug!("patching pod with architecture {architecture}."); 127 | patches.push(json!({ 128 | "op": "add", 129 | "path": "/spec/tolerations/-", 130 | "value": arch_toleration 131 | })); 132 | }; 133 | 134 | } 135 | 136 | let tolerations = spec.get("tolerations"); 137 | if tolerations.is_none() { 138 | // no previous tolerations, so we need to patch it in 139 | if patches.len() > 0 { 140 | debug!("pod did not have previous tolerations"); 141 | patches.insert(0, (json!({ 142 | "op": "add", 143 | "path": "/spec", 144 | "value": { 145 | "tolerations": [] 146 | } 147 | } 148 | ))); 149 | } 150 | }; 151 | } 152 | // build review wrapper 153 | if patches.len() > 0 { 154 | response.patch = Some(general_purpose::STANDARD.encode(json!(patches).to_string())); 155 | } 156 | let mut review = AdmissionReview::default(); 157 | review.response = Some(response); 158 | 159 | // send it 160 | web::Json(review) 161 | } 162 | -------------------------------------------------------------------------------- /src/tests/admission-review-not-pod.json: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "admission.k8s.io/v1", 3 | "kind": "AdmissionReview", 4 | "request": { 5 | "uid": "705ab4f5-6393-11e8-b7cc-42010a800002", 6 | "kind": {"group":"autoscaling","version":"v1","kind":"Scale"}, 7 | "resource": {"group":"apps","version":"v1","resource":"deployments"}, 8 | "subResource": "scale", 9 | "requestKind": {"group":"autoscaling","version":"v1","kind":"Scale"}, 10 | "requestResource": {"group":"apps","version":"v1","resource":"deployments"}, 11 | "requestSubResource": "scale", 12 | "name": "my-deployment", 13 | "namespace": "my-namespace", 14 | "operation": "UPDATE", 15 | "userInfo": { 16 | "username": "admin", 17 | "uid": "014fbff9a07c", 18 | "groups": ["system:authenticated","my-admin-group"], 19 | "extra": { 20 | "some-key":["some-value1", "some-value2"] 21 | } 22 | }, 23 | "object": {"apiVersion":"autoscaling/v1","kind":"Scale","spec":{},"metadata":{"labels":{},"annotations":{"ndots.vsix.me/ndots":"1"}}}, 24 | "oldObject": {"apiVersion":"autoscaling/v1","kind":"Scale"}, 25 | "options": {"apiVersion":"meta.k8s.io/v1","kind":"UpdateOptions"}, 26 | "dryRun": false 27 | } 28 | } 29 | 30 | -------------------------------------------------------------------------------- /src/tests/admission-review-pod-half-arm.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "AdmissionReview", 3 | "apiVersion": "admission.k8s.io/v1beta1", 4 | "request": { 5 | "uid": "0df28fbd-5f5f-11e8-bc74-36e6bb280816", 6 | "kind": { 7 | "group": "", 8 | "version": "v1", 9 | "kind": "Pod" 10 | }, 11 | "resource": { 12 | "group": "", 13 | "version": "v1", 14 | "resource": "pods" 15 | }, 16 | "namespace": "dummy", 17 | "operation": "CREATE", 18 | "userInfo": { 19 | "username": "system:serviceaccount:kube-system:replicaset-controller", 20 | "uid": "a7e0ab33-5f29-11e8-8a3c-36e6bb280816", 21 | "groups": [ 22 | "system:serviceaccounts", 23 | "system:serviceaccounts:kube-system", 24 | "system:authenticated" 25 | ] 26 | }, 27 | "object": { 28 | "metadata": { 29 | "generateName": "nginx-deployment-6c54bd5869-", 30 | "creationTimestamp": null, 31 | "labels": { 32 | "app": "nginx", 33 | "pod-template-hash": "2710681425" 34 | }, 35 | "annotations": { 36 | "openshift.io/scc": "restricted" 37 | }, 38 | "ownerReferences": [ 39 | { 40 | "apiVersion": "extensions/v1beta1", 41 | "kind": "ReplicaSet", 42 | "name": "nginx-deployment-6c54bd5869", 43 | "uid": "16c2b355-5f5d-11e8-ac91-36e6bb280816", 44 | "controller": true, 45 | "blockOwnerDeletion": true 46 | } 47 | ] 48 | }, 49 | "spec": { 50 | "volumes": [ 51 | { 52 | "name": "default-token-tq5lq", 53 | "secret": { 54 | "secretName": "default-token-tq5lq" 55 | } 56 | } 57 | ], 58 | "containers": [ 59 | { 60 | "command": [ 61 | "template", 62 | "-template", 63 | "/tmp/vault-config.json:/vault/config/vault.json" 64 | ], 65 | "env": [ 66 | { 67 | "name": "POD_NAME", 68 | "valueFrom": { 69 | "fieldRef": { 70 | "apiVersion": "v1", 71 | "fieldPath": "metadata.name" 72 | } 73 | } 74 | }, 75 | { 76 | "name": "AZURE_AUTH_LOCATION", 77 | "value": "/etc/azure/credentials" 78 | } 79 | ], 80 | "image": "docker.io/banzaicloud/bank-vaults:master", 81 | "imagePullPolicy": "IfNotPresent", 82 | "name": "config-templating", 83 | "resources": { 84 | "limits": { 85 | "cpu": "1", 86 | "memory": "512Mi" 87 | }, 88 | "requests": { 89 | "cpu": "250m", 90 | "memory": "256Mi" 91 | } 92 | }, 93 | "terminationMessagePath": "/dev/termination-log", 94 | "terminationMessagePolicy": "File", 95 | "volumeMounts": [ 96 | { 97 | "mountPath": "/etc/azure/credentials", 98 | "name": "azure-creds", 99 | "subPath": "credentials" 100 | }, 101 | { 102 | "mountPath": "/vault/config", 103 | "name": "vault-config" 104 | }, 105 | { 106 | "mountPath": "/tmp", 107 | "name": "vault-raw-config" 108 | }, 109 | { 110 | "mountPath": "/vault/tls", 111 | "name": "vault-tls" 112 | }, 113 | { 114 | "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount", 115 | "name": "kube-api-access-p2px7", 116 | "readOnly": true 117 | } 118 | ] 119 | }, 120 | { 121 | "name": "nginx", 122 | "image": "nginx:1.7.9", 123 | "ports": [ 124 | { 125 | "containerPort": 80, 126 | "protocol": "TCP" 127 | } 128 | ], 129 | "resources": {}, 130 | "volumeMounts": [ 131 | { 132 | "name": "default-token-tq5lq", 133 | "readOnly": true, 134 | "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount" 135 | } 136 | ], 137 | "terminationMessagePath": "/dev/termination-log", 138 | "terminationMessagePolicy": "File", 139 | "imagePullPolicy": "IfNotPresent", 140 | "securityContext": { 141 | "capabilities": { 142 | "drop": [ 143 | "KILL", 144 | "MKNOD", 145 | "SETGID", 146 | "SETUID" 147 | ] 148 | }, 149 | "runAsUser": 1000080000 150 | } 151 | } 152 | ], 153 | "restartPolicy": "Always", 154 | "terminationGracePeriodSeconds": 30, 155 | "dnsPolicy": "ClusterFirst", 156 | "serviceAccountName": "default", 157 | "serviceAccount": "default", 158 | "securityContext": { 159 | "seLinuxOptions": { 160 | "level": "s0:c9,c4" 161 | }, 162 | "fsGroup": 1000080000 163 | }, 164 | "imagePullSecrets": [ 165 | { 166 | "name": "default-dockercfg-kksdv" 167 | } 168 | ], 169 | "schedulerName": "default-scheduler" 170 | }, 171 | "status": {} 172 | }, 173 | "oldObject": null 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /src/tests/admission-review-pod-match.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "AdmissionReview", 3 | "apiVersion": "admission.k8s.io/v1beta1", 4 | "request": { 5 | "uid": "0df28fbd-5f5f-11e8-bc74-36e6bb280816", 6 | "kind": { 7 | "group": "", 8 | "version": "v1", 9 | "kind": "Pod" 10 | }, 11 | "resource": { 12 | "group": "", 13 | "version": "v1", 14 | "resource": "pods" 15 | }, 16 | "namespace": "dummy", 17 | "operation": "CREATE", 18 | "userInfo": { 19 | "username": "system:serviceaccount:kube-system:replicaset-controller", 20 | "uid": "a7e0ab33-5f29-11e8-8a3c-36e6bb280816", 21 | "groups": [ 22 | "system:serviceaccounts", 23 | "system:serviceaccounts:kube-system", 24 | "system:authenticated" 25 | ] 26 | }, 27 | "object": { 28 | "metadata": { 29 | "generateName": "nginx-deployment-6c54bd5869-", 30 | "creationTimestamp": null, 31 | "labels": { 32 | "app": "nginx", 33 | "pod-template-hash": "2710681425" 34 | }, 35 | "annotations": { 36 | "openshift.io/scc": "restricted" 37 | }, 38 | "ownerReferences": [ 39 | { 40 | "apiVersion": "extensions/v1beta1", 41 | "kind": "ReplicaSet", 42 | "name": "nginx-deployment-6c54bd5869", 43 | "uid": "16c2b355-5f5d-11e8-ac91-36e6bb280816", 44 | "controller": true, 45 | "blockOwnerDeletion": true 46 | } 47 | ] 48 | }, 49 | "spec": { 50 | "volumes": [ 51 | { 52 | "name": "default-token-tq5lq", 53 | "secret": { 54 | "secretName": "default-token-tq5lq" 55 | } 56 | } 57 | ], 58 | "containers": [ 59 | { 60 | "name": "nginx", 61 | "image": "nginx:1.23.3", 62 | "ports": [ 63 | { 64 | "containerPort": 80, 65 | "protocol": "TCP" 66 | } 67 | ], 68 | "resources": {}, 69 | "volumeMounts": [ 70 | { 71 | "name": "default-token-tq5lq", 72 | "readOnly": true, 73 | "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount" 74 | } 75 | ], 76 | "terminationMessagePath": "/dev/termination-log", 77 | "terminationMessagePolicy": "File", 78 | "imagePullPolicy": "IfNotPresent", 79 | "securityContext": { 80 | "capabilities": { 81 | "drop": [ 82 | "KILL", 83 | "MKNOD", 84 | "SETGID", 85 | "SETUID" 86 | ] 87 | }, 88 | "runAsUser": 1000080000 89 | } 90 | } 91 | ], 92 | "restartPolicy": "Always", 93 | "terminationGracePeriodSeconds": 30, 94 | "dnsPolicy": "ClusterFirst", 95 | "serviceAccountName": "default", 96 | "serviceAccount": "default", 97 | "securityContext": { 98 | "seLinuxOptions": { 99 | "level": "s0:c9,c4" 100 | }, 101 | "fsGroup": 1000080000 102 | }, 103 | "imagePullSecrets": [ 104 | { 105 | "name": "default-dockercfg-kksdv" 106 | } 107 | ], 108 | "schedulerName": "default-scheduler" 109 | }, 110 | "status": {} 111 | }, 112 | "oldObject": null 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/tests/admission-review-pod.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "AdmissionReview", 3 | "apiVersion": "admission.k8s.io/v1beta1", 4 | "request": { 5 | "uid": "0df28fbd-5f5f-11e8-bc74-36e6bb280816", 6 | "kind": { 7 | "group": "", 8 | "version": "v1", 9 | "kind": "Pod" 10 | }, 11 | "resource": { 12 | "group": "", 13 | "version": "v1", 14 | "resource": "pods" 15 | }, 16 | "namespace": "dummy", 17 | "operation": "CREATE", 18 | "userInfo": { 19 | "username": "system:serviceaccount:kube-system:replicaset-controller", 20 | "uid": "a7e0ab33-5f29-11e8-8a3c-36e6bb280816", 21 | "groups": [ 22 | "system:serviceaccounts", 23 | "system:serviceaccounts:kube-system", 24 | "system:authenticated" 25 | ] 26 | }, 27 | "object": { 28 | "metadata": { 29 | "generateName": "nginx-deployment-6c54bd5869-", 30 | "creationTimestamp": null, 31 | "labels": { 32 | "app": "nginx", 33 | "pod-template-hash": "2710681425" 34 | }, 35 | "annotations": { 36 | "openshift.io/scc": "restricted" 37 | }, 38 | "ownerReferences": [ 39 | { 40 | "apiVersion": "extensions/v1beta1", 41 | "kind": "ReplicaSet", 42 | "name": "nginx-deployment-6c54bd5869", 43 | "uid": "16c2b355-5f5d-11e8-ac91-36e6bb280816", 44 | "controller": true, 45 | "blockOwnerDeletion": true 46 | } 47 | ] 48 | }, 49 | "spec": { 50 | "volumes": [ 51 | { 52 | "name": "default-token-tq5lq", 53 | "secret": { 54 | "secretName": "default-token-tq5lq" 55 | } 56 | } 57 | ], 58 | "containers": [ 59 | { 60 | "name": "nginx", 61 | "image": "nginx:1.7.9", 62 | "ports": [ 63 | { 64 | "containerPort": 80, 65 | "protocol": "TCP" 66 | } 67 | ], 68 | "resources": {}, 69 | "volumeMounts": [ 70 | { 71 | "name": "default-token-tq5lq", 72 | "readOnly": true, 73 | "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount" 74 | } 75 | ], 76 | "terminationMessagePath": "/dev/termination-log", 77 | "terminationMessagePolicy": "File", 78 | "imagePullPolicy": "IfNotPresent", 79 | "securityContext": { 80 | "capabilities": { 81 | "drop": [ 82 | "KILL", 83 | "MKNOD", 84 | "SETGID", 85 | "SETUID" 86 | ] 87 | }, 88 | "runAsUser": 1000080000 89 | } 90 | }, 91 | { 92 | "name": "nginx", 93 | "image": "nginx:1.7.9", 94 | "ports": [ 95 | { 96 | "containerPort": 80, 97 | "protocol": "TCP" 98 | } 99 | ], 100 | "resources": {}, 101 | "volumeMounts": [ 102 | { 103 | "name": "default-token-tq5lq", 104 | "readOnly": true, 105 | "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount" 106 | } 107 | ], 108 | "terminationMessagePath": "/dev/termination-log", 109 | "terminationMessagePolicy": "File", 110 | "imagePullPolicy": "IfNotPresent", 111 | "securityContext": { 112 | "capabilities": { 113 | "drop": [ 114 | "KILL", 115 | "MKNOD", 116 | "SETGID", 117 | "SETUID" 118 | ] 119 | }, 120 | "runAsUser": 1000080000 121 | } 122 | } 123 | ], 124 | "restartPolicy": "Always", 125 | "terminationGracePeriodSeconds": 30, 126 | "dnsPolicy": "ClusterFirst", 127 | "serviceAccountName": "default", 128 | "serviceAccount": "default", 129 | "securityContext": { 130 | "seLinuxOptions": { 131 | "level": "s0:c9,c4" 132 | }, 133 | "fsGroup": 1000080000 134 | }, 135 | "imagePullSecrets": [ 136 | { 137 | "name": "default-dockercfg-kksdv" 138 | } 139 | ], 140 | "schedulerName": "default-scheduler" 141 | }, 142 | "status": {} 143 | }, 144 | "oldObject": null 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/tests/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod test_bl; 3 | #[cfg(test)] 4 | mod test_serde; 5 | -------------------------------------------------------------------------------- /src/tests/test_bl.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | #[ctor::ctor] 3 | fn init() { 4 | pretty_env_logger::init(); 5 | } 6 | 7 | use crate::models::AdmissionReview; 8 | use crate::mutation::mutate_handler; 9 | use actix_web::{test, App}; 10 | use serde_json; 11 | use std::fs; 12 | use crate::manifest::validate_manifest; 13 | 14 | #[test] 15 | async fn test_manifest_validator_v2() { 16 | let expected = "arm64".to_string(); 17 | let results = validate_manifest("nginx:latest".to_string()).await.unwrap(); 18 | assert!(results.contains(&expected)); 19 | } 20 | #[test] 21 | async fn test_manifest_validator_v2_quay() { 22 | let expected = "arm64".to_string(); 23 | let results = validate_manifest("quay.io/metallb/controller:latest".to_string()).await.unwrap(); 24 | assert!(results.contains(&expected)); 25 | } 26 | #[test] 27 | // redundant because I fixed docker.io schema v1 lookup, but, didn't want to remove the code just 28 | // in case 29 | async fn test_manifest_validator_v1() { 30 | let expected = "amd64".to_string(); 31 | let results = match validate_manifest("nginx:latest".to_string()).await { 32 | Some(s) => s, 33 | None => vec![] 34 | }; 35 | assert!(results.contains(&expected)); 36 | } 37 | 38 | #[actix_web::test] 39 | async fn test_mutate_handler_non_pod() { 40 | let app = test::init_service(App::new().service(mutate_handler)).await; 41 | let review_json = fs::read_to_string("./src/tests/admission-review-not-pod.json") 42 | .expect("Unable to read file!"); 43 | let review: AdmissionReview = serde_json::from_str(review_json.as_str()).unwrap(); 44 | let req = test::TestRequest::post() 45 | .uri("/mutate") 46 | .set_json(review) 47 | .to_request(); 48 | let resp: AdmissionReview = test::call_and_read_body_json(&app, req).await; 49 | let rs_resp = resp.response.unwrap(); 50 | assert_eq!(&rs_resp.allowed, &true); 51 | assert!(&rs_resp.patch.is_none()); 52 | } 53 | 54 | #[actix_web::test] 55 | async fn test_mutate_handler_as_pod_not_arm() { 56 | std::env::set_var("TOLERABLE_SUPPORTED_ARCHITECTURES","arm64"); 57 | let app = test::init_service(App::new().service(mutate_handler)).await; 58 | let review_json = 59 | fs::read_to_string("./src/tests/admission-review-pod.json").expect("Unable to read file!"); 60 | let review: AdmissionReview = serde_json::from_str(review_json.as_str()).unwrap(); 61 | let req = test::TestRequest::post() 62 | .uri("/mutate") 63 | .set_json(review) 64 | .to_request(); 65 | let resp: AdmissionReview = test::call_and_read_body_json(&app, req).await; 66 | let rs_resp = resp.response.unwrap(); 67 | assert_eq!(&rs_resp.allowed, &true); 68 | assert!(&rs_resp.patch.is_none()); 69 | } 70 | #[actix_web::test] 71 | async fn test_mutate_handler_as_pod_matches_arm() { 72 | std::env::set_var("TOLERABLE_SUPPORTED_ARCHITECTURES","arm64"); 73 | let app = test::init_service(App::new().service(mutate_handler)).await; 74 | let review_json = fs::read_to_string("./src/tests/admission-review-pod-match.json") 75 | .expect("Unable to read file!"); 76 | let review: AdmissionReview = serde_json::from_str(review_json.as_str()).unwrap(); 77 | let req = test::TestRequest::post() 78 | .uri("/mutate") 79 | .set_json(review) 80 | .to_request(); 81 | let resp: AdmissionReview = test::call_and_read_body_json(&app, req).await; 82 | let rs_resp = resp.response.unwrap(); 83 | assert_eq!(&rs_resp.allowed, &true); 84 | assert!(&rs_resp.patch.is_some()); 85 | } 86 | 87 | #[actix_web::test] 88 | async fn test_mutate_handler_half_pod_supports_arm() { 89 | std::env::set_var("TOLERABLE_SUPPORTED_ARCHITECTURES","arm64"); 90 | let app = test::init_service(App::new().service(mutate_handler)).await; 91 | let review_json = 92 | fs::read_to_string("./src/tests/admission-review-pod-half-arm.json").expect("Unable to read file!"); 93 | let review: AdmissionReview = serde_json::from_str(review_json.as_str()).unwrap(); 94 | let req = test::TestRequest::post() 95 | .uri("/mutate") 96 | .set_json(review) 97 | .to_request(); 98 | let resp: AdmissionReview = test::call_and_read_body_json(&app, req).await; 99 | let rs_resp = resp.response.unwrap(); 100 | assert_eq!(&rs_resp.allowed, &true); 101 | assert!(&rs_resp.patch.is_none()); 102 | } 103 | -------------------------------------------------------------------------------- /src/tests/test_serde.rs: -------------------------------------------------------------------------------- 1 | use crate::models::{AdmissionRequest, AdmissionResponse, AdmissionReview}; 2 | use serde_json; 3 | use std::fs; 4 | #[test] 5 | fn test_noop() -> () {} 6 | 7 | #[test] 8 | fn deserialize_review_request() { 9 | // tests that our AdmissionReview request parsing matches the specified documentation 10 | // see kubernetes docs for dynamic admission control, 'webhook request and response' 11 | let review_json = fs::read_to_string("./src/tests/admission-review-not-pod.json") 12 | .expect("Unable to read file!"); 13 | let review: AdmissionReview = serde_json::from_str(review_json.as_str()).unwrap(); 14 | 15 | // decompose the payload into sub objects for individual testing 16 | let request: AdmissionRequest = review.request.unwrap(); 17 | let object = request.object.unwrap(); 18 | let old_object = request.old_object.unwrap(); 19 | let options = request.options.unwrap(); 20 | 21 | assert_eq!(request.uid, "705ab4f5-6393-11e8-b7cc-42010a800002"); 22 | assert_eq!(request.kind.version, "v1"); 23 | assert_eq!(request.resource.group, "apps"); 24 | assert_eq!(request.user_info.get("username").unwrap(), "admin"); 25 | assert_eq!(object.get("kind").unwrap(), "Scale"); 26 | assert_eq!(old_object.get("kind").unwrap(), "Scale"); 27 | assert_eq!(options.get("kind").unwrap(), "UpdateOptions"); 28 | } 29 | 30 | #[test] 31 | fn serialize_response_to_api() { 32 | let mut response = AdmissionResponse::default(); 33 | 34 | response.uid = String::from("foo"); 35 | 36 | // build the object before serializing. 37 | let mut review: AdmissionReview = AdmissionReview::default(); 38 | review.response = Some(response); 39 | let json = serde_json::ser::to_string(&review).unwrap(); 40 | assert_eq!(json, "{\"response\":{\"uid\":\"foo\",\"allowed\":false}}"); 41 | } 42 | -------------------------------------------------------------------------------- /tolerable.toml: -------------------------------------------------------------------------------- 1 | ssl_key_path = "./key.pem" 2 | ssl_cert_path = "./cert.pem" 3 | registry_credential_path = "./creds" 4 | supported_architectures = [ "amd64", "arm64" ] 5 | 6 | [tolerations] 7 | effect = "NoSchedule" 8 | operator = "Equal" 9 | key = "kubernetes.io/arch" 10 | -------------------------------------------------------------------------------- /tools/target_arch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ### Thanks to qdrant/qdrant for this idea! 3 | case $TARGETARCH in 4 | "amd64") 5 | echo "x86_64-unknown-linux-gnu" 6 | ;; 7 | "arm64") 8 | echo "aarch64-unknown-linux-gnu" 9 | ;; 10 | esac 11 | --------------------------------------------------------------------------------