├── .github ├── dependabot.yaml └── workflows │ ├── clippy.yaml │ └── test-e2e.yaml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── cli ├── Cargo.toml └── src │ ├── client │ └── mod.rs │ └── main.rs ├── daemon ├── Cargo.toml └── src │ ├── engine │ ├── alpine.rs │ ├── containers.rs │ ├── mod.rs │ └── slirp.rs │ ├── handlers │ ├── container.rs │ └── mod.rs │ ├── main.rs │ └── util │ └── mod.rs ├── libsquish ├── Cargo.toml └── src │ ├── lib.rs │ └── squishfile.rs ├── pid1 ├── Cargo.toml └── src │ ├── engine │ └── mod.rs │ └── main.rs ├── setup.sh ├── test ├── e2e │ ├── 001-container-starts.sh │ ├── 002-container-networking-works.sh │ ├── 003-file-bind-mounts-work.sh │ ├── 004-other-alpine-versions-work.sh │ ├── 005-env-vars-work.sh │ ├── 006-rw-file-binds-work.sh │ └── 007-pid-ns-check.sh ├── http-asm ├── squishfiles │ ├── 004-squishfile-3.13.toml │ ├── 005-squishfile-port-env.toml │ ├── 006-squishfile-rw-mount.toml │ ├── 007-squishfile-pid-ns.toml │ └── default.toml ├── support │ ├── 005-env-to-tmp-and-run.sh │ ├── 006-rewrite-mounted-file.sh │ ├── 006-scratch │ ├── 007-pid-ns-check.sh │ └── 007-scratch └── test-e2e.sh └── tools └── install-hooks /.github/dependabot.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: cargo 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "12:00" 8 | open-pull-requests-limit: 10 9 | -------------------------------------------------------------------------------- /.github/workflows/clippy.yaml: -------------------------------------------------------------------------------- 1 | name: Run clippy lints 2 | on: 3 | push: 4 | branches: 5 | - mistress 6 | pull_request: 7 | branches: 8 | - mistress 9 | 10 | jobs: 11 | run-clippy: 12 | if: github.actor != 'dependabot' 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Install latest stable Rust 17 | uses: actions-rs/toolchain@v1 18 | with: 19 | toolchain: stable 20 | override: true 21 | - uses: Swatinem/rust-cache@v1 22 | with: 23 | key: clippy 24 | - name: Run clippy 25 | run: cargo clippy 26 | -------------------------------------------------------------------------------- /.github/workflows/test-e2e.yaml: -------------------------------------------------------------------------------- 1 | name: Run all tests on latest stable Rust 2 | on: 3 | push: 4 | branches: 5 | - mistress 6 | pull_request: 7 | branches: 8 | - mistress 9 | 10 | jobs: 11 | run-tests: 12 | if: github.actor != 'dependabot' 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Install latest stable Rust 17 | uses: actions-rs/toolchain@v1 18 | with: 19 | toolchain: stable 20 | override: true 21 | - uses: Swatinem/rust-cache@v1 22 | with: 23 | key: e2e 24 | - name: Run e2e tests 25 | run: ./test/test-e2e.sh 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /container 3 | /cache 4 | -------------------------------------------------------------------------------- /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 = "adler" 7 | version = "1.0.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 10 | 11 | [[package]] 12 | name = "aho-corasick" 13 | version = "0.7.18" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" 16 | dependencies = [ 17 | "memchr", 18 | ] 19 | 20 | [[package]] 21 | name = "atty" 22 | version = "0.2.14" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 25 | dependencies = [ 26 | "hermit-abi", 27 | "libc", 28 | "winapi", 29 | ] 30 | 31 | [[package]] 32 | name = "autocfg" 33 | version = "1.1.0" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 36 | 37 | [[package]] 38 | name = "base64" 39 | version = "0.13.0" 40 | source = "registry+https://github.com/rust-lang/crates.io-index" 41 | checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" 42 | 43 | [[package]] 44 | name = "bitflags" 45 | version = "1.3.2" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 48 | 49 | [[package]] 50 | name = "block-buffer" 51 | version = "0.9.0" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" 54 | dependencies = [ 55 | "generic-array", 56 | ] 57 | 58 | [[package]] 59 | name = "block-buffer" 60 | version = "0.10.3" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" 63 | dependencies = [ 64 | "generic-array", 65 | ] 66 | 67 | [[package]] 68 | name = "buf_redux" 69 | version = "0.8.4" 70 | source = "registry+https://github.com/rust-lang/crates.io-index" 71 | checksum = "b953a6887648bb07a535631f2bc00fbdb2a2216f135552cb3f534ed136b9c07f" 72 | dependencies = [ 73 | "memchr", 74 | "safemem", 75 | ] 76 | 77 | [[package]] 78 | name = "bumpalo" 79 | version = "3.7.0" 80 | source = "registry+https://github.com/rust-lang/crates.io-index" 81 | checksum = "9c59e7af012c713f529e7a3ee57ce9b31ddd858d4b512923602f74608b009631" 82 | 83 | [[package]] 84 | name = "byteorder" 85 | version = "1.4.3" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 88 | 89 | [[package]] 90 | name = "bytes" 91 | version = "1.0.1" 92 | source = "registry+https://github.com/rust-lang/crates.io-index" 93 | checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" 94 | 95 | [[package]] 96 | name = "cc" 97 | version = "1.0.68" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | checksum = "4a72c244c1ff497a746a7e1fb3d14bd08420ecda70c8f25c7112f2781652d787" 100 | 101 | [[package]] 102 | name = "cfg-if" 103 | version = "1.0.0" 104 | source = "registry+https://github.com/rust-lang/crates.io-index" 105 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 106 | 107 | [[package]] 108 | name = "clap" 109 | version = "3.2.22" 110 | source = "registry+https://github.com/rust-lang/crates.io-index" 111 | checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750" 112 | dependencies = [ 113 | "atty", 114 | "bitflags", 115 | "clap_lex", 116 | "indexmap", 117 | "strsim", 118 | "termcolor", 119 | "textwrap", 120 | ] 121 | 122 | [[package]] 123 | name = "clap_lex" 124 | version = "0.2.2" 125 | source = "registry+https://github.com/rust-lang/crates.io-index" 126 | checksum = "5538cd660450ebeb4234cfecf8f2284b844ffc4c50531e66d584ad5b91293613" 127 | dependencies = [ 128 | "os_str_bytes", 129 | ] 130 | 131 | [[package]] 132 | name = "cli" 133 | version = "0.1.0" 134 | dependencies = [ 135 | "clap", 136 | "hyper", 137 | "hyperlocal", 138 | "libsquish", 139 | "serde", 140 | "serde_json", 141 | "tokio", 142 | ] 143 | 144 | [[package]] 145 | name = "core-foundation" 146 | version = "0.9.1" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62" 149 | dependencies = [ 150 | "core-foundation-sys", 151 | "libc", 152 | ] 153 | 154 | [[package]] 155 | name = "core-foundation-sys" 156 | version = "0.8.2" 157 | source = "registry+https://github.com/rust-lang/crates.io-index" 158 | checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" 159 | 160 | [[package]] 161 | name = "cpufeatures" 162 | version = "0.1.4" 163 | source = "registry+https://github.com/rust-lang/crates.io-index" 164 | checksum = "ed00c67cb5d0a7d64a44f6ad2668db7e7530311dd53ea79bcd4fb022c64911c8" 165 | dependencies = [ 166 | "libc", 167 | ] 168 | 169 | [[package]] 170 | name = "cpufeatures" 171 | version = "0.2.5" 172 | source = "registry+https://github.com/rust-lang/crates.io-index" 173 | checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" 174 | dependencies = [ 175 | "libc", 176 | ] 177 | 178 | [[package]] 179 | name = "crc32fast" 180 | version = "1.2.1" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" 183 | dependencies = [ 184 | "cfg-if", 185 | ] 186 | 187 | [[package]] 188 | name = "crypto-common" 189 | version = "0.1.6" 190 | source = "registry+https://github.com/rust-lang/crates.io-index" 191 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" 192 | dependencies = [ 193 | "generic-array", 194 | "typenum", 195 | ] 196 | 197 | [[package]] 198 | name = "daemon" 199 | version = "0.1.0" 200 | dependencies = [ 201 | "flate2", 202 | "haikunator", 203 | "hex", 204 | "hmac-sha256", 205 | "libsquish", 206 | "log", 207 | "nix", 208 | "pretty_env_logger", 209 | "reqwest", 210 | "rlimit", 211 | "serde_json", 212 | "tar", 213 | "tokio", 214 | "tokio-stream", 215 | "tokio-util 0.7.4", 216 | "warp", 217 | "yaml-rust", 218 | ] 219 | 220 | [[package]] 221 | name = "derive-getters" 222 | version = "0.2.0" 223 | source = "registry+https://github.com/rust-lang/crates.io-index" 224 | checksum = "0c5905670fd9c320154f3a4a01c9e609733cd7b753f3c58777ab7d5ce26686b3" 225 | dependencies = [ 226 | "proc-macro2", 227 | "quote", 228 | "syn", 229 | ] 230 | 231 | [[package]] 232 | name = "digest" 233 | version = "0.9.0" 234 | source = "registry+https://github.com/rust-lang/crates.io-index" 235 | checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" 236 | dependencies = [ 237 | "generic-array", 238 | ] 239 | 240 | [[package]] 241 | name = "digest" 242 | version = "0.10.5" 243 | source = "registry+https://github.com/rust-lang/crates.io-index" 244 | checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c" 245 | dependencies = [ 246 | "block-buffer 0.10.3", 247 | "crypto-common", 248 | ] 249 | 250 | [[package]] 251 | name = "encoding_rs" 252 | version = "0.8.28" 253 | source = "registry+https://github.com/rust-lang/crates.io-index" 254 | checksum = "80df024fbc5ac80f87dfef0d9f5209a252f2a497f7f42944cff24d8253cac065" 255 | dependencies = [ 256 | "cfg-if", 257 | ] 258 | 259 | [[package]] 260 | name = "env_logger" 261 | version = "0.7.1" 262 | source = "registry+https://github.com/rust-lang/crates.io-index" 263 | checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" 264 | dependencies = [ 265 | "atty", 266 | "humantime", 267 | "log", 268 | "regex", 269 | "termcolor", 270 | ] 271 | 272 | [[package]] 273 | name = "filetime" 274 | version = "0.2.14" 275 | source = "registry+https://github.com/rust-lang/crates.io-index" 276 | checksum = "1d34cfa13a63ae058bfa601fe9e313bbdb3746427c1459185464ce0fcf62e1e8" 277 | dependencies = [ 278 | "cfg-if", 279 | "libc", 280 | "redox_syscall", 281 | "winapi", 282 | ] 283 | 284 | [[package]] 285 | name = "flate2" 286 | version = "1.0.25" 287 | source = "registry+https://github.com/rust-lang/crates.io-index" 288 | checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" 289 | dependencies = [ 290 | "crc32fast", 291 | "miniz_oxide", 292 | ] 293 | 294 | [[package]] 295 | name = "fnv" 296 | version = "1.0.7" 297 | source = "registry+https://github.com/rust-lang/crates.io-index" 298 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 299 | 300 | [[package]] 301 | name = "foreign-types" 302 | version = "0.3.2" 303 | source = "registry+https://github.com/rust-lang/crates.io-index" 304 | checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 305 | dependencies = [ 306 | "foreign-types-shared", 307 | ] 308 | 309 | [[package]] 310 | name = "foreign-types-shared" 311 | version = "0.1.1" 312 | source = "registry+https://github.com/rust-lang/crates.io-index" 313 | checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 314 | 315 | [[package]] 316 | name = "form_urlencoded" 317 | version = "1.0.1" 318 | source = "registry+https://github.com/rust-lang/crates.io-index" 319 | checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" 320 | dependencies = [ 321 | "matches", 322 | "percent-encoding", 323 | ] 324 | 325 | [[package]] 326 | name = "fuchsia-cprng" 327 | version = "0.1.1" 328 | source = "registry+https://github.com/rust-lang/crates.io-index" 329 | checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" 330 | 331 | [[package]] 332 | name = "futures" 333 | version = "0.3.25" 334 | source = "registry+https://github.com/rust-lang/crates.io-index" 335 | checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0" 336 | dependencies = [ 337 | "futures-channel", 338 | "futures-core", 339 | "futures-executor", 340 | "futures-io", 341 | "futures-sink", 342 | "futures-task", 343 | "futures-util", 344 | ] 345 | 346 | [[package]] 347 | name = "futures-channel" 348 | version = "0.3.25" 349 | source = "registry+https://github.com/rust-lang/crates.io-index" 350 | checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" 351 | dependencies = [ 352 | "futures-core", 353 | "futures-sink", 354 | ] 355 | 356 | [[package]] 357 | name = "futures-core" 358 | version = "0.3.25" 359 | source = "registry+https://github.com/rust-lang/crates.io-index" 360 | checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" 361 | 362 | [[package]] 363 | name = "futures-executor" 364 | version = "0.3.25" 365 | source = "registry+https://github.com/rust-lang/crates.io-index" 366 | checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2" 367 | dependencies = [ 368 | "futures-core", 369 | "futures-task", 370 | "futures-util", 371 | ] 372 | 373 | [[package]] 374 | name = "futures-io" 375 | version = "0.3.25" 376 | source = "registry+https://github.com/rust-lang/crates.io-index" 377 | checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" 378 | 379 | [[package]] 380 | name = "futures-macro" 381 | version = "0.3.25" 382 | source = "registry+https://github.com/rust-lang/crates.io-index" 383 | checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" 384 | dependencies = [ 385 | "proc-macro2", 386 | "quote", 387 | "syn", 388 | ] 389 | 390 | [[package]] 391 | name = "futures-sink" 392 | version = "0.3.25" 393 | source = "registry+https://github.com/rust-lang/crates.io-index" 394 | checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" 395 | 396 | [[package]] 397 | name = "futures-task" 398 | version = "0.3.25" 399 | source = "registry+https://github.com/rust-lang/crates.io-index" 400 | checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" 401 | 402 | [[package]] 403 | name = "futures-util" 404 | version = "0.3.25" 405 | source = "registry+https://github.com/rust-lang/crates.io-index" 406 | checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" 407 | dependencies = [ 408 | "futures-channel", 409 | "futures-core", 410 | "futures-io", 411 | "futures-macro", 412 | "futures-sink", 413 | "futures-task", 414 | "memchr", 415 | "pin-project-lite", 416 | "pin-utils", 417 | "slab", 418 | ] 419 | 420 | [[package]] 421 | name = "generic-array" 422 | version = "0.14.4" 423 | source = "registry+https://github.com/rust-lang/crates.io-index" 424 | checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" 425 | dependencies = [ 426 | "typenum", 427 | "version_check", 428 | ] 429 | 430 | [[package]] 431 | name = "getrandom" 432 | version = "0.2.3" 433 | source = "registry+https://github.com/rust-lang/crates.io-index" 434 | checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" 435 | dependencies = [ 436 | "cfg-if", 437 | "libc", 438 | "wasi 0.10.2+wasi-snapshot-preview1", 439 | ] 440 | 441 | [[package]] 442 | name = "h2" 443 | version = "0.3.10" 444 | source = "registry+https://github.com/rust-lang/crates.io-index" 445 | checksum = "0c9de88456263e249e241fcd211d3954e2c9b0ef7ccfc235a444eb367cae3689" 446 | dependencies = [ 447 | "bytes", 448 | "fnv", 449 | "futures-core", 450 | "futures-sink", 451 | "futures-util", 452 | "http", 453 | "indexmap", 454 | "slab", 455 | "tokio", 456 | "tokio-util 0.6.9", 457 | "tracing", 458 | ] 459 | 460 | [[package]] 461 | name = "haikunator" 462 | version = "0.1.2" 463 | source = "registry+https://github.com/rust-lang/crates.io-index" 464 | checksum = "506e99c70592a28ead6257ac8a20025caf09c7dfcfbe9683d015223f6a06bf2f" 465 | dependencies = [ 466 | "rand 0.3.23", 467 | ] 468 | 469 | [[package]] 470 | name = "hashbrown" 471 | version = "0.9.1" 472 | source = "registry+https://github.com/rust-lang/crates.io-index" 473 | checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" 474 | 475 | [[package]] 476 | name = "headers" 477 | version = "0.3.4" 478 | source = "registry+https://github.com/rust-lang/crates.io-index" 479 | checksum = "f0b7591fb62902706ae8e7aaff416b1b0fa2c0fd0878b46dc13baa3712d8a855" 480 | dependencies = [ 481 | "base64", 482 | "bitflags", 483 | "bytes", 484 | "headers-core", 485 | "http", 486 | "mime", 487 | "sha-1 0.9.6", 488 | "time", 489 | ] 490 | 491 | [[package]] 492 | name = "headers-core" 493 | version = "0.2.0" 494 | source = "registry+https://github.com/rust-lang/crates.io-index" 495 | checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" 496 | dependencies = [ 497 | "http", 498 | ] 499 | 500 | [[package]] 501 | name = "hermit-abi" 502 | version = "0.1.18" 503 | source = "registry+https://github.com/rust-lang/crates.io-index" 504 | checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" 505 | dependencies = [ 506 | "libc", 507 | ] 508 | 509 | [[package]] 510 | name = "hex" 511 | version = "0.4.3" 512 | source = "registry+https://github.com/rust-lang/crates.io-index" 513 | checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" 514 | 515 | [[package]] 516 | name = "hmac-sha256" 517 | version = "1.1.6" 518 | source = "registry+https://github.com/rust-lang/crates.io-index" 519 | checksum = "fc736091aacb31ddaa4cd5f6988b3c21e99913ac846b41f32538c5fae5d71bfe" 520 | 521 | [[package]] 522 | name = "http" 523 | version = "0.2.4" 524 | source = "registry+https://github.com/rust-lang/crates.io-index" 525 | checksum = "527e8c9ac747e28542699a951517aa9a6945af506cd1f2e1b53a576c17b6cc11" 526 | dependencies = [ 527 | "bytes", 528 | "fnv", 529 | "itoa 0.4.7", 530 | ] 531 | 532 | [[package]] 533 | name = "http-body" 534 | version = "0.4.2" 535 | source = "registry+https://github.com/rust-lang/crates.io-index" 536 | checksum = "60daa14be0e0786db0f03a9e57cb404c9d756eed2b6c62b9ea98ec5743ec75a9" 537 | dependencies = [ 538 | "bytes", 539 | "http", 540 | "pin-project-lite", 541 | ] 542 | 543 | [[package]] 544 | name = "httparse" 545 | version = "1.8.0" 546 | source = "registry+https://github.com/rust-lang/crates.io-index" 547 | checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" 548 | 549 | [[package]] 550 | name = "httpdate" 551 | version = "1.0.1" 552 | source = "registry+https://github.com/rust-lang/crates.io-index" 553 | checksum = "6456b8a6c8f33fee7d958fcd1b60d55b11940a79e63ae87013e6d22e26034440" 554 | 555 | [[package]] 556 | name = "humantime" 557 | version = "1.3.0" 558 | source = "registry+https://github.com/rust-lang/crates.io-index" 559 | checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" 560 | dependencies = [ 561 | "quick-error", 562 | ] 563 | 564 | [[package]] 565 | name = "hyper" 566 | version = "0.14.23" 567 | source = "registry+https://github.com/rust-lang/crates.io-index" 568 | checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c" 569 | dependencies = [ 570 | "bytes", 571 | "futures-channel", 572 | "futures-core", 573 | "futures-util", 574 | "h2", 575 | "http", 576 | "http-body", 577 | "httparse", 578 | "httpdate", 579 | "itoa 1.0.1", 580 | "pin-project-lite", 581 | "socket2", 582 | "tokio", 583 | "tower-service", 584 | "tracing", 585 | "want", 586 | ] 587 | 588 | [[package]] 589 | name = "hyper-tls" 590 | version = "0.5.0" 591 | source = "registry+https://github.com/rust-lang/crates.io-index" 592 | checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" 593 | dependencies = [ 594 | "bytes", 595 | "hyper", 596 | "native-tls", 597 | "tokio", 598 | "tokio-native-tls", 599 | ] 600 | 601 | [[package]] 602 | name = "hyperlocal" 603 | version = "0.8.0" 604 | source = "registry+https://github.com/rust-lang/crates.io-index" 605 | checksum = "0fafdf7b2b2de7c9784f76e02c0935e65a8117ec3b768644379983ab333ac98c" 606 | dependencies = [ 607 | "futures-util", 608 | "hex", 609 | "hyper", 610 | "pin-project", 611 | "tokio", 612 | ] 613 | 614 | [[package]] 615 | name = "idna" 616 | version = "0.2.3" 617 | source = "registry+https://github.com/rust-lang/crates.io-index" 618 | checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" 619 | dependencies = [ 620 | "matches", 621 | "unicode-bidi", 622 | "unicode-normalization", 623 | ] 624 | 625 | [[package]] 626 | name = "indexmap" 627 | version = "1.6.2" 628 | source = "registry+https://github.com/rust-lang/crates.io-index" 629 | checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3" 630 | dependencies = [ 631 | "autocfg", 632 | "hashbrown", 633 | ] 634 | 635 | [[package]] 636 | name = "ipnet" 637 | version = "2.3.0" 638 | source = "registry+https://github.com/rust-lang/crates.io-index" 639 | checksum = "47be2f14c678be2fdcab04ab1171db51b2762ce6f0a8ee87c8dd4a04ed216135" 640 | 641 | [[package]] 642 | name = "itoa" 643 | version = "0.4.7" 644 | source = "registry+https://github.com/rust-lang/crates.io-index" 645 | checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" 646 | 647 | [[package]] 648 | name = "itoa" 649 | version = "1.0.1" 650 | source = "registry+https://github.com/rust-lang/crates.io-index" 651 | checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" 652 | 653 | [[package]] 654 | name = "js-sys" 655 | version = "0.3.51" 656 | source = "registry+https://github.com/rust-lang/crates.io-index" 657 | checksum = "83bdfbace3a0e81a4253f73b49e960b053e396a11012cbd49b9b74d6a2b67062" 658 | dependencies = [ 659 | "wasm-bindgen", 660 | ] 661 | 662 | [[package]] 663 | name = "lazy_static" 664 | version = "1.4.0" 665 | source = "registry+https://github.com/rust-lang/crates.io-index" 666 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 667 | 668 | [[package]] 669 | name = "libc" 670 | version = "0.2.137" 671 | source = "registry+https://github.com/rust-lang/crates.io-index" 672 | checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" 673 | 674 | [[package]] 675 | name = "libsquish" 676 | version = "0.1.0" 677 | dependencies = [ 678 | "derive-getters", 679 | "serde", 680 | "serde_json", 681 | "toml", 682 | ] 683 | 684 | [[package]] 685 | name = "linked-hash-map" 686 | version = "0.5.4" 687 | source = "registry+https://github.com/rust-lang/crates.io-index" 688 | checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" 689 | 690 | [[package]] 691 | name = "lock_api" 692 | version = "0.4.9" 693 | source = "registry+https://github.com/rust-lang/crates.io-index" 694 | checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" 695 | dependencies = [ 696 | "autocfg", 697 | "scopeguard", 698 | ] 699 | 700 | [[package]] 701 | name = "log" 702 | version = "0.4.17" 703 | source = "registry+https://github.com/rust-lang/crates.io-index" 704 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 705 | dependencies = [ 706 | "cfg-if", 707 | ] 708 | 709 | [[package]] 710 | name = "matches" 711 | version = "0.1.8" 712 | source = "registry+https://github.com/rust-lang/crates.io-index" 713 | checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" 714 | 715 | [[package]] 716 | name = "memchr" 717 | version = "2.4.0" 718 | source = "registry+https://github.com/rust-lang/crates.io-index" 719 | checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" 720 | 721 | [[package]] 722 | name = "memoffset" 723 | version = "0.7.1" 724 | source = "registry+https://github.com/rust-lang/crates.io-index" 725 | checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" 726 | dependencies = [ 727 | "autocfg", 728 | ] 729 | 730 | [[package]] 731 | name = "mime" 732 | version = "0.3.16" 733 | source = "registry+https://github.com/rust-lang/crates.io-index" 734 | checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" 735 | 736 | [[package]] 737 | name = "mime_guess" 738 | version = "2.0.3" 739 | source = "registry+https://github.com/rust-lang/crates.io-index" 740 | checksum = "2684d4c2e97d99848d30b324b00c8fcc7e5c897b7cbb5819b09e7c90e8baf212" 741 | dependencies = [ 742 | "mime", 743 | "unicase", 744 | ] 745 | 746 | [[package]] 747 | name = "miniz_oxide" 748 | version = "0.6.2" 749 | source = "registry+https://github.com/rust-lang/crates.io-index" 750 | checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" 751 | dependencies = [ 752 | "adler", 753 | ] 754 | 755 | [[package]] 756 | name = "mio" 757 | version = "0.8.5" 758 | source = "registry+https://github.com/rust-lang/crates.io-index" 759 | checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" 760 | dependencies = [ 761 | "libc", 762 | "log", 763 | "wasi 0.11.0+wasi-snapshot-preview1", 764 | "windows-sys", 765 | ] 766 | 767 | [[package]] 768 | name = "multipart" 769 | version = "0.18.0" 770 | source = "registry+https://github.com/rust-lang/crates.io-index" 771 | checksum = "00dec633863867f29cb39df64a397cdf4a6354708ddd7759f70c7fb51c5f9182" 772 | dependencies = [ 773 | "buf_redux", 774 | "httparse", 775 | "log", 776 | "mime", 777 | "mime_guess", 778 | "quick-error", 779 | "rand 0.8.3", 780 | "safemem", 781 | "tempfile", 782 | "twoway", 783 | ] 784 | 785 | [[package]] 786 | name = "native-tls" 787 | version = "0.2.11" 788 | source = "registry+https://github.com/rust-lang/crates.io-index" 789 | checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" 790 | dependencies = [ 791 | "lazy_static", 792 | "libc", 793 | "log", 794 | "openssl", 795 | "openssl-probe", 796 | "openssl-sys", 797 | "schannel", 798 | "security-framework", 799 | "security-framework-sys", 800 | "tempfile", 801 | ] 802 | 803 | [[package]] 804 | name = "nix" 805 | version = "0.26.1" 806 | source = "registry+https://github.com/rust-lang/crates.io-index" 807 | checksum = "46a58d1d356c6597d08cde02c2f09d785b09e28711837b1ed667dc652c08a694" 808 | dependencies = [ 809 | "bitflags", 810 | "cfg-if", 811 | "libc", 812 | "memoffset", 813 | "pin-utils", 814 | "static_assertions", 815 | ] 816 | 817 | [[package]] 818 | name = "num_cpus" 819 | version = "1.13.0" 820 | source = "registry+https://github.com/rust-lang/crates.io-index" 821 | checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" 822 | dependencies = [ 823 | "hermit-abi", 824 | "libc", 825 | ] 826 | 827 | [[package]] 828 | name = "once_cell" 829 | version = "1.7.2" 830 | source = "registry+https://github.com/rust-lang/crates.io-index" 831 | checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" 832 | 833 | [[package]] 834 | name = "opaque-debug" 835 | version = "0.3.0" 836 | source = "registry+https://github.com/rust-lang/crates.io-index" 837 | checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" 838 | 839 | [[package]] 840 | name = "openssl" 841 | version = "0.10.34" 842 | source = "registry+https://github.com/rust-lang/crates.io-index" 843 | checksum = "6d7830286ad6a3973c0f1d9b73738f69c76b739301d0229c4b96501695cbe4c8" 844 | dependencies = [ 845 | "bitflags", 846 | "cfg-if", 847 | "foreign-types", 848 | "libc", 849 | "once_cell", 850 | "openssl-sys", 851 | ] 852 | 853 | [[package]] 854 | name = "openssl-probe" 855 | version = "0.1.4" 856 | source = "registry+https://github.com/rust-lang/crates.io-index" 857 | checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a" 858 | 859 | [[package]] 860 | name = "openssl-sys" 861 | version = "0.9.63" 862 | source = "registry+https://github.com/rust-lang/crates.io-index" 863 | checksum = "b6b0d6fb7d80f877617dfcb014e605e2b5ab2fb0afdf27935219bb6bd984cb98" 864 | dependencies = [ 865 | "autocfg", 866 | "cc", 867 | "libc", 868 | "pkg-config", 869 | "vcpkg", 870 | ] 871 | 872 | [[package]] 873 | name = "os_str_bytes" 874 | version = "6.0.0" 875 | source = "registry+https://github.com/rust-lang/crates.io-index" 876 | checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64" 877 | 878 | [[package]] 879 | name = "parking_lot" 880 | version = "0.12.1" 881 | source = "registry+https://github.com/rust-lang/crates.io-index" 882 | checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" 883 | dependencies = [ 884 | "lock_api", 885 | "parking_lot_core", 886 | ] 887 | 888 | [[package]] 889 | name = "parking_lot_core" 890 | version = "0.9.4" 891 | source = "registry+https://github.com/rust-lang/crates.io-index" 892 | checksum = "4dc9e0dc2adc1c69d09143aff38d3d30c5c3f0df0dad82e6d25547af174ebec0" 893 | dependencies = [ 894 | "cfg-if", 895 | "libc", 896 | "redox_syscall", 897 | "smallvec", 898 | "windows-sys", 899 | ] 900 | 901 | [[package]] 902 | name = "percent-encoding" 903 | version = "2.1.0" 904 | source = "registry+https://github.com/rust-lang/crates.io-index" 905 | checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" 906 | 907 | [[package]] 908 | name = "pid1" 909 | version = "0.1.0" 910 | dependencies = [ 911 | "clap", 912 | "futures", 913 | "libsquish", 914 | "nix", 915 | "reqwest", 916 | "rlimit", 917 | "tokio", 918 | ] 919 | 920 | [[package]] 921 | name = "pin-project" 922 | version = "1.0.7" 923 | source = "registry+https://github.com/rust-lang/crates.io-index" 924 | checksum = "c7509cc106041c40a4518d2af7a61530e1eed0e6285296a3d8c5472806ccc4a4" 925 | dependencies = [ 926 | "pin-project-internal", 927 | ] 928 | 929 | [[package]] 930 | name = "pin-project-internal" 931 | version = "1.0.7" 932 | source = "registry+https://github.com/rust-lang/crates.io-index" 933 | checksum = "48c950132583b500556b1efd71d45b319029f2b71518d979fcc208e16b42426f" 934 | dependencies = [ 935 | "proc-macro2", 936 | "quote", 937 | "syn", 938 | ] 939 | 940 | [[package]] 941 | name = "pin-project-lite" 942 | version = "0.2.6" 943 | source = "registry+https://github.com/rust-lang/crates.io-index" 944 | checksum = "dc0e1f259c92177c30a4c9d177246edd0a3568b25756a977d0632cf8fa37e905" 945 | 946 | [[package]] 947 | name = "pin-utils" 948 | version = "0.1.0" 949 | source = "registry+https://github.com/rust-lang/crates.io-index" 950 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 951 | 952 | [[package]] 953 | name = "pkg-config" 954 | version = "0.3.19" 955 | source = "registry+https://github.com/rust-lang/crates.io-index" 956 | checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" 957 | 958 | [[package]] 959 | name = "ppv-lite86" 960 | version = "0.2.10" 961 | source = "registry+https://github.com/rust-lang/crates.io-index" 962 | checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" 963 | 964 | [[package]] 965 | name = "pretty_env_logger" 966 | version = "0.4.0" 967 | source = "registry+https://github.com/rust-lang/crates.io-index" 968 | checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d" 969 | dependencies = [ 970 | "env_logger", 971 | "log", 972 | ] 973 | 974 | [[package]] 975 | name = "proc-macro2" 976 | version = "1.0.47" 977 | source = "registry+https://github.com/rust-lang/crates.io-index" 978 | checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" 979 | dependencies = [ 980 | "unicode-ident", 981 | ] 982 | 983 | [[package]] 984 | name = "quick-error" 985 | version = "1.2.3" 986 | source = "registry+https://github.com/rust-lang/crates.io-index" 987 | checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" 988 | 989 | [[package]] 990 | name = "quote" 991 | version = "1.0.9" 992 | source = "registry+https://github.com/rust-lang/crates.io-index" 993 | checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" 994 | dependencies = [ 995 | "proc-macro2", 996 | ] 997 | 998 | [[package]] 999 | name = "rand" 1000 | version = "0.3.23" 1001 | source = "registry+https://github.com/rust-lang/crates.io-index" 1002 | checksum = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c" 1003 | dependencies = [ 1004 | "libc", 1005 | "rand 0.4.6", 1006 | ] 1007 | 1008 | [[package]] 1009 | name = "rand" 1010 | version = "0.4.6" 1011 | source = "registry+https://github.com/rust-lang/crates.io-index" 1012 | checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" 1013 | dependencies = [ 1014 | "fuchsia-cprng", 1015 | "libc", 1016 | "rand_core 0.3.1", 1017 | "rdrand", 1018 | "winapi", 1019 | ] 1020 | 1021 | [[package]] 1022 | name = "rand" 1023 | version = "0.8.3" 1024 | source = "registry+https://github.com/rust-lang/crates.io-index" 1025 | checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" 1026 | dependencies = [ 1027 | "libc", 1028 | "rand_chacha", 1029 | "rand_core 0.6.2", 1030 | "rand_hc", 1031 | ] 1032 | 1033 | [[package]] 1034 | name = "rand_chacha" 1035 | version = "0.3.1" 1036 | source = "registry+https://github.com/rust-lang/crates.io-index" 1037 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 1038 | dependencies = [ 1039 | "ppv-lite86", 1040 | "rand_core 0.6.2", 1041 | ] 1042 | 1043 | [[package]] 1044 | name = "rand_core" 1045 | version = "0.3.1" 1046 | source = "registry+https://github.com/rust-lang/crates.io-index" 1047 | checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" 1048 | dependencies = [ 1049 | "rand_core 0.4.2", 1050 | ] 1051 | 1052 | [[package]] 1053 | name = "rand_core" 1054 | version = "0.4.2" 1055 | source = "registry+https://github.com/rust-lang/crates.io-index" 1056 | checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" 1057 | 1058 | [[package]] 1059 | name = "rand_core" 1060 | version = "0.6.2" 1061 | source = "registry+https://github.com/rust-lang/crates.io-index" 1062 | checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" 1063 | dependencies = [ 1064 | "getrandom", 1065 | ] 1066 | 1067 | [[package]] 1068 | name = "rand_hc" 1069 | version = "0.3.0" 1070 | source = "registry+https://github.com/rust-lang/crates.io-index" 1071 | checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" 1072 | dependencies = [ 1073 | "rand_core 0.6.2", 1074 | ] 1075 | 1076 | [[package]] 1077 | name = "rdrand" 1078 | version = "0.4.0" 1079 | source = "registry+https://github.com/rust-lang/crates.io-index" 1080 | checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" 1081 | dependencies = [ 1082 | "rand_core 0.3.1", 1083 | ] 1084 | 1085 | [[package]] 1086 | name = "redox_syscall" 1087 | version = "0.2.8" 1088 | source = "registry+https://github.com/rust-lang/crates.io-index" 1089 | checksum = "742739e41cd49414de871ea5e549afb7e2a3ac77b589bcbebe8c82fab37147fc" 1090 | dependencies = [ 1091 | "bitflags", 1092 | ] 1093 | 1094 | [[package]] 1095 | name = "regex" 1096 | version = "1.5.4" 1097 | source = "registry+https://github.com/rust-lang/crates.io-index" 1098 | checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" 1099 | dependencies = [ 1100 | "aho-corasick", 1101 | "memchr", 1102 | "regex-syntax", 1103 | ] 1104 | 1105 | [[package]] 1106 | name = "regex-syntax" 1107 | version = "0.6.25" 1108 | source = "registry+https://github.com/rust-lang/crates.io-index" 1109 | checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" 1110 | 1111 | [[package]] 1112 | name = "remove_dir_all" 1113 | version = "0.5.3" 1114 | source = "registry+https://github.com/rust-lang/crates.io-index" 1115 | checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" 1116 | dependencies = [ 1117 | "winapi", 1118 | ] 1119 | 1120 | [[package]] 1121 | name = "reqwest" 1122 | version = "0.11.13" 1123 | source = "registry+https://github.com/rust-lang/crates.io-index" 1124 | checksum = "68cc60575865c7831548863cc02356512e3f1dc2f3f82cb837d7fc4cc8f3c97c" 1125 | dependencies = [ 1126 | "base64", 1127 | "bytes", 1128 | "encoding_rs", 1129 | "futures-core", 1130 | "futures-util", 1131 | "h2", 1132 | "http", 1133 | "http-body", 1134 | "hyper", 1135 | "hyper-tls", 1136 | "ipnet", 1137 | "js-sys", 1138 | "log", 1139 | "mime", 1140 | "native-tls", 1141 | "once_cell", 1142 | "percent-encoding", 1143 | "pin-project-lite", 1144 | "serde", 1145 | "serde_json", 1146 | "serde_urlencoded", 1147 | "tokio", 1148 | "tokio-native-tls", 1149 | "tower-service", 1150 | "url", 1151 | "wasm-bindgen", 1152 | "wasm-bindgen-futures", 1153 | "web-sys", 1154 | "winreg", 1155 | ] 1156 | 1157 | [[package]] 1158 | name = "rlimit" 1159 | version = "0.8.3" 1160 | source = "registry+https://github.com/rust-lang/crates.io-index" 1161 | checksum = "f7278a1ec8bfd4a4e07515c589f5ff7b309a373f987393aef44813d9dcf87aa3" 1162 | dependencies = [ 1163 | "libc", 1164 | ] 1165 | 1166 | [[package]] 1167 | name = "rustls-pemfile" 1168 | version = "0.2.1" 1169 | source = "registry+https://github.com/rust-lang/crates.io-index" 1170 | checksum = "5eebeaeb360c87bfb72e84abdb3447159c0eaececf1bef2aecd65a8be949d1c9" 1171 | dependencies = [ 1172 | "base64", 1173 | ] 1174 | 1175 | [[package]] 1176 | name = "ryu" 1177 | version = "1.0.5" 1178 | source = "registry+https://github.com/rust-lang/crates.io-index" 1179 | checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" 1180 | 1181 | [[package]] 1182 | name = "safemem" 1183 | version = "0.3.3" 1184 | source = "registry+https://github.com/rust-lang/crates.io-index" 1185 | checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" 1186 | 1187 | [[package]] 1188 | name = "schannel" 1189 | version = "0.1.19" 1190 | source = "registry+https://github.com/rust-lang/crates.io-index" 1191 | checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" 1192 | dependencies = [ 1193 | "lazy_static", 1194 | "winapi", 1195 | ] 1196 | 1197 | [[package]] 1198 | name = "scoped-tls" 1199 | version = "1.0.0" 1200 | source = "registry+https://github.com/rust-lang/crates.io-index" 1201 | checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" 1202 | 1203 | [[package]] 1204 | name = "scopeguard" 1205 | version = "1.1.0" 1206 | source = "registry+https://github.com/rust-lang/crates.io-index" 1207 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 1208 | 1209 | [[package]] 1210 | name = "security-framework" 1211 | version = "2.3.1" 1212 | source = "registry+https://github.com/rust-lang/crates.io-index" 1213 | checksum = "23a2ac85147a3a11d77ecf1bc7166ec0b92febfa4461c37944e180f319ece467" 1214 | dependencies = [ 1215 | "bitflags", 1216 | "core-foundation", 1217 | "core-foundation-sys", 1218 | "libc", 1219 | "security-framework-sys", 1220 | ] 1221 | 1222 | [[package]] 1223 | name = "security-framework-sys" 1224 | version = "2.3.0" 1225 | source = "registry+https://github.com/rust-lang/crates.io-index" 1226 | checksum = "7e4effb91b4b8b6fb7732e670b6cee160278ff8e6bf485c7805d9e319d76e284" 1227 | dependencies = [ 1228 | "core-foundation-sys", 1229 | "libc", 1230 | ] 1231 | 1232 | [[package]] 1233 | name = "serde" 1234 | version = "1.0.148" 1235 | source = "registry+https://github.com/rust-lang/crates.io-index" 1236 | checksum = "e53f64bb4ba0191d6d0676e1b141ca55047d83b74f5607e6d8eb88126c52c2dc" 1237 | dependencies = [ 1238 | "serde_derive", 1239 | ] 1240 | 1241 | [[package]] 1242 | name = "serde_derive" 1243 | version = "1.0.148" 1244 | source = "registry+https://github.com/rust-lang/crates.io-index" 1245 | checksum = "a55492425aa53521babf6137309e7d34c20bbfbbfcfe2c7f3a047fd1f6b92c0c" 1246 | dependencies = [ 1247 | "proc-macro2", 1248 | "quote", 1249 | "syn", 1250 | ] 1251 | 1252 | [[package]] 1253 | name = "serde_json" 1254 | version = "1.0.89" 1255 | source = "registry+https://github.com/rust-lang/crates.io-index" 1256 | checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db" 1257 | dependencies = [ 1258 | "itoa 1.0.1", 1259 | "ryu", 1260 | "serde", 1261 | ] 1262 | 1263 | [[package]] 1264 | name = "serde_urlencoded" 1265 | version = "0.7.1" 1266 | source = "registry+https://github.com/rust-lang/crates.io-index" 1267 | checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" 1268 | dependencies = [ 1269 | "form_urlencoded", 1270 | "itoa 1.0.1", 1271 | "ryu", 1272 | "serde", 1273 | ] 1274 | 1275 | [[package]] 1276 | name = "sha-1" 1277 | version = "0.9.6" 1278 | source = "registry+https://github.com/rust-lang/crates.io-index" 1279 | checksum = "8c4cfa741c5832d0ef7fab46cabed29c2aae926db0b11bb2069edd8db5e64e16" 1280 | dependencies = [ 1281 | "block-buffer 0.9.0", 1282 | "cfg-if", 1283 | "cpufeatures 0.1.4", 1284 | "digest 0.9.0", 1285 | "opaque-debug", 1286 | ] 1287 | 1288 | [[package]] 1289 | name = "sha-1" 1290 | version = "0.10.0" 1291 | source = "registry+https://github.com/rust-lang/crates.io-index" 1292 | checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" 1293 | dependencies = [ 1294 | "cfg-if", 1295 | "cpufeatures 0.2.5", 1296 | "digest 0.10.5", 1297 | ] 1298 | 1299 | [[package]] 1300 | name = "signal-hook-registry" 1301 | version = "1.4.0" 1302 | source = "registry+https://github.com/rust-lang/crates.io-index" 1303 | checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" 1304 | dependencies = [ 1305 | "libc", 1306 | ] 1307 | 1308 | [[package]] 1309 | name = "slab" 1310 | version = "0.4.3" 1311 | source = "registry+https://github.com/rust-lang/crates.io-index" 1312 | checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527" 1313 | 1314 | [[package]] 1315 | name = "smallvec" 1316 | version = "1.6.1" 1317 | source = "registry+https://github.com/rust-lang/crates.io-index" 1318 | checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" 1319 | 1320 | [[package]] 1321 | name = "socket2" 1322 | version = "0.4.7" 1323 | source = "registry+https://github.com/rust-lang/crates.io-index" 1324 | checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" 1325 | dependencies = [ 1326 | "libc", 1327 | "winapi", 1328 | ] 1329 | 1330 | [[package]] 1331 | name = "static_assertions" 1332 | version = "1.1.0" 1333 | source = "registry+https://github.com/rust-lang/crates.io-index" 1334 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 1335 | 1336 | [[package]] 1337 | name = "strsim" 1338 | version = "0.10.0" 1339 | source = "registry+https://github.com/rust-lang/crates.io-index" 1340 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 1341 | 1342 | [[package]] 1343 | name = "syn" 1344 | version = "1.0.104" 1345 | source = "registry+https://github.com/rust-lang/crates.io-index" 1346 | checksum = "4ae548ec36cf198c0ef7710d3c230987c2d6d7bd98ad6edc0274462724c585ce" 1347 | dependencies = [ 1348 | "proc-macro2", 1349 | "quote", 1350 | "unicode-ident", 1351 | ] 1352 | 1353 | [[package]] 1354 | name = "tar" 1355 | version = "0.4.38" 1356 | source = "registry+https://github.com/rust-lang/crates.io-index" 1357 | checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6" 1358 | dependencies = [ 1359 | "filetime", 1360 | "libc", 1361 | "xattr", 1362 | ] 1363 | 1364 | [[package]] 1365 | name = "tempfile" 1366 | version = "3.2.0" 1367 | source = "registry+https://github.com/rust-lang/crates.io-index" 1368 | checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" 1369 | dependencies = [ 1370 | "cfg-if", 1371 | "libc", 1372 | "rand 0.8.3", 1373 | "redox_syscall", 1374 | "remove_dir_all", 1375 | "winapi", 1376 | ] 1377 | 1378 | [[package]] 1379 | name = "termcolor" 1380 | version = "1.1.2" 1381 | source = "registry+https://github.com/rust-lang/crates.io-index" 1382 | checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" 1383 | dependencies = [ 1384 | "winapi-util", 1385 | ] 1386 | 1387 | [[package]] 1388 | name = "textwrap" 1389 | version = "0.15.1" 1390 | source = "registry+https://github.com/rust-lang/crates.io-index" 1391 | checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16" 1392 | 1393 | [[package]] 1394 | name = "thiserror" 1395 | version = "1.0.30" 1396 | source = "registry+https://github.com/rust-lang/crates.io-index" 1397 | checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" 1398 | dependencies = [ 1399 | "thiserror-impl", 1400 | ] 1401 | 1402 | [[package]] 1403 | name = "thiserror-impl" 1404 | version = "1.0.30" 1405 | source = "registry+https://github.com/rust-lang/crates.io-index" 1406 | checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" 1407 | dependencies = [ 1408 | "proc-macro2", 1409 | "quote", 1410 | "syn", 1411 | ] 1412 | 1413 | [[package]] 1414 | name = "time" 1415 | version = "0.1.43" 1416 | source = "registry+https://github.com/rust-lang/crates.io-index" 1417 | checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" 1418 | dependencies = [ 1419 | "libc", 1420 | "winapi", 1421 | ] 1422 | 1423 | [[package]] 1424 | name = "tinyvec" 1425 | version = "1.2.0" 1426 | source = "registry+https://github.com/rust-lang/crates.io-index" 1427 | checksum = "5b5220f05bb7de7f3f53c7c065e1199b3172696fe2db9f9c4d8ad9b4ee74c342" 1428 | dependencies = [ 1429 | "tinyvec_macros", 1430 | ] 1431 | 1432 | [[package]] 1433 | name = "tinyvec_macros" 1434 | version = "0.1.0" 1435 | source = "registry+https://github.com/rust-lang/crates.io-index" 1436 | checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" 1437 | 1438 | [[package]] 1439 | name = "tokio" 1440 | version = "1.22.0" 1441 | source = "registry+https://github.com/rust-lang/crates.io-index" 1442 | checksum = "d76ce4a75fb488c605c54bf610f221cea8b0dafb53333c1a67e8ee199dcd2ae3" 1443 | dependencies = [ 1444 | "autocfg", 1445 | "bytes", 1446 | "libc", 1447 | "memchr", 1448 | "mio", 1449 | "num_cpus", 1450 | "parking_lot", 1451 | "pin-project-lite", 1452 | "signal-hook-registry", 1453 | "socket2", 1454 | "tokio-macros", 1455 | "winapi", 1456 | ] 1457 | 1458 | [[package]] 1459 | name = "tokio-macros" 1460 | version = "1.7.0" 1461 | source = "registry+https://github.com/rust-lang/crates.io-index" 1462 | checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" 1463 | dependencies = [ 1464 | "proc-macro2", 1465 | "quote", 1466 | "syn", 1467 | ] 1468 | 1469 | [[package]] 1470 | name = "tokio-native-tls" 1471 | version = "0.3.0" 1472 | source = "registry+https://github.com/rust-lang/crates.io-index" 1473 | checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" 1474 | dependencies = [ 1475 | "native-tls", 1476 | "tokio", 1477 | ] 1478 | 1479 | [[package]] 1480 | name = "tokio-stream" 1481 | version = "0.1.11" 1482 | source = "registry+https://github.com/rust-lang/crates.io-index" 1483 | checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce" 1484 | dependencies = [ 1485 | "futures-core", 1486 | "pin-project-lite", 1487 | "tokio", 1488 | ] 1489 | 1490 | [[package]] 1491 | name = "tokio-tungstenite" 1492 | version = "0.17.2" 1493 | source = "registry+https://github.com/rust-lang/crates.io-index" 1494 | checksum = "f714dd15bead90401d77e04243611caec13726c2408afd5b31901dfcdcb3b181" 1495 | dependencies = [ 1496 | "futures-util", 1497 | "log", 1498 | "tokio", 1499 | "tungstenite", 1500 | ] 1501 | 1502 | [[package]] 1503 | name = "tokio-util" 1504 | version = "0.6.9" 1505 | source = "registry+https://github.com/rust-lang/crates.io-index" 1506 | checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0" 1507 | dependencies = [ 1508 | "bytes", 1509 | "futures-core", 1510 | "futures-sink", 1511 | "log", 1512 | "pin-project-lite", 1513 | "tokio", 1514 | ] 1515 | 1516 | [[package]] 1517 | name = "tokio-util" 1518 | version = "0.7.4" 1519 | source = "registry+https://github.com/rust-lang/crates.io-index" 1520 | checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" 1521 | dependencies = [ 1522 | "bytes", 1523 | "futures-core", 1524 | "futures-sink", 1525 | "pin-project-lite", 1526 | "tokio", 1527 | ] 1528 | 1529 | [[package]] 1530 | name = "toml" 1531 | version = "0.5.9" 1532 | source = "registry+https://github.com/rust-lang/crates.io-index" 1533 | checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" 1534 | dependencies = [ 1535 | "serde", 1536 | ] 1537 | 1538 | [[package]] 1539 | name = "tower-service" 1540 | version = "0.3.1" 1541 | source = "registry+https://github.com/rust-lang/crates.io-index" 1542 | checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" 1543 | 1544 | [[package]] 1545 | name = "tracing" 1546 | version = "0.1.26" 1547 | source = "registry+https://github.com/rust-lang/crates.io-index" 1548 | checksum = "09adeb8c97449311ccd28a427f96fb563e7fd31aabf994189879d9da2394b89d" 1549 | dependencies = [ 1550 | "cfg-if", 1551 | "log", 1552 | "pin-project-lite", 1553 | "tracing-core", 1554 | ] 1555 | 1556 | [[package]] 1557 | name = "tracing-core" 1558 | version = "0.1.18" 1559 | source = "registry+https://github.com/rust-lang/crates.io-index" 1560 | checksum = "a9ff14f98b1a4b289c6248a023c1c2fa1491062964e9fed67ab29c4e4da4a052" 1561 | dependencies = [ 1562 | "lazy_static", 1563 | ] 1564 | 1565 | [[package]] 1566 | name = "try-lock" 1567 | version = "0.2.3" 1568 | source = "registry+https://github.com/rust-lang/crates.io-index" 1569 | checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" 1570 | 1571 | [[package]] 1572 | name = "tungstenite" 1573 | version = "0.17.3" 1574 | source = "registry+https://github.com/rust-lang/crates.io-index" 1575 | checksum = "e27992fd6a8c29ee7eef28fc78349aa244134e10ad447ce3b9f0ac0ed0fa4ce0" 1576 | dependencies = [ 1577 | "base64", 1578 | "byteorder", 1579 | "bytes", 1580 | "http", 1581 | "httparse", 1582 | "log", 1583 | "rand 0.8.3", 1584 | "sha-1 0.10.0", 1585 | "thiserror", 1586 | "url", 1587 | "utf-8", 1588 | ] 1589 | 1590 | [[package]] 1591 | name = "twoway" 1592 | version = "0.1.8" 1593 | source = "registry+https://github.com/rust-lang/crates.io-index" 1594 | checksum = "59b11b2b5241ba34be09c3cc85a36e56e48f9888862e19cedf23336d35316ed1" 1595 | dependencies = [ 1596 | "memchr", 1597 | ] 1598 | 1599 | [[package]] 1600 | name = "typenum" 1601 | version = "1.15.0" 1602 | source = "registry+https://github.com/rust-lang/crates.io-index" 1603 | checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" 1604 | 1605 | [[package]] 1606 | name = "unicase" 1607 | version = "2.6.0" 1608 | source = "registry+https://github.com/rust-lang/crates.io-index" 1609 | checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" 1610 | dependencies = [ 1611 | "version_check", 1612 | ] 1613 | 1614 | [[package]] 1615 | name = "unicode-bidi" 1616 | version = "0.3.5" 1617 | source = "registry+https://github.com/rust-lang/crates.io-index" 1618 | checksum = "eeb8be209bb1c96b7c177c7420d26e04eccacb0eeae6b980e35fcb74678107e0" 1619 | dependencies = [ 1620 | "matches", 1621 | ] 1622 | 1623 | [[package]] 1624 | name = "unicode-ident" 1625 | version = "1.0.5" 1626 | source = "registry+https://github.com/rust-lang/crates.io-index" 1627 | checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" 1628 | 1629 | [[package]] 1630 | name = "unicode-normalization" 1631 | version = "0.1.19" 1632 | source = "registry+https://github.com/rust-lang/crates.io-index" 1633 | checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" 1634 | dependencies = [ 1635 | "tinyvec", 1636 | ] 1637 | 1638 | [[package]] 1639 | name = "url" 1640 | version = "2.2.2" 1641 | source = "registry+https://github.com/rust-lang/crates.io-index" 1642 | checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" 1643 | dependencies = [ 1644 | "form_urlencoded", 1645 | "idna", 1646 | "matches", 1647 | "percent-encoding", 1648 | ] 1649 | 1650 | [[package]] 1651 | name = "utf-8" 1652 | version = "0.7.6" 1653 | source = "registry+https://github.com/rust-lang/crates.io-index" 1654 | checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" 1655 | 1656 | [[package]] 1657 | name = "vcpkg" 1658 | version = "0.2.13" 1659 | source = "registry+https://github.com/rust-lang/crates.io-index" 1660 | checksum = "025ce40a007e1907e58d5bc1a594def78e5573bb0b1160bc389634e8f12e4faa" 1661 | 1662 | [[package]] 1663 | name = "version_check" 1664 | version = "0.9.3" 1665 | source = "registry+https://github.com/rust-lang/crates.io-index" 1666 | checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" 1667 | 1668 | [[package]] 1669 | name = "want" 1670 | version = "0.3.0" 1671 | source = "registry+https://github.com/rust-lang/crates.io-index" 1672 | checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" 1673 | dependencies = [ 1674 | "log", 1675 | "try-lock", 1676 | ] 1677 | 1678 | [[package]] 1679 | name = "warp" 1680 | version = "0.3.3" 1681 | source = "registry+https://github.com/rust-lang/crates.io-index" 1682 | checksum = "ed7b8be92646fc3d18b06147664ebc5f48d222686cb11a8755e561a735aacc6d" 1683 | dependencies = [ 1684 | "bytes", 1685 | "futures-channel", 1686 | "futures-util", 1687 | "headers", 1688 | "http", 1689 | "hyper", 1690 | "log", 1691 | "mime", 1692 | "mime_guess", 1693 | "multipart", 1694 | "percent-encoding", 1695 | "pin-project", 1696 | "rustls-pemfile", 1697 | "scoped-tls", 1698 | "serde", 1699 | "serde_json", 1700 | "serde_urlencoded", 1701 | "tokio", 1702 | "tokio-stream", 1703 | "tokio-tungstenite", 1704 | "tokio-util 0.7.4", 1705 | "tower-service", 1706 | "tracing", 1707 | ] 1708 | 1709 | [[package]] 1710 | name = "wasi" 1711 | version = "0.10.2+wasi-snapshot-preview1" 1712 | source = "registry+https://github.com/rust-lang/crates.io-index" 1713 | checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" 1714 | 1715 | [[package]] 1716 | name = "wasi" 1717 | version = "0.11.0+wasi-snapshot-preview1" 1718 | source = "registry+https://github.com/rust-lang/crates.io-index" 1719 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1720 | 1721 | [[package]] 1722 | name = "wasm-bindgen" 1723 | version = "0.2.74" 1724 | source = "registry+https://github.com/rust-lang/crates.io-index" 1725 | checksum = "d54ee1d4ed486f78874278e63e4069fc1ab9f6a18ca492076ffb90c5eb2997fd" 1726 | dependencies = [ 1727 | "cfg-if", 1728 | "wasm-bindgen-macro", 1729 | ] 1730 | 1731 | [[package]] 1732 | name = "wasm-bindgen-backend" 1733 | version = "0.2.74" 1734 | source = "registry+https://github.com/rust-lang/crates.io-index" 1735 | checksum = "3b33f6a0694ccfea53d94db8b2ed1c3a8a4c86dd936b13b9f0a15ec4a451b900" 1736 | dependencies = [ 1737 | "bumpalo", 1738 | "lazy_static", 1739 | "log", 1740 | "proc-macro2", 1741 | "quote", 1742 | "syn", 1743 | "wasm-bindgen-shared", 1744 | ] 1745 | 1746 | [[package]] 1747 | name = "wasm-bindgen-futures" 1748 | version = "0.4.24" 1749 | source = "registry+https://github.com/rust-lang/crates.io-index" 1750 | checksum = "5fba7978c679d53ce2d0ac80c8c175840feb849a161664365d1287b41f2e67f1" 1751 | dependencies = [ 1752 | "cfg-if", 1753 | "js-sys", 1754 | "wasm-bindgen", 1755 | "web-sys", 1756 | ] 1757 | 1758 | [[package]] 1759 | name = "wasm-bindgen-macro" 1760 | version = "0.2.74" 1761 | source = "registry+https://github.com/rust-lang/crates.io-index" 1762 | checksum = "088169ca61430fe1e58b8096c24975251700e7b1f6fd91cc9d59b04fb9b18bd4" 1763 | dependencies = [ 1764 | "quote", 1765 | "wasm-bindgen-macro-support", 1766 | ] 1767 | 1768 | [[package]] 1769 | name = "wasm-bindgen-macro-support" 1770 | version = "0.2.74" 1771 | source = "registry+https://github.com/rust-lang/crates.io-index" 1772 | checksum = "be2241542ff3d9f241f5e2cb6dd09b37efe786df8851c54957683a49f0987a97" 1773 | dependencies = [ 1774 | "proc-macro2", 1775 | "quote", 1776 | "syn", 1777 | "wasm-bindgen-backend", 1778 | "wasm-bindgen-shared", 1779 | ] 1780 | 1781 | [[package]] 1782 | name = "wasm-bindgen-shared" 1783 | version = "0.2.74" 1784 | source = "registry+https://github.com/rust-lang/crates.io-index" 1785 | checksum = "d7cff876b8f18eed75a66cf49b65e7f967cb354a7aa16003fb55dbfd25b44b4f" 1786 | 1787 | [[package]] 1788 | name = "web-sys" 1789 | version = "0.3.51" 1790 | source = "registry+https://github.com/rust-lang/crates.io-index" 1791 | checksum = "e828417b379f3df7111d3a2a9e5753706cae29c41f7c4029ee9fd77f3e09e582" 1792 | dependencies = [ 1793 | "js-sys", 1794 | "wasm-bindgen", 1795 | ] 1796 | 1797 | [[package]] 1798 | name = "winapi" 1799 | version = "0.3.9" 1800 | source = "registry+https://github.com/rust-lang/crates.io-index" 1801 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1802 | dependencies = [ 1803 | "winapi-i686-pc-windows-gnu", 1804 | "winapi-x86_64-pc-windows-gnu", 1805 | ] 1806 | 1807 | [[package]] 1808 | name = "winapi-i686-pc-windows-gnu" 1809 | version = "0.4.0" 1810 | source = "registry+https://github.com/rust-lang/crates.io-index" 1811 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1812 | 1813 | [[package]] 1814 | name = "winapi-util" 1815 | version = "0.1.5" 1816 | source = "registry+https://github.com/rust-lang/crates.io-index" 1817 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 1818 | dependencies = [ 1819 | "winapi", 1820 | ] 1821 | 1822 | [[package]] 1823 | name = "winapi-x86_64-pc-windows-gnu" 1824 | version = "0.4.0" 1825 | source = "registry+https://github.com/rust-lang/crates.io-index" 1826 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1827 | 1828 | [[package]] 1829 | name = "windows-sys" 1830 | version = "0.42.0" 1831 | source = "registry+https://github.com/rust-lang/crates.io-index" 1832 | checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" 1833 | dependencies = [ 1834 | "windows_aarch64_gnullvm", 1835 | "windows_aarch64_msvc", 1836 | "windows_i686_gnu", 1837 | "windows_i686_msvc", 1838 | "windows_x86_64_gnu", 1839 | "windows_x86_64_gnullvm", 1840 | "windows_x86_64_msvc", 1841 | ] 1842 | 1843 | [[package]] 1844 | name = "windows_aarch64_gnullvm" 1845 | version = "0.42.0" 1846 | source = "registry+https://github.com/rust-lang/crates.io-index" 1847 | checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" 1848 | 1849 | [[package]] 1850 | name = "windows_aarch64_msvc" 1851 | version = "0.42.0" 1852 | source = "registry+https://github.com/rust-lang/crates.io-index" 1853 | checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" 1854 | 1855 | [[package]] 1856 | name = "windows_i686_gnu" 1857 | version = "0.42.0" 1858 | source = "registry+https://github.com/rust-lang/crates.io-index" 1859 | checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" 1860 | 1861 | [[package]] 1862 | name = "windows_i686_msvc" 1863 | version = "0.42.0" 1864 | source = "registry+https://github.com/rust-lang/crates.io-index" 1865 | checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" 1866 | 1867 | [[package]] 1868 | name = "windows_x86_64_gnu" 1869 | version = "0.42.0" 1870 | source = "registry+https://github.com/rust-lang/crates.io-index" 1871 | checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" 1872 | 1873 | [[package]] 1874 | name = "windows_x86_64_gnullvm" 1875 | version = "0.42.0" 1876 | source = "registry+https://github.com/rust-lang/crates.io-index" 1877 | checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" 1878 | 1879 | [[package]] 1880 | name = "windows_x86_64_msvc" 1881 | version = "0.42.0" 1882 | source = "registry+https://github.com/rust-lang/crates.io-index" 1883 | checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" 1884 | 1885 | [[package]] 1886 | name = "winreg" 1887 | version = "0.10.1" 1888 | source = "registry+https://github.com/rust-lang/crates.io-index" 1889 | checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" 1890 | dependencies = [ 1891 | "winapi", 1892 | ] 1893 | 1894 | [[package]] 1895 | name = "xattr" 1896 | version = "0.2.2" 1897 | source = "registry+https://github.com/rust-lang/crates.io-index" 1898 | checksum = "244c3741f4240ef46274860397c7c74e50eb23624996930e484c16679633a54c" 1899 | dependencies = [ 1900 | "libc", 1901 | ] 1902 | 1903 | [[package]] 1904 | name = "yaml-rust" 1905 | version = "0.4.5" 1906 | source = "registry+https://github.com/rust-lang/crates.io-index" 1907 | checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" 1908 | dependencies = [ 1909 | "linked-hash-map", 1910 | ] 1911 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "libsquish", 4 | "daemon", 5 | "cli", 6 | "pid1", 7 | ] 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2021-present amy null 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # squish 2 | 3 | squish is a novel rootless-only container runtime. The name is never 4 | capitalised, so if it were to come at the start of a sentence, it would still 5 | be written `squish`. 6 | 7 | ## Why? 8 | 9 | squish was born out of frustration with existing container runtimes. They all 10 | have their drawbacks (rootful, lack of port rebinds, cache-unfriendliness, slow 11 | container starts, ...) that lead to usage thereof being a frustrating 12 | experience at best. squish attempts to address these shortcomings. 13 | 14 | Preliminary testing shows that squish can get a viable Alpine-rootfs container 15 | up in ~5ms. This is an **initial** figure, and will change over time. 16 | 17 | ![](https://cdn.mewna.xyz/2021/11/21/vTiW66Bnc2Png.png) 18 | 19 | ### Things that haven't been implemented but are planned 20 | 21 | squish also avoids the typical OCI-style container images. The goal of squish 22 | is that the only "image" you deploy is a binary, and a manifest with the list 23 | of SDKs it uses. At container runtime, the various SDKs are bind-mounted into 24 | the container dynamically. Both the rootfs and all SDK layers are mounted 25 | read-only. 26 | 27 | ## Roadmap 28 | 29 | Feature | Description | State 30 | ----------------------|---------------------------------------------|------ 31 | Alpine | Read-only Alpine rootfs | ✔️ 32 | Networking | slirp4netns networking + port binds | ✔️ 33 | Mounts | Bind-mount files and directories ro and rw | ✔️ 34 | Rootless | Containers without root | ✔️ 35 | Container networking | Inter-container networking | TODO 36 | Cgroups | Resource limitations etc | TODO 37 | Systemd cgroup driver | Set up cgroups via systemd | TODO 38 | Layer downloads | Download layers via HTTP | TODO 39 | Seccomp | Syscall filtering | TODO 40 | Dynamic port rebinds | (Re)bind container ports at runtime | TODO 41 | /proc & /sys driver | FUSE vfs driver to safely mock /proc & /sys | TODO 42 | 43 | ### What won't be implemented? 44 | 45 | - Persistence of containers between daemon reboots 46 | - Getting a shell in a container 47 | 48 | ## Local development 49 | 50 | 1. Set up your environment by running `./setup.sh` 51 | 2. Run the daemon with `env RUST_BACKTRACE=1 RUST_LOG=debug cargo run -p daemon` 52 | 3. Create a container with `cargo run -p cli -- create test/squishfiles/default.toml` 53 | 4. You did it! Read the cli source to learn more commands 54 | 55 | ## Testing 56 | 57 | squish currently only has e2e tests. You can run them by running 58 | `./test/test-e2e.sh`. 59 | 60 | ## Where did the name come from? 61 | 62 | The idea started out as making something like [Flatpak](https://flatpak.org/) 63 | for servers -- although squish has significantly diverged since then -- and so 64 | the original working name was "squishpak," which eventually shortened into 65 | "squish." 66 | 67 | ## Misc 68 | 69 | `http-asm`: https://github.com/poletaevvlad/http-asm -------------------------------------------------------------------------------- /cli/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cli" 3 | version = "0.1.0" 4 | authors = ["amy null "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | libsquish = { path = "../libsquish" } 11 | 12 | clap = "3.2.22" 13 | hyper = { version = "0.14.23", features = [ "stream" ] } 14 | hyperlocal = "0.8.0" 15 | serde = "1.0.148" 16 | serde_json = "1.0.89" 17 | tokio = { version = "1.22.0", features = [ "full" ] } 18 | -------------------------------------------------------------------------------- /cli/src/client/mod.rs: -------------------------------------------------------------------------------- 1 | use std::vec; 2 | 3 | use hyper::Body; 4 | use hyper::{body::HttpBody, Client}; 5 | use hyperlocal::{UnixClientExt, Uri}; 6 | use libsquish::Result; 7 | 8 | #[derive(Debug)] 9 | pub enum Method { 10 | Get, 11 | Post, 12 | Put, 13 | Patch, 14 | Delete, 15 | } 16 | 17 | pub async fn get>(route: S) -> Result { 18 | request::(Method::Get, route, None).await 19 | } 20 | 21 | pub async fn post, T: Into>(route: S, body: Option) -> Result { 22 | request(Method::Post, route, body).await 23 | } 24 | 25 | #[allow(dead_code)] 26 | pub async fn put, T: Into>(route: S, body: Option) -> Result { 27 | request(Method::Put, route, body).await 28 | } 29 | 30 | #[allow(dead_code)] 31 | pub async fn patch, T: Into>(route: S, body: Option) -> Result { 32 | request(Method::Patch, route, body).await 33 | } 34 | 35 | #[allow(dead_code)] 36 | pub async fn delete, T: Into>(route: S, body: Option) -> Result { 37 | request(Method::Delete, route, body).await 38 | } 39 | 40 | pub async fn request, T: Into>( 41 | method: Method, 42 | route: S, 43 | body: Option, 44 | ) -> Result { 45 | let url: hyper::http::Uri = Uri::new("/tmp/squishd.sock", &route.into()).into(); 46 | let client = Client::unix(); 47 | let body = match body { 48 | Some(s) => Body::from(s.into()), 49 | None => Body::empty(), 50 | }; 51 | let mut response = match method { 52 | Method::Get => client.get(url).await?, 53 | Method::Post => { 54 | client 55 | .request(hyper::Request::post(url).body(body)?) 56 | .await? 57 | } 58 | Method::Put => client.request(hyper::Request::put(url).body(body)?).await?, 59 | Method::Patch => { 60 | client 61 | .request(hyper::Request::patch(url).body(body)?) 62 | .await? 63 | } 64 | Method::Delete => { 65 | client 66 | .request(hyper::Request::delete(url).body(body)?) 67 | .await? 68 | } 69 | #[allow(unreachable_patterns)] 70 | _ => panic!("unimplemented method: {:?}", method), 71 | }; 72 | let mut body: Vec = vec![]; 73 | while let Some(next) = response.data().await { 74 | let chunk = next?; 75 | let bytes: Vec = chunk.to_vec(); 76 | body.extend(&bytes); 77 | // io::stdout().write_all(&chunk).await?; 78 | } 79 | // The server should never send back invalid UTF-8 80 | Ok(String::from_utf8(body).unwrap()) 81 | } 82 | -------------------------------------------------------------------------------- /cli/src/main.rs: -------------------------------------------------------------------------------- 1 | #![warn(clippy::needless_pass_by_value)] 2 | 3 | extern crate hyper; 4 | extern crate hyperlocal; 5 | extern crate serde_json; 6 | extern crate tokio; 7 | 8 | mod client; 9 | 10 | use std::cmp::max; 11 | 12 | use clap::{Arg, Command}; 13 | use libsquish::squishfile; 14 | use libsquish::Result; 15 | 16 | #[tokio::main] 17 | async fn main() -> Result<()> { 18 | let matches = Command::new("squish") 19 | .arg(Arg::new("debug").long("debug").short('d').help("")) 20 | .subcommand(Command::new("ps").about("List running containers")) 21 | .subcommand( 22 | Command::new("create") 23 | .about("Create new containers") 24 | .arg(Arg::new("squishfile").required(true)), 25 | ) 26 | .subcommand( 27 | Command::new("validate") 28 | .about("Validate a squishfile") 29 | .arg(Arg::new("squishfile").required(true)), 30 | ) 31 | .subcommand( 32 | Command::new("stop") 33 | .about("Stop a container") 34 | .arg(Arg::new("id").required(true)), 35 | ) 36 | .get_matches(); 37 | 38 | match matches.subcommand_name() { 39 | Some("ps") => { 40 | let res = client::get("/containers/list").await?; 41 | let value: Vec = 42 | serde_json::from_str(res.as_str()).unwrap(); 43 | 44 | let mut max_name = 0; 45 | for container in &value { 46 | max_name = max(container.name.len(), max_name); 47 | } 48 | println!( 49 | "{:id_width$} {:name_width$} PID", 50 | "ID", 51 | "NAME", 52 | id_width = 7, 53 | name_width = max_name 54 | ); 55 | for container in &value { 56 | println!( 57 | "{} {:name_width$} {}", 58 | &container.id[..7], 59 | container.name, 60 | container.pid, 61 | name_width = max_name 62 | ); 63 | } 64 | } 65 | Some("create") => { 66 | // safe 67 | let path = matches 68 | .subcommand_matches("create") 69 | .ok_or("impossible")? 70 | .value_of("squishfile") 71 | .ok_or("impossible")?; 72 | let mut squishfile = squishfile::parse(path)?; 73 | squishfile.resolve_paths(); 74 | 75 | // Send to daemon 76 | let res = client::post( 77 | "/containers/create", 78 | Some(serde_json::to_string(&squishfile)?), 79 | ) 80 | .await?; 81 | let ids: serde_json::Value = serde_json::from_str(res.as_str())?; 82 | for id in ids.as_array().unwrap() { 83 | println!("{}", id.as_str().unwrap()); 84 | } 85 | } 86 | Some("stop") => { 87 | // safe 88 | let container_id = matches 89 | .subcommand_matches("stop") 90 | .ok_or("impossible")? 91 | .value_of("id") 92 | .ok_or("impossible")?; 93 | 94 | // Send to daemon 95 | let res = 96 | client::post::(format!("/containers/stop/{}", container_id), None) 97 | .await?; 98 | let ids = serde_json::from_str(res.as_str())?; 99 | match ids { 100 | serde_json::Value::Array(ids) => { 101 | for id in ids { 102 | println!("{}", id.as_str().unwrap()); 103 | } 104 | } 105 | _ => eprintln!("got unknown value: {}", res), 106 | } 107 | } 108 | Some("validate") => { 109 | // safe 110 | let path = matches 111 | .subcommand_matches("validate") 112 | .ok_or("impossible")? 113 | .value_of("squishfile") 114 | .ok_or("impossible")?; 115 | let _squishfile = squishfile::parse(path)?; 116 | println!("ok"); 117 | } 118 | Some(cmd) => { 119 | println!("Unknown subcommand '{}'", cmd); 120 | } 121 | None => { 122 | println!("No subcommand provided :<"); 123 | } 124 | } 125 | 126 | Ok(()) 127 | } 128 | -------------------------------------------------------------------------------- /daemon/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "daemon" 3 | version = "0.1.0" 4 | authors = ["amy null "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | libsquish = { path = "../libsquish" } 11 | 12 | flate2 = "1.0.25" 13 | haikunator = "0.1.2" 14 | hex = "0.4.3" 15 | hmac-sha256 = "1.1.6" 16 | log = "0.4.17" 17 | nix = "0.26.1" 18 | pretty_env_logger = "0.4.0" 19 | rlimit = "0.8.3" 20 | reqwest = "0.11.13" 21 | serde_json = "1.0.89" 22 | tar = "0.4.38" 23 | tokio = { version = "1.22.0", features = [ "full" ] } 24 | tokio-stream = { version = "0.1.11", features = [ "net" ] } 25 | tokio-util = { version = "0.7", features = ["io"] } 26 | warp = "0.3.3" 27 | yaml-rust = "0.4.5" 28 | -------------------------------------------------------------------------------- /daemon/src/engine/alpine.rs: -------------------------------------------------------------------------------- 1 | use crate::{engine::USER_AGENT, util::SquishError}; 2 | 3 | use std::fs; 4 | use std::fs::File; 5 | use std::io::Write; 6 | use std::path::Path; 7 | 8 | use libsquish::SyncResult; 9 | use yaml_rust::{Yaml, YamlLoader}; 10 | 11 | /// The current version of Alpine that this squishd knows about. 12 | pub const VERSION: &str = "3.14"; 13 | /// The architecture of Alpine that this squishd knows about. Maybe this will 14 | /// support ARM etc. in the future. 15 | pub const ARCH: &str = "x86_64"; 16 | 17 | /// The rootfs directory. This is the directory that Alpine rootfs images are 18 | /// cached in. 19 | pub fn rootfs_directory() -> &'static str { 20 | "cache/alpine/rootfs" 21 | } 22 | 23 | /// The path to the current rootfs tarball. This is 24 | /// `rootfs_directory()/alpine-rootfs-{VERSION}-{ARCH}.tar.gz`. 25 | pub fn current_rootfs_tarball(version: &str, arch: &str) -> String { 26 | format!( 27 | "{}/alpine-rootfs-{}-{}.tar.gz", 28 | rootfs_directory(), 29 | version, 30 | arch 31 | ) 32 | } 33 | 34 | /// The current rootfs. This is determined by the baked-in version / arch, and 35 | /// resolves to a path under the main rootfs directory. 36 | pub fn current_rootfs(version: &str, arch: &str) -> String { 37 | format!("{}/alpine-rootfs-{}-{}", rootfs_directory(), version, arch) 38 | } 39 | 40 | /// The base URL to download Alpine rootfs images from. 41 | /// TODO: Use a mirror list properly 42 | pub fn base_url(version: &str, arch: &str) -> String { 43 | format!( 44 | "https://cz.alpinelinux.org/alpine/v{}/releases/{}", 45 | version, arch 46 | ) 47 | } 48 | 49 | /// Download the base Alpine rootfs image. This will download and cache the 50 | /// rootfs image from a mirror (based on `base_url()`). 51 | pub async fn download_base_image(version: &str, arch: &str) -> SyncResult<()> { 52 | if Path::new(¤t_rootfs_tarball(version, arch)).exists() { 53 | info!("rootfs tarball already exists, not downloading again"); 54 | return Ok(()); 55 | } 56 | let manifest_url = format!("{}/latest-releases.yaml", base_url(version, arch)); 57 | debug!("downloading alpine minirootfs from {}", &manifest_url); 58 | let manifest_text = reqwest::Client::builder() 59 | .user_agent(USER_AGENT) 60 | .build()? 61 | .get(manifest_url) 62 | .send() 63 | .await? 64 | .text() 65 | .await?; 66 | 67 | let docs = YamlLoader::load_from_str(manifest_text.as_str())?; 68 | let manifest = &docs[0]; 69 | if let Some(vec) = manifest.as_vec() { 70 | let maybe_rootfs_manifest = vec.iter().find(|yaml| { 71 | matches!( 72 | yaml["flavor"].as_str(), 73 | Some("minirootfs") | Some("alpine-minirootfs") 74 | ) 75 | }); 76 | if let Some(rootfs_manifest) = maybe_rootfs_manifest { 77 | info!("found alpine minirootfs! downloading..."); 78 | let tarball = download_rootfs(rootfs_manifest, version, arch).await?; 79 | extract_tarball(tarball, current_rootfs(version, arch))?; 80 | setup_rootfs(¤t_rootfs(version, arch)) 81 | } else { 82 | error!( 83 | "expected alpine minirootfs in manifest, but manifest was\n{}", 84 | manifest_text 85 | ); 86 | Err(Box::new(SquishError::AlpineManifestMissing)) 87 | } 88 | } else { 89 | Err(Box::new(SquishError::AlpineManifestInvalid)) 90 | } 91 | } 92 | 93 | async fn download_rootfs(rootfs_manifest: &Yaml, version: &str, arch: &str) -> SyncResult { 94 | match rootfs_manifest["file"].as_str() { 95 | Some(rootfs_filename) => { 96 | // minirootfs is a ~3MB tarball, so we can afford to hold 97 | // it all in memory. 98 | let rootfs_url = format!("{}/{}", base_url(version, arch), rootfs_filename); 99 | 100 | let download_response = reqwest::get(rootfs_url).await?; 101 | let rootfs_bytes = download_response.bytes().await?; 102 | 103 | let output_path = current_rootfs_tarball(version, arch); 104 | debug!("downloading alpine minirootfs into {}", &output_path); 105 | fs::create_dir_all(&rootfs_directory())?; 106 | let mut output_file = fs::OpenOptions::new() 107 | .write(true) 108 | .create_new(true) 109 | .open(&output_path)?; 110 | output_file.write_all(&rootfs_bytes)?; 111 | Ok(output_path) 112 | } 113 | None => Err(Box::new(SquishError::AlpineManifestFileMissing)), 114 | } 115 | } 116 | 117 | fn extract_tarball(path: String, target_path: String) -> SyncResult<()> { 118 | info!("extracting alpine rootfs from {} to {}", path, target_path); 119 | let tarball = fs::File::open(path)?; 120 | let tar = flate2::read::GzDecoder::new(tarball); 121 | let mut archive = tar::Archive::new(tar); 122 | archive.unpack(target_path)?; 123 | Ok(()) 124 | } 125 | 126 | fn setup_rootfs(rootfs: &str) -> SyncResult<()> { 127 | // devices 128 | info!("setting up dummy devices"); 129 | File::create(format!("{}/dev/null", rootfs))?; 130 | File::create(format!("{}/dev/zero", rootfs))?; 131 | File::create(format!("{}/dev/random", rootfs))?; 132 | File::create(format!("{}/dev/urandom", rootfs))?; 133 | File::create(format!("{}/dev/console", rootfs))?; 134 | fs::create_dir_all(format!("{}/dev/shm", rootfs))?; 135 | fs::create_dir_all(format!("{}/dev/pts", rootfs))?; 136 | 137 | // mountable dirs 138 | info!("setting up /proc and /sys stubs"); 139 | fs::create_dir_all(format!("{}/proc", rootfs))?; 140 | fs::create_dir_all(format!("{}/sys", rootfs))?; 141 | 142 | // squish layers 143 | info!("setting up squish layer stubs"); 144 | fs::create_dir_all(format!("{}/app", rootfs))?; 145 | fs::create_dir_all(format!("{}/sdk", rootfs))?; 146 | 147 | // networking 148 | info!("setting up resolv.conf"); 149 | let mut resolv = File::create(format!("{}/etc/resolv.conf", rootfs))?; 150 | resolv.write_all("nameserver 10.0.2.3".as_bytes())?; // slirp4netns 151 | Ok(()) 152 | } 153 | -------------------------------------------------------------------------------- /daemon/src/engine/containers.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::fs; 3 | use std::path::Path; 4 | use std::sync::{Arc, Mutex}; 5 | use std::time::Duration; 6 | 7 | use haikunator::Haikunator; 8 | use libsquish::Result; 9 | use nix::sys::signal; 10 | use nix::sys::signal::kill; 11 | use nix::unistd::Pid; 12 | use tokio::time::sleep; 13 | 14 | /// A squish container. A container is basically just a tracked pid, that has a 15 | /// hexadecimal id and a name attached to it. Containers also contain a pid for 16 | /// their respective slirp4netns instances, as well as a timestamp for when 17 | /// they were created. 18 | #[derive(Debug, Clone)] 19 | pub struct Container { 20 | pub name: String, 21 | pub pid: nix::unistd::Pid, // TODO: Support multi-pid 22 | pub slirp_pid: nix::unistd::Pid, 23 | pub id: String, 24 | pub created_at: u128, 25 | } 26 | 27 | impl From<&Container> for libsquish::RunningContainer { 28 | fn from(container: &Container) -> Self { 29 | libsquish::RunningContainer { 30 | id: container.id.clone(), 31 | name: container.name.clone(), 32 | pid: container.pid.into(), 33 | } 34 | } 35 | } 36 | 37 | /// The global state of the daemon. To avoid constant locking, this is kept 38 | /// fairly small. It contains a mapping from container ids to `Container` 39 | /// structs, as well as a mapping from container pids to container ids. This 40 | /// data can be relied on to always be up to date. 41 | #[derive(Debug)] 42 | pub struct ContainerState { 43 | id_map: HashMap, 44 | pid_id_map: HashMap, 45 | } 46 | 47 | impl ContainerState { 48 | pub fn new() -> Self { 49 | ContainerState { 50 | id_map: HashMap::new(), 51 | pid_id_map: HashMap::new(), 52 | } 53 | } 54 | 55 | /// Generates a (id, name) tuple. The id is a SHA256 hash of the name. 56 | pub fn generate_id() -> (String, String) { 57 | let haiku = Haikunator::default(); 58 | let name = haiku.haikunate(); 59 | let hash = hmac_sha256::Hash::hash(name.as_bytes()); 60 | let id = hex::encode(hash); 61 | (id, name) 62 | } 63 | 64 | /// Add a container to the global container state. 65 | pub fn add_container( 66 | &mut self, 67 | pid: nix::unistd::Pid, 68 | slirp_pid: nix::unistd::Pid, 69 | id: &str, 70 | name: String, 71 | ) -> Result<()> { 72 | self.id_map.insert( 73 | id.to_string(), 74 | Container { 75 | name, 76 | id: id.to_string(), 77 | pid, 78 | slirp_pid, 79 | created_at: libsquish::now()?, 80 | }, 81 | ); 82 | self.pid_id_map.insert(pid, id.to_string()); 83 | Ok(()) 84 | } 85 | 86 | /// Remove a container or set of containers based on "fuzzy" matching of 87 | /// container names or ids. This partially matches the container name or id 88 | /// based on starting characters. That is, a container is removed if its 89 | /// name or its id starts with the partial value passed in. This is not a 90 | /// general substring match. 91 | pub fn fuzzy_remove_container(&mut self, partial_id_or_name: &str) -> Result> { 92 | let matches: Vec<&Container> = (&self.id_map) 93 | .iter() 94 | .filter(|(id, container)| { 95 | id.starts_with(partial_id_or_name) || container.name.starts_with(partial_id_or_name) 96 | }) 97 | .map(|(_id, container)| container) 98 | .collect(); 99 | let mut matched_ids = vec![]; 100 | for container in matches { 101 | matched_ids.push(container.id.clone()); 102 | } 103 | self.remove_all_containers(matched_ids.clone())?; 104 | Ok(matched_ids) 105 | } 106 | 107 | /// Remove the container with the given id. 108 | pub fn remove_container(&mut self, id: &str) -> Result<()> { 109 | self.remove_all_containers(vec![id.to_string()])?; 110 | Ok(()) 111 | } 112 | 113 | /// Remove all containers matching the ids in the list. This will kill the 114 | /// container and slirp4netns instances as a side effect. 115 | pub fn remove_all_containers(&mut self, ids: Vec) -> Result<()> { 116 | for id in ids { 117 | let container = self.id_map.remove(&id); 118 | if let Some(container) = container { 119 | info!("Cleaning and killing {}...", container.id); 120 | self.pid_id_map.remove(&container.pid); 121 | // TODO: Wait and SIGKILL the container as needed 122 | match kill(container.pid, signal::SIGTERM) { 123 | Ok(_) => (), 124 | Err(e) => { 125 | error!("Failed to kill container {}: {}", container.id, e); 126 | } 127 | } 128 | match kill(container.slirp_pid, signal::SIGTERM) { 129 | Ok(_) => (), 130 | Err(e) => { 131 | error!("Failed to kill container {}: {}", container.id, e); 132 | } 133 | } 134 | cleanup_container(container.id.as_str())?; 135 | } 136 | } 137 | Ok(()) 138 | } 139 | 140 | /// Returns a list of all currently-running containers. This is guaranteed 141 | /// to never contain state of currently-stopped containers. 142 | pub fn running_containers(&self) -> Vec { 143 | let mut out = vec![]; 144 | for v in self.id_map.values() { 145 | out.push(v.into()); 146 | } 147 | out 148 | } 149 | } 150 | 151 | /// A background task for reaping dead containers. This checks the global state 152 | /// 10 times per second, removing all container pids that no longer exist. 153 | pub async fn reap_children(state: Arc>) { 154 | loop { 155 | sleep(Duration::from_millis(100)).await; 156 | let mut container_state = state.lock().unwrap(); 157 | 158 | // This is dumb, but it SHOULD be dropped at the end of the scope so it 159 | // SHOULD be fine? 160 | let clone = container_state.pid_id_map.clone(); 161 | 162 | for (pid, id) in clone.iter() { 163 | // debug!("checking {}", pid.as_raw()); 164 | let path = format!("/proc/{}", pid.as_raw()); 165 | let path = Path::new(&path); 166 | if !path.exists() { 167 | match container_state.remove_container(id) { 168 | Ok(_) => (), 169 | Err(e) => { 170 | error!("Failed to remove container files for {}: {}", id, e); 171 | } 172 | } 173 | match cleanup_container(id) { 174 | Ok(_) => info!("cleaned up dead container {}", pid.as_raw()), 175 | Err(e) => error!("error cleaning up dead container {}: {}", pid.as_raw(), e), 176 | } 177 | } 178 | } 179 | } 180 | } 181 | 182 | fn cleanup_container(id: &str) -> Result<()> { 183 | fs::remove_dir_all(path_to(id))?; 184 | fs::remove_file(format!("/tmp/slirp4netns-{}.sock", id))?; 185 | Ok(()) 186 | } 187 | 188 | pub fn path_to(id: &str) -> String { 189 | format!("container/{}", id) 190 | } 191 | -------------------------------------------------------------------------------- /daemon/src/engine/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod alpine; 2 | pub mod containers; 3 | pub mod slirp; 4 | 5 | use std::ffi::CStr; 6 | use std::fs::File; 7 | use std::io::Write; 8 | use std::net::TcpListener; 9 | use std::os::unix::io::FromRawFd; 10 | use std::process::{Command, Stdio}; 11 | 12 | use libsquish::squishfile::Squishfile; 13 | use libsquish::SyncResult; 14 | use nix::fcntl; 15 | use nix::sys::memfd; 16 | use nix::unistd::{lseek, Pid, Whence}; 17 | 18 | pub const USER_AGENT: &str = "squish (https://github.com/queer/squish)"; 19 | 20 | /// (container pid, slirp pid) 21 | /// Spawns a container, taking in the new container's ID and the squishfile 22 | /// describing it. This function copies the squishfile to a temporary directory, 23 | /// spawns the `pid1` binary, starts the slirp4netns process, and then applies 24 | /// all port forwards. A tuple of pids (container, slirp4netns) is returned. 25 | pub async fn spawn_container(id: &str, squishfile: Squishfile) -> SyncResult<(Pid, Pid)> { 26 | // TODO: Ensure layers are cached 27 | for port in squishfile.ports() { 28 | check_port_bind(port.host())?; 29 | } 30 | 31 | // Write squishfile into a memfd that's inherited by child processes 32 | let mut memfd_name = format!("squishfile-{}", id).as_bytes().to_vec(); 33 | memfd_name.push(0); 34 | let memfd = memfd::memfd_create( 35 | CStr::from_bytes_with_nul(&memfd_name)?, 36 | memfd::MemFdCreateFlag::empty(), 37 | )?; 38 | 39 | // Safety: We just created the fd so we know it exists 40 | let mut memfd_file = unsafe { File::from_raw_fd(memfd) }; 41 | memfd_file.write_all( 42 | squishfile 43 | .to_json() 44 | .expect("impossible (couldn't ser squishfile!?)") 45 | .as_bytes(), 46 | )?; 47 | // Turn off FD_CLOEXEC 48 | let old_flags = fcntl::fcntl(memfd, fcntl::FcntlArg::F_GETFL)?; 49 | let fixed_flags = old_flags & !(fcntl::FdFlag::FD_CLOEXEC.bits()); 50 | fcntl::fcntl( 51 | memfd, 52 | fcntl::FcntlArg::F_SETFD(fcntl::FdFlag::from_bits_truncate(fixed_flags)), 53 | )?; 54 | // Seek to zero 55 | lseek(memfd, 0, Whence::SeekSet)?; 56 | 57 | // Spawn stuff 58 | debug!("{}: pid1 setup", &id); 59 | let base_version = alpine::VERSION.to_string(); 60 | let base_arch = alpine::ARCH.to_string(); 61 | // TODO: Allow not having an alpine base image for "FROM scratch"-equiv containers 62 | let alpine_version = match squishfile.layers().get("alpine") { 63 | Some(version) => version 64 | .version() 65 | .as_ref() 66 | .expect("No alpine version present!?"), 67 | None => &base_version, 68 | }; 69 | alpine::download_base_image(alpine_version, &base_arch).await?; 70 | let pid1 = Command::new("target/debug/pid1") 71 | .args(vec![ 72 | "--rootfs", 73 | alpine::current_rootfs(alpine_version, &base_arch).as_str(), 74 | "--id", 75 | id, 76 | "--path", 77 | containers::path_to(id).as_str(), 78 | "--squishfile-memfd", 79 | format!("{}", memfd).as_str(), 80 | ]) 81 | .envs(squishfile.env()) 82 | .output()?; 83 | 84 | let stderr = String::from_utf8(pid1.stderr).unwrap(); 85 | debug!("{}: container spawn stderr:\n{}", &id, stderr); 86 | 87 | let stdout = String::from_utf8(pid1.stdout).unwrap(); 88 | let child_pid = stdout.trim().parse::()?; 89 | 90 | debug!("{}: slirp4netns setup", &id); 91 | let slirp_socket_path = format!("/tmp/slirp4netns-{}.sock", &id); 92 | let slirp = tokio::process::Command::new("cache/slirp4netns") 93 | .args(vec![ 94 | "--configure", 95 | "--mtu=65520", 96 | "--disable-host-loopback", 97 | "--api-socket", 98 | slirp_socket_path.as_str(), 99 | format!("{}", child_pid).as_str(), 100 | "tap0", 101 | ]) 102 | // TODO: Should we be capturing these logs? 103 | .stdout(Stdio::null()) 104 | .stderr(Stdio::null()) 105 | .spawn()?; 106 | 107 | let slirp_pid = slirp.id().expect("no slirp4netns pid!?") as i32; 108 | 109 | tokio::spawn(async move { 110 | // debug!("{}: await slirp4netns exit", &id); 111 | let _output = slirp.wait_with_output().await.unwrap(); 112 | // let stdout = String::from_utf8(output.stdout).unwrap(); 113 | // let stderr = String::from_utf8(output.stderr).unwrap(); 114 | // debug!("{}: s4nns exit: {}:\n--------\nstdout:\n{}\n--------\nstderr:\n{}\n--------", &id, output.status, stdout, stderr); 115 | }); 116 | 117 | debug!("{}: port forward setup", &id); 118 | for port in squishfile.ports() { 119 | slirp::add_port_forward(&slirp_socket_path, port.host(), port.container()).await?; 120 | debug!( 121 | "{}: added port forward: {} -> {}", 122 | &id, 123 | port.host(), 124 | port.container() 125 | ); 126 | } 127 | 128 | Ok((Pid::from_raw(child_pid), Pid::from_raw(slirp_pid))) 129 | } 130 | 131 | fn check_port_bind(port: &u16) -> SyncResult<()> { 132 | TcpListener::bind(("127.0.0.1".to_string(), *port)) 133 | .map(|_| ()) 134 | .map_err(|e| e.into()) 135 | } 136 | -------------------------------------------------------------------------------- /daemon/src/engine/slirp.rs: -------------------------------------------------------------------------------- 1 | use crate::engine::USER_AGENT; 2 | use crate::util::SquishError; 3 | 4 | use std::fs; 5 | use std::fs::Permissions; 6 | use std::io::{Read, Write}; 7 | use std::os::unix::net::UnixStream; 8 | use std::os::unix::prelude::PermissionsExt; 9 | use std::path::Path; 10 | use std::time::Duration; 11 | 12 | use libsquish::SyncResult; 13 | use tokio::time::sleep; 14 | 15 | const URL: &str = "https://github.com/rootless-containers/slirp4netns/releases/download/v1.1.11/slirp4netns-x86_64"; 16 | 17 | /// Downloads the current slirp4netns binary. This caches in the same directory 18 | /// as the Alpine rootfs images. 19 | pub async fn download_slirp4netns() -> SyncResult<&'static str> { 20 | // TODO: Version this 21 | let output_path = "cache/slirp4netns"; 22 | if Path::new(output_path).exists() { 23 | info!("slirp4netns binary already exists, not downloading again"); 24 | return Ok(output_path); 25 | } 26 | info!("downloading slirp4netns binary from {}", URL); 27 | // TODO: Refactor this to reuse code from alpine / layers where possible 28 | let slirp_bytes = reqwest::Client::builder() 29 | .user_agent(USER_AGENT) 30 | .build()? 31 | .get(URL) 32 | .send() 33 | .await? 34 | .bytes() 35 | .await?; 36 | let mut output_file = fs::OpenOptions::new() 37 | .write(true) 38 | .create_new(true) 39 | .open(&output_path)?; 40 | output_file.write_all(&slirp_bytes)?; 41 | fs::set_permissions(output_path, Permissions::from_mode(0o755))?; 42 | // eprintln!("{:o}", output_file.metadata()?.permissions().mode()); 43 | Ok(output_path) 44 | } 45 | 46 | /// Adds a port-forward to the given slirp4netns instance via its socket. 47 | pub async fn add_port_forward(socket: &str, host: &u16, container: &u16) -> SyncResult { 48 | slirp_exec( 49 | socket, 50 | format!( 51 | r#" 52 | {{ 53 | "execute": "add_hostfwd", 54 | "arguments": {{ 55 | "proto": "tcp", 56 | "host_ip": "127.0.0.1", 57 | "host_port": {}, 58 | "guest_port": {} 59 | }} 60 | }} 61 | "#, 62 | host, container 63 | ) 64 | .as_str(), 65 | ) 66 | .await 67 | } 68 | 69 | /// Executes a slirp4netns command over the given socket. 70 | pub async fn slirp_exec(slirp_socket_path: &str, command: &str) -> SyncResult { 71 | info!("connecting to: {}", slirp_socket_path); 72 | let mut attempts: u8 = 0; 73 | let mut slirp_socket; 74 | loop { 75 | if let Ok(s) = UnixStream::connect(slirp_socket_path) { 76 | slirp_socket = s; 77 | break; 78 | } 79 | attempts += 1; 80 | if attempts > 100 { 81 | return Err(Box::new(SquishError::SlirpSocketCouldntBeFound)); 82 | } 83 | sleep(Duration::from_millis(1)).await; 84 | } 85 | debug!("slirp socket connected (attempts={})", attempts); 86 | slirp_socket.write_all(command.as_bytes())?; 87 | let mut res = String::new(); 88 | slirp_socket.read_to_string(&mut res)?; 89 | Ok(res) 90 | } 91 | -------------------------------------------------------------------------------- /daemon/src/handlers/container.rs: -------------------------------------------------------------------------------- 1 | use crate::engine; 2 | use crate::engine::containers::ContainerState; 3 | use crate::util::SquishError; 4 | 5 | use std::sync::Arc; 6 | use std::sync::Mutex; 7 | 8 | use libsquish::squishfile::Squishfile; 9 | use warp::Rejection; 10 | 11 | pub async fn create_container( 12 | state: Arc>, 13 | squishfile: Squishfile, 14 | ) -> Result { 15 | let (id, name) = ContainerState::generate_id(); 16 | info!("spawning container {} ({})", name, id); 17 | let (container_pid, slirp_pid) = engine::spawn_container(&id, squishfile) 18 | .await 19 | .map_err(|e| SquishError::GenericError(e))?; 20 | info!( 21 | "spawned container {} in pid {} (slirp={})", 22 | name, 23 | container_pid.as_raw(), 24 | slirp_pid.as_raw(), 25 | ); 26 | 27 | // Minimise use so as to avoid lock contention 28 | let mut container_state = state.lock().unwrap(); 29 | // TODO: Real struct for responses someday 30 | container_state 31 | .add_container(container_pid, slirp_pid, &id, name) 32 | .unwrap(); 33 | Ok(warp::reply::json(&vec![&id])) 34 | } 35 | 36 | pub async fn list_containers( 37 | state: Arc>, 38 | ) -> Result { 39 | info!("listing containers"); 40 | let container_state = state.lock().unwrap(); 41 | let running_containers = container_state.running_containers(); 42 | Ok(warp::reply::json(&running_containers)) 43 | } 44 | 45 | pub async fn stop_container( 46 | id: String, 47 | state: Arc>, 48 | ) -> Result { 49 | let mut container_state = state.lock().unwrap(); 50 | let ids = container_state.fuzzy_remove_container(&id).unwrap(); 51 | Ok(warp::reply::json(&ids)) 52 | } 53 | -------------------------------------------------------------------------------- /daemon/src/handlers/mod.rs: -------------------------------------------------------------------------------- 1 | use warp::Rejection; 2 | 3 | pub mod container; 4 | 5 | pub async fn status() -> Result { 6 | Ok(warp::reply::reply()) 7 | } 8 | -------------------------------------------------------------------------------- /daemon/src/main.rs: -------------------------------------------------------------------------------- 1 | #![warn(clippy::needless_pass_by_value)] 2 | 3 | extern crate flate2; 4 | extern crate haikunator; 5 | extern crate hex; 6 | extern crate hmac_sha256; 7 | extern crate libsquish; 8 | #[macro_use] 9 | extern crate log; 10 | extern crate nix; 11 | extern crate pretty_env_logger; 12 | extern crate reqwest; 13 | extern crate tar; 14 | extern crate tokio; 15 | extern crate warp; 16 | extern crate yaml_rust; 17 | 18 | use crate::engine::containers::ContainerState; 19 | 20 | use std::fs; 21 | use std::path::Path; 22 | use std::sync::Arc; 23 | use std::sync::Mutex; 24 | 25 | use libsquish::SyncResult; 26 | use warp::hyper::body::Bytes; 27 | use warp::Filter; 28 | 29 | mod engine; 30 | mod handlers; 31 | mod util; 32 | 33 | #[cfg(unix)] 34 | #[tokio::main] 35 | 36 | async fn main() -> SyncResult<()> { 37 | use tokio::net::UnixListener; 38 | use tokio_stream::wrappers::UnixListenerStream; 39 | 40 | pretty_env_logger::init(); 41 | info!("squishd booting..."); 42 | 43 | info!("prefetching alpine base image..."); 44 | engine::alpine::download_base_image( 45 | &engine::alpine::VERSION.to_string(), 46 | &engine::alpine::ARCH.to_string(), 47 | ) 48 | .await?; 49 | info!("prefetching slirp4netns binary..."); 50 | engine::slirp::download_slirp4netns().await?; 51 | 52 | let path = Path::new("/tmp/squishd.sock"); 53 | if path.exists() { 54 | fs::remove_file(path)?; 55 | } 56 | 57 | let global_state = Arc::new(Mutex::new(ContainerState::new())); 58 | 59 | let clone = global_state.clone(); 60 | tokio::spawn(engine::containers::reap_children(clone)); 61 | 62 | // Container routes 63 | let container_create = warp::path!("containers" / "create") 64 | .and(warp::post()) 65 | .and(with_state(global_state.clone())) 66 | .and(warp::body::bytes().map(|bytes: Bytes| { 67 | let vec: Vec = bytes.to_vec(); 68 | let body = String::from_utf8(vec).expect("squishfile not valid string"); 69 | serde_json::from_str(&*body).expect("squishfile invalid") 70 | })) 71 | .and_then(handlers::container::create_container); 72 | let container_list = warp::path!("containers" / "list") 73 | .and(warp::get()) 74 | .and(with_state(global_state.clone())) 75 | .and_then(handlers::container::list_containers); 76 | let container_stop = warp::path!("containers" / "stop" / String) 77 | .and(warp::post()) 78 | .and(with_state(global_state.clone())) 79 | .and_then(handlers::container::stop_container); 80 | 81 | // Utility routes 82 | let status = warp::path!("status") 83 | .and(warp::get()) 84 | .and_then(handlers::status); 85 | 86 | let log = warp::log("squishd"); 87 | let routes = warp::any() 88 | .and( 89 | container_create 90 | .or(container_list) 91 | .or(container_stop) 92 | .or(status), 93 | ) 94 | .with(log); 95 | 96 | let listener = UnixListener::bind(path).unwrap(); 97 | let incoming = UnixListenerStream::new(listener); 98 | warp::serve(routes).run_incoming(incoming).await; 99 | 100 | Ok(()) 101 | } 102 | 103 | #[cfg(not(unix))] 104 | #[tokio::main] 105 | async fn main() { 106 | panic!("squishd must be run on a unix-like os!"); 107 | } 108 | 109 | fn with_state( 110 | state: T, 111 | ) -> impl Filter + Clone { 112 | warp::any().map(move || state.clone()) 113 | } 114 | -------------------------------------------------------------------------------- /daemon/src/util/mod.rs: -------------------------------------------------------------------------------- 1 | use std::{error::Error, fmt::Display}; 2 | 3 | #[derive(Debug)] 4 | pub enum SquishError { 5 | GenericError(Box), 6 | 7 | SlirpSocketCouldntBeFound, 8 | 9 | AlpineManifestInvalid, 10 | AlpineManifestMissing, 11 | AlpineManifestFileMissing, 12 | } 13 | 14 | impl Display for SquishError { 15 | fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { 16 | todo!() 17 | } 18 | } 19 | 20 | impl warp::reject::Reject for SquishError {} 21 | 22 | impl Error for SquishError {} 23 | -------------------------------------------------------------------------------- /libsquish/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libsquish" 3 | version = "0.1.0" 4 | authors = ["amy null "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | derive-getters = "0.2.0" 11 | serde = { version = "1.0.148", features = [ "derive" ] } 12 | serde_json = "1.0.89" 13 | toml = "0.5.9" 14 | -------------------------------------------------------------------------------- /libsquish/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![warn(clippy::needless_pass_by_value)] 2 | 3 | pub mod squishfile; 4 | 5 | use std::error::Error; 6 | use std::io::ErrorKind; 7 | use std::time::SystemTime; 8 | 9 | use serde::{Deserialize, Serialize}; 10 | 11 | pub type Result = std::result::Result>; 12 | pub type SyncResult = std::result::Result>; 13 | 14 | /// A currently-running container. This is effectively a three-typle of the 15 | /// container's id, name, and pid. 16 | #[derive(Serialize, Deserialize)] 17 | pub struct RunningContainer { 18 | pub id: String, 19 | pub name: String, 20 | pub pid: i32, 21 | } 22 | 23 | /// Returns the current time in milliseconds since the UNIX epoch. 24 | pub fn now() -> Result { 25 | Ok(SystemTime::now() 26 | .duration_since(SystemTime::UNIX_EPOCH)? 27 | .as_millis()) 28 | } 29 | 30 | pub fn err>(reason: S) -> Result { 31 | Err(Box::new(std::io::Error::new( 32 | ErrorKind::Other, 33 | reason.into(), 34 | ))) 35 | } 36 | -------------------------------------------------------------------------------- /libsquish/src/squishfile.rs: -------------------------------------------------------------------------------- 1 | use std::collections::{BTreeMap, HashMap}; 2 | use std::error::Error; 3 | use std::fs; 4 | use std::path::Path; 5 | 6 | use derive_getters::Getters; 7 | use serde::{Deserialize, Serialize}; 8 | 9 | #[derive(Deserialize, Serialize, Getters, Debug)] 10 | pub struct Squishfile { 11 | run: Run, 12 | layers: BTreeMap, 13 | env: HashMap, 14 | ports: Vec, 15 | } 16 | 17 | impl Squishfile { 18 | fn update_layer(&mut self, layer: &str, new_data: &LayerSpec) { 19 | self.layers.insert(layer.to_string(), new_data.clone()); 20 | } 21 | 22 | /// Resolves paths in the squishfile to absolute paths where possible. This 23 | /// is primarily to allow local file mounts. Local paths are resolved when 24 | /// we detect that a version is NOT specified. Generally speaking, this 25 | /// means that either a `path` is explicitly specified, or the given path 26 | /// starts with `./` or `../`. 27 | pub fn resolve_paths(&mut self) { 28 | let resolved: Vec<(String, LayerSpec)> = self 29 | .layers 30 | .iter() 31 | // Easiest way to detect local paths -- generic labels means we 32 | // can't resolve every possible path 33 | .filter(|(_k, v)| { 34 | matches!( 35 | v, 36 | &&LayerSpec { 37 | version: None, 38 | path: Some(_), 39 | target: _, 40 | rw: _, 41 | } 42 | ) 43 | }) 44 | // This is safe because we just checked it 45 | .map(|(k, v)| match fs::canonicalize(v.path.as_ref().unwrap()) { 46 | Ok(path) => { 47 | // Resolve the path to an absolute path 48 | let path = path.as_path().display().to_string(); 49 | let new_target = v.target.as_ref().cloned(); 50 | ( 51 | k.clone(), 52 | LayerSpec { 53 | version: None, 54 | path: Some(path), 55 | target: new_target, 56 | rw: *v.rw(), 57 | }, 58 | ) 59 | } 60 | Err(e) => panic!("squishfile: error resolving relative path: {}", e), 61 | }) 62 | .collect(); 63 | 64 | resolved.iter().for_each(|(k, v)| self.update_layer(k, v)); 65 | } 66 | 67 | pub fn to_json(&self) -> Result> { 68 | serde_json::to_string(&self).map_err(|e| e.into()) 69 | } 70 | 71 | pub fn from_json<'a, S: Into<&'a str>>(json: S) -> Result> { 72 | serde_json::from_str(json.into()).map_err(|e| e.into()) 73 | } 74 | } 75 | 76 | impl From for String { 77 | fn from(squishfile: Squishfile) -> Self { 78 | toml::to_string(&squishfile) 79 | .unwrap_or_else(|_| panic!("unable to serialise config: {:?}", squishfile)) 80 | } 81 | } 82 | 83 | #[derive(Deserialize, Serialize, Getters, Debug, Clone)] 84 | pub struct LayerSpec { 85 | version: Option, 86 | // TODO: Don't assume path is always valid UTF-8? 87 | path: Option, 88 | target: Option, 89 | rw: Option, 90 | } 91 | 92 | #[derive(Deserialize, Serialize, Getters, Debug)] 93 | pub struct Run { 94 | command: String, 95 | args: Vec, 96 | } 97 | 98 | #[derive(Deserialize, Serialize, Getters, Debug)] 99 | pub struct Port { 100 | container: u16, 101 | host: u16, 102 | protocol: PortProtocol, 103 | } 104 | 105 | #[derive(Deserialize, Serialize, Debug)] 106 | #[serde(rename_all = "lowercase")] 107 | pub enum PortProtocol { 108 | Tcp, 109 | Udp, 110 | } 111 | 112 | pub fn parse_str<'a, T: Into<&'a str>>(squishfile: T) -> Result> { 113 | let mut parsed: toml::Value = toml::from_str(squishfile.into())?; 114 | let table = match parsed.as_table_mut() { 115 | Some(table) => table, 116 | None => panic!("squishfile: root not table"), 117 | }; 118 | let run: Run = match table.get("run") { 119 | Some(run) => run.clone().try_into()?, 120 | None => panic!("squishfile: run not found"), 121 | }; 122 | let env: HashMap = match table.get("env") { 123 | Some(env) => env.clone().try_into()?, 124 | None => HashMap::new(), 125 | }; 126 | let ports: Vec = match table.get("ports") { 127 | Some(ports) => ports 128 | .as_array() 129 | .expect("ports not array") 130 | .iter() 131 | .map(|p| p.clone().try_into::().unwrap()) 132 | .collect(), 133 | None => vec![], 134 | }; 135 | 136 | let layers_table = match table.get("layers") { 137 | Some(layers) => layers, 138 | None => panic!("squishfile: layers not found"), 139 | }; 140 | 141 | let layers_table = match layers_table.as_table() { 142 | Some(layers_table) => layers_table, 143 | None => panic!("squishfile: layers: not table"), 144 | }; 145 | 146 | let mut layers = BTreeMap::new(); 147 | for (k, v) in layers_table { 148 | layers.insert(k.clone(), parse_layer(v)?); 149 | } 150 | 151 | Ok(Squishfile { 152 | layers, 153 | run, 154 | env, 155 | ports, 156 | }) 157 | } 158 | 159 | fn parse_layer(value: &toml::Value) -> Result> { 160 | match value.as_str() { 161 | // If the layer spec is just a string, we try to resolve it to a local 162 | // path if possible. In this case, the file is NOT intended to be 163 | // mounted rw, so we always set rw = Some(false). 164 | Some(maybe_path) => { 165 | if maybe_path.starts_with("./") || maybe_path.starts_with("../") { 166 | Ok(LayerSpec { 167 | version: None, 168 | path: Some(maybe_path.to_string()), 169 | target: None, 170 | rw: Some(false), 171 | }) 172 | } else { 173 | Ok(LayerSpec { 174 | version: Some(maybe_path.to_string()), 175 | path: None, 176 | target: None, 177 | rw: Some(false), 178 | }) 179 | } 180 | } 181 | None => value.clone().try_into::().map_err(|e| e.into()), 182 | } 183 | } 184 | 185 | pub fn parse>(squishfile: T) -> Result> { 186 | let content = fs::read_to_string(squishfile)?; 187 | parse_str(&*content) 188 | } 189 | -------------------------------------------------------------------------------- /pid1/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pid1" 3 | version = "0.1.0" 4 | authors = ["amy null "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | libsquish = { path = "../libsquish" } 11 | 12 | clap = "3.2.22" 13 | futures = "0.3.25" 14 | nix = "0.26.1" 15 | reqwest = "0.11.13" 16 | rlimit = "0.8.3" 17 | tokio = { version = "1.22.0", features = [ "full" ] } 18 | -------------------------------------------------------------------------------- /pid1/src/engine/mod.rs: -------------------------------------------------------------------------------- 1 | use std::fs::{self, OpenOptions}; 2 | use std::os::unix::io::IntoRawFd; 3 | use std::path::Path; 4 | use std::process; 5 | 6 | use libsquish::squishfile::{LayerSpec, Squishfile}; 7 | use libsquish::Result; 8 | use nix::mount::{mount, MsFlags}; 9 | use nix::unistd::{chdir, chroot, close, dup, dup2}; 10 | 11 | pub struct Engine<'a> { 12 | squishfile: &'a Squishfile, 13 | rootfs_path: &'a str, 14 | container_path: &'a str, 15 | #[allow(dead_code)] 16 | container_id: &'a str, 17 | container_rootfs_path: String, 18 | } 19 | 20 | impl<'a> Engine<'a> { 21 | pub fn new( 22 | squishfile: &'a Squishfile, 23 | rootfs: &'a str, 24 | container_path: &'a str, 25 | container_id: &'a str, 26 | ) -> Self { 27 | Engine { 28 | squishfile, 29 | rootfs_path: rootfs, 30 | container_path, 31 | container_id, 32 | container_rootfs_path: format!("{}/rootfs", container_path), 33 | } 34 | } 35 | 36 | pub fn setup_container(&self) -> Result<&Self> { 37 | // Set up container rootfs 38 | fs::create_dir_all(&self.container_rootfs_path).expect("couldn't create rootfs directory!"); 39 | 40 | // redirect stdout/err 41 | let stdout_dup = dup(1)?; 42 | let stderr_dup = dup(2)?; 43 | close(1)?; 44 | close(2)?; 45 | 46 | let stdout_log = fs::OpenOptions::new() 47 | .write(true) 48 | .create_new(true) 49 | .open(format!("{}/stdout.log", &self.container_path))?; 50 | let stdout_log_fd = stdout_log.into_raw_fd(); 51 | let stderr_log = fs::OpenOptions::new() 52 | .write(true) 53 | .create_new(true) 54 | .open(format!("{}/stderr.log", &self.container_path))?; 55 | let stderr_log_fd = stderr_log.into_raw_fd(); 56 | 57 | dup2(stdout_log_fd, stdout_dup)?; 58 | dup2(stderr_log_fd, stderr_dup)?; 59 | close(stdout_dup)?; 60 | close(stderr_dup)?; 61 | 62 | // Bindmount rootfs ro 63 | self.bind_mount( 64 | self.rootfs_path, 65 | &self.container_rootfs_path, 66 | MsFlags::MS_RDONLY | MsFlags::MS_NOATIME | MsFlags::MS_NOSUID, 67 | )?; 68 | 69 | // Bind-mount *nix stuff in 70 | println!(">> bindmounting devices"); 71 | self.bind_mount_dev( 72 | "/dev/null", 73 | &format!("{}/dev/null", self.container_rootfs_path), 74 | )?; 75 | self.bind_mount_dev( 76 | "/dev/zero", 77 | &format!("{}/dev/zero", self.container_rootfs_path), 78 | )?; 79 | self.bind_mount_dev( 80 | "/dev/random", 81 | &format!("{}/dev/random", self.container_rootfs_path), 82 | )?; 83 | self.bind_mount_dev( 84 | "/dev/urandom", 85 | &format!("{}/dev/urandom", self.container_rootfs_path), 86 | )?; 87 | println!(">> bindmounting devices finished!"); 88 | 89 | // Bindmount /tmp rw 90 | let tmp_path = format!("{}/tmp", &self.container_path); 91 | fs::create_dir_all(&tmp_path)?; 92 | self.bind_mount( 93 | &tmp_path, 94 | &format!("{}/tmp", self.container_rootfs_path), 95 | MsFlags::MS_NOSUID, 96 | )?; 97 | 98 | for (layer_name, layer) in self.squishfile.layers() { 99 | if layer_name != "alpine" && layer_name != "app" { 100 | self.bind_mount_layer::<&str>( 101 | &self.container_rootfs_path, 102 | layer_name, 103 | layer, 104 | None, 105 | )?; 106 | } else if layer_name == "app" { 107 | self.bind_mount_layer( 108 | &self.container_rootfs_path, 109 | layer_name, 110 | layer, 111 | Some("/app/"), 112 | )?; 113 | } 114 | } 115 | 116 | Ok(self) 117 | } 118 | 119 | pub fn run_container(&self) -> Result<()> { 120 | // chroot! 121 | chroot(self.container_rootfs_path.as_str()).expect("couldn't chroot!?"); 122 | chdir("/").expect("couldn't chdir to /!?"); 123 | 124 | println!(">> listing /proc"); 125 | for entry in fs::read_dir("/proc")? { 126 | let entry = entry?; 127 | let path = entry.path(); 128 | if path.is_dir() { 129 | println!("{}/", path.display()); 130 | } else { 131 | println!("{}", path.display()); 132 | } 133 | } 134 | 135 | self.run_in_container()?; 136 | println!(">> done!"); 137 | Ok(()) 138 | } 139 | 140 | fn run_in_container(&self) -> Result<()> { 141 | println!(">> inside the container!"); 142 | println!(">> i am {}", process::id()); 143 | println!( 144 | ">> running: {} {:?}", 145 | self.squishfile.run().command(), 146 | self.squishfile.run().args() 147 | ); 148 | 149 | std::process::Command::new(self.squishfile.run().command()) 150 | .envs(self.squishfile.env()) 151 | .args(self.squishfile.run().args()) 152 | .output() 153 | .unwrap(); 154 | Ok(()) 155 | } 156 | 157 | fn bind_mount_layer( 158 | &self, 159 | container_path: &str, 160 | layer_name: &str, 161 | layer: &LayerSpec, 162 | target_override: Option, 163 | ) -> Result<()> 164 | where 165 | TO: Into, 166 | { 167 | // Bind-mount squishfile layers 168 | println!(">> bindmounting {:?} => {:?}", layer.path(), layer.target()); 169 | if layer.path().is_none() && layer.version().is_none() { 170 | panic!("squishfile: nothing to mount for layer {}!?", layer_name); 171 | } 172 | let target = match layer.target() { 173 | Some(target) => target.clone(), 174 | None => { 175 | if layer.path().is_some() { 176 | // If path but no target, mount into /app 177 | let target = layer 178 | .path() 179 | .as_ref() 180 | .unwrap() 181 | .replace("../", "") 182 | .replace("./", ""); 183 | format!("/app/{}", target) 184 | } else if layer.version().is_none() { 185 | // If no path and no target and no version, panic 186 | panic!("squishfile no path or version for layer {}", layer_name); 187 | } else { 188 | // If no path or target, but there is a version, mount into /sdk 189 | format!("/sdk/{}", layer_name) 190 | } 191 | } 192 | }; 193 | let target = if let Some(target_override) = target_override { 194 | let file_name = Path::new(&target) 195 | .file_name() 196 | .unwrap() 197 | .to_str() 198 | .unwrap() 199 | .to_string(); 200 | format!("{}/{}", target_override.into(), file_name) 201 | } else { 202 | target 203 | }; 204 | let target = format!("{}/{}", container_path, target); 205 | // if layer.path().is_none() && layer.version().is_some() && layer.target().is_none() { 206 | // todo!("mounting squish layer normally"); 207 | // } 208 | let path = layer.path().as_ref().unwrap(); 209 | let mount_path = Path::new(path); 210 | // Yeah this is technically racy, but literally who cares? 211 | if mount_path.exists() { 212 | let meta = fs::metadata(path)?; 213 | let target_path = Path::new(&target); 214 | if meta.is_dir() { 215 | self.touch_dir(target_path)?; 216 | } else if meta.is_file() { 217 | let parent = target_path.parent().unwrap(); 218 | self.touch_dir(parent)?; 219 | self.touch(target_path)?; 220 | } else { 221 | println!(">> mount is not a directory or file"); 222 | } 223 | let mut bind_flags = MsFlags::MS_NOATIME | MsFlags::MS_NOSUID; 224 | if !matches!(layer.rw(), Some(true)) { 225 | bind_flags |= MsFlags::MS_RDONLY; 226 | } 227 | self.bind_mount(path, &target, bind_flags)?; 228 | } else { 229 | println!(">> mount didn't exist"); 230 | } 231 | Ok(()) 232 | } 233 | 234 | fn bind_mount_dev(&self, dev: &'static str, target: &str) -> Result<()> { 235 | println!(">> bindmount dev {} -> {}", dev, target); 236 | mount(Some(dev), target, Some(""), MsFlags::MS_BIND, Some(""))?; 237 | Ok(()) 238 | } 239 | 240 | fn bind_mount(&self, src: &str, target: &str, flags: MsFlags) -> Result<()> { 241 | println!(">> bindmount {} -> {}", src, target); 242 | 243 | mount( 244 | Some(src), 245 | target, 246 | Some(""), 247 | MsFlags::MS_BIND | flags, 248 | Some(""), 249 | )?; 250 | Ok(()) 251 | } 252 | 253 | fn touch(&self, path: &Path) -> Result<()> { 254 | match OpenOptions::new().create(true).write(true).open(path) { 255 | Ok(_) => Ok(()), 256 | Err(e) => Err(Box::new(e)), 257 | } 258 | } 259 | 260 | fn touch_dir(&self, path: &Path) -> Result<()> { 261 | match fs::create_dir_all(path) { 262 | Ok(_) => Ok(()), 263 | Err(e) => Err(Box::new(e)), 264 | } 265 | } 266 | } 267 | -------------------------------------------------------------------------------- /pid1/src/main.rs: -------------------------------------------------------------------------------- 1 | #![warn(clippy::needless_pass_by_value)] 2 | 3 | extern crate clap; 4 | extern crate futures; 5 | extern crate nix; 6 | extern crate reqwest; 7 | extern crate rlimit; 8 | extern crate tokio; 9 | 10 | mod engine; 11 | 12 | use std::fs::File; 13 | use std::io::Read; 14 | use std::os::unix::io::FromRawFd; 15 | 16 | use clap::{Arg, Command}; 17 | use libsquish::squishfile::Squishfile; 18 | use libsquish::Result; 19 | use nix::sched::{clone, CloneFlags}; 20 | use rlimit::Resource; 21 | 22 | fn main() -> Result<()> { 23 | let matches = Command::new("pid1") 24 | .arg( 25 | Arg::new("rootfs") 26 | .long("rootfs") 27 | .takes_value(true) 28 | .required(true) 29 | .help("path to rootfs"), 30 | ) 31 | .arg( 32 | Arg::new("id") 33 | .long("id") 34 | .takes_value(true) 35 | .required(true) 36 | .help("container id"), 37 | ) 38 | .arg( 39 | Arg::new("path") 40 | .long("path") 41 | .takes_value(true) 42 | .required(true) 43 | .help("path to container directory"), 44 | ) 45 | .arg( 46 | Arg::new("squishfile-memfd") 47 | .long("squishfile-memfd") 48 | .takes_value(true) 49 | .required(true) 50 | .help("squishfile memfd to run from"), 51 | ) 52 | .get_matches(); 53 | 54 | let squishfile_memfd: i32 = matches 55 | .value_of("squishfile-memfd") 56 | .unwrap() 57 | .to_string() 58 | .parse()?; 59 | // Safety: We created this in the daemon, and since this is cloned off of 60 | // the daemon process, we know that the fd exists. Since the daemon 61 | // disables FD_CLOEXEC before forking, we know that the fd is 62 | // guaranteed to exist. 63 | let mut squishfile_json = unsafe { File::from_raw_fd(squishfile_memfd) }; 64 | let mut squishfile = String::new(); 65 | squishfile_json.read_to_string(&mut squishfile)?; 66 | 67 | let pid = spawn_container( 68 | matches.value_of("rootfs").unwrap().to_string(), 69 | matches.value_of("path").unwrap().to_string(), 70 | matches.value_of("id").unwrap().to_string(), 71 | Squishfile::from_json(squishfile.as_str()) 72 | .expect("impossible (couldn't deser squishfile)!?"), 73 | )?; 74 | println!("{}", pid.as_raw()); 75 | Ok(()) 76 | } 77 | 78 | fn spawn_container( 79 | rootfs: String, 80 | path: String, 81 | container_id: String, 82 | squishfile: Squishfile, 83 | ) -> Result { 84 | let stack_size = match Resource::STACK.get() { 85 | Ok((soft, _hard)) => { 86 | // debug!( 87 | // "soft stack={}, hard stack={}", 88 | // soft.as_usize(), 89 | // hard.as_usize() 90 | // ); 91 | soft as usize 92 | } 93 | Err(_) => { 94 | // 8MB 95 | 8 * 1024 * 1024 96 | } 97 | }; 98 | 99 | let callback = move || { 100 | let engine = engine::Engine::new(&squishfile, &rootfs, &path, &container_id); 101 | match engine.setup_container().unwrap().run_container() { 102 | Ok(_) => 0, 103 | _ => 1, 104 | } 105 | }; 106 | 107 | let mut stack_vec = vec![0u8; stack_size]; 108 | let stack: &mut [u8] = stack_vec.as_mut_slice(); 109 | 110 | let pid = clone( 111 | Box::new(callback), 112 | stack, 113 | // Oh boy, where do I even begin with this? 114 | // If you read the man pages for clone(2), a common sentence to see is: 115 | // 116 | // > Only a privileged process (CAP_SYS_ADMIN) can employ CLONE_NEWXXX. 117 | // 118 | // It turns out that this is just... not true? I don't know if it's 119 | // because my system is configured weirdly, or if it's because I'm 120 | // somehow accidentally invoking sudo(1) or something else, I honestly 121 | // don't know. 122 | // 123 | // This shouldn't work. The man pages say so. 124 | // 125 | // But it does. 126 | CloneFlags::CLONE_NEWPID 127 | | CloneFlags::CLONE_NEWUTS 128 | | CloneFlags::CLONE_NEWNS 129 | | CloneFlags::CLONE_NEWNET 130 | | CloneFlags::CLONE_NEWUSER 131 | | CloneFlags::CLONE_NEWCGROUP, 132 | None, 133 | )?; 134 | if (pid.as_raw() as i32) == -1 { 135 | println!("clone error"); 136 | println!("{:?}", std::io::Error::last_os_error()); 137 | } 138 | 139 | Ok(pid) 140 | } 141 | -------------------------------------------------------------------------------- /setup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | echo ">> Installing git hooks..." 4 | ./tools/install-hooks 5 | echo ">> Disabling empty \`git add\` warnings.." 6 | git config --local advice.addEmptyPathspec false 7 | echo ">> Done!" 8 | -------------------------------------------------------------------------------- /test/e2e/001-container-starts.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 001-container-starts 4 | # Assert that the container starts. 5 | 6 | CONTAINER_COUNT=$(cargo -q run -p cli -- ps | wc -l) 7 | CONTAINER_COUNT=$((CONTAINER_COUNT - 1)) 8 | 9 | if [ $CONTAINER_COUNT -ne 1 ]; then 10 | echo "Expected 1 container to be running, but found $CONTAINER_COUNT" 11 | exit 1 12 | fi 13 | -------------------------------------------------------------------------------- /test/e2e/002-container-networking-works.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 002-container-networking-works 4 | # Assert that a container can forward ports to the host as expected. 5 | 6 | STATUS=$(curl -s -o /dev/null -w "%{http_code}" localhost:42069) 7 | if [ "$STATUS" != "200" ]; then 8 | echo "Expected status code 200, got $STATUS" 9 | exit 1 10 | fi 11 | -------------------------------------------------------------------------------- /test/e2e/003-file-bind-mounts-work.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 003-file-bind-mounts-work 4 | # Assert that a file bind-mounts into a container and can be read. 5 | 6 | FILE_CONTENTS=$(curl -s -o- localhost:42069/Cargo.toml) 7 | if [ "$FILE_CONTENTS" != "$(cat Cargo.toml)" ]; then 8 | echo "Expected Cargo.toml match, got:\n$FILE_CONTENTS" 9 | exit 1 10 | fi 11 | -------------------------------------------------------------------------------- /test/e2e/004-other-alpine-versions-work.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 004-other-alpine-versions-work 4 | # Assert that default-uncached Alpine versions work. 5 | # SQUISHFILE_OVERRIDE=./test/squishfiles/004-squishfile-3.13.toml 6 | 7 | FILE_CONTENTS=$(curl -s -o- localhost:42069/alpine-release) 8 | if [ "$FILE_CONTENTS" != "3.13.11" ]; then 9 | echo "Expected Alpine 3.13.11, got:\n$FILE_CONTENTS" 10 | echo -n "\n\n" 11 | echo "NOTE: This test can be flaky, as different IPs(?) seem to recv. different Alpine versions." 12 | exit 1 13 | fi 14 | -------------------------------------------------------------------------------- /test/e2e/005-env-vars-work.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 005-env-vars-work 4 | # Assert that env var injection works 5 | # SQUISHFILE_OVERRIDE=./test/squishfiles/005-squishfile-port-env.toml 6 | 7 | FILE_CONTENTS=$(curl -s -o- localhost:42069/asdf) 8 | if [ "$FILE_CONTENTS" != "ed4a92f174398375a5a036f5b06b61984d977640ff03e55288a5c62c7e8fcd0c" ]; then 9 | echo "Expected 'ed4a92f174398375a5a036f5b06b61984d977640ff03e55288a5c62c7e8fcd0c', got:\n$FILE_CONTENTS" 10 | exit 1 11 | fi 12 | -------------------------------------------------------------------------------- /test/e2e/006-rw-file-binds-work.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 006-rw-file-binds-work 4 | # Assert that a file bind-mounts into a container and can be read and written. 5 | # SQUISHFILE_OVERRIDE=./test/squishfiles/006-squishfile-rw-mount.toml 6 | 7 | rm ./test/support/006-scratch 8 | touch ./test/support/006-scratch 9 | 10 | FILE_CONTENTS=$(curl -s -o- localhost:42069/scratch) 11 | if [ "$FILE_CONTENTS" != "asdf" ]; then 12 | echo "Expected 'asdf', got:\n$FILE_CONTENTS" 13 | exit 1 14 | fi 15 | -------------------------------------------------------------------------------- /test/e2e/007-pid-ns-check.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 007-pid-ns-check 4 | # Ensure that we actually entire a new pid namespace 5 | # SQUISHFILE_OVERRIDE=./test/squishfiles/007-squishfile-pid-ns.toml 6 | 7 | rm ./test/support/007-scratch 8 | touch ./test/support/007-scratch 9 | 10 | FILE_CONTENTS=$(curl -s -o- localhost:42069/scratch) 11 | if [ "$FILE_CONTENTS" != "2" ]; then 12 | echo "Expected '2', got:\n$FILE_CONTENTS" 13 | exit 1 14 | fi -------------------------------------------------------------------------------- /test/http-asm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/queer/squish/26106ff255dd9e6d1b220a051a614ebee49e5763/test/http-asm -------------------------------------------------------------------------------- /test/squishfiles/004-squishfile-3.13.toml: -------------------------------------------------------------------------------- 1 | [layers] 2 | alpine = "3.13" 3 | app = "./test/http-asm" 4 | 5 | [run] 6 | command = "/app/http-asm" 7 | args = ["2000", "/etc"] 8 | 9 | [env] 10 | KEY = "value" 11 | IN_SQUISH_CONTAINER = "true" 12 | 13 | [[ports]] 14 | container = 2000 15 | host = 42069 16 | protocol = "tcp" 17 | -------------------------------------------------------------------------------- /test/squishfiles/005-squishfile-port-env.toml: -------------------------------------------------------------------------------- 1 | [layers] 2 | alpine = "3.13" 3 | runner = { path = "./test/support/005-env-to-tmp-and-run.sh", target = "/app/run.sh" } 4 | app = "./test/http-asm" 5 | 6 | [run] 7 | command = "/app/run.sh" 8 | args = [] 9 | 10 | [env] 11 | TEST_KEY = "ed4a92f174398375a5a036f5b06b61984d977640ff03e55288a5c62c7e8fcd0c" 12 | 13 | [[ports]] 14 | container = 2000 15 | host = 42069 16 | protocol = "tcp" 17 | -------------------------------------------------------------------------------- /test/squishfiles/006-squishfile-rw-mount.toml: -------------------------------------------------------------------------------- 1 | [layers] 2 | alpine = "3.14" 3 | cargo-toml = { path = "./Cargo.toml", target = "/app/Cargo.toml" } 4 | scratch-file = { path = "./test/support/006-scratch", target = "/app/scratch", rw = true } 5 | runner = { path = "./test/support/006-rewrite-mounted-file.sh", target = "/app/run.sh" } 6 | app = "./test/http-asm" 7 | 8 | [run] 9 | command = "/app/run.sh" 10 | args = [] 11 | 12 | [env] 13 | KEY = "value" 14 | IN_SQUISH_CONTAINER = "true" 15 | 16 | [[ports]] 17 | container = 2000 18 | host = 42069 19 | protocol = "tcp" 20 | -------------------------------------------------------------------------------- /test/squishfiles/007-squishfile-pid-ns.toml: -------------------------------------------------------------------------------- 1 | [layers] 2 | alpine = "3.14" 3 | scratch-file = { path = "./test/support/007-scratch", target = "/app/scratch", rw = true } 4 | runner = { path = "./test/support/007-pid-ns-check.sh", target = "/app/run.sh" } 5 | app = "./test/http-asm" 6 | 7 | [run] 8 | command = "/app/run.sh" 9 | args = [] 10 | 11 | [env] 12 | KEY = "value" 13 | IN_SQUISH_CONTAINER = "true" 14 | 15 | [[ports]] 16 | container = 2000 17 | host = 42069 18 | protocol = "tcp" 19 | -------------------------------------------------------------------------------- /test/squishfiles/default.toml: -------------------------------------------------------------------------------- 1 | [layers] 2 | alpine = "3.14" # If not specified, uses squishd's latest-known-version 3 | # python = "3.9" # Support more ways of choosing versions? (ex. ~3.9 gives any 3.9.x or some shit) 4 | cargo-toml = { path = "./Cargo.toml", target = "/app/Cargo.toml" } 5 | cli = { path = "./cli", target = "/app/cli" } 6 | app = "./test/http-asm" # Tarballs, zips, etc. should be automatically detected and extracted (ideally hash+cache?) 7 | 8 | [run] 9 | # command = "/sdk/python/bin/python" 10 | # args = ["/app/main.py", "--flag", "--other-flag=whatever"] 11 | command = "/app/http-asm" 12 | args = ["2000", "/app"] 13 | 14 | [env] 15 | KEY = "value" 16 | IN_SQUISH_CONTAINER = "true" 17 | 18 | [[ports]] 19 | container = 2000 20 | host = 42069 21 | protocol = "tcp" 22 | -------------------------------------------------------------------------------- /test/support/005-env-to-tmp-and-run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo $TEST_KEY > /tmp/asdf 4 | /app/http-asm 2000 /tmp 5 | -------------------------------------------------------------------------------- /test/support/006-rewrite-mounted-file.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | echo -n "asdf" >> /app/scratch 6 | /app/http-asm 2000 /app 7 | -------------------------------------------------------------------------------- /test/support/006-scratch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/queer/squish/26106ff255dd9e6d1b220a051a614ebee49e5763/test/support/006-scratch -------------------------------------------------------------------------------- /test/support/007-pid-ns-check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | echo $$ >> /app/scratch 2>&1 6 | /app/http-asm 2000 /app 7 | -------------------------------------------------------------------------------- /test/support/007-scratch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/queer/squish/26106ff255dd9e6d1b220a051a614ebee49e5763/test/support/007-scratch -------------------------------------------------------------------------------- /test/test-e2e.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | DEFAULT="\e[39m" 4 | RED="\e[91m" 5 | GREEN="\e[92m" 6 | 7 | log() { 8 | TS=$(date +%T) 9 | echo -e "[$TS] $1" 10 | } 11 | 12 | start_container() { 13 | cargo -q run -p cli -- create $1 > /dev/null 14 | 15 | CONTAINER_COUNT=$(cargo -q run -p cli -- ps | wc -l) 16 | CONTAINER_COUNT=$((CONTAINER_COUNT - 1)) 17 | 18 | if [ $CONTAINER_COUNT -ne 1 ]; then 19 | log "${RED}ERROR:${DEFAULT} Expected 1 container to be running, but found $CONTAINER_COUNT" 20 | exit 1 21 | fi 22 | } 23 | 24 | stop_container() { 25 | CONTAINER_ID=$(cargo -q run -p cli -- ps | grep -v "ID" | awk '{print $1}') 26 | cargo -q run -p cli -- stop $CONTAINER_ID > /dev/null 27 | 28 | CONTAINER_COUNT=$(cargo -q run -p cli -- ps | wc -l) 29 | CONTAINER_COUNT=$((CONTAINER_COUNT - 1)) 30 | 31 | if [ $CONTAINER_COUNT -ne 0 ]; then 32 | log "${RED}ERROR:${DEFAULT} Expected 0 containers to be running, but found $CONTAINER_COUNT" 33 | exit 1 34 | fi 35 | } 36 | 37 | log "Running build..." 38 | cargo build 39 | 40 | # Run daemon 41 | cargo -q run -p daemon & 42 | # Await daemon up 43 | while [ "`curl -s -o /dev/null -w "%{http_code}" --unix-socket /tmp/squishd.sock http:/x/status`" != "200" ]; do 44 | sleep 0.5 45 | done 46 | sleep 1 47 | log "Daemon up!" 48 | DAEMON=$(pidof daemon) 49 | 50 | log "Asserting sanity..." 51 | 52 | # Assert no containers running 53 | CONTAINER_COUNT=$(cargo -q run -p cli -- ps | wc -l) 54 | CONTAINER_COUNT=$((CONTAINER_COUNT - 1)) 55 | 56 | if [ $CONTAINER_COUNT -ne 0 ]; then 57 | log "Expected 0 containers to be running, but found $CONTAINER_COUNT" 58 | exit 1 59 | fi 60 | 61 | log "Starting tests!" 62 | # Run tests! 63 | TOTAL=0 64 | PASSED=0 65 | for f in test/e2e/*.sh; do 66 | SQUISHFILE=$(grep "SQUISHFILE_OVERRIDE=" $f | sed -e 's/# SQUISHFILE_OVERRIDE=//') 67 | SQUISHFILE="${SQUISHFILE:-./test/squishfiles/default.toml}" 68 | # log "Starting container from: ${SQUISHFILE}" 69 | 70 | echo -e -n "[$(date +%T)] Running $(basename $f)..." 71 | 72 | start_container $SQUISHFILE 73 | START=$(($(date +%s%N)/1000000)) 74 | OUTPUT=$(bash $f) 75 | LAST_STATUS=$? 76 | END=$(($(date +%s%N)/1000000)) 77 | DURATION=$((END - START)) 78 | stop_container 79 | let TOTAL++ 80 | if [ $LAST_STATUS -ne 0 ]; then 81 | echo -e "${RED} FAILED${DEFAULT} (${DURATION}ms)" 82 | echo -e $OUTPUT 83 | echo -e 84 | else 85 | let PASSED++ 86 | echo -e "${GREEN} PASSED${DEFAULT} (${DURATION}ms)" 87 | fi 88 | done 89 | 90 | # Clean up 91 | kill $DAEMON 92 | 93 | if [ $PASSED -ne $TOTAL ]; then 94 | log "${RED}Failed $((TOTAL - PASSED))/$TOTAL tests!$DEFAULT" 95 | exit 1 96 | else 97 | log "${GREEN}Passed $PASSED/$TOTAL tests!$DEFAULT" 98 | fi 99 | -------------------------------------------------------------------------------- /tools/install-hooks: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Pre-commit hook 4 | HOOK="./.git/hooks/pre-commit" 5 | rm $HOOK > /dev/null 6 | echo 'echo ">> Running \`cargo fmt\`..."' >> $HOOK 7 | echo 'git add $(cargo fmt --message-format short)' >> $HOOK 8 | echo 'echo ">> Done!"' >> $HOOK 9 | chmod +x $HOOK 10 | --------------------------------------------------------------------------------